fix: exclude DeepSeek from OpenCode caching path to prevent HTTP 400

OpenCode Zen's relay rejects the Anthropic-style content block format
that cache markers produce (content becomes a block array instead of a
plain string), causing HTTP 400 with "content must be string, not block
array" for DeepSeek models.

Reverts the DeepSeek addition from commit 6b6435a874 while preserving
the Qwen/Alibaba caching path which continues to work.

Fixes #77217
This commit is contained in:
JonthanaHanh
2026-08-03 09:04:44 +07:00
committed by kshitij
parent 633bd354f9
commit 5b5a29f96e
2 changed files with 26 additions and 21 deletions

View File

@@ -2011,12 +2011,12 @@ def anthropic_prompt_cache_policy(
gateway implements the Anthropic cache_control contract
(MiniMax, Zhipu GLM, LiteLLM's Anthropic proxy mode all do).
Qwen models on OpenCode and direct Alibaba (DashScope), plus DeepSeek
models on OpenCode, also honour Anthropic-style ``cache_control`` markers
on OpenAI-wire chat completions. Upstream pi-mono #3392 / pi #3393
documented this for opencode-go Qwen; #24617 reports the same gateway
contract for DeepSeek. Without markers these providers serve zero cache
hits, re-billing the full prompt on every turn.
Qwen / Alibaba-family models on OpenCode, OpenCode Go, and direct
Alibaba (DashScope) also honour Anthropic-style ``cache_control``
markers on OpenAI-wire chat completions. Upstream pi-mono #3392 /
pi #3393 documented this for opencode-go Qwen. Without markers
these providers serve zero cache hits, re-billing the full prompt
on every turn.
If the operator has set ``prompt_caching.cache_ttl`` to a falsy value
(``false``, ``null``, ``"off"``, etc.) in config.yaml, prompt caching
@@ -2143,22 +2143,21 @@ def anthropic_prompt_cache_policy(
if is_minimax_provider or is_minimax_host:
return True, True
# Qwen on OpenCode (Zen/Go) and native DashScope, plus DeepSeek on
# OpenCode only: OpenAI-wire transports that accept Anthropic-style
# cache_control markers and reward them with real cache hits. Keep direct
# Alibaba specific to Qwen; its catalog does not establish the same
# contract for DeepSeek.
# Qwen/Alibaba on OpenCode (Zen/Go) and native DashScope: OpenAI-wire
# transport that accepts Anthropic-style cache_control markers and
# rewards them with real cache hits. Without this branch
# qwen3.6-plus on opencode-go reports 0% cached tokens and burns
# through the subscription on every turn.
#
# NOTE: DeepSeek models on OpenCode are intentionally excluded.
# OpenCode Zen's relay rejects the Anthropic-style content block
# format that cache markers produce (content becomes a block array
# instead of a plain string), causing HTTP 400 (#77217).
model_is_qwen = "qwen" in model_lower
model_is_deepseek = "deepseek" in model_lower
provider_is_opencode = provider_lower in {
"opencode", "opencode-zen", "opencode-go",
}
provider_is_alibaba_family = provider_lower in {
"opencode", "opencode-zen", "opencode-go", "alibaba",
}
if (provider_is_alibaba_family and model_is_qwen) or (
provider_is_opencode and model_is_deepseek
):
if provider_is_alibaba_family and model_is_qwen:
# Envelope layout (native_anthropic=False): markers on inner
# content parts, not top-level tool messages. Matches
# pi-mono's "alibaba" cacheControlFormat.

View File

@@ -291,13 +291,19 @@ class TestQwenAlibabaFamily:
class TestDeepSeekOpenCode:
"""DeepSeek uses OpenCode's envelope-layout cache markers (#24617)."""
"""DeepSeek on OpenCode does NOT use cache markers (#77217).
OpenCode Zen's relay rejects the Anthropic-style content block format
that cache markers produce (content becomes a block array instead of a
plain string), causing HTTP 400. DeepSeek is intentionally excluded
from the caching path.
"""
@pytest.mark.parametrize(
"provider",
["opencode", "opencode-zen", "opencode-go"],
)
def test_deepseek_on_opencode_caches_with_envelope_layout(self, provider):
def test_deepseek_on_opencode_does_not_cache(self, provider):
agent = _make_agent(
provider=provider,
base_url="https://opencode.ai/v1",
@@ -305,7 +311,7 @@ class TestDeepSeekOpenCode:
model="deepseek-v4-pro",
)
assert agent._anthropic_prompt_cache_policy() == (True, False)
assert agent._anthropic_prompt_cache_policy() == (False, False)
def test_deepseek_on_direct_alibaba_does_not_cache(self):
agent = _make_agent(