diff --git a/.castiron.stats.yml b/.castiron.stats.yml index 593f552e6d..e5d699ccb5 100644 --- a/.castiron.stats.yml +++ b/.castiron.stats.yml @@ -1,8 +1,8 @@ schema_version: 1 -generation_id: 55efa5f7-488d-441d-9d8a-122049a1529a -openapi_spec_hash: dd4d5f4645d390dcaa730d96ff8256b9 -openapi_transformed_spec_hash: 46069ae7031d4c47bc28d2e565e74465 -config_hash: 18f602826c88bad76c6fdf08ab67247a -codegen_sha: c3ce80b15a62c6073590d3d3dae24c3add7c078d -codegen_hash: 3558f0e3ddec29671f90a9da98e765a85fce601f27cc11fea5a4357490a845fb -public_codegen_sha: 37caadca9e7294870cac83ccc132d1df69d06b31 +generation_id: ec3bb85c-c75f-4b0b-8713-a9377f2068d9 +openapi_spec_hash: 6ffdf0b6c2bafb456b14fda3f7231658 +openapi_transformed_spec_hash: d6d1e240c0615a9349169c2f2d04c388 +config_hash: 84cdcc959ee7140cc9a65e9b642c097b +codegen_sha: 878642b3dcf465c78f575b8e49ebc181b58fc5ce +codegen_hash: 614193cc88f476d3dbfe7d149d8adf21489f235af4454fb1e93628fe43e1e351 +public_codegen_sha: 9a6784d449a729fc0e8d97a4947a8fa858a0041f diff --git a/.release-please-manifest.json b/.release-please-manifest.json index df3292ba24..08ebb64c87 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "3.18.0" + ".": "3.19.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 777b79f3ae..7d2ff2afdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## [3.19.0](https://github.com/openai/openai-python/compare/v3.18.0...v3.19.0) (2026-09-22) + + +### Features + +* **api:** add GCP external storage support ([#3943](https://github.com/openai/openai-python/issues/3943)) ([d12d60f](https://github.com/openai/openai-python/commit/d12d60fff9f5624f2dc50ade3d832028f4fecaa7)) +* **api:** add GPT-Rosalind research model ([#3940](https://github.com/openai/openai-python/issues/3940)) ([d041d73](https://github.com/openai/openai-python/commit/d041d736f75f03f6c87aa77f932823011691bac5)) + + +### Bug Fixes + +* **_utils/_transform:** propagate __api_exclude__ in _async_transform_recursive ([#3324](https://github.com/openai/openai-python/issues/3324)) ([161ae65](https://github.com/openai/openai-python/commit/161ae65ea676a000a156ba0dacfd904532b52473)) +* **api:** handle omission markers in queued WebSocket events ([#3944](https://github.com/openai/openai-python/issues/3944)) ([63e4616](https://github.com/openai/openai-python/commit/63e46169a7c6525ec90c7f2815a05d56e795cbcf)) +* **client:** retry only replayable request content ([#3771](https://github.com/openai/openai-python/issues/3771)) ([6e0c2be](https://github.com/openai/openai-python/commit/6e0c2beeb639e42ff74f14135151d8bcb1e5b245)) +* **client:** tolerate older optional aiohttp installations ([#3941](https://github.com/openai/openai-python/issues/3941)) ([0d91efb](https://github.com/openai/openai-python/commit/0d91efbe5084c2f572688e8e9a05c0a91c8c0319)) +* **helpers:** use asyncio.get_running_loop() inside async methods ([#3289](https://github.com/openai/openai-python/issues/3289)) ([bac3545](https://github.com/openai/openai-python/commit/bac3545eee5a0dfd348a6766bca3e904308792dd)) + ## [3.18.0](https://github.com/openai/openai-python/compare/v3.17.0...v3.18.0) (2026-09-22) diff --git a/api.md b/api.md index fb2f91789f..74b2276e7f 100644 --- a/api.md +++ b/api.md @@ -1510,6 +1510,7 @@ from openai.types.admin.organization import ( AzureExternalStorageProvider, ExternalStorageConfiguration, ExternalStorageDeleted, + GcpExternalStorageProvider, ) ``` diff --git a/api_reference/openapi.transformed.yml b/api_reference/openapi.transformed.yml index daa1172db5..0fdad4550b 100644 --- a/api_reference/openapi.transformed.yml +++ b/api_reference/openapi.transformed.yml @@ -45850,6 +45850,7 @@ components: - gpt-daybreak-blue-latest - gpt-daybreak-red-latest - gpt-5.6-cyber + - gpt-rosalind-research ModelIdsShared: example: gpt-6-astra anyOf: @@ -70943,12 +70944,42 @@ components: - account_name - container - region + GcpExternalStorageProviderResponse: + properties: + type: + type: string + enum: + - gcp + default: gcp + x-stainless-const: true + bucket: + type: string + workload_identity_project_number: + type: string + workload_identity_pool_id: + type: string + workload_identity_provider_id: + type: string + region: + type: string + audience: + type: string + type: object + required: + - type + - bucket + - workload_identity_project_number + - workload_identity_pool_id + - workload_identity_provider_id + - region + - audience provider: discriminator: propertyName: type anyOf: - $ref: '#/components/schemas/AwsExternalStorageProviderResponse' - $ref: '#/components/schemas/AzureExternalStorageProviderResponse' + - $ref: '#/components/schemas/GcpExternalStorageProviderResponse' PublicProjectResidency: type: string enum: @@ -71570,6 +71601,7 @@ components: anyOf: - $ref: '#/components/schemas/AwsExternalStorageProviderResponse' - $ref: '#/components/schemas/AzureExternalStorageProviderResponse' + - $ref: '#/components/schemas/GcpExternalStorageProviderResponse' geography: type: string status: @@ -71658,6 +71690,29 @@ components: - resource_group - account_name - container + GcpExternalStorageProviderParams: + properties: + type: + type: string + enum: + - gcp + default: gcp + x-stainless-const: true + bucket: + type: string + workload_identity_project_number: + type: string + workload_identity_pool_id: + type: string + workload_identity_provider_id: + type: string + type: object + required: + - type + - bucket + - workload_identity_project_number + - workload_identity_pool_id + - workload_identity_provider_id CreateExternalStorageBody: properties: project_id: @@ -71669,6 +71724,7 @@ components: anyOf: - $ref: '#/components/schemas/AwsExternalStorageProviderParams' - $ref: '#/components/schemas/AzureExternalStorageProviderParams' + - $ref: '#/components/schemas/GcpExternalStorageProviderParams' type: object required: - project_id @@ -72065,6 +72121,13 @@ components: message: type: string description: A human-readable description of the error that was returned. + headers: + additionalProperties: + type: string + description: The retry header value. + type: object + description: The Retry-After and Retry-After-Ms headers returned with the original error, if any. + x-oaiTypeLabel: map misalignment: $ref: '#/components/schemas/MisalignmentErrorDetailsResource' type: object @@ -73939,43 +74002,14 @@ components: - disconnected - expired - failed + x-enumDescriptions: + - '' + - '' + - '' + - '' + - '' description: The public lifecycle status of an execution environment. - HostedEnvironmentFileResourceFileId: - type: object - properties: - type: - type: string - enum: - - file_id - default: file_id - x-stainless-const: true - description: The type of the object. Always `file_id`. - id: - type: string - minLength: 0 - description: The session-scoped ID of the file in the execution environment. - file_id: - type: string - minLength: 0 - description: The ID of the uploaded file. - path: - type: string - minLength: 0 - description: The file's absolute path inside the environment. - size_bytes: - type: integer - format: int64 - minimum: 0 - description: The decoded file size in bytes. - required: - - type - - id - - file_id - - path - - size_bytes - additionalProperties: false - description: A file copied from the OpenAI Files API. - HostedEnvironmentFileResourceInline: + HostedPluginResourceInline: type: object properties: type: @@ -73985,39 +74019,30 @@ components: default: inline x-stainless-const: true description: The type of the object. Always `inline`. - id: + name: type: string minLength: 0 - description: The session-scoped ID of the file in the execution environment. - path: + description: The installed plugin name. + description: type: string minLength: 0 - description: The file's absolute path inside the environment. - size_bytes: - type: integer - format: int64 - minimum: 0 - description: The decoded file size in bytes. + description: The installed plugin description. required: - type - - id - - path - - size_bytes + - name + - description additionalProperties: false - description: A file supplied inline when the session was created. - HostedEnvironmentFileResource: - description: Metadata for a file materialized in an OpenAI-hosted execution environment. + description: A plugin installed from an inline ZIP archive. + HostedPluginResource: + description: A plugin installed in an OpenAI-hosted environment. discriminator: propertyName: type mapping: - file_id: '#/components/schemas/HostedEnvironmentFileResourceFileId' - inline: '#/components/schemas/HostedEnvironmentFileResourceInline' + inline: '#/components/schemas/HostedPluginResourceInline' x-oai-discriminator-values: - - file_id - inline anyOf: - - $ref: '#/components/schemas/HostedEnvironmentFileResourceFileId' - - $ref: '#/components/schemas/HostedEnvironmentFileResourceInline' + - $ref: '#/components/schemas/HostedPluginResourceInline' HostedSkillResourceSkillReference: type: object properties: @@ -74089,7 +74114,42 @@ components: anyOf: - $ref: '#/components/schemas/HostedSkillResourceSkillReference' - $ref: '#/components/schemas/HostedSkillResourceInline' - HostedPluginResourceInline: + HostedEnvironmentFileResourceFileId: + type: object + properties: + type: + type: string + enum: + - file_id + default: file_id + x-stainless-const: true + description: The type of the object. Always `file_id`. + id: + type: string + minLength: 0 + description: The session-scoped ID of the file in the execution environment. + file_id: + type: string + minLength: 0 + description: The ID of the uploaded file. + path: + type: string + minLength: 0 + description: The file's absolute path inside the environment. + size_bytes: + type: integer + format: int64 + minimum: 0 + description: The decoded file size in bytes. + required: + - type + - id + - file_id + - path + - size_bytes + additionalProperties: false + description: A file copied from the OpenAI Files API. + HostedEnvironmentFileResourceInline: type: object properties: type: @@ -74099,30 +74159,39 @@ components: default: inline x-stainless-const: true description: The type of the object. Always `inline`. - name: + id: type: string minLength: 0 - description: The installed plugin name. - description: + description: The session-scoped ID of the file in the execution environment. + path: type: string minLength: 0 - description: The installed plugin description. + description: The file's absolute path inside the environment. + size_bytes: + type: integer + format: int64 + minimum: 0 + description: The decoded file size in bytes. required: - type - - name - - description + - id + - path + - size_bytes additionalProperties: false - description: A plugin installed from an inline ZIP archive. - HostedPluginResource: - description: A plugin installed in an OpenAI-hosted environment. + description: A file supplied inline when the session was created. + HostedEnvironmentFileResource: + description: Metadata for a file materialized in an OpenAI-hosted execution environment. discriminator: propertyName: type mapping: - inline: '#/components/schemas/HostedPluginResourceInline' + file_id: '#/components/schemas/HostedEnvironmentFileResourceFileId' + inline: '#/components/schemas/HostedEnvironmentFileResourceInline' x-oai-discriminator-values: + - file_id - inline anyOf: - - $ref: '#/components/schemas/HostedPluginResourceInline' + - $ref: '#/components/schemas/HostedEnvironmentFileResourceFileId' + - $ref: '#/components/schemas/HostedEnvironmentFileResourceInline' PublicEnvironmentResource: type: object properties: @@ -74143,13 +74212,13 @@ components: status: $ref: '#/components/schemas/EnvironmentStatusResource' description: The current environment connection status. - files: + plugins: type: array items: - $ref: '#/components/schemas/HostedEnvironmentFileResource' + $ref: '#/components/schemas/HostedPluginResource' minItems: 0 maxItems: 2000 - description: Files installed in the environment, without their contents. + description: Plugins installed in the environment, without their archive contents. skills: type: array items: @@ -74157,13 +74226,13 @@ components: minItems: 0 maxItems: 2000 description: Skills installed in the environment, without their archive contents. - plugins: + files: type: array items: - $ref: '#/components/schemas/HostedPluginResource' + $ref: '#/components/schemas/HostedEnvironmentFileResource' minItems: 0 maxItems: 2000 - description: Plugins installed in the environment, without their archive contents. + description: Files installed in the environment, without their contents. required: - id - object @@ -78275,11 +78344,11 @@ components: maxItems: 2000 description: Actions that must be completed before the session can continue. error: + description: The error that caused the session to fail, if any. type: - string - 'null' minLength: 0 - description: The error that caused the session to fail, if any. agent: $ref: '#/components/schemas/SessionAgentResource' description: The agent running in the session. @@ -78820,7 +78889,7 @@ components: required: - type additionalProperties: false - description: An OpenAI-hosted environment, optionally based on a reusable template. + description: An existing OpenAI-hosted environment or new inline/template-based hosted configuration. EnvironmentParamSelfHosted: type: object properties: @@ -89136,6 +89205,7 @@ components: - gpt-daybreak-blue-latest - gpt-daybreak-red-latest - gpt-5.6-cyber + - gpt-rosalind-research BetaModelIdsShared: example: gpt-6-astra anyOf: diff --git a/pyproject.toml b/pyproject.toml index 49b53b0b8b..9b06ea0c2b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "3.18.0" +version = "3.19.0" description = "The official Python library for the openai API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/openai/_utils/_transform.py b/src/openai/_utils/_transform.py index 304fd12ffe..9f1e66ebb5 100644 --- a/src/openai/_utils/_transform.py +++ b/src/openai/_utils/_transform.py @@ -387,7 +387,7 @@ async def _async_transform_recursive( return data if isinstance(data, pydantic.BaseModel): - return model_dump(data, exclude_unset=True, mode="json") + return model_dump(data, exclude_unset=True, mode="json", exclude=getattr(data, "__api_exclude__", None)) annotated_type = _get_annotated_type(annotation) if annotated_type is None: diff --git a/src/openai/_vendor/httpx_aiohttp/FORK.md b/src/openai/_vendor/httpx_aiohttp/FORK.md index d66699bce0..2c7117dc81 100644 --- a/src/openai/_vendor/httpx_aiohttp/FORK.md +++ b/src/openai/_vendor/httpx_aiohttp/FORK.md @@ -8,6 +8,13 @@ This directory vendors the HTTPX2-specific adapter from httpx-aiohttp 0.2.0. - License: BSD 3-Clause; the original license is preserved in `LICENSE`. - The upstream README is preserved without modification in `README.md`. -The upstream distribution always installs and imports legacy HTTPX even when only its HTTPX2 adapter is used. Vendoring the three HTTPX2-specific modules lets `openai[aiohttp]` depend only on HTTPX2 and aiohttp while preserving the upstream transport behavior. No functional changes have been made to the upstream adapter. +The upstream distribution always installs and imports legacy HTTPX even when only its HTTPX2 adapter is used. Vendoring the three HTTPX2-specific modules lets `openai[aiohttp]` depend only on HTTPX2 and aiohttp while preserving the upstream transport behavior. + +Local changes: + +- Import `SocketTimeoutError` explicitly. aiohttp before 3.10 does not export + this exception, so importing the adapter raises `ImportError` and the SDK + keeps its default HTTPX2 clients available. This does not enable the aiohttp + adapter on older versions or lower the `openai[aiohttp]` dependency minimum. The fork may be removed when upstream provides an HTTPX2-only distribution or makes legacy HTTPX genuinely optional. diff --git a/src/openai/_vendor/httpx_aiohttp/transport.py b/src/openai/_vendor/httpx_aiohttp/transport.py index 263bfe106d..27f6b5e346 100644 --- a/src/openai/_vendor/httpx_aiohttp/transport.py +++ b/src/openai/_vendor/httpx_aiohttp/transport.py @@ -9,12 +9,12 @@ import aiohttp import httpx2 as httpx -from aiohttp import BasicAuth, ClientTimeout +from aiohttp import BasicAuth, ClientTimeout, SocketTimeoutError from aiohttp.client import ClientResponse, ClientSession AIOHTTP_EXC_MAP = { aiohttp.ServerTimeoutError: httpx.TimeoutException, - aiohttp.SocketTimeoutError: httpx.ReadTimeout, + SocketTimeoutError: httpx.ReadTimeout, aiohttp.ClientConnectionError: httpx.ConnectTimeout, aiohttp.ClientConnectorError: httpx.ConnectError, aiohttp.ClientPayloadError: httpx.ReadError, diff --git a/src/openai/_version.py b/src/openai/_version.py index bbbb06e3ca..6dbc2a2d6c 100644 --- a/src/openai/_version.py +++ b/src/openai/_version.py @@ -1,2 +1,2 @@ __title__ = "openai" -__version__ = "3.18.0" # x-release-please-version +__version__ = "3.19.0" # x-release-please-version diff --git a/src/openai/helpers/local_audio_player.py b/src/openai/helpers/local_audio_player.py index 8f12c27a56..8d8cb3d340 100644 --- a/src/openai/helpers/local_audio_player.py +++ b/src/openai/helpers/local_audio_player.py @@ -71,7 +71,7 @@ async def play( else: audio_content = await self._tts_response_to_buffer(input) - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() event = asyncio.Event() idx = 0 @@ -105,7 +105,7 @@ async def play_stream( self, buffer_stream: AsyncGenerator[Union[npt.NDArray[np.float32], npt.NDArray[np.int16], None], None], ) -> None: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() event = asyncio.Event() buffer_queue: queue.Queue[Union[npt.NDArray[np.float32], npt.NDArray[np.int16], None]] = queue.Queue(maxsize=50) diff --git a/src/openai/helpers/microphone.py b/src/openai/helpers/microphone.py index 62a6d8d8a9..5a1e8ae87d 100644 --- a/src/openai/helpers/microphone.py +++ b/src/openai/helpers/microphone.py @@ -54,7 +54,7 @@ async def record(self, return_ndarray: Literal[False]) -> FileTypes: ... async def record(self, return_ndarray: None = ...) -> FileTypes: ... async def record(self, return_ndarray: Union[bool, None] = False) -> Union[npt.NDArray[DType], FileTypes]: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() event = asyncio.Event() self.buffer_chunks: list[npt.NDArray[DType]] = [] start_time = time.perf_counter() diff --git a/src/openai/resources/beta/responses/responses.py b/src/openai/resources/beta/responses/responses.py index 0a47ebb47d..7bccb4cbad 100644 --- a/src/openai/resources/beta/responses/responses.py +++ b/src/openai/resources/beta/responses/responses.py @@ -230,6 +230,7 @@ def create( "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], str, ] @@ -636,6 +637,7 @@ def create( "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], str, ] @@ -1041,6 +1043,7 @@ def create( "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], str, ] @@ -1444,6 +1447,7 @@ def create( "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], str, ] @@ -1930,6 +1934,7 @@ def compact( "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], str, None, @@ -2227,6 +2232,7 @@ async def create( "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], str, ] @@ -2633,6 +2639,7 @@ async def create( "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], str, ] @@ -3038,6 +3045,7 @@ async def create( "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], str, ] @@ -3441,6 +3449,7 @@ async def create( "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], str, ] @@ -3927,6 +3936,7 @@ async def compact( "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], str, None, @@ -4551,7 +4561,7 @@ def send(self, event: BetaResponsesClientEvent | BetaResponsesClientEventParam) data = ( event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) if isinstance(event, BaseModel) - else json.dumps(event) + else json.dumps(maybe_transform(event, BetaResponsesClientEventParam)) ) self.__send_queue.enqueue(data) @@ -5037,7 +5047,7 @@ def send(self, event: BetaResponsesClientEvent | BetaResponsesClientEventParam) data = ( event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) if isinstance(event, BaseModel) - else json.dumps(event) + else json.dumps(maybe_transform(event, BetaResponsesClientEventParam)) ) self.__send_queue.enqueue(data) @@ -5304,6 +5314,7 @@ def create( "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], str, ] @@ -5557,6 +5568,7 @@ async def create( "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], str, ] diff --git a/src/openai/resources/live/forks.py b/src/openai/resources/live/forks.py index 75e0672bac..9c1a243238 100644 --- a/src/openai/resources/live/forks.py +++ b/src/openai/resources/live/forks.py @@ -465,7 +465,7 @@ def send(self, event: ForkClientEvent | ForkClientEventParam) -> None: data = ( event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) if isinstance(event, BaseModel) - else json.dumps(event) + else json.dumps(maybe_transform(event, ForkClientEventParam)) ) self.__send_queue.enqueue(data) @@ -952,7 +952,7 @@ def send(self, event: ForkClientEvent | ForkClientEventParam) -> None: data = ( event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) if isinstance(event, BaseModel) - else json.dumps(event) + else json.dumps(maybe_transform(event, ForkClientEventParam)) ) self.__send_queue.enqueue(data) diff --git a/src/openai/resources/live/live.py b/src/openai/resources/live/live.py index 2d0ca709df..36c08a6e30 100644 --- a/src/openai/resources/live/live.py +++ b/src/openai/resources/live/live.py @@ -684,7 +684,7 @@ def send(self, event: ClientEvent | ClientEventParam) -> None: data = ( event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) if isinstance(event, BaseModel) - else json.dumps(event) + else json.dumps(maybe_transform(event, ClientEventParam)) ) self.__send_queue.enqueue(data) @@ -1166,7 +1166,7 @@ def send(self, event: ClientEvent | ClientEventParam) -> None: data = ( event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) if isinstance(event, BaseModel) - else json.dumps(event) + else json.dumps(maybe_transform(event, ClientEventParam)) ) self.__send_queue.enqueue(data) diff --git a/src/openai/resources/live/sideband.py b/src/openai/resources/live/sideband.py index e644fba8c1..e15a836139 100644 --- a/src/openai/resources/live/sideband.py +++ b/src/openai/resources/live/sideband.py @@ -472,7 +472,7 @@ def send(self, event: ConnectClientEvent | ConnectClientEventParam) -> None: data = ( event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) if isinstance(event, BaseModel) - else json.dumps(event) + else json.dumps(maybe_transform(event, ConnectClientEventParam)) ) self.__send_queue.enqueue(data) @@ -964,7 +964,7 @@ def send(self, event: ConnectClientEvent | ConnectClientEventParam) -> None: data = ( event.to_json(use_api_names=True, exclude_defaults=True, exclude_unset=True) if isinstance(event, BaseModel) - else json.dumps(event) + else json.dumps(maybe_transform(event, ConnectClientEventParam)) ) self.__send_queue.enqueue(data) diff --git a/src/openai/resources/realtime/realtime.py b/src/openai/resources/realtime/realtime.py index 0cd4cfbdd0..fab966898e 100644 --- a/src/openai/resources/realtime/realtime.py +++ b/src/openai/resources/realtime/realtime.py @@ -627,7 +627,7 @@ 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(event) + else json.dumps(maybe_transform(event, RealtimeClientEventParam)) ) self.__send_queue.enqueue(data) @@ -1138,7 +1138,7 @@ 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(event) + else json.dumps(maybe_transform(event, RealtimeClientEventParam)) ) self.__send_queue.enqueue(data) diff --git a/src/openai/resources/responses/responses.py b/src/openai/resources/responses/responses.py index 4d587f7884..5fb62c3740 100644 --- a/src/openai/resources/responses/responses.py +++ b/src/openai/resources/responses/responses.py @@ -1854,6 +1854,7 @@ def compact( "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], str, None, @@ -3743,6 +3744,7 @@ async def compact( "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], str, None, @@ -4406,7 +4408,7 @@ 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(event) + else json.dumps(maybe_transform(event, ResponsesClientEventParam)) ) self.__send_queue.enqueue(data) @@ -4904,7 +4906,7 @@ 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(event) + else json.dumps(maybe_transform(event, ResponsesClientEventParam)) ) self.__send_queue.enqueue(data) diff --git a/src/openai/types/admin/organization/__init__.py b/src/openai/types/admin/organization/__init__.py index 20101b841e..d2be7183e8 100644 --- a/src/openai/types/admin/organization/__init__.py +++ b/src/openai/types/admin/organization/__init__.py @@ -68,6 +68,7 @@ from .aws_external_storage_provider import AwsExternalStorageProvider as AwsExternalStorageProvider from .certificate_activate_response import CertificateActivateResponse as CertificateActivateResponse from .certificate_deactivate_params import CertificateDeactivateParams as CertificateDeactivateParams +from .gcp_external_storage_provider import GcpExternalStorageProvider as GcpExternalStorageProvider from .usage_audio_speeches_response import UsageAudioSpeechesResponse as UsageAudioSpeechesResponse from .usage_web_search_calls_params import UsageWebSearchCallsParams as UsageWebSearchCallsParams from .external_storage_configuration import ExternalStorageConfiguration as ExternalStorageConfiguration 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 c7406ed84f..a0686e4255 100644 --- a/src/openai/types/admin/organization/audit_log_list_response.py +++ b/src/openai/types/admin/organization/audit_log_list_response.py @@ -8,6 +8,7 @@ from ...._utils import PropertyInfo from ...._models import BaseModel from .aws_external_storage_provider import AwsExternalStorageProvider +from .gcp_external_storage_provider import GcpExternalStorageProvider from .azure_external_storage_provider import AzureExternalStorageProvider __all__ = [ @@ -313,7 +314,8 @@ class ExternalKeyRemoved(BaseModel): ExternalStorageRegisteredDataProvider: TypeAlias = Annotated[ - Union[AwsExternalStorageProvider, AzureExternalStorageProvider], PropertyInfo(discriminator="type") + Union[AwsExternalStorageProvider, AzureExternalStorageProvider, GcpExternalStorageProvider], + PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/admin/organization/external_storage_configuration.py b/src/openai/types/admin/organization/external_storage_configuration.py index f5ad1e56d7..afb027b8bb 100644 --- a/src/openai/types/admin/organization/external_storage_configuration.py +++ b/src/openai/types/admin/organization/external_storage_configuration.py @@ -6,12 +6,14 @@ from ...._utils import PropertyInfo from ...._models import BaseModel from .aws_external_storage_provider import AwsExternalStorageProvider +from .gcp_external_storage_provider import GcpExternalStorageProvider from .azure_external_storage_provider import AzureExternalStorageProvider __all__ = ["ExternalStorageConfiguration", "Provider"] Provider: TypeAlias = Annotated[ - Union[AwsExternalStorageProvider, AzureExternalStorageProvider], PropertyInfo(discriminator="type") + Union[AwsExternalStorageProvider, AzureExternalStorageProvider, GcpExternalStorageProvider], + PropertyInfo(discriminator="type"), ] diff --git a/src/openai/types/admin/organization/external_storage_create_params.py b/src/openai/types/admin/organization/external_storage_create_params.py index f459e588b0..41b0514d6b 100644 --- a/src/openai/types/admin/organization/external_storage_create_params.py +++ b/src/openai/types/admin/organization/external_storage_create_params.py @@ -5,7 +5,7 @@ from typing import Union from typing_extensions import Literal, Required, TypeAlias, TypedDict -__all__ = ["ExternalStorageCreateParams", "Provider", "ProviderAws", "ProviderAzure"] +__all__ = ["ExternalStorageCreateParams", "Provider", "ProviderAws", "ProviderAzure", "ProviderGcp"] class ExternalStorageCreateParams(TypedDict, total=False): @@ -36,4 +36,16 @@ class ProviderAzure(TypedDict, total=False): type: Required[Literal["azure"]] -Provider: TypeAlias = Union[ProviderAws, ProviderAzure] +class ProviderGcp(TypedDict, total=False): + bucket: Required[str] + + type: Required[Literal["gcp"]] + + workload_identity_pool_id: Required[str] + + workload_identity_project_number: Required[str] + + workload_identity_provider_id: Required[str] + + +Provider: TypeAlias = Union[ProviderAws, ProviderAzure, ProviderGcp] diff --git a/src/openai/types/admin/organization/gcp_external_storage_provider.py b/src/openai/types/admin/organization/gcp_external_storage_provider.py new file mode 100644 index 0000000000..0b6c5ff96f --- /dev/null +++ b/src/openai/types/admin/organization/gcp_external_storage_provider.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["GcpExternalStorageProvider"] + + +class GcpExternalStorageProvider(BaseModel): + audience: str + + bucket: str + + region: str + + type: Literal["gcp"] + + workload_identity_pool_id: str + + workload_identity_project_number: str + + workload_identity_provider_id: str diff --git a/src/openai/types/beta/beta_response.py b/src/openai/types/beta/beta_response.py index 6089aee9af..f983478749 100644 --- a/src/openai/types/beta/beta_response.py +++ b/src/openai/types/beta/beta_response.py @@ -447,6 +447,7 @@ class BetaResponse(BaseModel): "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], str, ] diff --git a/src/openai/types/beta/beta_responses_client_event.py b/src/openai/types/beta/beta_responses_client_event.py index 6d4815ea95..2917eba07c 100644 --- a/src/openai/types/beta/beta_responses_client_event.py +++ b/src/openai/types/beta/beta_responses_client_event.py @@ -435,6 +435,7 @@ class ResponseCreate(BaseModel): "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], str, None, 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 53a97177a8..20699a8b53 100644 --- a/src/openai/types/beta/beta_responses_client_event_param.py +++ b/src/openai/types/beta/beta_responses_client_event_param.py @@ -435,6 +435,7 @@ class ResponseCreate(TypedDict, total=False): "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], str, ] diff --git a/src/openai/types/beta/environment_param.py b/src/openai/types/beta/environment_param.py index 6eb5f3bc82..c6ab930bb1 100644 --- a/src/openai/types/beta/environment_param.py +++ b/src/openai/types/beta/environment_param.py @@ -60,7 +60,9 @@ class EnvironmentParamOpenAIHostedPackages(TypedDict, total=False): class EnvironmentParamOpenAIHosted(TypedDict, total=False): - """An OpenAI-hosted environment, optionally based on a reusable template.""" + """ + An existing OpenAI-hosted environment or new inline/template-based hosted configuration. + """ type: Required[Literal["openai_hosted"]] """The type of the object. Always `openai_hosted`.""" diff --git a/src/openai/types/beta/response_compact_params.py b/src/openai/types/beta/response_compact_params.py index 3586df2143..583efef3be 100644 --- a/src/openai/types/beta/response_compact_params.py +++ b/src/openai/types/beta/response_compact_params.py @@ -122,6 +122,7 @@ class ResponseCompactParams(TypedDict, total=False): "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], str, None, diff --git a/src/openai/types/beta/response_create_params.py b/src/openai/types/beta/response_create_params.py index 4d75cb93aa..3b447f04ec 100644 --- a/src/openai/types/beta/response_create_params.py +++ b/src/openai/types/beta/response_create_params.py @@ -235,6 +235,7 @@ class ResponseCreateParamsBase(TypedDict, total=False): "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], str, ] diff --git a/src/openai/types/responses/response_compact_params.py b/src/openai/types/responses/response_compact_params.py index c881d4b6b1..8fccf637c6 100644 --- a/src/openai/types/responses/response_compact_params.py +++ b/src/openai/types/responses/response_compact_params.py @@ -121,6 +121,7 @@ class ResponseCompactParams(TypedDict, total=False): "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], str, None, diff --git a/src/openai/types/shared/all_models.py b/src/openai/types/shared/all_models.py index 8db004bcf5..f702f8a8f9 100644 --- a/src/openai/types/shared/all_models.py +++ b/src/openai/types/shared/all_models.py @@ -30,5 +30,6 @@ "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], ] diff --git a/src/openai/types/shared/responses_model.py b/src/openai/types/shared/responses_model.py index 460087a867..efbbb06033 100644 --- a/src/openai/types/shared/responses_model.py +++ b/src/openai/types/shared/responses_model.py @@ -30,5 +30,6 @@ "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], ] diff --git a/src/openai/types/shared_params/responses_model.py b/src/openai/types/shared_params/responses_model.py index 19e301efc7..3c9f91cca1 100644 --- a/src/openai/types/shared_params/responses_model.py +++ b/src/openai/types/shared_params/responses_model.py @@ -32,5 +32,6 @@ "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest", "gpt-5.6-cyber", + "gpt-rosalind-research", ], ] diff --git a/src/openai/types/video_create_error.py b/src/openai/types/video_create_error.py index 96c5e44025..556e95463a 100644 --- a/src/openai/types/video_create_error.py +++ b/src/openai/types/video_create_error.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Castiron. See CONTRIBUTING.md for details. -from typing import Union, Optional +from typing import Dict, Union, Optional from typing_extensions import Literal from .._models import BaseModel @@ -44,4 +44,10 @@ class VideoCreateError(BaseModel): message: str """A human-readable description of the error that was returned.""" + headers: Optional[Dict[str, str]] = None + """ + The Retry-After and Retry-After-Ms headers returned with the original error, if + any. + """ + misalignment: Optional[Misalignment] = None diff --git a/tests/test_external_storage.py b/tests/test_external_storage.py index 5070d20af2..c5551dc6ab 100644 --- a/tests/test_external_storage.py +++ b/tests/test_external_storage.py @@ -9,7 +9,7 @@ from openai import OpenAI, AsyncOpenAI from tests.respx2 import MockRouter from tests.respx2.models import Call -from openai.types.admin.organization import ExternalStorageConfiguration +from openai.types.admin.organization import GcpExternalStorageProvider, ExternalStorageConfiguration from openai.types.admin.organization.external_storage_create_params import Provider BASE_URL = "https://example.com/v1" @@ -46,6 +46,25 @@ "future_provider_field": {"retained": True}, }, ), + ( + { + "type": "gcp", + "bucket": "test-bucket", + "workload_identity_pool_id": "test-pool", + "workload_identity_project_number": "000000000000", + "workload_identity_provider_id": "test-provider", + }, + { + "type": "gcp", + "bucket": "test-bucket", + "workload_identity_pool_id": "test-pool", + "workload_identity_project_number": "000000000000", + "workload_identity_provider_id": "test-provider", + "audience": "test-audience", + "region": "us-central1", + "future_provider_field": {"retained": True}, + }, + ), ] @@ -63,7 +82,7 @@ def configuration(provider: dict[str, object], storage_id: str = "extstorage_tes @pytest.mark.respx2(base_url=BASE_URL) -@pytest.mark.parametrize("provider,response_provider", PROVIDERS, ids=["aws", "azure"]) +@pytest.mark.parametrize("provider,response_provider", PROVIDERS, ids=["aws", "azure", "gcp"]) def test_sync_external_storage_provider_and_admin_auth( provider: Provider, response_provider: dict[str, object], respx2_mock: MockRouter ) -> None: @@ -78,6 +97,8 @@ def test_sync_external_storage_provider_and_admin_auth( result = client.admin.organization.external_storage.create(project_id="proj_test", provider=provider) assert isinstance(result, ExternalStorageConfiguration) + if provider["type"] == "gcp": + assert isinstance(result.provider, GcpExternalStorageProvider) assert result.to_dict() == payload request = route.calls.last.request assert request.headers["Authorization"] == "Bearer test-admin-key" @@ -85,7 +106,7 @@ def test_sync_external_storage_provider_and_admin_auth( @pytest.mark.respx2(base_url=BASE_URL) -@pytest.mark.parametrize("provider,response_provider", PROVIDERS, ids=["aws", "azure"]) +@pytest.mark.parametrize("provider,response_provider", PROVIDERS, ids=["aws", "azure", "gcp"]) async def test_async_external_storage_provider_and_admin_auth( provider: Provider, response_provider: dict[str, object], respx2_mock: MockRouter ) -> None: @@ -100,6 +121,8 @@ async def test_async_external_storage_provider_and_admin_auth( result = await client.admin.organization.external_storage.create(project_id="proj_test", provider=provider) assert isinstance(result, ExternalStorageConfiguration) + if provider["type"] == "gcp": + assert isinstance(result.provider, GcpExternalStorageProvider) assert result.to_dict() == payload request = route.calls.last.request assert request.headers["Authorization"] == "Bearer test-admin-key" diff --git a/tests/test_transform.py b/tests/test_transform.py index 93f7ad8dd8..69a5549d9b 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -357,6 +357,21 @@ async def test_pydantic_default_field(use_async: bool) -> None: assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": "bar", "with_str_default": "baz"} +class ModelWithApiExcludedField(BaseModel): + foo: str + client_only: str + + __api_exclude__ = {"client_only"} + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_model_api_exclude(use_async: bool) -> None: + model = ModelWithApiExcludedField(foo="hello!", client_only="local") + assert cast(Any, await transform(model, Any, use_async)) == {"foo": "hello!"} + assert cast(Any, await transform([model], List[ModelWithApiExcludedField], use_async)) == [{"foo": "hello!"}] + + class TypedDictIterableUnion(TypedDict): foo: Annotated[Union[Bar8, Iterable[Baz8]], PropertyInfo(alias="FOO")] diff --git a/uv.lock b/uv.lock index 6abb04281c..fc908a349a 100644 --- a/uv.lock +++ b/uv.lock @@ -1535,7 +1535,7 @@ wheels = [ [[package]] name = "openai" -version = "3.18.0" # x-release-please-version +version = "3.19.0" # x-release-please-version source = { editable = "." } dependencies = [ { name = "anyio" },