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..fa4d75253f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.26.0" + ".": "2.28.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index bd550123f1..3e6307edaf 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-cb3e4451108eed58d59cff25bf77ec0dc960ec9c6f3dba68f90e7a9847c09d21.yml +openapi_spec_hash: dec6d9be64a5ba8f474a1f2a7a4fafef +config_hash: e922f01e25accd07d8fd3641c37fbd62 diff --git a/CHANGELOG.md b/CHANGELOG.md index c8508a0b1d..dfc7ddca89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,37 @@ # 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) + +### 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..ea82be70c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openai" -version = "2.26.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/_compat.py b/src/openai/_compat.py index 020ffeb2ca..50027c62dc 100644 --- a/src/openai/_compat.py +++ b/src/openai/_compat.py @@ -149,7 +149,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, + by_alias=by_alias if by_alias is not None else True, ) return cast( "dict[str, Any]", diff --git a/src/openai/_version.py b/src/openai/_version.py index 2feccec170..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.26.0" # x-release-please-version +__version__ = "2.28.0" # x-release-please-version 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/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/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/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/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/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_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/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/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/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/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"], 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"