You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
OpenAI-compatible providers report zero tokens_cache_read despite upstream returning cached_tokens #34022
tokens_cache_read stays 0 for OpenAI-compatible providers (litellm proxying deepseek in my case) even when the upstream returns usage.prompt_tokens_details.cached_tokens in both streaming and non-streaming responses. Other provider adapters in the same install (anthropic, zhipu, minimax) record cache hits correctly, so this is specific to the openai-compatible path.
Plugins
None relevant to this repro.
OpenCode version
1.17.11
Steps to reproduce
Point opencode at an OpenAI-compatible endpoint that surfaces cache tokens. Confirm upstream actually returns them:
#30663 reports zero cache tokens for subagent sessions on custom baseURL providers. My case differs: the main session records 0, and it reproduces deterministically, not just for subagents.
The mapping exists end to end, yet the value is lost somewhere between mapUsage and the session write. I did not pin the exact line where cacheReadInputTokens drops for the openai-compatible route, but the symptom is isolated to it.
Impact
Any OpenAI-compatible provider (litellm, openrouter, vLLM, Ollama compat, LM Studio) silently loses cache accounting. Cost tables and any resume/caching savings measurement against those providers are wrong by the cache fraction.
On V2 (0.0.0-beta-17498) this mapping exists. openai-chat.ts reads prompt_tokens_details.cached_tokens into cacheReadInputTokens, and every OpenAI-compatible
provider reuses that protocol unmodified:
Streaming is wired up as well — stream_options: { include_usage: true } is set on every request
(openai-chat.ts:548), so the final chunk carrying cached_tokens is both requested and parsed.
What can still lose the value on this path
Usage state is replaced rather than merged. openai-chat.ts:692:
constusage=mapUsage(event.usage)??state.usage
The last chunk carrying any usage object wins outright. A proxy that emits usage more than once in a
stream — a chunk with prompt_tokens_details, then a bare prompt_tokens totals chunk — loses the
cache read to the second one. The native Anthropic protocol has no such exposure; it folds per-field
rather than replacing:
This fits the contrast in the report, where zhipu and minimax record cache correctly and only the
litellm route reports zero — it would be a property of how that proxy chunks usage rather than of the
mapping. Unconfirmed: no litellm traffic available here to reproduce against.
The raw SSE tail from a single litellm request settles it. If usage appears in more than one data:
frame, that is the cause.
Narrowing which layer drops it
tokens_cache_read on the session row is a cumulative projection, one step removed from what the
protocol produced. The per-message value splits the problem (V2 schema below; on 1.17.x the equivalent
is the message table filtered on $.role):
opencode db "SELECT json_extract(data,'$.tokens.cache.read')
FROM session_message WHERE type='assistant'
ORDER BY time_created DESC LIMIT 10"
Non-zero per-message against a zero session total puts the loss in the rollup. Zero in both means it
never survived mapUsage.
Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.
Activity
github-actions commentedon Jun 26, 2026
This issue might be a duplicate of existing issues. Please check:
feat: Agent-based review with tools + GitHub Action wrapper
Zaczero commentedon Aug 18, 2026
On V2 (
0.0.0-beta-17498) this mapping exists.openai-chat.tsreadsprompt_tokens_details.cached_tokensintocacheReadInputTokens, and every OpenAI-compatibleprovider reuses that protocol unmodified:
opencode/packages/ai/src/protocols/openai-chat.ts
Lines 582 to 600 in 39be159
Streaming is wired up as well —
stream_options: { include_usage: true }is set on every request(
openai-chat.ts:548), so the final chunk carryingcached_tokensis both requested and parsed.What can still lose the value on this path
Usage state is replaced rather than merged.
openai-chat.ts:692:The last chunk carrying any usage object wins outright. A proxy that emits usage more than once in a
stream — a chunk with
prompt_tokens_details, then a bareprompt_tokenstotals chunk — loses thecache read to the second one. The native Anthropic protocol has no such exposure; it folds per-field
rather than replacing:
opencode/packages/ai/src/protocols/anthropic-messages.ts
Lines 709 to 716 in 39be159
This fits the contrast in the report, where zhipu and minimax record cache correctly and only the
litellm route reports zero — it would be a property of how that proxy chunks usage rather than of the
mapping. Unconfirmed: no litellm traffic available here to reproduce against.
The raw SSE tail from a single litellm request settles it. If
usageappears in more than onedata:frame, that is the cause.
Narrowing which layer drops it
tokens_cache_readon the session row is a cumulative projection, one step removed from what theprotocol produced. The per-message value splits the problem (V2 schema below; on 1.17.x the equivalent
is the
messagetable filtered on$.role):Non-zero per-message against a zero session total puts the loss in the rollup. Zero in both means it
never survived
mapUsage.thdxr commentedon Oct 11, 2026
Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (
opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊