fix(providers/openai): emit item_reference for reasoning when store=true#30
Open
ibetitsmike wants to merge 1 commit intocoder_2_33from
Open
fix(providers/openai): emit item_reference for reasoning when store=true#30ibetitsmike wants to merge 1 commit intocoder_2_33from
ibetitsmike wants to merge 1 commit intocoder_2_33from
Conversation
When an OpenAI Responses API reasoning model produces both a reasoning item and a provider-executed item (e.g. web_search_call) in the same response, the API requires the reasoning item to be replayed alongside the following item on the next turn. Without this pairing the API rejects the request with: Item 'ws_xxx' of type 'web_search_call' was provided without its required 'reasoning' item: 'rs_xxx'. Previously toResponsesPrompt unconditionally skipped reasoning items during replay, both for store=true and store=false. The skip was introduced to fix charmbracelet#181 (inline OfReasoning replay was rejected when no following item was present), but it now produces the dual failure mode for any conversation that combines reasoning with a provider-executed tool call or function call. Fix: when store=true, replay the reasoning item via item_reference using the persisted ItemID stored in ResponsesReasoningMetadata. This mirrors how provider-executed tool calls are already replayed and matches the OpenAI documented contract for stored items. When store=false, server-side reasoning IDs are ephemeral and cannot be referenced; provider-executed tool calls are likewise skipped, so there is nothing to pair with. Behavior in this mode is unchanged. Tests: - TestResponsesToPrompt_ReasoningWithStore: now asserts that store=true emits item_reference for the reasoning item; covers the no-ItemID skip path; keeps the store=false skip assertion. - TestResponsesToPrompt_ReasoningWithWebSearchCombined: full reasoning + web_search_call replay with assertions on order (user, item_reference(rs), item_reference(ws), text, user). - TestResponsesToPrompt_ReasoningWithFunctionCallCombined: reasoning + function_call + function_call_output round-trip with order assertions. Generated by Coder Agents.
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.
Disclaimer: opened by a Coder Agent on behalf of @ibetitsmike.
Fixes the
gpt-5.5 xhigh(and other Responses API reasoning models) follow-up turn failure when an assistant turn produces reasoning + a provider-executedweb_search_call(or any item that requires a preceding reasoning item).Problem
When
Store: trueis enabled and an assistant turn contains both areasoningitem and a following provider-executed item (e.g.web_search_call), the OpenAI Responses API requires the reasoning item to be replayed alongside the following item on the next turn. Otherwise it rejects the request with:Previously
toResponsesPromptunconditionally skipped reasoning items. The skip was introduced to fix charmbracelet#181 (inlineOfReasoningreplay was rejected when no following item was present), but it produces a dual failure mode for any conversation that combines reasoning with a provider-executed tool call or function call.Fix
store=true: replay the reasoning item viaitem_referenceusing the persistedItemIDfromResponsesReasoningMetadata. Mirrors how provider-executed tool calls are already replayed and matches the OpenAI documented contract for stored items.store=false: server-side reasoning IDs are ephemeral and cannot be referenced. Provider-executed tool calls are likewise skipped, so there is nothing to pair with. Behavior is unchanged.ItemIDis missing from metadata, the reasoning part is skipped (rest of the assistant message still replays).Inline
OfReasoningreplay is intentionally not used to avoid re-introducing the failure mode that charmbracelet#181 fixed.Tests
Updated and added in
providers/openai/openai_test.go:TestResponsesToPrompt_ReasoningWithStore— now assertsstore=trueemitsitem_referencefor the reasoning item; covers the no-ItemIDskip path; keeps thestore=falseskip assertion.TestResponsesToPrompt_ReasoningWithWebSearchCombined— full reasoning +web_search_callreplay with order assertions (user, item_reference(rs), item_reference(ws), text, user).TestResponsesToPrompt_ReasoningWithFunctionCallCombined— reasoning +function_call+function_call_outputround-trip with order assertions.Full suites pass:
go test ./providers/openai/...go test ./...Related
coder/coderPR will bump the pin and add chatd integration tests + a defensive sanitizer.Implementation plan and decision log
Follow-up to closed PR #7. Plan kept the change minimal:
TestResponsesToPrompt_ReasoningWithStoreand added two combined-replay tests; all failed against the previous unconditional skip.continuewith astore-gateditem_referenceemission usingGetReasoningMetadata. Build + tests green.Decisions:
item_referenceover inlineOfReasoningto avoid re-introducing fix(openai): skip reasoning items in Responses API replay charmbracelet/fantasy#181's failure mode.ItemIDkeeps existing behavior (skip) instead of erroring.store=falsepath unchanged.