magnificent-hairdresser-38202
10/10/2025, 6:00 PM"accumulated_token_usage": {
"model": "default",
"prompt_tokens": 105287,
"completion_tokens": 4815,
"cache_read_tokens": 36608,
"cache_write_tokens": 0,
"context_window": 272000,
"per_turn_token": 12740,
"response_id": ""
},
Does prompt_tokens include cached_tokens or not? Please point out exact code that retrieves and computes those metrics. You can reference https://docs.litellm.ai/docs/completion/prompt_caching to understand the data returned by liteLLM.nutritious-balloon-65098
10/10/2025, 6:00 PMnutritious-balloon-65098
10/10/2025, 6:03 PMprompt_tokens includes cached tokens in the trajectory's accumulated_token_usage.
Answer: prompt_tokens does NOT include cached tokens.
Key Findings:
1. Token Separation: OpenHands tracks cached and non-cached tokens separately:
◦ `prompt_tokens`: Contains only non-cached input tokens
◦ `cache_read_tokens`: Contains tokens retrieved from cache (cache hits)
◦ `cache_write_tokens`: Contains tokens written to cache (for providers like Anthropic)
2. Exact Code Location: The token metrics are extracted and computed in:
◦ File: /workspace/project/OpenHands/openhands/llm/llm.py
◦ Lines: 617-666
◦ Method: add_token_usage() in the Metrics class
3. Implementation Details:
◦ Line 617: prompt_tokens = usage.get('prompt_tokens', 0) extracts non-cached tokens
◦ Lines 632-639: cache_hit_tokens extracted from prompt_tokens_details.cached_tokens
◦ Line 647: cache_write_tokens extracted from model_extra.get('cache_creation_input_tokens', 0)
◦ Lines 659-666: These are passed as separate parameters to the metrics system
4. LiteLLM Compliance: This implementation follows the LiteLLM documentation standard where:
◦ prompt_tokens represents non-cached prompt tokens only
◦ cached_tokens in prompt_tokens_details represents cache-hit tokens
◦ Cache creation tokens are tracked separately for accurate cost calculation
This separation allows OpenHands to accurately track and calculate costs since cached tokens often have different pricing than non-cached tokens across different LLM providers.
<sub>View full conversation</sub>magnificent-hairdresser-38202
10/10/2025, 6:28 PMnutritious-balloon-65098
10/10/2025, 6:28 PMnutritious-balloon-65098
10/10/2025, 6:30 PMprompt_tokens include cached tokens?
No, prompt_tokens does NOT include cached tokens. They are tracked separately.
2. Token Usage in Web GUI
The web GUI receives and displays the same token structure, maintaining the separation between cached and non-cached tokens.
Complete Token Usage Flow:
Backend Processing (openhands/llm/llm.py, lines 617-666):
1. prompt_tokens = usage.get('prompt_tokens', 0) - non-cached tokens only
2. cache_read_tokens = prompt_tokens_details.cached_tokens - cache hits
3. cache_write_tokens = model_extra.get('cache_creation_input_tokens', 0) - cache writes
Data Transmission:
• Backend serializes metrics via Metrics.get() method (openhands/llm/metrics.py, line 203)
• Sent to frontend through llm_metrics.accumulated_token_usage in action messages
• Event serialization handled in openhands/events/serialization/event.py, line 120
Frontend Display (frontend/src/components/features/conversation/metrics-modal/usage-section.tsx):
• Input tokens: usage.prompt_tokens (non-cached only)
• Cache hit: usage.cache_read_tokens (displayed separately)
• Cache write: usage.cache_write_tokens (displayed separately)
• Output tokens: usage.completion_tokens
• Total: usage.prompt_tokens + usage.completion_tokens (excludes cached tokens from total)
Key Finding:
Both in trajectories and the web GUI, prompt_tokens represents only non-cached input tokens, while cached tokens are tracked separately as cache_read_tokens and cache_write_tokens. This separation allows for accurate cost calculation since cached tokens often have different pricing than non-cached tokens.
The web GUI maintains this same structure and displays cached tokens as separate line items under the input tokens section, ensuring transparency about cache usage.
<sub>View full conversation</sub>