Replay assistant reasoning as Anthropic thinking blocks in buildMessageParam - #600
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes Anthropic conversation replay for extended-thinking/tool-use by ensuring prior assistant reasoning (TextReasoningContent) is serialized back into Anthropic thinking / redacted_thinking content blocks (including the required signature), instead of being silently dropped.
Changes:
- Add
*message.TextReasoningContenthandling inbuildMessageParamto emitthinking/redacted_thinkingblocks and skip unsigned partial reasoning. - Add black-box request-capture tests validating correct replay behavior, ordering, and skipping of unsigned partials.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| provider/anthropicprovider/agent.go | Re-serializes TextReasoningContent into Anthropic thinking blocks so signatures round-trip across turns. |
| provider/anthropicprovider/agent_test.go | Adds request-body assertions to verify thinking/redacted_thinking replay and skipping unsigned partial reasoning. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
fdb090f to
5a91f90
Compare
This comment has been minimized.
This comment has been minimized.
5a91f90 to
9649fd1
Compare
This comment has been minimized.
This comment has been minimized.
…geParam buildMessageParam had no case for *message.TextReasoningContent, so a prior assistant thinking block (and its signature) produced on the inbound path was silently dropped when the message history was sent back to Anthropic. Add a case that replays it as a thinking block (signature + text) or a redacted_thinking block (data only), skipping unsigned streamed partials that would be rejected as invalid unsigned thinking blocks.
9649fd1 to
99ee6b6
Compare
This comment has been minimized.
This comment has been minimized.
# Conflicts: # provider/anthropicprovider/agent_test.go
This comment has been minimized.
This comment has been minimized.
# Conflicts: # provider/anthropicprovider/agent_test.go
This comment has been minimized.
This comment has been minimized.
|
Needs rebase. |
# Conflicts: # provider/anthropicprovider/agent_test.go
Parity Review: ✅ No issues foundThis PR fixes a bug in the unexported No exported Go APIs changed. The Cross-SDK parity check: The upstream Python implementation ( The
|
What
buildMessageParaminprovider/anthropicprovider/agent.goswitches over the outboundmsg.Contentsbut had no case for*message.TextReasoningContent. It handledTextContent,FunctionCallContent,FunctionResultContentandDataContentonly.The inbound path
buildBlockproduces exactly that type from Anthropic responses:anthropic.ThinkingBlock->TextReasoningContent{ProtectedData: Signature, Text: Thinking}anthropic.RedactedThinkingBlock->TextReasoningContent{ProtectedData: Data}So when a prior assistant turn's history was sent back (e.g. multi-turn tool loops with extended thinking), the reasoning block and its cryptographic signature were silently dropped.
Fix
Add a
*message.TextReasoningContentcase that replays the block, appended in iteration order (Anthropic emits reasoning before the rest of the turn):anthropic.NewThinkingBlock(ProtectedData, Text)anthropic.NewRedactedThinkingBlock(ProtectedData)Why
Preserving thinking-block signatures across turns is required for Anthropic extended-thinking with tool use; the signature must round-trip or the API rejects the replayed assistant turn. This matches the .NET/Python SDKs, which reserialize reasoning content back into thinking / redacted_thinking blocks rather than dropping them, keeping cross-SDK conversation replay behavior aligned.
Tests
Added black-box tests in the canonical
agent_test.go, driven through the exportedRun().Collect()path with the existing request-capturing httptest harness:TestAssistantReasoningReplayedAsThinkingBlock— asserts a thinking block carrying signaturesig123is emitted first, ahead of the tool_use block.TestAssistantRedactedReasoningReplayedAsRedactedThinkingBlock— asserts a redacted_thinking block with its data is emitted.TestAssistantUnsignedReasoningIsSkipped— asserts an unsigned partial is dropped.All three fail before the fix and pass after.
go build ./...,go vetandgo teston the package are green.