diff --git a/docs/edge/en/concepts/flows.mdx b/docs/edge/en/concepts/flows.mdx index 38a1eb282..f4f0a95ff 100644 --- a/docs/edge/en/concepts/flows.mdx +++ b/docs/edge/en/concepts/flows.mdx @@ -277,10 +277,13 @@ The returned [`UsageMetrics`](https://github.com/crewAIInc/crewAI/blob/main/lib/ | `prompt_tokens` | Full input/prompt tokens billed for the request | | `completion_tokens` | Output/completion tokens billed for the request | | `cached_prompt_tokens` | Cache-read subset of prompt tokens (breakdown only) | +| `cache_creation_tokens` | Cache-write subset of prompt tokens (breakdown only, Anthropic) | | `reasoning_tokens` | Reasoning/thinking subset where the provider reports it separately (breakdown only) | | `successful_requests` | Number of LLM calls aggregated | -Breakdown fields such as `cached_prompt_tokens` and `reasoning_tokens` are **not** added on top of `total_tokens` — they describe portions already included in `prompt_tokens` or `completion_tokens`. +Breakdown fields such as `cached_prompt_tokens`, `cache_creation_tokens`, and +`reasoning_tokens` are **not** added on top of `total_tokens` — they describe +portions already included in `prompt_tokens` or `completion_tokens`. For Anthropic, cache read and cache write counters are folded into `prompt_tokens`, so cached workloads are fully reflected in `total_tokens`. OpenAI-style providers already include cached input inside `prompt_tokens`; CrewAI surfaces the cached portion separately for visibility. diff --git a/docs/edge/en/concepts/llms.mdx b/docs/edge/en/concepts/llms.mdx index c928aa755..6e8eb5e33 100644 --- a/docs/edge/en/concepts/llms.mdx +++ b/docs/edge/en/concepts/llms.mdx @@ -427,7 +427,7 @@ In this section, you'll find detailed examples that help you select, configure, `cached_prompt_tokens` records the cache-read portion as a breakdown only; it is already included in `prompt_tokens` and must not be added again to - `total_tokens`. + `total_tokens`. `cache_creation_tokens` records cache writes the same way. ```python Code usage = llm.get_token_usage_summary() diff --git a/lib/crewai/src/crewai/types/usage_metrics.py b/lib/crewai/src/crewai/types/usage_metrics.py index a6bc34c52..56e56666f 100644 --- a/lib/crewai/src/crewai/types/usage_metrics.py +++ b/lib/crewai/src/crewai/types/usage_metrics.py @@ -177,11 +177,16 @@ class UsageMetrics(BaseModel): if isinstance(details, dict): cached_prompt_tokens = _coerce_int(details.get("cached_tokens")) + cache_creation_tokens = _coerce_int( + usage_data.get("cache_creation_tokens") + ) or _coerce_int(usage_data.get("cache_creation_input_tokens")) + return cls( total_tokens=prompt_tokens + completion_tokens, prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, cached_prompt_tokens=cached_prompt_tokens, reasoning_tokens=_coerce_int(usage_data.get("reasoning_tokens")), + cache_creation_tokens=cache_creation_tokens, successful_requests=1, ) diff --git a/lib/crewai/tests/events/test_llm_usage_event.py b/lib/crewai/tests/events/test_llm_usage_event.py index db4f2b976..0a8ffc69f 100644 --- a/lib/crewai/tests/events/test_llm_usage_event.py +++ b/lib/crewai/tests/events/test_llm_usage_event.py @@ -331,6 +331,7 @@ class TestFromProviderDictAnthropicCacheTokens: assert metrics is not None assert metrics.prompt_tokens == 120 assert metrics.total_tokens == 170 + assert metrics.cache_creation_tokens == 20 def test_cache_read_and_creation_tokens_both_included(self): from crewai.types.usage_metrics import UsageMetrics @@ -348,6 +349,7 @@ class TestFromProviderDictAnthropicCacheTokens: assert metrics.prompt_tokens == 150 assert metrics.total_tokens == 200 assert metrics.cached_prompt_tokens == 30 + assert metrics.cache_creation_tokens == 20 def test_missing_cache_fields_preserve_non_cached_totals(self): from crewai.types.usage_metrics import UsageMetrics @@ -360,6 +362,7 @@ class TestFromProviderDictAnthropicCacheTokens: assert metrics.prompt_tokens == 100 assert metrics.total_tokens == 150 assert metrics.cached_prompt_tokens == 0 + assert metrics.cache_creation_tokens == 0 def test_reconciled_native_dict_is_not_double_counted(self): from crewai.types.usage_metrics import UsageMetrics @@ -369,12 +372,14 @@ class TestFromProviderDictAnthropicCacheTokens: "input_tokens": 150, "output_tokens": 50, "cached_prompt_tokens": 30, + "cache_creation_tokens": 20, } ) assert metrics is not None assert metrics.prompt_tokens == 150 assert metrics.total_tokens == 200 + assert metrics.cache_creation_tokens == 20 def test_openai_cached_prompt_tokens_are_not_added_twice(self): from crewai.types.usage_metrics import UsageMetrics diff --git a/lib/crewai/tests/test_flow_usage_metrics.py b/lib/crewai/tests/test_flow_usage_metrics.py index 355ddcae0..48d2274b4 100644 --- a/lib/crewai/tests/test_flow_usage_metrics.py +++ b/lib/crewai/tests/test_flow_usage_metrics.py @@ -130,6 +130,7 @@ class TestUsageDictToMetrics: "total_tokens": 180, "cached_prompt_tokens": 40, "reasoning_tokens": 25, + "cache_creation_tokens": 10, }, UsageMetrics( prompt_tokens=100, @@ -137,6 +138,7 @@ class TestUsageDictToMetrics: total_tokens=180, cached_prompt_tokens=40, reasoning_tokens=25, + cache_creation_tokens=10, successful_requests=1, ), ),