Skip to content

Preserve cache_control for Qwen on OpenRouter + Anthropic adapter - #2

Merged
jwbron merged 1 commit into
mainfrom
cache-control-passthrough-qwen
May 28, 2026
Merged

Preserve cache_control for Qwen on OpenRouter + Anthropic adapter#2
jwbron merged 1 commit into
mainfrom
cache-control-passthrough-qwen

Conversation

@jwbron

@jwbron jwbron commented May 28, 2026

Copy link
Copy Markdown
Owner

Why

When Claude Code is pointed at Qwen/DeepSeek through the LiteLLM proxy (Anthropic /v1/messages -> OpenRouter), cache_control was lost end-to-end, so every turn paid the full input rate (cache_read_input_tokens stayed at 0). These fixes let the prefix cache land (measured ~0% -> ~95% hit rate on Alibaba/DeepSeek routes).

This supersedes #1, which incorrectly added cache_control support for DeepSeek. Per OpenRouter's prompt-caching docs, DeepSeek caching is automatic/prefix-based and ignores cache_control; only Alibaba (Qwen) requires explicit cache_control breakpoints. DeepSeek's caching is restored solely by the billing-header strip (fix 3).

Changes

  1. openrouter/chat/transformation.py — add QWEN (only) to CacheControlSupportedModels. Alibaba/Qwen requires explicit cache_control content blocks; without this the handler strips the marker before the upstream call. DeepSeek is intentionally omitted (its caching is automatic and ignores cache_control, so listing it would be inert).

  2. anthropic/.../adapters/transformation.pyis_anthropic_claude_model — recognize qwen so the Anthropic->OpenAI adapter applies cache_control for Qwen. DeepSeek is omitted here too; this also keeps DeepSeek's thinking translated to reasoning_effort rather than passed through raw. The one behavioral tradeoff (raw thinking passthrough) now applies to Qwen only; callers use drop_params: true and/or set reasoning_effort explicitly, so it is benign in practice.

  3. anthropic/.../adapters/transformation.py_add_system_message_to_messages — drop the x-anthropic-billing-header: system block. Claude Code injects this ahead of the cache_control marker with a per-request hash; left in, it invalidates the upstream prefix-cache key every turn. This is model-agnostic and is what restores DeepSeek's automatic prefix caching. Mirrors _filter_billing_headers_from_system in the sibling messages adapter.

Tests

  • is_anthropic_claude_model recognizes qwen and rejects deepseek (and still rejects gpt/gemini).
  • transform_request preserves cache_control for qwen and drops it for deepseek.
  • Billing-header system block is dropped while a real block keeps its cache_control.

All touched files pass black; the targeted unit tests pass.

Three related fixes so cache_control survives end-to-end when routing
Anthropic /v1/messages traffic to Qwen/DeepSeek upstreams via OpenRouter
(measured ~0% -> ~95% cache hit rate on Alibaba/DeepSeek routes):

1. Add QWEN to CacheControlSupportedModels so the OpenRouter chat handler
   stops stripping cache_control before the upstream call. Alibaba (Qwen)
   requires explicit cache_control breakpoints. DeepSeek is intentionally
   omitted: its caching is automatic/prefix-based and ignores cache_control,
   so listing it would be inert.

2. Widen is_anthropic_claude_model to recognize qwen so the Anthropic->OpenAI
   adapter applies cache_control for Qwen models. DeepSeek is likewise omitted
   here; this also keeps DeepSeek's thinking translated to reasoning_effort
   rather than passed through raw.

3. Drop the x-anthropic-billing-header system block in the adapter path
   (mirrors _filter_billing_headers_from_system in the messages adapter). Its
   per-request hash otherwise invalidates the prefix cache every turn, pinning
   cache_read_input_tokens at 0. This is model-agnostic and is what restores
   DeepSeek's automatic prefix caching.

Adds unit tests for all paths, including that DeepSeek does not receive a
cache_control block and is not treated as cache_control-capable.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant