chore(release): backport #30480 to stable/1.84.x and cut 1.84.9 - #30591
Conversation
…30480) * fix(integrations): cap Anthropic cache_control injection at 4 blocks Respect Anthropic's 4 cache_control breakpoint limit by counting client-supplied blocks, skipping messages that already carry cache_control, and stopping further auto-injection once the limit is reached. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(integrations): reserve cache slot for tool_config and short-circuit cap Address review feedback on the cache_control cap: break out of the injection loop before resolving target indices once the limit is reached, and reserve one of the four breakpoint slots when a tool_config injection point is present so the cachePoint appended by the Bedrock transform does not push the total past Anthropic's limit. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit fc9d789)
Greptile SummaryCherry-picks staging commit fc9d789 onto
Confidence Score: 4/5The backport is a clean patch-identical cherry-pick with six focused regression tests; the only rough edge is a log message that reports the wrong block limit when a tool_config reservation reduces the cap from 4 to 3. The hook's cap-and-count logic is correct, the client-TTL-preservation behavior is tested end-to-end, and no existing callers are broken. A minor logging inaccuracy in the warning message is the sole finding — the number printed is always 4 even when the effective injection limit is 3 due to the tool_config slot reservation. anthropic_cache_control_hook.py — specifically the warning message in
|
| Filename | Overview |
|---|---|
| litellm/integrations/anthropic_cache_control_hook.py | Core fix: adds MAX_CACHE_CONTROL_BLOCKS constant, counts client-supplied breakpoints before injecting, skips already-tagged messages, caps injection at 4, and reserves a slot for tool_config points. One minor logging inaccuracy (warning always prints 4 even when effective limit is 3 due to reservation). |
| tests/test_litellm/integrations/test_anthropic_cache_control_hook.py | Adds 6 regression tests covering the cap, client-TTL preservation, tool_config reservation, and end-to-end Bedrock payload shape; all use mocks, no real network calls. |
| pyproject.toml | Version bump from 1.84.8 to 1.84.9 in both [project] and [tool.commitizen] sections. |
Reviews (1): Last reviewed commit: "chore: refresh uv.lock for 1.84.9" | Re-trigger Greptile
| if limit_reached: | ||
| verbose_logger.warning( | ||
| f"AnthropicCacheControlHook: Reached the Anthropic limit of " | ||
| f"{MAX_CACHE_CONTROL_BLOCKS} cache_control blocks. Skipping further injection." | ||
| ) |
There was a problem hiding this comment.
The warning always prints
MAX_CACHE_CONTROL_BLOCKS (4) even when the effective cap passed to this method is 3 (because a tool_config reservation reduced max_blocks). In that scenario the log says "Reached the Anthropic limit of 4" while the actual stop was at 3, which will confuse anyone debugging a tool_config-capped request.
| if limit_reached: | |
| verbose_logger.warning( | |
| f"AnthropicCacheControlHook: Reached the Anthropic limit of " | |
| f"{MAX_CACHE_CONTROL_BLOCKS} cache_control blocks. Skipping further injection." | |
| ) | |
| if limit_reached: | |
| verbose_logger.warning( | |
| f"AnthropicCacheControlHook: Reached the injection limit of " | |
| f"{max_blocks} cache_control blocks. Skipping further injection." | |
| ) |
Relevant issues
Backports #30480 onto stable/1.84.x and cuts 1.84.9.
Anthropic and Bedrock Claude reject any request carrying more than 4 cache_control breakpoints with "A maximum of 4 blocks with cache_control may be provided." When a proxy model has cache_control_injection_points configured (for example role: system plus index: -1) and the client already sends cached system messages (Hermes-style), the AnthropicCacheControlHook was auto-injecting additional breakpoints without counting the client-supplied ones, pushing the total past 4 and turning otherwise valid requests into 400s. The fix caps auto-injection at the 4-breakpoint limit, counts client-supplied blocks toward it, skips messages that already carry cache_control so the client's TTL is preserved, reserves a slot when a tool_config injection point is present (the Bedrock transform appends a cachePoint), and stops once the limit is reached while honoring injection points in config order
The tip of stable/1.84.x (1.84.8) has shipped (tag v1.84.8, DockerHub image, and a published GitHub release all confirm it), so this backport bumps to 1.84.9
What is included
The pick is patch-identical to the staging commit (patch-id match; the only range-diff delta is the cherry-pick provenance footer), so there are no adaptation notes
Known noise on this line
None. The targeted test file passed cleanly at baseline before any pick (16 passed, 0 failed) once the proxy extra is installed so boto3 is available; the Bedrock-path tests fail only if that extra is missing, which is an environment artifact and not a code failure
Pre-Submission checklist
Screenshots / Proof of Fix
Live proxy on stable/1.84.x (worktree at the branch tip) against the real Anthropic API, using a model configured with both cache_control_injection_points (role: system and index: -1). The reproducer sends 4 system messages that already carry client cache_control plus a user message; before the pick the hook injects a 5th breakpoint and Anthropic rejects it, after the pick the request is capped at 4 and succeeds
Reproducer payload (
anthropic-cache-reproisanthropic/claude-haiku-4-5with the two injection points configured):{ "model": "anthropic-cache-repro", "max_tokens": 50, "messages": [ {"role":"system","content":[{"type":"text","text":"System block 0","cache_control":{"type":"ephemeral"}}]}, {"role":"system","content":[{"type":"text","text":"System block 1","cache_control":{"type":"ephemeral"}}]}, {"role":"system","content":[{"type":"text","text":"System block 2","cache_control":{"type":"ephemeral"}}]}, {"role":"system","content":[{"type":"text","text":"System block 3","cache_control":{"type":"ephemeral"}}]}, {"role":"user","content":"hello"} ] }Before the pick (baseline on stable/1.84.x tip):
After the pick (same request, same line, fix applied):
Sanity (both before and after): proxy health returns alive, a DB-backed virtual key generates, and a plain
anthropic-plaincompletion returns a real responseTargeted test delta on stable/1.84.x: 16 passed at baseline, 22 passed after the pick (the 6 regression tests the PR adds all pass on this line), 0 new failures
Gauntlet (behavioral, universal): SURVIVED on all three sub-claims; every identifier the pick introduces resolves on this tree, the 6 new regression tests plus the 16 pre-existing ones pass (22/22), and no existing caller of AnthropicCacheControlHook breaks (the rename of _process_message_injection into the new helpers leaves no orphaned callers). The adversarial edge investigator could not construct a single input that exceeds the 4-block cap
Type
🐛 Bug Fix
Changes
Cherry-pick of #30480 onto stable/1.84.x with a patch version bump to 1.84.9 and a lock refresh