mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-06 02:48:13 +00:00
refactor(responses): pass through extra usage fields generically
Avoid hard-coding provider-specific usage keys in shared Responses utilities; forward any non-standard usage attributes onto chat Usage for provider cost tracking (e.g. server_side_tool_usage_details).
This commit is contained in:
parent
6963cfe047
commit
014d59f4c4
2 changed files with 62 additions and 11 deletions
|
|
@ -1022,6 +1022,20 @@ class ResponsesAPIRequestUtils:
|
|||
|
||||
|
||||
class ResponseAPILoggingUtils:
|
||||
# Standard Responses usage keys mapped explicitly below; extras pass through to Usage.
|
||||
_RESPONSE_API_USAGE_MAPPED_KEYS = frozenset(
|
||||
{
|
||||
"input_tokens",
|
||||
"output_tokens",
|
||||
"total_tokens",
|
||||
"input_tokens_details",
|
||||
"output_tokens_details",
|
||||
"input_token_details",
|
||||
"output_token_details",
|
||||
"cost", # handled separately after Usage construction
|
||||
}
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _is_response_api_usage(usage: dict | ResponseAPIUsage) -> bool:
|
||||
"""returns True if usage is from OpenAI Response API"""
|
||||
|
|
@ -1031,6 +1045,33 @@ class ResponseAPILoggingUtils:
|
|||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _extra_fields_from_response_api_usage(
|
||||
usage_input: dict | ResponseAPIUsage,
|
||||
response_api_usage: ResponseAPIUsage,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
Preserve provider/extension usage fields not part of the standard token mapping.
|
||||
|
||||
ResponseAPIUsage allows extra attributes; without forwarding them, the rebuilt
|
||||
chat Usage would drop fields needed for provider-specific cost tracking.
|
||||
"""
|
||||
extras: dict[str, Any] = {}
|
||||
if isinstance(usage_input, dict):
|
||||
for key, value in usage_input.items():
|
||||
if key not in ResponseAPILoggingUtils._RESPONSE_API_USAGE_MAPPED_KEYS and value is not None:
|
||||
extras[key] = value
|
||||
return extras
|
||||
|
||||
model_extra = getattr(response_api_usage, "model_extra", None) or getattr(
|
||||
response_api_usage, "__pydantic_extra__", None
|
||||
)
|
||||
if isinstance(model_extra, dict):
|
||||
for key, value in model_extra.items():
|
||||
if key not in ResponseAPILoggingUtils._RESPONSE_API_USAGE_MAPPED_KEYS and value is not None:
|
||||
extras[key] = value
|
||||
return extras
|
||||
|
||||
@staticmethod
|
||||
def _transform_response_api_usage_to_chat_usage(
|
||||
usage_input: dict | ResponseAPIUsage | None,
|
||||
|
|
@ -1089,17 +1130,10 @@ class ResponseAPILoggingUtils:
|
|||
audio_tokens=getattr(output_tokens_details, "audio_tokens", None),
|
||||
)
|
||||
|
||||
usage_kwargs: dict[str, Any] = {}
|
||||
# Keep xAI tool billing fields; dropped if we only pass token fields below.
|
||||
if isinstance(usage_input, dict):
|
||||
if usage_input.get("server_side_tool_usage_details") is not None:
|
||||
usage_kwargs["server_side_tool_usage_details"] = usage_input["server_side_tool_usage_details"]
|
||||
else:
|
||||
details = getattr(
|
||||
response_api_usage, "server_side_tool_usage_details", None
|
||||
)
|
||||
if details is not None:
|
||||
usage_kwargs["server_side_tool_usage_details"] = details
|
||||
usage_kwargs: Final = ResponseAPILoggingUtils._extra_fields_from_response_api_usage(
|
||||
usage_input=usage_input,
|
||||
response_api_usage=response_api_usage,
|
||||
)
|
||||
|
||||
chat_usage: Final = Usage(
|
||||
prompt_tokens=prompt_tokens,
|
||||
|
|
|
|||
|
|
@ -278,6 +278,23 @@ class TestResponseAPILoggingUtils:
|
|||
and result.prompt_tokens_details.cached_tokens == 2
|
||||
)
|
||||
|
||||
def test_transform_response_api_usage_preserves_extra_usage_fields(self):
|
||||
"""Non-standard usage keys pass through for provider cost tracking."""
|
||||
usage = {
|
||||
"input_tokens": 10,
|
||||
"output_tokens": 5,
|
||||
"total_tokens": 15,
|
||||
"server_side_tool_usage_details": {"web_search_calls": 2},
|
||||
"num_server_side_tools_used": 2,
|
||||
}
|
||||
result = ResponseAPILoggingUtils._transform_response_api_usage_to_chat_usage(
|
||||
usage
|
||||
)
|
||||
assert getattr(result, "server_side_tool_usage_details", None) == {
|
||||
"web_search_calls": 2
|
||||
}
|
||||
assert getattr(result, "num_server_side_tools_used", None) == 2
|
||||
|
||||
def test_transform_response_api_usage_with_none_values(self):
|
||||
"""Test transformation handles None values properly"""
|
||||
# Setup
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue