Skip to content

fix(llms): emit Anthropic cache_control on LiteLLM and OpenAI-Compatible providers - #14332

Open
errmakov wants to merge 1 commit into
cline:mainfrom
errmakov:fix/litellm-openai-compatible-prompt-cache
Open

errmakov wants to merge 1 commit into
cline:mainfrom
errmakov:fix/litellm-openai-compatible-prompt-cache

Conversation

@errmakov

Copy link
Copy Markdown

Related Issue

Issue: #13667

Description

Requests sent to Anthropic models through the LiteLLM and OpenAI-Compatible providers never include cache_control, so Anthropic prompt caching never engages — cacheReadTokens/cacheWriteTokens are 0 on every request regardless of context size.

resolvePromptCacheRoute attaches cache markers only when the provider manifest declares metadata.routing.promptCache. The litellm and openai-compatible entries in sdk/packages/llms/src/providers/builtins.ts declare no routing metadata at all, so the route never resolves and applyPromptCacheToLastTextPart is never reached. The other pass-through aggregators — openrouter, vercel-ai-gateway, aihubmix — already declare it, which is why caching works there.

This gives both providers the same Anthropic cache-control routing:

  • litellmANTHROPIC_AND_QWEN_CACHE_ROUTING_METADATA. LiteLLM forwards cache_control to the upstream Anthropic/Bedrock deployment, and its /v1/model/info response already populates the catalog prompt-cache capability that the Qwen route requires (fetchLiteLlmPrivateModels maps supports_prompt_caching).
  • openai-compatibleANTHROPIC_ROUTING_METADATA. Custom endpoints pointed at Anthropic models get caching; the Qwen route is omitted because this provider has no model catalog to supply the prompt-cache capability it requires, so it could never resolve.

Both routes match only Anthropic-lineage model ids, so non-Claude models on these providers are unaffected. No new user-facing setting: this follows the metadata-driven routing the SDK already uses for the other aggregators rather than reviving a per-provider toggle. The orphaned liteLlmUsePromptCache field noted in the issue stays unused; removing it is a separate cleanup.

Usage read-back needed no change — normalizeUsage in ai-sdk.ts already reads prompt_tokens_details.cached_tokens and cache_creation_input_tokens, which is what LiteLLM returns.

Test Procedure

Added to sdk/packages/llms/src/providers/gateway.test.ts, following the existing prompt-cache routing tests:

  • forwards Anthropic prompt cache controls for $providerId — parameterized over litellm and openai-compatible, asserts cache_control: { type: "ephemeral" } lands on the user content part in the provider's providerOptions bucket for bedrock/converse/eu.anthropic.claude-sonnet-5 (the model id from the report). Both cases fail on main and pass with this change.
  • keeps prompt cache controls off non-Anthropic $providerId models — same providers with gpt-4o, asserts no cache_control anywhere in the request. Guards the blast radius of enabling routing on the generic provider.
  • Updated keeps Anthropic cache-control routing on the expected provider set, the catalog-level assertion that enumerates which providers carry anthropic-cache-control routing.

Reasoning routing is unchanged in effect: before this change these providers fell through resolveUnroutedAnthropicReasoningRoute, which gave Anthropic-lineage models the anthropic-compatible reasoning route; the explicit metadata now declares the same route for the same set.

Ran locally (Bun 1.4.2, Node 22.14):

  • @cline/llms — 876 passed, 4 skipped
  • @cline/core — 2431 passed; 11 pre-existing failures in src/hub/server/boundary.test.ts and src/session/search/session-history-search.test.ts, verified identical on an unmodified tree
  • apps/vscode bun run test:unit — 1121 passed
  • @cline/cli 1202, @cline/shared 420, @cline/agents 87, @cline/cline-hub 111, @cline/vscode 4 — all passing
  • bun run lint and bun run types — clean

Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • ♻️ Refactor Changes
  • 💅 Cosmetic Changes
  • 📚 Documentation update
  • 🏃 Workflow Changes

Pre-flight Checklist

  • Changes are limited to a single feature, bugfix or chore (split larger changes into separate PRs)
  • Tests are passing (bun test) and code is formatted and linted (bun run format && bun run lint)
  • I have reviewed contributor guidelines

Additional Notes

I have not been able to test against a live LiteLLM proxy — verification is the unit tests plus tracing the @ai-sdk/openai-compatible passthrough (getOpenAIMetadata spreads providerOptions.openaiCompatible onto each content part, which is what createPromptCacheProviderOptions sets).

The LiteLLM and OpenAI-Compatible provider entries carry no prompt-cache
routing metadata, so `resolvePromptCacheRoute` never resolves a route for
them and `cache_control` is never attached to message content. Anthropic
models proxied through either provider report zero cache read/write tokens
on every request, no matter how large the context, and there is no setting
that turns it on.

Give both providers the same Anthropic cache-control routing the other
pass-through aggregators already declare. The route matches only
Anthropic-lineage model ids, so non-Claude models on these providers are
untouched. LiteLLM also gets the Qwen route, which still requires the
catalog `prompt-cache` capability that its `/model/info` response supplies.

Fixes cline#13667
@greptile-apps

greptile-apps Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

PR author is not in the allowed authors list.

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