mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-13 23:11:40 +00:00
fix(logging): write spend log for streaming /v1/responses when response is a dict
This commit is contained in:
parent
cd6e8cdf23
commit
d617862c9c
2 changed files with 55 additions and 13 deletions
|
|
@ -3161,21 +3161,23 @@ class Logging(LiteLLMLoggingBaseClass):
|
|||
(ResponseCompletedEvent, ResponseIncompleteEvent, ResponseFailedEvent),
|
||||
):
|
||||
## return unified Usage object
|
||||
if isinstance(result.response.usage, ResponseAPIUsage):
|
||||
transformed_usage = ResponseAPILoggingUtils._transform_response_api_usage_to_chat_usage(
|
||||
result.response.usage
|
||||
)
|
||||
resp = result.response
|
||||
usage = resp.get("usage") if isinstance(resp, dict) else resp.usage
|
||||
if isinstance(usage, ResponseAPIUsage) or (
|
||||
isinstance(usage, dict) and ResponseAPILoggingUtils._is_response_api_usage(usage)
|
||||
):
|
||||
transformed_usage = ResponseAPILoggingUtils._transform_response_api_usage_to_chat_usage(usage)
|
||||
# Set as dict instead of Usage object so model_dump() serializes it correctly
|
||||
setattr(
|
||||
result.response,
|
||||
"usage",
|
||||
(
|
||||
transformed_usage.model_dump()
|
||||
if hasattr(transformed_usage, "model_dump")
|
||||
else dict(transformed_usage)
|
||||
),
|
||||
transformed_usage_dict = (
|
||||
transformed_usage.model_dump()
|
||||
if hasattr(transformed_usage, "model_dump")
|
||||
else dict(transformed_usage)
|
||||
)
|
||||
return result.response
|
||||
if isinstance(resp, dict):
|
||||
resp["usage"] = transformed_usage_dict
|
||||
else:
|
||||
setattr(resp, "usage", transformed_usage_dict)
|
||||
return resp
|
||||
else:
|
||||
return None
|
||||
|
||||
|
|
|
|||
|
|
@ -3707,3 +3707,43 @@ def test_set_cost_breakdown_stores_reasoning_cost():
|
|||
cost_for_built_in_tools_cost_usd_dollar=0.0,
|
||||
)
|
||||
assert "reasoning_cost" not in no_reasoning.cost_breakdown
|
||||
|
||||
|
||||
def test_get_assembled_streaming_response_handles_dict_response(logging_obj):
|
||||
"""
|
||||
Regression test for streaming /v1/responses spend logging crash.
|
||||
|
||||
In the async streaming Responses path, ResponseCompletedEvent.response is a
|
||||
plain dict at runtime (the base models allow extra fields), so accessing
|
||||
result.response.usage raised AttributeError and the success handler crashed
|
||||
before writing a SpendLogs row. The assembly helper must accept a dict
|
||||
response and transform its Responses-API usage into chat-completion usage.
|
||||
"""
|
||||
from datetime import datetime
|
||||
|
||||
from litellm.types.llms.openai import (
|
||||
ResponseCompletedEvent,
|
||||
ResponsesAPIStreamEvents,
|
||||
)
|
||||
|
||||
event = ResponseCompletedEvent.model_construct(
|
||||
type=ResponsesAPIStreamEvents.RESPONSE_COMPLETED,
|
||||
response={
|
||||
"id": "resp_dict",
|
||||
"usage": {"input_tokens": 11, "output_tokens": 7, "total_tokens": 18},
|
||||
},
|
||||
)
|
||||
assert isinstance(event.response, dict)
|
||||
|
||||
assembled = logging_obj._get_assembled_streaming_response(
|
||||
result=event,
|
||||
start_time=datetime.now(),
|
||||
end_time=datetime.now(),
|
||||
is_async=True,
|
||||
streaming_chunks=[],
|
||||
)
|
||||
|
||||
assert isinstance(assembled, dict)
|
||||
assert assembled["usage"]["prompt_tokens"] == 11
|
||||
assert assembled["usage"]["completion_tokens"] == 7
|
||||
assert assembled["usage"]["total_tokens"] == 18
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue