From 6b6435a8748859cc256c7b1d1b4bf1a195d99197 Mon Sep 17 00:00:00 2001 From: wz-heng <68931789+wz-heng@users.noreply.github.com> Date: Thu, 30 Jul 2026 20:39:08 +0800 Subject: [PATCH] feat(cache): enable DeepSeek caching on OpenCode --- agent/agent_runtime_helpers.py | 30 ++++++++------ .../test_anthropic_prompt_cache_policy.py | 41 ++++++++++++++++++- 2 files changed, 58 insertions(+), 13 deletions(-) diff --git a/agent/agent_runtime_helpers.py b/agent/agent_runtime_helpers.py index 11f98c82e945d..5f72bedfb5628 100644 --- a/agent/agent_runtime_helpers.py +++ b/agent/agent_runtime_helpers.py @@ -1860,12 +1860,12 @@ def anthropic_prompt_cache_policy( gateway implements the Anthropic cache_control contract (MiniMax, Zhipu GLM, LiteLLM's Anthropic proxy mode all do). - 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. + 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. """ eff_provider = (provider if provider is not None else agent.provider) or "" eff_base_url = base_url if base_url is not None else (agent.base_url or "") @@ -1980,16 +1980,22 @@ def anthropic_prompt_cache_policy( if is_minimax_provider or is_minimax_host: return True, True - # 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. + # 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. 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: + if (provider_is_alibaba_family and model_is_qwen) or ( + provider_is_opencode and model_is_deepseek + ): # Envelope layout (native_anthropic=False): markers on inner # content parts, not top-level tool messages. Matches # pi-mono's "alibaba" cacheControlFormat. diff --git a/tests/run_agent/test_anthropic_prompt_cache_policy.py b/tests/run_agent/test_anthropic_prompt_cache_policy.py index 34cc748a2e4bc..f456c107eefd6 100644 --- a/tests/run_agent/test_anthropic_prompt_cache_policy.py +++ b/tests/run_agent/test_anthropic_prompt_cache_policy.py @@ -10,6 +10,8 @@ from __future__ import annotations from unittest.mock import MagicMock +import pytest + from run_agent import AIAgent @@ -256,6 +258,44 @@ class TestQwenAlibabaFamily: assert agent._anthropic_prompt_cache_policy() == (False, False) +class TestDeepSeekOpenCode: + """DeepSeek uses OpenCode's envelope-layout cache markers (#24617).""" + + @pytest.mark.parametrize( + "provider", + ["opencode", "opencode-zen", "opencode-go"], + ) + def test_deepseek_on_opencode_caches_with_envelope_layout(self, provider): + agent = _make_agent( + provider=provider, + base_url="https://opencode.ai/v1", + api_mode="chat_completions", + model="deepseek-v4-pro", + ) + + assert agent._anthropic_prompt_cache_policy() == (True, False) + + def test_deepseek_on_direct_alibaba_does_not_cache(self): + agent = _make_agent( + provider="alibaba", + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", + api_mode="chat_completions", + model="deepseek-v4-pro", + ) + + assert agent._anthropic_prompt_cache_policy() == (False, False) + + def test_deepseek_on_openrouter_does_not_cache(self): + agent = _make_agent( + provider="openrouter", + base_url="https://openrouter.ai/api/v1", + api_mode="chat_completions", + model="deepseek/deepseek-chat", + ) + + assert agent._anthropic_prompt_cache_policy() == (False, False) + + class TestNousPortalAnthropicWire: def test_portal_claude_on_the_messages_wire_uses_the_native_layout(self): agent = _make_agent( @@ -315,4 +355,3 @@ class TestExplicitOverrides: # ───────────────────────────────────────────────────────────────────── # Long-lived prefix cache policy (cross-session 1h tier) # ───────────────────────────────────────────────────────────────────── -