From bb183375624c6207eb7a6cca5749876b571cd4b0 Mon Sep 17 00:00:00 2001 From: Guilherme Pires Date: Fri, 13 Feb 2026 12:50:47 -0800 Subject: [PATCH] Fix pydantic serialization warning for ResponseAPIUsage in streaming logging In _get_assembled_streaming_response, usage was transformed from ResponseAPIUsage to Chat Completion format and set as a raw dict via setattr. This bypassed pydantic validation, so subsequent model_dump() calls on ResponsesAPIResponse emitted a PydanticSerializationUnexpectedValue warning on every streaming LLM call. Fix: wrap the transformed usage in a proper ResponseAPIUsage instance (which allows extra fields) instead of setting a raw dict. The Chat Completion keys (prompt_tokens, completion_tokens, etc.) are preserved as extra fields for downstream logging consumers. --- litellm/litellm_core_utils/litellm_logging.py | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index bdbbc7579b7..13302eb72b5 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -3226,14 +3226,26 @@ class Logging(LiteLLMLoggingBaseClass): result.response.usage ) ) - # Set as dict instead of Usage object so model_dump() serializes it correctly + # Transform usage to Chat Completion format for internal logging, + # but wrap it in a ResponseAPIUsage so that model_dump() on + # ResponsesAPIResponse serializes cleanly without pydantic warnings. + # ResponseAPIUsage has model_config = {"extra": "allow"}, so Chat + # Completion keys (prompt_tokens, completion_tokens, etc.) are + # preserved as extra fields for downstream logging consumers. + usage_dict = ( + transformed_usage.model_dump() + if hasattr(transformed_usage, "model_dump") + else dict(transformed_usage) + ) setattr( result.response, "usage", - ( - transformed_usage.model_dump() - if hasattr(transformed_usage, "model_dump") - else dict(transformed_usage) + ResponseAPIUsage( + input_tokens=usage_dict.get("prompt_tokens", 0) or 0, + output_tokens=usage_dict.get("completion_tokens", 0) or 0, + total_tokens=usage_dict.get("total_tokens", 0) or 0, + **{k: v for k, v in usage_dict.items() + if k not in ("prompt_tokens", "completion_tokens", "total_tokens")}, ), ) return result.response