mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-02 08:21:53 +00:00
[LLM Translation] Fix Model Usage not having text tokens (#13234)
* fix + test * remove test comments * fix mypy * fix mypy * fix tests
This commit is contained in:
@@ -1265,7 +1265,7 @@ class BaseTokenUsageProcessor:
|
||||
Combine multiple Usage objects into a single Usage object, checking model keys for nested values.
|
||||
"""
|
||||
from litellm.types.utils import (
|
||||
CompletionTokensDetails,
|
||||
CompletionTokensDetailsWrapper,
|
||||
PromptTokensDetailsWrapper,
|
||||
Usage,
|
||||
)
|
||||
@@ -1320,7 +1320,7 @@ class BaseTokenUsageProcessor:
|
||||
not hasattr(combined, "completion_tokens_details")
|
||||
or not combined.completion_tokens_details
|
||||
):
|
||||
combined.completion_tokens_details = CompletionTokensDetails()
|
||||
combined.completion_tokens_details = CompletionTokensDetailsWrapper()
|
||||
|
||||
# Check what keys exist in the model's completion_tokens_details
|
||||
for attr in usage.completion_tokens_details.model_fields:
|
||||
|
||||
@@ -527,7 +527,12 @@ class ChunkProcessor:
|
||||
returned_usage, "cache_read_input_tokens", cache_read_input_tokens
|
||||
) # for anthropic
|
||||
if completion_tokens_details is not None:
|
||||
returned_usage.completion_tokens_details = completion_tokens_details
|
||||
if isinstance(completion_tokens_details, CompletionTokensDetails):
|
||||
returned_usage.completion_tokens_details = CompletionTokensDetailsWrapper(
|
||||
**completion_tokens_details.model_dump()
|
||||
)
|
||||
else:
|
||||
returned_usage.completion_tokens_details = completion_tokens_details
|
||||
|
||||
if reasoning_tokens is not None:
|
||||
if returned_usage.completion_tokens_details is None:
|
||||
|
||||
@@ -888,6 +888,9 @@ class Usage(CompletionUsage):
|
||||
|
||||
server_tool_use: Optional[ServerToolUse] = None
|
||||
|
||||
completion_tokens_details: Optional[CompletionTokensDetailsWrapper] = None
|
||||
"""Breakdown of tokens used in a completion."""
|
||||
|
||||
prompt_tokens_details: Optional[PromptTokensDetailsWrapper] = None
|
||||
"""Breakdown of tokens used in the prompt."""
|
||||
|
||||
|
||||
@@ -2155,8 +2155,8 @@ async def test_caching_kwargs_input(sync_mode):
|
||||
Message,
|
||||
ModelResponse,
|
||||
Usage,
|
||||
CompletionTokensDetails,
|
||||
PromptTokensDetails,
|
||||
CompletionTokensDetailsWrapper,
|
||||
PromptTokensDetailsWrapper,
|
||||
)
|
||||
from datetime import datetime
|
||||
|
||||
@@ -2187,10 +2187,10 @@ async def test_caching_kwargs_input(sync_mode):
|
||||
completion_tokens=31,
|
||||
prompt_tokens=16,
|
||||
total_tokens=47,
|
||||
completion_tokens_details=CompletionTokensDetails(
|
||||
completion_tokens_details=CompletionTokensDetailsWrapper(
|
||||
audio_tokens=None, reasoning_tokens=0
|
||||
),
|
||||
prompt_tokens_details=PromptTokensDetails(
|
||||
prompt_tokens_details=PromptTokensDetailsWrapper(
|
||||
audio_tokens=None, cached_tokens=0
|
||||
),
|
||||
),
|
||||
|
||||
@@ -73,3 +73,56 @@ def test_usage_dump():
|
||||
|
||||
new_usage = Usage(**current_usage.model_dump())
|
||||
assert new_usage.prompt_tokens_details.web_search_requests == 1
|
||||
|
||||
|
||||
def test_usage_completion_tokens_details_text_tokens():
|
||||
from litellm.types.utils import Usage
|
||||
|
||||
# Test data from the reported issue
|
||||
usage_data = {
|
||||
'completion_tokens': 77,
|
||||
'prompt_tokens': 11937,
|
||||
'total_tokens': 12014,
|
||||
'completion_tokens_details': {
|
||||
'accepted_prediction_tokens': None,
|
||||
'audio_tokens': None,
|
||||
'reasoning_tokens': 65,
|
||||
'rejected_prediction_tokens': None,
|
||||
'text_tokens': 12
|
||||
},
|
||||
'prompt_tokens_details': {
|
||||
'audio_tokens': None,
|
||||
'cached_tokens': None,
|
||||
'text_tokens': 11937,
|
||||
'image_tokens': None
|
||||
}
|
||||
}
|
||||
|
||||
# Create Usage object
|
||||
u = Usage(**usage_data)
|
||||
|
||||
# Verify the object has the text_tokens field
|
||||
assert hasattr(u.completion_tokens_details, 'text_tokens')
|
||||
assert u.completion_tokens_details.text_tokens == 12
|
||||
|
||||
# Get model_dump output
|
||||
dump_result = u.model_dump()
|
||||
|
||||
# Verify text_tokens is present in the model_dump output
|
||||
assert 'completion_tokens_details' in dump_result
|
||||
assert 'text_tokens' in dump_result['completion_tokens_details']
|
||||
assert dump_result['completion_tokens_details']['text_tokens'] == 12
|
||||
|
||||
# Verify the full completion_tokens_details structure
|
||||
expected_completion_details = {
|
||||
'accepted_prediction_tokens': None,
|
||||
'audio_tokens': None,
|
||||
'reasoning_tokens': 65,
|
||||
'rejected_prediction_tokens': None,
|
||||
'text_tokens': 12
|
||||
}
|
||||
assert dump_result['completion_tokens_details'] == expected_completion_details
|
||||
|
||||
# Verify round-trip serialization works
|
||||
new_usage = Usage(**dump_result)
|
||||
assert new_usage.completion_tokens_details.text_tokens == 12
|
||||
|
||||
Reference in New Issue
Block a user