Skip to content

Commit 2470d25

Browse files
committed
fix(client): add support for streaming binary responses (#866)
1 parent af67cfa commit 2470d25

File tree

6 files changed

+69
-9
lines changed

6 files changed

+69
-9
lines changed

Diff for: examples/audio.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
def main() -> None:
1414
# Create text-to-speech audio file
1515
response = openai.audio.speech.create(
16-
model="tts-1", voice="alloy", input="the quick brown fox jumped over the lazy dogs"
16+
model="tts-1",
17+
voice="alloy",
18+
input="the quick brown fox jumped over the lazy dogs",
19+
stream=True,
1720
)
1821

1922
response.stream_to_file(speech_file_path)

Diff for: src/openai/_base_client.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -863,7 +863,7 @@ def _request(
863863
self._prepare_request(request)
864864

865865
try:
866-
response = self._client.send(request, auth=self.custom_auth, stream=stream)
866+
response = self._client.send(request, auth=self.custom_auth, stream=stream or options.stream or False)
867867
log.debug(
868868
'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase
869869
)
@@ -1304,7 +1304,7 @@ async def _request(
13041304
await self._prepare_request(request)
13051305

13061306
try:
1307-
response = await self._client.send(request, auth=self.custom_auth, stream=stream)
1307+
response = await self._client.send(request, auth=self.custom_auth, stream=stream or options.stream or False)
13081308
log.debug(
13091309
'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase
13101310
)
@@ -1541,6 +1541,7 @@ def make_request_options(
15411541
idempotency_key: str | None = None,
15421542
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
15431543
post_parser: PostParser | NotGiven = NOT_GIVEN,
1544+
stream: bool | None = None,
15441545
) -> RequestOptions:
15451546
"""Create a dict of type RequestOptions without keys of NotGiven values."""
15461547
options: RequestOptions = {}
@@ -1562,6 +1563,9 @@ def make_request_options(
15621563
if idempotency_key is not None:
15631564
options["idempotency_key"] = idempotency_key
15641565

1566+
if stream is not None:
1567+
options["stream"] = stream
1568+
15651569
if is_given(post_parser):
15661570
# internal
15671571
options["post_parser"] = post_parser # type: ignore

Diff for: src/openai/_models.py

+2
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ class FinalRequestOptionsInput(TypedDict, total=False):
403403
params: Query
404404
headers: Headers
405405
max_retries: int
406+
stream: bool | None
406407
timeout: float | Timeout | None
407408
files: HttpxRequestFiles | None
408409
idempotency_key: str
@@ -420,6 +421,7 @@ class FinalRequestOptions(pydantic.BaseModel):
420421
timeout: Union[float, Timeout, None, NotGiven] = NotGiven()
421422
files: Union[HttpxRequestFiles, None] = None
422423
idempotency_key: Union[str, None] = None
424+
stream: Union[bool, None] = None
423425
post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven()
424426

425427
# It should be noted that we cannot use `json` here as that would override

Diff for: src/openai/_types.py

+21-2
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,16 @@ def stream_to_file(
130130
chunk_size: int | None = None,
131131
) -> None:
132132
"""
133-
Stream the output to the given file.
133+
Stream the output to the given file. NOTE, requires passing `stream=True`
134+
to the request for expected behavior, e.g.,
135+
136+
response = openai.audio.speech.create(
137+
model="tts-1",
138+
voice="alloy",
139+
input="the quick brown fox jumped over the lazy dogs",
140+
stream=True,
141+
)
142+
response.stream_to_file(speech_file_path)
134143
"""
135144
pass
136145

@@ -185,7 +194,16 @@ async def astream_to_file(
185194
chunk_size: int | None = None,
186195
) -> None:
187196
"""
188-
Stream the output to the given file.
197+
Stream the output to the given file. NOTE, requires passing `stream=True`
198+
to the request for expected behavior, e.g.,
199+
200+
response = await openai.audio.speech.create(
201+
model="tts-1",
202+
voice="alloy",
203+
input="the quick brown fox jumped over the lazy dogs",
204+
stream=True,
205+
)
206+
response.stream_to_file(speech_file_path)
189207
"""
190208
pass
191209

@@ -257,6 +275,7 @@ async def aclose(self) -> None:
257275
class RequestOptions(TypedDict, total=False):
258276
headers: Headers
259277
max_retries: int
278+
stream: bool
260279
timeout: float | Timeout | None
261280
params: Query
262281
extra_json: AnyMapping

Diff for: src/openai/resources/audio/speech.py

+18-2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def create(
4141
extra_query: Query | None = None,
4242
extra_body: Body | None = None,
4343
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
44+
stream: bool | None = None,
4445
) -> HttpxBinaryResponseContent:
4546
"""
4647
Generates audio from the input text.
@@ -67,6 +68,9 @@ def create(
6768
extra_body: Add additional JSON properties to the request
6869
6970
timeout: Override the client-level default timeout for this request, in seconds
71+
72+
stream: Whether or not the response content should be streamed (i.e. not read to
73+
completion immediately), default False
7074
"""
7175
return self._post(
7276
"/audio/speech",
@@ -81,7 +85,11 @@ def create(
8185
speech_create_params.SpeechCreateParams,
8286
),
8387
options=make_request_options(
84-
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
88+
extra_headers=extra_headers,
89+
extra_query=extra_query,
90+
extra_body=extra_body,
91+
timeout=timeout,
92+
stream=stream,
8593
),
8694
cast_to=HttpxBinaryResponseContent,
8795
)
@@ -108,6 +116,7 @@ async def create(
108116
extra_query: Query | None = None,
109117
extra_body: Body | None = None,
110118
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
119+
stream: bool | None = None,
111120
) -> HttpxBinaryResponseContent:
112121
"""
113122
Generates audio from the input text.
@@ -134,6 +143,9 @@ async def create(
134143
extra_body: Add additional JSON properties to the request
135144
136145
timeout: Override the client-level default timeout for this request, in seconds
146+
147+
stream: Whether or not the response content should be streamed (i.e. not read to
148+
completion immediately), default False
137149
"""
138150
return await self._post(
139151
"/audio/speech",
@@ -148,7 +160,11 @@ async def create(
148160
speech_create_params.SpeechCreateParams,
149161
),
150162
options=make_request_options(
151-
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
163+
extra_headers=extra_headers,
164+
extra_query=extra_query,
165+
extra_body=extra_body,
166+
timeout=timeout,
167+
stream=stream,
152168
),
153169
cast_to=HttpxBinaryResponseContent,
154170
)

Diff for: src/openai/resources/files.py

+18-2
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ def content(
212212
extra_query: Query | None = None,
213213
extra_body: Body | None = None,
214214
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
215+
stream: bool | None = None,
215216
) -> HttpxBinaryResponseContent:
216217
"""
217218
Returns the contents of the specified file.
@@ -224,11 +225,18 @@ def content(
224225
extra_body: Add additional JSON properties to the request
225226
226227
timeout: Override the client-level default timeout for this request, in seconds
228+
229+
stream: Whether or not the response content should be streamed (i.e. not read to
230+
completion immediately), default False
227231
"""
228232
return self._get(
229233
f"/files/{file_id}/content",
230234
options=make_request_options(
231-
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
235+
extra_headers=extra_headers,
236+
extra_query=extra_query,
237+
extra_body=extra_body,
238+
timeout=timeout,
239+
stream=stream,
232240
),
233241
cast_to=HttpxBinaryResponseContent,
234242
)
@@ -475,6 +483,7 @@ async def content(
475483
extra_query: Query | None = None,
476484
extra_body: Body | None = None,
477485
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
486+
stream: bool | None = None,
478487
) -> HttpxBinaryResponseContent:
479488
"""
480489
Returns the contents of the specified file.
@@ -487,11 +496,18 @@ async def content(
487496
extra_body: Add additional JSON properties to the request
488497
489498
timeout: Override the client-level default timeout for this request, in seconds
499+
500+
stream: Whether or not the response content should be streamed (i.e. not read to
501+
completion immediately), default False
490502
"""
491503
return await self._get(
492504
f"/files/{file_id}/content",
493505
options=make_request_options(
494-
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
506+
extra_headers=extra_headers,
507+
extra_query=extra_query,
508+
extra_body=extra_body,
509+
timeout=timeout,
510+
stream=stream,
495511
),
496512
cast_to=HttpxBinaryResponseContent,
497513
)

0 commit comments

Comments
 (0)