From b2b4239bc95a2c81d9db49416ec4095f8a72d5e2 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Wed, 22 Nov 2023 21:23:46 -0500 Subject: [PATCH] fix(client): add support for streaming binary responses (#866) --- examples/audio.py | 5 ++++- src/openai/_base_client.py | 8 ++++++-- src/openai/_models.py | 2 ++ src/openai/_types.py | 23 +++++++++++++++++++++-- src/openai/resources/audio/speech.py | 20 ++++++++++++++++++-- src/openai/resources/files.py | 20 ++++++++++++++++++-- 6 files changed, 69 insertions(+), 9 deletions(-) diff --git a/examples/audio.py b/examples/audio.py index a5f535dcd6..e86acbf828 100755 --- a/examples/audio.py +++ b/examples/audio.py @@ -13,7 +13,10 @@ def main() -> None: # Create text-to-speech audio file response = openai.audio.speech.create( - model="tts-1", voice="alloy", input="the quick brown fox jumped over the lazy dogs" + model="tts-1", + voice="alloy", + input="the quick brown fox jumped over the lazy dogs", + stream=True, ) response.stream_to_file(speech_file_path) diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index a168301f75..9a023ba961 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_base_client.py @@ -863,7 +863,7 @@ def _request( self._prepare_request(request) try: - response = self._client.send(request, auth=self.custom_auth, stream=stream) + response = self._client.send(request, auth=self.custom_auth, stream=stream or options.stream or False) log.debug( 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase ) @@ -1304,7 +1304,7 @@ async def _request( await self._prepare_request(request) try: - response = await self._client.send(request, auth=self.custom_auth, stream=stream) + response = await self._client.send(request, auth=self.custom_auth, stream=stream or options.stream or False) log.debug( 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase ) @@ -1541,6 +1541,7 @@ def make_request_options( idempotency_key: str | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, post_parser: PostParser | NotGiven = NOT_GIVEN, + stream: bool | None = None, ) -> RequestOptions: """Create a dict of type RequestOptions without keys of NotGiven values.""" options: RequestOptions = {} @@ -1562,6 +1563,9 @@ def make_request_options( if idempotency_key is not None: options["idempotency_key"] = idempotency_key + if stream is not None: + options["stream"] = stream + if is_given(post_parser): # internal options["post_parser"] = post_parser # type: ignore diff --git a/src/openai/_models.py b/src/openai/_models.py index 5b8c96010f..a0e596149c 100644 --- a/src/openai/_models.py +++ b/src/openai/_models.py @@ -403,6 +403,7 @@ class FinalRequestOptionsInput(TypedDict, total=False): params: Query headers: Headers max_retries: int + stream: bool | None timeout: float | Timeout | None files: HttpxRequestFiles | None idempotency_key: str @@ -420,6 +421,7 @@ class FinalRequestOptions(pydantic.BaseModel): timeout: Union[float, Timeout, None, NotGiven] = NotGiven() files: Union[HttpxRequestFiles, None] = None idempotency_key: Union[str, None] = None + stream: Union[bool, None] = None post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() # It should be noted that we cannot use `json` here as that would override diff --git a/src/openai/_types.py b/src/openai/_types.py index 9e962a1078..013b658f0e 100644 --- a/src/openai/_types.py +++ b/src/openai/_types.py @@ -130,7 +130,16 @@ def stream_to_file( chunk_size: int | None = None, ) -> None: """ - Stream the output to the given file. + Stream the output to the given file. NOTE, requires passing `stream=True` + to the request for expected behavior, e.g., + + response = openai.audio.speech.create( + model="tts-1", + voice="alloy", + input="the quick brown fox jumped over the lazy dogs", + stream=True, + ) + response.stream_to_file(speech_file_path) """ pass @@ -185,7 +194,16 @@ async def astream_to_file( chunk_size: int | None = None, ) -> None: """ - Stream the output to the given file. + Stream the output to the given file. NOTE, requires passing `stream=True` + to the request for expected behavior, e.g., + + response = await openai.audio.speech.create( + model="tts-1", + voice="alloy", + input="the quick brown fox jumped over the lazy dogs", + stream=True, + ) + response.stream_to_file(speech_file_path) """ pass @@ -257,6 +275,7 @@ async def aclose(self) -> None: class RequestOptions(TypedDict, total=False): headers: Headers max_retries: int + stream: bool timeout: float | Timeout | None params: Query extra_json: AnyMapping diff --git a/src/openai/resources/audio/speech.py b/src/openai/resources/audio/speech.py index 458843866f..66916d0d50 100644 --- a/src/openai/resources/audio/speech.py +++ b/src/openai/resources/audio/speech.py @@ -41,6 +41,7 @@ def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + stream: bool | None = None, ) -> HttpxBinaryResponseContent: """ Generates audio from the input text. @@ -67,6 +68,9 @@ def create( extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds + + stream: Whether or not the response content should be streamed (i.e. not read to + completion immediately), default False """ return self._post( "/audio/speech", @@ -81,7 +85,11 @@ def create( speech_create_params.SpeechCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + stream=stream, ), cast_to=HttpxBinaryResponseContent, ) @@ -108,6 +116,7 @@ async def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + stream: bool | None = None, ) -> HttpxBinaryResponseContent: """ Generates audio from the input text. @@ -134,6 +143,9 @@ async def create( extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds + + stream: Whether or not the response content should be streamed (i.e. not read to + completion immediately), default False """ return await self._post( "/audio/speech", @@ -148,7 +160,11 @@ async def create( speech_create_params.SpeechCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + stream=stream, ), cast_to=HttpxBinaryResponseContent, ) diff --git a/src/openai/resources/files.py b/src/openai/resources/files.py index a6f75e5a4c..be6eff1e08 100644 --- a/src/openai/resources/files.py +++ b/src/openai/resources/files.py @@ -212,6 +212,7 @@ def content( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + stream: bool | None = None, ) -> HttpxBinaryResponseContent: """ Returns the contents of the specified file. @@ -224,11 +225,18 @@ def content( extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds + + stream: Whether or not the response content should be streamed (i.e. not read to + completion immediately), default False """ return self._get( f"/files/{file_id}/content", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + stream=stream, ), cast_to=HttpxBinaryResponseContent, ) @@ -475,6 +483,7 @@ async def content( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + stream: bool | None = None, ) -> HttpxBinaryResponseContent: """ Returns the contents of the specified file. @@ -487,11 +496,18 @@ async def content( extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds + + stream: Whether or not the response content should be streamed (i.e. not read to + completion immediately), default False """ return await self._get( f"/files/{file_id}/content", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + stream=stream, ), cast_to=HttpxBinaryResponseContent, )