Skip to content

fix(bedrock): preserve cache_control for ARN models in /v1/messages adapter - #29822

Closed
mateo-berri wants to merge 1 commit into
litellm_internal_stagingfrom
claude/sharp-babbage-fVsMA
Closed

fix(bedrock): preserve cache_control for ARN models in /v1/messages adapter#29822
mateo-berri wants to merge 1 commit into
litellm_internal_stagingfrom
claude/sharp-babbage-fVsMA

Conversation

@mateo-berri

Copy link
Copy Markdown
Contributor

Relevant issues

Fixes #26625

Linear ticket

Pre-Submission checklist

  • I have added meaningful tests
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

CI (LiteLLM team)

  • Branch creation CI run
    Link:

  • CI run for the last commit
    Link:

  • Merge / cherry-pick CI run
    Links:

Screenshots / Proof of Fix

I don't have Bedrock credentials in this environment to hit a real Application Inference Profile, so here is a runbook to confirm the fix against a live proxy. Point a profile at any Claude model and run both endpoints; only the /v1/messages path was broken before.

  1. Configure a Bedrock Application Inference Profile in your config
model_list:
  - model_name: my-claude-model
    litellm_params:
      model: bedrock/converse/arn:aws:bedrock:us-east-1:ACCOUNT:application-inference-profile/PROFILE_ID
  1. Start the proxy
python litellm/proxy/proxy_cli.py --config litellm/proxy/dev_config.yaml --detailed_debug --reload --use_v2_migration_resolver 2>&1 | tee litellm.log
  1. Send a large cached prompt through the Anthropic endpoint (the previously broken path). Use enough text to clear Bedrock's minimum cache token threshold
BIG=$(python -c "print('the quick brown fox jumps over the lazy dog. ' * 3000)")
curl -s -X POST http://localhost:4000/v1/messages \
  -H "x-api-key: $LITELLM_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d "{\"model\":\"my-claude-model\",\"max_tokens\":16,\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"$BIG\",\"cache_control\":{\"type\":\"ephemeral\"}}]}]}" \
  | python -c "import sys,json; u=json.load(sys.stdin)['usage']; print(u)"
  1. Before this fix the response usage shows cache_creation_input_tokens: 0 and cache_read_input_tokens: 0. After this fix the first call returns a non-zero cache_creation_input_tokens, and an immediate identical second call returns a non-zero cache_read_input_tokens, matching what /v1/chat/completions already returned for the same profile

Type

🐛 Bug Fix

Changes

When a Bedrock Application Inference Profile is called through the Anthropic /v1/messages endpoint (the path Claude Code uses), cache_control was silently dropped during the Anthropic to OpenAI message translation, so prompt caching never activated. Several users reported large unexpected Bedrock bills from this since the same profile cached correctly through /v1/chat/completions.

The root cause is in litellm/llms/anthropic/experimental_pass_through/adapters/transformation.py. _add_cache_control_if_applicable() only preserves cache_control when is_anthropic_claude_model() returns true, and that helper looks for the substrings anthropic or claude in the model string. An Application Inference Profile model is an ARN like bedrock/converse/arn:aws:bedrock:us-east-1:ACCOUNT:application-inference-profile/ID, which contains neither substring, so the check failed and the directive was dropped before reaching the Bedrock Converse cachePoint conversion.

The fix adds a small is_bedrock_arn_model() helper and uses it as an additional condition in _add_cache_control_if_applicable() only. I deliberately did not broaden is_anthropic_claude_model() itself, because that helper also gates thinking to reasoning_effort translation; widening it would let thinking pass through unmodified for a genuinely non-Claude Bedrock profile and break that request. Scoping the change to cache control keeps the blast radius to the one path that was broken, and preserving cache_control is safe even for the rare non-Claude ARN since Bedrock Converse ignores cachePoint blocks it doesn't support rather than rejecting the request.

Tests live in the mapped file tests/test_litellm/llms/anthropic/experimental_pass_through/adapters/test_anthropic_experimental_pass_through_adapters_transformation.py. test_cache_control_preserved_for_bedrock_arn_inference_profile is the regression for this issue and fails on main with a KeyError on the missing cache_control. test_is_bedrock_arn_model pins the ARN-plus-bedrock detection including the GovCloud partition and rejects non-ARN bedrock model ids and non-bedrock ARNs. test_cache_control_fix_does_not_broaden_claude_detection guards the scoping decision so a future change can't quietly widen is_anthropic_claude_model and regress thinking translation.

This is the same problem and approach validated in the discussion on the stale community PR #26627; this revives it on top of current internal staging with added regression coverage so it can land


Generated by Claude Code

…dapter

Bedrock Application Inference Profile ARNs contain neither "anthropic" nor
"claude", so is_anthropic_claude_model could not detect them and the
/v1/messages adapter silently dropped cache_control during the Anthropic to
OpenAI translation. Prompt caching never activated for these models, while the
same profile cached correctly through /v1/chat/completions.

Add an is_bedrock_arn_model check scoped to _add_cache_control_if_applicable so
cache_control is preserved for ARN-based models without broadening the shared
is_anthropic_claude_model helper, which also drives thinking translation.

Fixes #26625
@mateo-berri

Copy link
Copy Markdown
Contributor Author

Superseded by #29823, which carries the same change on a litellm_-prefixed branch per our internal naming convention. Closing this one


Generated by Claude Code

@mateo-berri mateo-berri closed this Jun 6, 2026
@codecov

codecov Bot commented Jun 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

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.

[Bug]: Prompt caching broken for Bedrock Application Inference Profiles via /v1/messages endpoint

1 participant