Skip to content

fix(bedrock): preserve adaptive-thinking signatures in streaming reasoning#1683

Merged
gold-silver-copper merged 1 commit into0xPlaygrounds:mainfrom
byQuexo:fix/bedrock-streaming-adaptive-thinking
Apr 28, 2026
Merged

fix(bedrock): preserve adaptive-thinking signatures in streaming reasoning#1683
gold-silver-copper merged 1 commit into0xPlaygrounds:mainfrom
byQuexo:fix/bedrock-streaming-adaptive-thinking

Conversation

@byQuexo
Copy link
Copy Markdown
Contributor

@byQuexo byQuexo commented Apr 28, 2026

Closes #1684.

Summary

Streaming counterpart to #1675. The non-streaming assistant_content.rs empty-text guard fixed in that PR has an analogue in streaming.rs: the ContentBlockStop handler skips emitting RawStreamingChoice::Reasoning whenever reasoning_state.content.is_empty(), even if a Signature delta has populated reasoning_state.signature. Bedrock adaptive thinking can produce exactly that shape — ReasoningContentBlockDelta::Signature with no preceding non-empty Text delta — and the signature is silently dropped.

Symptom

In a multi-turn streaming conversation against eu.anthropic.claude-opus-4-7 (or any Bedrock Anthropic model with adaptive thinking enabled), the consumer never receives a RawStreamingChoice::Reasoning { signature: Some(_), .. } event for signature-only blocks. On the next turn, replaying that conversation back to Bedrock fails with:

messages.N.content.0.thinking.signature: Field required

— the same error that motivated #1675 on the non-streaming path.

Fix

Extracted a finalize_reasoning(state) helper from the inline emission logic and relaxed the gate so a Reasoning chunk is emitted whenever either content or signature is present; both-empty is still skipped.

fn finalize_reasoning(
    state: ReasoningState,
) -> Option<RawStreamingChoice<BedrockStreamingResponse>> {
    if state.content.is_empty() && state.signature.is_none() {
        return None;
    }
    Some(RawStreamingChoice::Reasoning { ... })
}

The helper extraction makes the emission decision unit-testable — the original inline form had no driver for the stream! block. Four cases are now covered:

  • content + signature → emits Text block with both
  • signature only → emits Text block with empty text + signature (the bug fix)
  • content only → emits Text block with content + no signature
  • both empty → emits nothing (preserves prior behavior)

Verification

Confirmed end-to-end against real Bedrock with eu.anthropic.claude-opus-4-7 and thinking={type: adaptive}, output_config={effort: high}:

[smoke] Reasoning emitted: text_len=0, sig_prefix=Er0OClkIDRAB
[smoke] totals: reasoning_chunks=1, text_chunks=0
test streaming_emits_reasoning_signature_for_adaptive_thinking ... ok

text_len=0 with a populated signature is the exact shape that pre-fix would have been dropped at the !content.is_empty() guard.

Test plan

  • cargo test -p rig-bedrock --lib (70 + 4 new tests pass)
  • cargo clippy -p rig-bedrock --tests -- -D warnings
  • cargo fmt -p rig-bedrock --check
  • Live verification against Bedrock Opus 4.7 with adaptive thinking (output above)

Notes

@gold-silver-copper gold-silver-copper added this pull request to the merge queue Apr 28, 2026
Merged via the queue into 0xPlaygrounds:main with commit e91cda9 Apr 28, 2026
6 checks passed
@github-actions github-actions Bot mentioned this pull request Apr 28, 2026
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.

bedrock: streaming reasoning drops adaptive-thinking signatures when text is empty

2 participants