Repository navigation
fix(anthropic): register thinking-binding-controls-2026-08-01 in beta headers config - #41203
Conversation
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
… headers config Anthropic's preserved-thinking controls (`thinking.block_binding`, Claude Fable 5.1) are only accepted alongside the beta header `thinking-binding-controls-2026-08-01`. The proxy forwards the body field untouched but `filter_and_transform_beta_headers` drops the header because it has no entry in `anthropic_beta_headers_config.json`, so Bedrock and Vertex reject the request with "thinking.adaptive.block_binding: Extra inputs are not permitted". Map the header for anthropic, bedrock, bedrock_converse, vertex_ai and databricks (same beta name on all of them per Anthropic's docs). azure_ai is left null pending verification on Foundry.
1b50ef8 to
c19a199
Compare
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit c19a199. Configure here.
mateo-berri
left a comment
There was a problem hiding this comment.
LGTM. Thanks for the contribution!
TLDR
Problem this solves:
thinking.block_binding(preserved thinking, Claude Fable 5.1) needs betathinking-binding-controls-2026-08-01How it solves it:
anthropic_beta_headers_config.jsonfor anthropic, bedrock, bedrock_converse, vertex_ai, databricksazure_aileftnull(not verified on Foundry)User Flow
Before: a developer sending Anthropic's documented preserved-thinking opt-in to a Claude Fable 5.1 deployment behind the gateway gets a 400 from the provider
POST https://litellm-domain/v1/messageswith headeranthropic-beta: thinking-binding-controls-2026-08-01and"thinking": {"type": "adaptive", "block_binding": {"prefix_mismatch_behavior": "drop_block"}}against avertex_ai/claude-fable-5-1orbedrock/us.anthropic.claude-fable-5-1deploymentinvalid_request_errorwith the provider textthinking.adaptive.block_binding: Extra inputs are not permittedblock_bindingmakes the request succeed but they loseinput_transformationsand any way to enforce the prefix check through the proxyAfter: the same request succeeds and the API reports what it dropped
POST https://litellm-domain/v1/messageswith the same header and bodycontentplus a top-levelinput_transformations: []input_transformationslists the dropped blocks withreason: "prefix_binding_mismatch"(or the documented 400 under"error")Relevant issues
Fixes #41202
Affected release
Linear ticket
Resolves LIT-7852
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
uv run pytest tests/test_litellm/test_anthropic_beta_headers_filtering.py -vpasses locally (27 passed; the 5 new cases fail on unpatchedmain).tests/test_litellm/llms/bedrock/messages/invoke_transformations/test_anthropic_claude3_transformation.pystill passes (112)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
QA run at this PR's tip
c19a19999f(after) against its merge base15bd8b0e4a(before). Each leg is its own worktree and venv, booted withpython litellm/proxy/proxy_cli.py --config config.yaml --port <random free port> --num_workers 2 --detailed_debug(2 uvicorn workers per leg, before on 40307, after on 25461) withLITELLM_LOCAL_ANTHROPIC_BETA_HEADERS=Truein the env so each leg reads its own bundledanthropic_beta_headers_config.json. Without that flag the proxy downloads the config frommainat boot, which is why the after leg kept returning 400 on its first boot: it was running main's config, not this branch's. Every request below hit real Bedrock (us-east-1), first-party Anthropic, Databricks and Vertex AI, all paid. The proof cases use the Fable 5.1 entries, plus the Vertex Sonnet 5 entry for the reason given in the Vertex AI section; the blast radius section further down uses the rest$LITELLM_KEYis the master key fromgeneral_settings. The/v1/messagescases send the beta as ananthropic-betaheader the way the Anthropic SDK does; the/v1/chat/completionsand/v1/responsescases send it throughextra_headers, which is how OpenAI-format clients reach itBefore (merge base
15bd8b0e4a, port 40307)/v1/messages -> bedrock-fable
/v1/messages -> vertex-fable
/v1/messages -> anthropic-fable
/v1/chat/completions -> bedrock-fable
/v1/chat/completions -> anthropic-fable
/v1/responses -> anthropic-fable
After (
c19a19999f, port 25461)/v1/messages -> bedrock-fable
/v1/messages -> vertex-fable
/v1/messages -> anthropic-fable
/v1/chat/completions -> bedrock-fable
/v1/chat/completions -> anthropic-fable
/v1/responses -> anthropic-fable
Vertex AI
Both legs got HTTP 429 from Vertex AI for Claude Fable 5.1 for the whole run, in
us-east5(above),globalandeurope-west1:Quota exceeded for aiplatform.googleapis.com/online_prediction_input_tokens_per_minute_per_base_model with base model: anthropic-claude-fable-5-1and theglobal_online_prediction_requests_per_base_modelvariant. A direct call to Vertex without the proxy answers the same 429 in under a second, so the project has no Fable 5.1 quota. It does have Claude Sonnet 5 quota inglobal, and Sonnet 5 shows the same symptom directly against Vertex: HTTP 400thinking.adaptive.block_binding: Extra inputs are not permittedwithout the header and HTTP 200 withinput_transformations: []with it. So thevertex_aileg runs onvertex_ai/claude-sonnet-5atglobal(thevertex-sonnet5-globalentry above), on the same two worktrees rebooted on the same portsBefore (merge base
15bd8b0e4a, port 40307):/v1/messages-> vertex-sonnet5-globalAfter (
c19a19999f, port 25461): same requestClaude Code
Claude Code v2.1.277 pointed at each proxy through
ANTHROPIC_BASE_URLwith modelbedrock-fable, as a regression check on the largest/v1/messagesclient. Both legs answer. Through a custom base URL Claude Code sends neitherthinking-binding-controls-2026-08-01in itsanthropic-betalist norblock_bindingin the body, so it cannot show this symptom; these panes only show the PR leaves its path aloneBefore (15bd8b0):
After (c19a199):
Blast radius
Same two proxies and ports, driven through every
filter_and_transform_beta_headerscall site with the beta on models that do and do not implement it; the checkbox at the bottom of this description is that pass's verdict. The one behavior change beyond the fix is Bedrock's own reply to the forwarded header on a model that lacks it (Claude Sonnet 4.5 here), where the merge base silently dropped the header and answered 200Before:
/v1/messages-> bedrock-sonnet45 with the beta header (dropped by the filter)After: same request (header forwarded, Bedrock answers 400)
Before:
/v1/chat/completions-> bedrock-sonnet45 withextra_headersAfter: same request
After: other listed betas the model lacks stay 200 on Bedrock
After: the CI e2e suite's request shape (every non-null
anthropicheader of this config on Sonnet 4.5, first-party)Every other case answered the same on both legs:
bedrock/invoke/chat (which forwards client betas unfiltered on both trees), Databricks Opus 5 (accepts the header, rejectsblock_bindingin the body on both trees), first-party Sonnet 4.5 (accepts the header), an unknown beta (still dropped) and the no-beta regression guards. The merged tree (origin/mainata2626726a2plus this tip, clean merge) answered identically to the tip on all 17 scenarios, the Vertex Sonnet 5 case includedObservations
main/reload/anthropic_beta_headers/v1/chat/completionsand/v1/responsesfixed too viaextra_headersbedrock/invoke/chat forwards client betas unfiltered; PR unrelatedblock_bindingbody on both legsorigin/mainplus tip) matches tipVerdict: PASS. Before is HTTP 400
thinking.adaptive.block_binding: Extra inputs are not permittedand after is HTTP 200 withinput_transformations: []on Bedrock and first-party Anthropic across/v1/messages,/v1/chat/completionsand/v1/responses, and the same 400 to 200 flip on Vertex AI/v1/messageswith Claude Sonnet 5 atglobal, since the project has no Fable 5.1 quota on VertexType
🐛 Bug Fix
Caveats (if any)
Medium
is_empty_thinking_block, run on every/v1/messagesrequest) dropsthinkingblocks whose text is empty even when they carry a validsignature. Claude Fable 5.1 returns exactly that shape by default (display: "omitted"), so replayed thinking never reaches the provider and the prefix check has nothing to bind, which is why the runs above showdropped: 0even when an earlier turn is edited. This PR is the prerequisite for that fix: once the block is forwarded,block_bindingneeds the header or the provider 400sLow
invalid beta flagwhen a client sendsanthropic-beta: thinking-binding-controls-2026-08-01to a Bedrock model that does not implement it (Claude Sonnet 4.5 in the blast radius run above), where the merge base answered HTTP 200 after silently dropping the header. The 400 is Bedrock's own reply to a beta the client asked for: first-party Anthropic accepts the same header on Sonnet 4.5, Databricks ignores it, Bedrock still accepts the other listed betas the model lacks (compact-2026-01-12,effort-2025-11-24,computer-use-2025-11-24all 200), and neither Claude Code nor the Anthropic SDK sends this beta on its own. Left as is: a per-model allowlist for one header would be the only per-model rule in the config, would go stale the day Bedrock accepts the flag on more models, and would keep silently downgrading a feature the client explicitly asked for. Deployments on the default remote config pick this up at their next boot after the merge, or earlier throughPOST /reload/anthropic_beta_headers, together with the fixazure_aiis mapped tonull(dropped, as today) because I could not verify the header on Foundry; Anthropic states the same beta names apply on Bedrock and Google Cloud, which are the providers mapped hereglobalrather than the Claude Fable 5.1 deployment the issue names, because the QA project has no Fable 5.1 quota on Vertex (HTTP 429 in three regions, direct calls included). The mapping is per provider, not per model, so the samevertex_ailookup is what forwarded the header; what this run did not observe is Vertex's own reply to the header on Fable 5.1, which the issue reporter's 400 and the author's earlier run in this PR's history both showFinal Attestation