Repository navigation
Conversation
ChatViaResponses dropped reasoning on the floor: chatResponseFromResponses ignored reasoning output items and responsesChatStream had no case for reasoning delta events. An Anthropic client on /v1/messages with thinking enabled against a Responses-only upstream (chatgpt/Codex) never saw a thinking block — silent loss of a feature the client asked for. Non-streaming: reasoning item summaries join onto the message's reasoning_content extra field. Streaming: reasoning_summary_text and reasoning_text deltas emit as reasoning_content chunk deltas. Both ride the existing chat dialect handling (anthropicapi renders them as unsigned thinking blocks; the streaming codec relays the member). Refs ENTERPILOT#1085
…nses CodeRabbit review on weselben-orion#11: - chatToResponsesRequest asked only for reasoning.effort; the Responses API emits summaries only on request, so the converter had nothing to render in the real flow. Reasoning gains an optional Summary member, set to "auto" whenever the request asks for reasoning (off efforts excluded). - Non-streaming joined reasoning items with no separator; items now join with the same \n\n reasoningSummaryText uses between parts, empty items contributing nothing. - Streaming ignored summary_index; an advancing index per item now emits the separator delta, an omitted index forwards untouched. Refs ENTERPILOT#1085
ADR-0011's ladder is a living table (edited in place by ENTERPILOT#973 and ENTERPILOT#963; the doc itself says 'add a row when an adapter gains a rule'): record the Responses reasoning-summary -> reasoning_content rendering rule and the gateway-set reasoning.summary=auto request member.
…ream Second CodeRabbit pass: - Streaming emitted no separator between reasoning items, so two items merged into one run-on string while the non-streaming converter joins them with \n\n. The last text-contributing item is now tracked and an item boundary emits the item separator. - An empty delta at a new summary_index emitted a leading separator and advanced the tracked index; empty deltas carry no text and now skip all separator and index handling, matching the non-streaming filter of empty parts.
- ADR-0011: qualify the reasoning.summary ladder row — the gateway sets it only for a non-off effort (cubic) - streaming: skip empty response.reasoning_text.delta events like the summary handler already does (cubic) - request translation: drop echoed reasoning_content instead of rejecting the turn, so multi-turn chat requests keep working after the first response carried reasoning (greptile) - reasoning effort override: preserve a client-sent reasoning.summary when replacing the effort (coderabbit)
Preview approval requiredThis pull request comes from a fork, so Mintlify won't build a preview until someone with write access approves commit |
Contributor
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
A provider that streams reasoning between the argument deltas of a function call made the Messages stream converter close the tool_use block when the reasoning opened a thinking block. The remaining argument deltas then landed on a stopped block index, which no Anthropic client accepts. The content that arrives while a tool_use block streams its arguments is now held back and rendered once the tool call is complete: when the next tool call starts, or when the turn ends. The reasoning is not lost, and the tool_use block never closes before its arguments are done. The buffered path is unchanged: the canonical chat shape carries the reasoning in one member, so the thinking block leads the answer text and the tool calls, as the Anthropic contract requires.
The reasoning held back while a tool call streamed its arguments opens a thinking block of its own when it is flushed. That block was never closed: the next tool block opened on top of it, and the message ended with it still open. A block that is opened and never closed leaves the client with a dangling index, because the Anthropic stream allows exactly one open block at a time. The block is now closed before the next block opens and before the message ends. The order of the flush and the close matters in both places: the flush opens the block of the held content, so the close has to follow it.
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rebase notice
This branch is based on the current head of PR #1343. Rebase it onto updated
mainafter PR #1343 merges, before marking this draft ready for review.Scope
Follow-up for #1344, which is a sub-issue of #723. Verify whether interleaved Anthropic thinking blocks and tool calls preserve ordering across the full Messages → chat → Responses → chat → Messages path, and fix the protocol defects the verification exposes.
What the verification found
Two defects in the Anthropic Messages stream converter, both on the path where a Responses-only upstream interleaves reasoning with the argument deltas of a tool call.
A tool_use block closed before its arguments were done. Anthropic streams one content block at a time. When reasoning arrived between the argument deltas of a call, the reasoning opened a thinking block, which closed the tool_use block; the remaining argument deltas then landed on a stopped block index. No Anthropic client accepts that.
The content that arrives while a tool_use block streams its arguments is now held back and rendered once the tool call is complete: when the next tool call starts, or when the turn ends. The reasoning is not lost and the tool_use block never closes before its arguments are done.
The thinking block the held reasoning opened was never closed. The flush opens a block of its own. That block was left open: the next tool block opened on top of it and the message ended with it still open, which leaves the client with a dangling index. It is now closed before the next block opens and before the message ends.
Ordering: what is preserved and what is not
The streaming path preserves the upstream order, because it emits the blocks as they arrive:
thinking A → tool call A → thinking B → tool call Breaches the client in that order.The buffered path cannot. The canonical chat shape carries the reasoning in one member beside the answer text and the tool calls, so the buffered response renders
thinkingfirst, thentext, thentool_use. That is what the Anthropic contract asks for, and the upstream interleaving is not representable in that shape. This is documented in ADR-0011 rather than papered over.The two directions are separate contracts, and ADR-0011 now states both: a signed
thinkingblock in the request history is the client's artifact and is not replayed to a Responses-only upstream, while the upstream's reasoning summary is rendered as athinkingblock in the response with an empty signature.Tests
internal/server/messages_interleaved_test.godrives the real Messages handler over the real ChatGPT provider against a fake Responses upstream, reusingechotestandprovidertest:internal/anthropicapi/stream_test.gopins the two defects at the converter level, andinternal/providers/chatgpt/interleaved_reasoning_test.gocovers the composed path.Each regression test fails on the unfixed code.
Verification
go build ./...,gofmt,go vet,golangci-lint(0 issues), the full./internal/...suite,-raceon the touched packages, and the repository's test conventions all pass.