Skip to content

fix(ai): bind Bedrock Claude thinking blocks by default - #51959

Merged
rekram1-node merged 3 commits into
v2from
bedrock-block-binding
Sep 29, 2026
Merged

rekram1-node merged 3 commits into
v2from
bedrock-block-binding

Conversation

@rekram1-node

@rekram1-node rekram1-node commented Sep 28, 2026 •

Copy link
Copy Markdown
Collaborator

Issue for this PR

No linked issue.

Type of change

  • Bug fix

What does this PR do?

Claude 5.1+ binds each thinking signature to the system prompt, tools, and messages above it. The Anthropic route already asks the API to drop affected blocks instead of failing (block_binding.prefix_mismatch_behavior: "drop_block" plus the thinking-binding-controls-2026-08-01 beta, #47884), and v1 did the same for Bedrock through its patched SDK (#46653, #46848). The v2 Bedrock Converse protocol never sent either, so a rebuilt prefix (prompt rebuild, tool change, compaction) fails the request on any account and model that enforces the check.

Bedrock supports this on Converse: AWS documents passing the beta and thinking.block_binding through additionalModelRequestFields. It is on by default the same way it is for Anthropic:

  • Models: Claude 5.1+ by ID, with compatibility.supportsThinkingBlockBinding as an override in both directions. The detection moves into protocols/utils/claude-model.ts so both protocols share it; Anthropic behavior is unchanged.
  • Default: drop_block, injecting adaptive thinking where none is configured. Explicit settings from providerOptions or http.body win field by field, and disabled thinking is left alone. This mirrors applyThinkingBindingDefault in the Anthropic route.
  • Beta: Converse takes betas in additionalModelRequestFields.anthropic_beta, so it is set in the request body whenever the body carries block_binding (Bedrock rejects block_binding without it). Like any other http.body field, a caller-supplied anthropic_beta replaces it, so someone who sets their own betas on a Claude 5.1+ model must include this one or set thinking themselves. I could not find anyone doing this today; the documented betas users set on Bedrock (1M context) target Claude 4.x.

Not covered: I could not exercise Fable 5.1 itself because my Bedrock account has no data-retention access to it. Opus 5.5, which enforces the same check, is what the live tests use. Converse does not return input_transformations, so dropped blocks are not reported.

How did you verify your code works?

  • New unit tests for the request body: default for 7 Claude 5.1+ IDs, 9 IDs that must stay untouched, manual budget with top_k, disabled, and compatibility overrides. 9 of the 19 fail against the base source; the rest are negative controls.
  • Two recordings against real Bedrock (Opus 5.5, Converse stream): the bare default is accepted, and a session whose system prompt changes between turns succeeds by default while the same replay with error gets Bedrock's 400 bound to a different conversation. The recorded requests include the high variant's display and effort merged beside the default. Both fail against the base source with the missing block_binding and anthropic_beta in the diff.
  • Ran the real V2 CLI against Bedrock through a logging proxy with the high variant. Both requests, including a tool-loop continuation replaying signed thinking, carried block_binding and the beta and returned 200. After denying tools mid-session (system hash changed and tools went 13 to 10) the default still returned 200, while forcing error in config returned 400 The system prompt and the tools list both differ.
  • packages/ai full suite (1625 pass), the Bedrock, variant and provider tests in packages/core, and bun run check at the root.

Screenshots / recordings

Not applicable.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Claude 5.1+ binds each thinking signature to the system prompt, tools, and messages above it. Bedrock Converse never sent block_binding or its beta, so a rebuilt prefix (prompt rebuild, tool change, compaction) fails the request on accounts and models that enforce the check.

Mirror the Anthropic route: default to prefix_mismatch_behavior drop_block for Claude 5.1+ with adaptive thinking where none is set, honor explicit thinking settings from providerOptions or http.body, and send the thinking-binding-controls beta in additionalModelRequestFields.anthropic_beta, unioned with betas the caller already set because http.body replaces arrays.
Apply the default through a helper that mirrors the Anthropic route so fromRequest keeps a single thinking value, and skip binding only for disabled thinking instead of decoding the overlay type.
Converse takes Anthropic betas in additionalModelRequestFields, so set anthropic_beta in fromRequest whenever the body carries block_binding instead of rewriting http.body in a transport wrapper. Like any other http.body field, a caller-supplied anthropic_beta replaces it.
@rekram1-node
rekram1-node enabled auto-merge (squash) September 29, 2026 04:38
@rekram1-node
rekram1-node merged commit 34a8938 into v2 Sep 29, 2026
10 checks passed
@rekram1-node
rekram1-node deleted the bedrock-block-binding branch September 29, 2026 04:38
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