Description
For the Anthropic provider, total_tokens is computed as input_tokens + output_tokens only. Anthropic reports cache reads (cache_read_input_tokens) and cache writes (cache_creation_input_tokens) as separate counters that are not included in input_tokens — and both are billed (cache writes at 1.25×, cache reads at 0.1×).
The result: any prompt-cached request reports a total_tokens that is wildly lower than what was actually billed. Anyone computing cost from CrewAI's usage metrics undercounts, and the undercount grows with how well caching works.
This bit us while instrumenting per-session cost for a conversational Flow app — we ended up wrapping the Anthropic SDK ourselves to get real numbers. (Filing under the same banner as our other conversational-flow findings, though the bug itself is provider-level, not conversational-specific.)
Root cause
crewai/llms/providers/anthropic/completion.py ~L1971:
cache_read_tokens = getattr(usage, "cache_read_input_tokens", 0) or 0
cache_creation_tokens = (
getattr(usage, "cache_creation_input_tokens", 0) or 0
)
result: dict[str, Any] = {
"input_tokens": input_tokens,
"output_tokens": output_tokens,
"total_tokens": input_tokens + output_tokens, # <-- omits both cache counters
"cached_prompt_tokens": cache_read_tokens,
"cache_creation_tokens": cache_creation_tokens,
}
Both cache values are captured correctly and surfaced individually — they're just excluded from the total.
Steps to Reproduce
- Make an Anthropic call large enough to cache, with a
cache_breakpoint.
- Repeat it so the second call is a cache read.
- Compare
total_tokens against the sum of all four counters.
Expected behavior
total_tokens should account for every billed token, i.e.
total_tokens = input_tokens + output_tokens + cache_read_tokens + cache_creation_tokens
(or, if total_tokens is deliberately meant to exclude cache traffic, that should be documented loudly, since it makes the field unusable for cost estimation on any cached workload.)
Screenshots/Code snippets
"""Minimal repro: total_tokens omits Anthropic cache read/write tokens."""
from crewai import LLM
llm = LLM(model="anthropic/claude-haiku-4-5", max_tokens=64)
BIG = "You are a helpful assistant.\n" + ("Filler context line for the cache. " * 500)
for i in (1, 2):
llm.call([
{"role": "system", "content": BIG, "cache_breakpoint": True},
{"role": "user", "content": "Reply with the single word OK."},
])
u = llm._token_usage
billed = (u["prompt_tokens"] + u["completion_tokens"]
+ u["cached_prompt_tokens"] + u["cache_creation_tokens"])
print(f"call {i}: reported total_tokens={u['total_tokens']:5} | "
f"prompt={u['prompt_tokens']:5} completion={u['completion_tokens']:4} "
f"cache_write={u['cache_creation_tokens']:5} cache_read={u['cached_prompt_tokens']:5} "
f"-> actually billed={billed}")
Actual output (counters are cumulative across calls):
call 1: reported total_tokens= 17 | prompt= 13 completion= 4 cache_write= 4509 cache_read= 0 -> actually billed=4526
call 2: reported total_tokens= 34 | prompt= 26 completion= 8 cache_write= 4509 cache_read= 4509 -> actually billed=9052
total_tokens reports 17 where 4526 tokens were billed — a 266× undercount on the first call.
The same pattern is visible in production traces. An llm_call_completed event from one of our agent turns:
"usage": {
"input_tokens": 2,
"total_tokens": 42,
"output_tokens": 40,
"cached_prompt_tokens": 0,
"cache_creation_tokens": 2285
}
2285 billed cache-write tokens, reported total of 42.
Environment
- crewai
1.15.10
- Python 3.12
- macOS (Darwin 25.1)
- Provider:
anthropic (native provider path, not LiteLLM)
Description
For the Anthropic provider,
total_tokensis computed asinput_tokens + output_tokensonly. Anthropic reports cache reads (cache_read_input_tokens) and cache writes (cache_creation_input_tokens) as separate counters that are not included ininput_tokens— and both are billed (cache writes at 1.25×, cache reads at 0.1×).The result: any prompt-cached request reports a
total_tokensthat is wildly lower than what was actually billed. Anyone computing cost from CrewAI's usage metrics undercounts, and the undercount grows with how well caching works.This bit us while instrumenting per-session cost for a conversational Flow app — we ended up wrapping the Anthropic SDK ourselves to get real numbers. (Filing under the same banner as our other conversational-flow findings, though the bug itself is provider-level, not conversational-specific.)
Root cause
crewai/llms/providers/anthropic/completion.py~L1971:Both cache values are captured correctly and surfaced individually — they're just excluded from the total.
Steps to Reproduce
cache_breakpoint.total_tokensagainst the sum of all four counters.Expected behavior
total_tokensshould account for every billed token, i.e.(or, if
total_tokensis deliberately meant to exclude cache traffic, that should be documented loudly, since it makes the field unusable for cost estimation on any cached workload.)Screenshots/Code snippets
Actual output (counters are cumulative across calls):
total_tokensreports 17 where 4526 tokens were billed — a 266× undercount on the first call.The same pattern is visible in production traces. An
llm_call_completedevent from one of our agent turns:2285 billed cache-write tokens, reported total of 42.
Environment
1.15.10anthropic(native provider path, not LiteLLM)