|
20 | 20 | _T = TypeVar("_T") |
21 | 21 |
|
22 | 22 |
|
| 23 | +def _build_streaming_api_error( |
| 24 | + *, |
| 25 | + data: object, |
| 26 | + request: httpx.Request, |
| 27 | + is_error_event: bool, |
| 28 | +) -> APIError | None: |
| 29 | + if not is_mapping(data): |
| 30 | + return None |
| 31 | + |
| 32 | + error = data.get("error") |
| 33 | + if is_error_event: |
| 34 | + body = error if error is not None else data |
| 35 | + elif error is not None: |
| 36 | + body = error |
| 37 | + else: |
| 38 | + return None |
| 39 | + |
| 40 | + message = data.get("message") |
| 41 | + if not isinstance(message, str) and is_mapping(error): |
| 42 | + nested_message = error.get("message") |
| 43 | + if isinstance(nested_message, str): |
| 44 | + message = nested_message |
| 45 | + |
| 46 | + if not isinstance(message, str) or not message: |
| 47 | + message = "An error occurred during streaming" |
| 48 | + |
| 49 | + return APIError(message=message, request=request, body=body) |
| 50 | + |
| 51 | + |
23 | 52 | class Stream(Generic[_T]): |
24 | 53 | """Provides the core interface to iterate over a synchronous stream response.""" |
25 | 54 |
|
@@ -63,41 +92,19 @@ def __stream__(self) -> Iterator[_T]: |
63 | 92 | if sse.data.startswith("[DONE]"): |
64 | 93 | break |
65 | 94 |
|
| 95 | + data = sse.json() |
| 96 | + api_error = _build_streaming_api_error( |
| 97 | + data=data, |
| 98 | + request=self.response.request, |
| 99 | + is_error_event=sse.event == "error", |
| 100 | + ) |
| 101 | + if api_error is not None: |
| 102 | + raise api_error |
| 103 | + |
66 | 104 | # we have to special case the Assistants `thread.` events since we won't have an "event" key in the data |
67 | 105 | if sse.event and sse.event.startswith("thread."): |
68 | | - data = sse.json() |
69 | | - |
70 | | - if sse.event == "error" and is_mapping(data) and data.get("error"): |
71 | | - message = None |
72 | | - error = data.get("error") |
73 | | - if is_mapping(error): |
74 | | - message = error.get("message") |
75 | | - if not message or not isinstance(message, str): |
76 | | - message = "An error occurred during streaming" |
77 | | - |
78 | | - raise APIError( |
79 | | - message=message, |
80 | | - request=self.response.request, |
81 | | - body=data["error"], |
82 | | - ) |
83 | | - |
84 | 106 | yield process_data(data={"data": data, "event": sse.event}, cast_to=cast_to, response=response) |
85 | 107 | else: |
86 | | - data = sse.json() |
87 | | - if is_mapping(data) and data.get("error"): |
88 | | - message = None |
89 | | - error = data.get("error") |
90 | | - if is_mapping(error): |
91 | | - message = error.get("message") |
92 | | - if not message or not isinstance(message, str): |
93 | | - message = "An error occurred during streaming" |
94 | | - |
95 | | - raise APIError( |
96 | | - message=message, |
97 | | - request=self.response.request, |
98 | | - body=data["error"], |
99 | | - ) |
100 | | - |
101 | 108 | yield process_data( |
102 | 109 | data={"data": data, "event": sse.event} |
103 | 110 | if self._options is not None and self._options.synthesize_event_and_data |
@@ -173,41 +180,19 @@ async def __stream__(self) -> AsyncIterator[_T]: |
173 | 180 | if sse.data.startswith("[DONE]"): |
174 | 181 | break |
175 | 182 |
|
| 183 | + data = sse.json() |
| 184 | + api_error = _build_streaming_api_error( |
| 185 | + data=data, |
| 186 | + request=self.response.request, |
| 187 | + is_error_event=sse.event == "error", |
| 188 | + ) |
| 189 | + if api_error is not None: |
| 190 | + raise api_error |
| 191 | + |
176 | 192 | # we have to special case the Assistants `thread.` events since we won't have an "event" key in the data |
177 | 193 | if sse.event and sse.event.startswith("thread."): |
178 | | - data = sse.json() |
179 | | - |
180 | | - if sse.event == "error" and is_mapping(data) and data.get("error"): |
181 | | - message = None |
182 | | - error = data.get("error") |
183 | | - if is_mapping(error): |
184 | | - message = error.get("message") |
185 | | - if not message or not isinstance(message, str): |
186 | | - message = "An error occurred during streaming" |
187 | | - |
188 | | - raise APIError( |
189 | | - message=message, |
190 | | - request=self.response.request, |
191 | | - body=data["error"], |
192 | | - ) |
193 | | - |
194 | 194 | yield process_data(data={"data": data, "event": sse.event}, cast_to=cast_to, response=response) |
195 | 195 | else: |
196 | | - data = sse.json() |
197 | | - if is_mapping(data) and data.get("error"): |
198 | | - message = None |
199 | | - error = data.get("error") |
200 | | - if is_mapping(error): |
201 | | - message = error.get("message") |
202 | | - if not message or not isinstance(message, str): |
203 | | - message = "An error occurred during streaming" |
204 | | - |
205 | | - raise APIError( |
206 | | - message=message, |
207 | | - request=self.response.request, |
208 | | - body=data["error"], |
209 | | - ) |
210 | | - |
211 | 196 | yield process_data( |
212 | 197 | data={"data": data, "event": sse.event} |
213 | 198 | if self._options is not None and self._options.synthesize_event_and_data |
|
0 commit comments