mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-08-10 08:21:54 +00:00
Fix Anthropic native provider to include cache tokens in input totals.
Anthropic reports cache read and cache creation separately from input_tokens; fold them into input_tokens and total_tokens so billed usage is not underreported on cached workloads.
This commit is contained in:
@@ -12,6 +12,7 @@ from crewai.llms.base_llm import BaseLLM, JsonResponseFormat, llm_call_context
|
||||
from crewai.llms.hooks.base import BaseInterceptor
|
||||
from crewai.llms.hooks.transport import AsyncHTTPTransport, HTTPTransport
|
||||
from crewai.llms.providers.utils.common import safe_tool_conversion
|
||||
from crewai.types.usage_metrics import _coerce_int
|
||||
from crewai.utilities.agent_utils import is_context_length_exceeded
|
||||
from crewai.utilities.exceptions.context_window_exceeding_exception import (
|
||||
LLMContextLengthExceededError,
|
||||
@@ -1965,12 +1966,15 @@ class AnthropicCompletion(BaseLLM):
|
||||
"""Extract token usage and response metadata from Anthropic response."""
|
||||
if hasattr(response, "usage") and response.usage:
|
||||
usage = response.usage
|
||||
input_tokens = getattr(usage, "input_tokens", 0)
|
||||
output_tokens = getattr(usage, "output_tokens", 0)
|
||||
cache_read_tokens = getattr(usage, "cache_read_input_tokens", 0) or 0
|
||||
cache_creation_tokens = (
|
||||
getattr(usage, "cache_creation_input_tokens", 0) or 0
|
||||
input_tokens = _coerce_int(getattr(usage, "input_tokens", 0))
|
||||
output_tokens = _coerce_int(getattr(usage, "output_tokens", 0))
|
||||
cache_read_tokens = _coerce_int(
|
||||
getattr(usage, "cache_read_input_tokens", 0)
|
||||
)
|
||||
cache_creation_tokens = _coerce_int(
|
||||
getattr(usage, "cache_creation_input_tokens", 0)
|
||||
)
|
||||
input_tokens = input_tokens + cache_read_tokens + cache_creation_tokens
|
||||
result: dict[str, Any] = {
|
||||
"input_tokens": input_tokens,
|
||||
"output_tokens": output_tokens,
|
||||
|
||||
@@ -549,7 +549,12 @@ def test_anthropic_token_usage_tracking():
|
||||
with patch.object(llm._client.messages, 'create') as mock_create:
|
||||
mock_response = MagicMock()
|
||||
mock_response.content = [MagicMock(text="test response")]
|
||||
mock_response.usage = MagicMock(input_tokens=50, output_tokens=25)
|
||||
mock_response.usage = MagicMock(
|
||||
input_tokens=50,
|
||||
output_tokens=25,
|
||||
cache_read_input_tokens=0,
|
||||
cache_creation_input_tokens=0,
|
||||
)
|
||||
mock_create.return_value = mock_response
|
||||
|
||||
result = llm.call("Hello")
|
||||
@@ -1653,9 +1658,9 @@ def test_anthropic_cache_creation_tokens_extraction():
|
||||
mock_response.model = None
|
||||
|
||||
usage = llm._extract_anthropic_token_usage(mock_response)
|
||||
assert usage["input_tokens"] == 100
|
||||
assert usage["input_tokens"] == 150
|
||||
assert usage["output_tokens"] == 50
|
||||
assert usage["total_tokens"] == 150
|
||||
assert usage["total_tokens"] == 200
|
||||
assert usage["cached_prompt_tokens"] == 30
|
||||
assert usage["cache_creation_tokens"] == 20
|
||||
|
||||
|
||||
Reference in New Issue
Block a user