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
OpenCode Go: no prompt caching on deepseek-v4-flash and qwen3.8-max (glm-5.2 caches fine) #48116
The OpenCode Go endpoint (https://opencode.ai/zen/go/v1/chat/completions) does not perform prompt caching for deepseek-v4-flash or qwen3.8-max, while glm-5.2 on the same endpoint does. Every request re-processes the full prefix from scratch, so Go sessions on DeepSeek/Qwen burn the $12/$30/$60 metered limits much faster than the Go docs' request estimates assume (those assume ~50-80k cached tokens per request).
Related: #41125 (same symptom for DeepSeek, reported earlier).
Reproduction (live, 2026-09-08, Go subscription key, x-opencode-session set consistently across calls)
Test 1 - deepseek-v4-flash, 3 byte-identical requests:
call
prompt_cache_hit_tokens
prompt_tokens_details.cached_tokens
latency
1
0
0
2.4s
2
(absent)
0
1.1s
3
(absent)
0
2.1s
prompt_cache_miss_tokens = 96 on call 1 (full prompt re-processed). Cache never engages.
Test 2 - same pattern on other Go models:
model
call 1 cached
call 2 cached
deepseek-v4-flash
0
0
qwen3.8-max
0
0
glm-5.2
10
25
Impact
DeepSeek/Qwen turns on Go reprocess the whole prefix (system prompt + tools + history) every tool-loop iteration: higher latency and faster limit consumption.
The Go pricing/docs assume caching works (per-model "Cached Read" rates and request estimates with ~70k cached tokens per request).
Expected
Repeated identical prompts should return prompt_tokens_details.cached_tokens > 0 after the first call, as glm-5.2 does on the same endpoint.
If your issue covers new models (qwen3.8-max) or new test evidence that the existing reports don't capture, please add a note to the oldest open duplicate so the team can track it in one place.
Investigation notes (from repo walkthrough) to speed this up
The Go gateway lives in this repo (packages/console/app/src/routes/zen/go/v1/chat/completions.ts, modelList: "lite"). The routing/cache-affinity flow is:
handler.ts:172 → createStickyTracker(model.id, model.stickyProvider, stickyId) — only active if the model config sets stickyProvider (stickyProviderTracker.ts:9; no-op otherwise).
handler.ts:636-642 → provider pick by hashing the last 4 chars of stickyId over the weighted provider list; pinned in DB after first 200 (handler.ts:299).
Usage forwarding is fine: openai-compatible.ts:61-70 maps cached_tokens/prompt_tokens_details.cached_tokens correctly (plus an optional adjustCacheUsage 90% estimate fallback at :68-70). So a reported cached_tokens: 0 means the upstream genuinely re-processed — the cache domain changed between calls.
Likely root cause: the liteModels entries for deepseek-v4-flash and qwen3.8-max (loaded from env ZEN_MODELS_*, packages/console/core/src/model.ts:85-117) either lack stickyProvider and/or have multiple weighted providers/keys. selectProvider (handler.ts:614-627) drops providers mid-session via the TPM/TPS/budget filters, so the deterministic hash lands on a different upstream on the next call → cache-domain hop → full prefix reprocessed. glm-5.2 is pinned (single provider or stickyProvider set) and therefore caches. This matches the "gateway can leave the cache domain without the client changing anything" analysis in #42935 and the GLM warm/cold/warm in #35402.
Suggested fixes:
Config (fastest, no code): in the ZEN_MODELS_* env secrets for liteModels, set stickyProvider: "strict" on deepseek-v4-flash and qwen3.8-max (like GLM), or collapse each to a single provider/key, or enable adjustCacheUsage: true on those providers as an estimate fallback.
Code hardening (optional): handler.ts:649-663 — the budget/TPS overrides (return provider at :663) apply even in "strict" mode; gate them to "prefer" only so "strict" really pins the provider.
Live re-test after latest dev (2026-09-15, legacy sk- key, raw API)
Ran the exact repro from the issue: N identical requests to https://opencode.ai/zen/go/v1/chat/completions, fixed ~3.3k-token prompt, x-opencode-session set consistently per session.
Result: caching now engages, but sessions still hop between two upstream stacks and lose the cache when they do.
Model
Session
Call 1
Call 2
Verdict
deepseek-v4-flash
ses_probe_A
prompt=3338, hit=0, cached=0
hit=3200, cached=3200
caches
deepseek-v4-flash
ses_probe_C
prompt=3338, hit=0, cached=0
hit=3200, cached=3200
caches
deepseek-v4-flash
ses_probe_B
prompt=3338, hit=0, cached=0
prompt=3391, hit/miss/cached absent
hop → cache lost
qwen3.8-max
ses_livecache_qw01
prompt=3360, cached=0
cached=3072
caches
glm-5.2 (control)
ses_livecache_glm01
cached=10
cached=3320
caches
Observations:
The literal "cached_tokens always 0" symptom is no longer reproducible for deepseek-v4-flash or qwen3.8-max — repeated identical requests now return cached reads (3200/3072), so part of this is resolved.
There are two distinct upstream shapes behind the same session:
Stack A (DeepSeek-native): prompt_tokens=3338, reports prompt_cache_hit_tokens/prompt_cache_miss_tokens.
When the session hops A→B mid-conversation, the prefix cache does not carry over and the next call is cold again (ses_probe_B above). 1 of 3 fresh sessions hopped in this sample. A 6-call run pinned to stack B cached consistently (cached=3328), so each stack caches well on its own — the loss is the hop.
Update: retested live today with a legacy sk- key and caching is now working on the Go endpoint for the models in this issue. Identical repeated requests on the same session now return cached reads:
model
call 1 cached
call 2 cached
deepseek-v4-flash (session A)
cold
3328
deepseek-v4-flash (session B)
3328
3328
deepseek-v4.1-flash
0
3328
qwen3.8-max
0
3072
prompt_cache_hit_tokens / prompt_cache_miss_tokens are still not forwarded for these upstreams (same hollow-usage shape as #45606), but prompt_tokens_details.cached_tokens reports correctly and the effective behavior matches the expected output.
Closing this as resolved. If the upstream-stack hop described in the earlier comments reappears (cache dropping mid-session), reopen and we can pick it up from there.
Activity
github-actions commentedon Sep 9, 2026
This issue might be a duplicate of existing issues. Please check:
If your issue covers new models (qwen3.8-max) or new test evidence that the existing reports don't capture, please add a note to the oldest open duplicate so the team can track it in one place.
csalvarado commentedon Sep 9, 2026
Investigation notes (from repo walkthrough) to speed this up
The Go gateway lives in this repo (
packages/console/app/src/routes/zen/go/v1/chat/completions.ts,modelList: "lite"). The routing/cache-affinity flow is:handler.ts:124,171→stickyId=x-opencode-session(or workspaceID/ip).handler.ts:172→createStickyTracker(model.id, model.stickyProvider, stickyId)— only active if the model config setsstickyProvider(stickyProviderTracker.ts:9; no-op otherwise).handler.ts:636-642→ provider pick by hashing the last 4 chars ofstickyIdover the weighted provider list; pinned in DB after first 200 (handler.ts:299).provider/openai-compatible.ts:30-31→ upstreamx-session-affinity: stickyId.openai-compatible.ts:61-70mapscached_tokens/prompt_tokens_details.cached_tokenscorrectly (plus an optionaladjustCacheUsage90% estimate fallback at :68-70). So a reportedcached_tokens: 0means the upstream genuinely re-processed — the cache domain changed between calls.Likely root cause: the
liteModelsentries fordeepseek-v4-flashandqwen3.8-max(loaded from envZEN_MODELS_*,packages/console/core/src/model.ts:85-117) either lackstickyProviderand/or have multiple weighted providers/keys.selectProvider(handler.ts:614-627) drops providers mid-session via the TPM/TPS/budget filters, so the deterministic hash lands on a different upstream on the next call → cache-domain hop → full prefix reprocessed.glm-5.2is pinned (single provider orstickyProviderset) and therefore caches. This matches the "gateway can leave the cache domain without the client changing anything" analysis in #42935 and the GLM warm/cold/warm in #35402.Suggested fixes:
ZEN_MODELS_*env secrets forliteModels, setstickyProvider: "strict"ondeepseek-v4-flashandqwen3.8-max(like GLM), or collapse each to a single provider/key, or enableadjustCacheUsage: trueon those providers as an estimate fallback.handler.ts:649-663— the budget/TPS overrides (return providerat :663) apply even in"strict"mode; gate them to"prefer"only so"strict"really pins the provider.csalvarado commentedon Sep 16, 2026
Live re-test after latest
dev(2026-09-15, legacysk-key, raw API)Ran the exact repro from the issue: N identical requests to
https://opencode.ai/zen/go/v1/chat/completions, fixed ~3.3k-token prompt,x-opencode-sessionset consistently per session.Result: caching now engages, but sessions still hop between two upstream stacks and lose the cache when they do.
Observations:
cached_tokensalways 0" symptom is no longer reproducible fordeepseek-v4-flashorqwen3.8-max— repeated identical requests now return cached reads (3200/3072), so part of this is resolved.prompt_tokens=3338, reportsprompt_cache_hit_tokens/prompt_cache_miss_tokens.prompt_tokens=3391(different tokenizer), reports onlyprompt_tokens_details.cached_tokensand sometimes hollows that too (same missing-usage-fields pattern as [Bug] Zen Go /v1/chat/completions strips usage.completion_tokens_details for deepseek-v4-flash - official DeepSeek API sends it #45606).handler.tsselectProvider: the budget/TPS overrides can abandon the sticky provider even in"strict"mode).stickyProvideron theliteModelsentries for these models would prevent the hop;adjustCacheUsagealone would only mask the accounting.Conclusion: partially resolved — caching works now, but it is not session-stable while the gateway can switch upstream stacks mid-session.
csalvarado commentedon Oct 2, 2026
Update: retested live today with a legacy
sk-key and caching is now working on the Go endpoint for the models in this issue. Identical repeated requests on the same session now return cached reads:prompt_cache_hit_tokens/prompt_cache_miss_tokensare still not forwarded for these upstreams (same hollow-usage shape as #45606), butprompt_tokens_details.cached_tokensreports correctly and the effective behavior matches the expected output.Closing this as resolved. If the upstream-stack hop described in the earlier comments reappears (cache dropping mid-session), reopen and we can pick it up from there.