mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-08 03:08:45 +00:00
[Bug Fix] Anthropic - Token Usage Null Handling in calculate_usage (#12068)
* [Bug Fix] Anthropic - Token Usage Null Handling in calculate_usage (BerriAI/litellm#11920) * [Fix] Missed a null check and used a cast instead by error
This commit is contained in:
parent
8bd1f8f6ab
commit
fc17da0aef
2 changed files with 96 additions and 7 deletions
|
|
@ -708,7 +708,8 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
|
|||
_litellm_metadata
|
||||
and isinstance(_litellm_metadata, dict)
|
||||
and "user_id" in _litellm_metadata
|
||||
and not _valid_user_id(_litellm_metadata.get("user_id", None))
|
||||
and _litellm_metadata["user_id"] is not None
|
||||
and not _valid_user_id(_litellm_metadata["user_id"])
|
||||
):
|
||||
optional_params["metadata"] = {"user_id": _litellm_metadata["user_id"]}
|
||||
|
||||
|
|
@ -805,19 +806,29 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
|
|||
def calculate_usage(
|
||||
self, usage_object: dict, reasoning_content: Optional[str]
|
||||
) -> Usage:
|
||||
prompt_tokens = usage_object.get("input_tokens", 0)
|
||||
completion_tokens = usage_object.get("output_tokens", 0)
|
||||
# NOTE: Sometimes the usage object has None set explicitly for token counts, meaning .get() & key access returns None, and we need to account for this
|
||||
prompt_tokens = usage_object.get("input_tokens", 0) or 0
|
||||
completion_tokens = usage_object.get("output_tokens", 0) or 0
|
||||
_usage = usage_object
|
||||
cache_creation_input_tokens: int = 0
|
||||
cache_read_input_tokens: int = 0
|
||||
web_search_requests: Optional[int] = None
|
||||
if "cache_creation_input_tokens" in _usage:
|
||||
if (
|
||||
"cache_creation_input_tokens" in _usage
|
||||
and _usage["cache_creation_input_tokens"] is not None
|
||||
):
|
||||
cache_creation_input_tokens = _usage["cache_creation_input_tokens"]
|
||||
if "cache_read_input_tokens" in _usage:
|
||||
if (
|
||||
"cache_read_input_tokens" in _usage
|
||||
and _usage["cache_read_input_tokens"] is not None
|
||||
):
|
||||
cache_read_input_tokens = _usage["cache_read_input_tokens"]
|
||||
prompt_tokens += cache_read_input_tokens
|
||||
if "server_tool_use" in _usage:
|
||||
if "web_search_requests" in _usage["server_tool_use"]:
|
||||
if "server_tool_use" in _usage and _usage["server_tool_use"] is not None:
|
||||
if (
|
||||
"web_search_requests" in _usage["server_tool_use"]
|
||||
and _usage["server_tool_use"]["web_search_requests"] is not None
|
||||
):
|
||||
web_search_requests = cast(
|
||||
int, _usage["server_tool_use"]["web_search_requests"]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ sys.path.insert(
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from litellm.llms.anthropic.chat.transformation import AnthropicConfig
|
||||
from litellm.types.utils import PromptTokensDetailsWrapper, ServerToolUse
|
||||
|
||||
|
||||
def test_response_format_transformation_unit_test():
|
||||
|
|
@ -57,6 +58,83 @@ def test_calculate_usage():
|
|||
assert usage._cache_creation_input_tokens == 12304
|
||||
assert usage._cache_read_input_tokens == 0
|
||||
|
||||
@pytest.mark.parametrize("usage_object,expected_usage", [
|
||||
[
|
||||
{
|
||||
"cache_creation_input_tokens": None,
|
||||
"cache_read_input_tokens": None,
|
||||
"input_tokens": None,
|
||||
"output_tokens": 43,
|
||||
"server_tool_use": None
|
||||
},
|
||||
{
|
||||
"prompt_tokens": 0,
|
||||
"completion_tokens": 43,
|
||||
"total_tokens": 43,
|
||||
"_cache_creation_input_tokens": 0,
|
||||
"_cache_read_input_tokens": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"cache_creation_input_tokens": 100,
|
||||
"cache_read_input_tokens": 200,
|
||||
"input_tokens": 1,
|
||||
"output_tokens": None,
|
||||
"server_tool_use": None
|
||||
},
|
||||
{
|
||||
"prompt_tokens": 1 + 200,
|
||||
"completion_tokens": 0,
|
||||
"total_tokens": 1 + 200,
|
||||
"_cache_creation_input_tokens": 100,
|
||||
"_cache_read_input_tokens": 200,
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"server_tool_use": {
|
||||
"web_search_requests": 10
|
||||
}
|
||||
},
|
||||
{
|
||||
"server_tool_use": ServerToolUse(web_search_requests=10)
|
||||
}
|
||||
]
|
||||
])
|
||||
def test_calculate_usage_nulls(usage_object, expected_usage):
|
||||
"""
|
||||
Correctly deal with null values in usage object
|
||||
|
||||
Fixes https://github.com/BerriAI/litellm/issues/11920
|
||||
"""
|
||||
config = AnthropicConfig()
|
||||
|
||||
usage = config.calculate_usage(usage_object=usage_object, reasoning_content=None)
|
||||
for k, v in expected_usage.items():
|
||||
assert hasattr(usage, k)
|
||||
assert getattr(usage, k) == v
|
||||
|
||||
@pytest.mark.parametrize("usage_object", [
|
||||
{
|
||||
"server_tool_use": {
|
||||
"web_search_requests": None
|
||||
}
|
||||
},
|
||||
{
|
||||
"server_tool_use": None
|
||||
}
|
||||
])
|
||||
def test_calculate_usage_server_tool_null(usage_object):
|
||||
"""
|
||||
Correctly deal with null values in usage object
|
||||
|
||||
Fixes https://github.com/BerriAI/litellm/issues/11920
|
||||
"""
|
||||
config = AnthropicConfig()
|
||||
|
||||
usage = config.calculate_usage(usage_object=usage_object, reasoning_content=None)
|
||||
assert not hasattr(usage, "server_tool_use")
|
||||
|
||||
def test_extract_response_content_with_citations():
|
||||
config = AnthropicConfig()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue