|
17 | 17 | LLMConfigValue, |
18 | 18 | LLMError, |
19 | 19 | LLMResponse, |
| 20 | + LLMResponseValidationError, |
20 | 21 | Message, |
21 | 22 | MessageRole, |
22 | 23 | OutOfTokensOrSymbolsError, |
@@ -236,27 +237,41 @@ async def request_llm_message( |
236 | 237 | ) |
237 | 238 | except httpx.HTTPStatusError as exception: |
238 | 239 | _handle_status_error(status_code=exception.response.status_code, content=exception.response.content) |
| 240 | + |
239 | 241 | try: |
240 | 242 | validated_message_model: typing.Final = ( |
241 | 243 | ChatCompletionsNotStreamingResponse.model_validate_json(response.content).choices[0].message |
242 | 244 | ) |
243 | | - return LLMResponse( |
244 | | - content=validated_message_model.content, |
245 | | - reasoning_content=validated_message_model.reasoning_content, |
246 | | - ) |
| 245 | + except pydantic.ValidationError as validation_error: |
| 246 | + raise LLMResponseValidationError( |
| 247 | + response_content=response.content, original_error=validation_error |
| 248 | + ) from validation_error |
247 | 249 | finally: |
248 | 250 | await response.aclose() |
249 | 251 |
|
| 252 | + return LLMResponse( |
| 253 | + content=validated_message_model.content, |
| 254 | + reasoning_content=validated_message_model.reasoning_content, |
| 255 | + ) |
| 256 | + |
250 | 257 | async def _iter_response_chunks(self, response: httpx.Response) -> typing.AsyncIterable[LLMResponse]: |
251 | 258 | async for event in httpx_sse.EventSource(response).aiter_sse(): |
252 | 259 | if event.data == "[DONE]": |
253 | 260 | break |
254 | | - validated_response = ChatCompletionsStreamingEvent.model_validate_json(event.data) |
| 261 | + |
| 262 | + try: |
| 263 | + validated_response = ChatCompletionsStreamingEvent.model_validate_json(event.data) |
| 264 | + except pydantic.ValidationError as validation_error: |
| 265 | + raise LLMResponseValidationError( |
| 266 | + response_content=response.content, original_error=validation_error |
| 267 | + ) from validation_error |
| 268 | + |
255 | 269 | if not ( |
256 | 270 | (validated_delta := validated_response.choices[0].delta) |
257 | 271 | and (validated_delta.content or validated_delta.reasoning_content) |
258 | 272 | ): |
259 | 273 | continue |
| 274 | + |
260 | 275 | yield LLMResponse(content=validated_delta.content, reasoning_content=validated_delta.reasoning_content) |
261 | 276 |
|
262 | 277 | @contextlib.asynccontextmanager |
|
0 commit comments