Skip to content

fix: replay OpenAI Responses reasoning from encrypted content - #63

Open
ibetitsmike wants to merge 4 commits into
mike/mcp-tool-media-batch-orderfrom
mike/openai-encrypted-reasoning
Open

ibetitsmike wants to merge 4 commits into
mike/mcp-tool-media-batch-orderfrom
mike/openai-encrypted-reasoning

Conversation

@ibetitsmike

@ibetitsmike ibetitsmike commented Oct 2, 2026 •

Copy link
Copy Markdown

OpenAI Responses reasoning was only replayed as an item_reference, which needs store=true. With the default store=false it was dropped from later requests, so reasoning models lost their chain of thought between tool steps and turns.

This captures the completed reasoning item (ID, encrypted_content, summary) from response.output_item.done and from the Generate output, marks that metadata Finalized, and replays it as a full reasoning input item regardless of store, before its function, computer, and web search items. Completed summaries are kept verbatim, so an empty summary stays []. Unfinalized metadata (stream placeholders and rows persisted before this field) keeps the old behavior: an item reference with store=true, skipped otherwise.

Finalized metadata also records whether the source request was stored. A following web_search_call item reference is replayed only when both the source and the destination request store; otherwise it is omitted, because a reference to an unstored item returns 404, while the reasoning is still replayed inline.

The follow-up request bodies of the six summary-thinking cassettes were updated offline to include the replayed reasoning items; they were not re-recorded live.

The base is mike/mcp-tool-media-batch-order, the unmerged branch coder/coder currently pins, so this diff contains only these changes. Consumed by coder/coder#30298.

Testing

  • go test ./..., go build ./..., go vet ./..., golangci-lint, and targeted race tests for providers/openai pass.
  • responses_reasoning_replay_test.go covers Generate and Stream capture, replay under store true and false, unfinalized fallbacks, and web search reference eligibility from stored and unstored sources. Red-green verified by disabling each gate.
  • Live behavior was verified through remote UAT of fix: replay stateless OpenAI reasoning across chat turns coder#30298 with real gpt-5-mini and gpt-5.4 requests: round 3 PASS at f8a25c7.

Review record

  • Independent test audits at fc95e43 and f8a25c7: PASS, no findings.
  • Codex: no major issues at f8a25c7, no review threads.
  • CI: build (Linux, macOS, Windows) and lint pass. govulncheck fails on GO-2026-6505 (otel v1.44.0) and GO-2026-6348 (grpc v1.83.0), which the open dependabot bumps fix; this PR does not change go.mod.

Xum, an AI coding agent, implemented, tested, and opened this PR on behalf of @ibetitsmike.

Capture encrypted_content, item ID and summary from the completed
reasoning output item (stream output_item.done and Generate output) and
mark that metadata Finalized. Replay finalized metadata with a blob as a
full reasoning input item regardless of store, keeping its position
before function, computer and web_search items. Unfinalized metadata
(stream placeholders and rows persisted before this field) keeps the
previous behavior: item_reference with store=true, skipped otherwise.

Update the recorded follow-up request bodies of the summary-thinking
cassettes to include the replayed reasoning items.
Empty summaries remain empty arrays rather than invented summary text entries.
…nses

Record whether the source response was stored on finalized reasoning
metadata and only replay a following web_search_call item reference
when it was. Reasoning from an unstored response is still replayed
inline from its encrypted content.
@ibetitsmike

Copy link
Copy Markdown
Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-03T06:26:38.649346Z f8a25c7 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. What shall we delve into next?

Reviewed commit: f8a25c79b3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@mafredri mafredri left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capturing the reasoning item from output_item.done and replaying it inline regardless of store is the right shape, and gating inline replay on Finalized keeps the output_item.added placeholders already stored in Coder chats away from the API. One concern, one check, two suggestions.

Suggestions:

  • The six summary-thinking cassettes (Azure gpt-5-mini, OpenAI gpt-5, OpenAI o4-mini) were edited offline, and the UAT through Coder ran gpt-5-mini and gpt-5.4. Could we re-record them, so each edited follow-up request has been accepted by the real API?
  • charmbracelet#407 replays the same item fields inline, but only for store=false, and has no Finalized gate, so it would also replay the .added placeholder blobs in rows persisted before the fix. Could we propose Finalized there, so the fork does not have to carry it?

Didn't review the tests in depth.

🤖 This review was automatically generated with Coder Agents.

continue
}
// Item-reference replay predates source storage metadata.
canReferenceWebSearch = !meta.Finalized || meta.SourceStoreEnabled

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concern: a web search the model emitted without a reasoning item before it is still never replayed. This is unchanged by the PR, but since it rewrites this gate it seems the place to fix it. OpenAI does emit those: gpt-4.1-mini (search), and gpt-5.4-mini and gpt-5.5 at effort none (open_page), returned [web_search_call, message] with no reasoning item in every run I tried. With store=true, a bare item_reference to the ws_ id was accepted, and for open_page it brought the page back into the next turn (gpt-5.5: 11,485 input tokens with the reference, 4,574 without). For search it only restores the query.

From probes (not documented), the reasoning item is only required when one preceded the search: the 400 names that specific rs_ id. The previous prompt part can't decide this: Coder drops empty-summary legacy reasoning, and Generate drops reasoning with neither summary nor encrypted_content, which leaves a bare ws_ reference that returns 400. I think it needs, per search, whether its source response was stored and which reasoning id preceded it, then a reference only when that reasoning item is in the input or there was none. Searches without that record (rows written before it) would keep today's gate. validateResponsesItemReferences (line 948) also rejects a bare ws_ reference, so it needs the same rule.

🤖

ID: done.Item.ID,
ProviderMetadata: fantasy.ProviderMetadata{
Name: state.metadata,
Name: finalResponsesReasoningMetadata(done.Item, params.Store.Value),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check: SourceStoreEnabled records the store the request asked for. For ZDR orgs OpenAI says store "will always be treated as false" (https://developers.openai.com/api/docs/guides/your-data), so a later ws_ reference to that response should 404. Responses echo store, readable via JSON.ExtraFields["store"] in Generate and on response.created in Stream, which arrives before the reasoning output_item.done. Not tested on a ZDR org. If ZDR responses echo false, could SourceStoreEnabled take the echoed value instead?

🤖

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants