Repository navigation
fix(openai): replay reasoning signatures on function calls - #13
Merged
arjunkmrm merged 2 commits intoOct 15, 2026
Conversation
Some Responses-compatible providers return the model's opaque reasoning state as `encrypted_content` on `function_call` items. In stateless (`store: false`) tool loops the client must send it back unchanged, otherwise the model loses its reasoning state between turns. - Accept `encrypted_content` on function call items in the request and response schemas. - Keep it in tool-call part metadata for generated responses and for streamed calls emitted on argument or item completion. - Replay it on the `function_call` input item. - Declare the matching tool-call metadata field in the compatible provider so the shared module augmentation stays identical.
A stream can send a function call's `encrypted_content` only on `response.output_item.done`, after `response.function_call_arguments.done`. Emitting the call on argument completion dropped the signature. - Record arguments on `response.function_call_arguments.done` and emit the call on `response.output_item.done` with the completed item's `encrypted_content`. - Emit calls whose item never completes at `response.completed`, `response.incomplete` or `response.failed`, in output order, before the finish part. - Cover a signature only on the completed item and a stream without item completion.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
arjunkmrm
force-pushed
the
fix/openai-replay-reasoning-signatures
branch
from
October 15, 2026 08:40
2f6d65a to
29fb4e7
Compare
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.
Some providers that serve reasoning models over the Responses protocol return the model's opaque reasoning state as
encrypted_contentonfunction_calloutput items, not only onreasoningitems. A stream can send this value only onresponse.output_item.done, afterresponse.function_call_arguments.done. In a stateless (store: false) tool loop, the client must send the value back unchanged on the next request. Before this change it was lost in four places:function_callinput item did not include it.Without the value, the model can lose its reasoning state between turns, and a later turn can complete with an empty output.
Changes:
OpenAiSchema: thefunction_callitem accepts an optionalencrypted_content(string or null). One schema covers request input and response output, so the field survives response decoding, stream event decoding and request encoding.OpenAiLanguageModel: generated responses keep the value asmetadata.openai.encryptedContenton the tool-call part. Reasoning parts already use this metadata key.OpenAiLanguageModel: streaming records the arguments whenresponse.function_call_arguments.donearrives. It emits the call whenresponse.output_item.donearrives, with that item'sencrypted_content. The value fromresponse.output_item.addedis the fallback.OpenAiLanguageModel: if a call's item never completes, the call is emitted atresponse.completed,response.incompleteorresponse.failed. These calls are emitted in output order, before the finish part. Without this, a stream that never completes the item would lose calls that the old argument-completion path emitted.OpenAiLanguageModel:prepareMessagessendsencryptedContentfrom tool-call part options asencrypted_contenton thefunction_callinput item. Reasoning items keep their existing replay and stay in the received order relative to function calls.ToolCallPartOptions/ToolCallPartMetadata: declareencryptedContent?: string | null. The OpenAI-compatible package declares the same field. Both packages augment the same Effect module interfaces, so their declarations must stay identical.CLAVIA_PATCHES.md: add a "Function call signatures" row and section.An explicit
include: ["reasoning.encrypted_content"]already reaches the request for model ids that are not in the built-in reasoning-model list. The new tests check this together with the replay. OpenAI requests are unchanged:encrypted_contentreplay without the key.includevalues are unchanged.Verification:
pnpm test --run packages/ai/clavia/test/LanguageModel.test.ts packages/ai/clavia/test/ProviderStreams.test.ts packages/ai/openai/test/OpenAiLanguageModel.test.ts packages/ai/anthropic/test/AnthropicLanguageModel.test.ts packages/ai/openai-compat/test/OpenAiLanguageModel.test.ts: 5 files, 202 tests passed.pnpm test --run packages/ai/openai/test packages/ai/openai-compat/test packages/ai/clavia/test: 10 files, 255 tests passed.reasoningandfunction_callitems carry their encrypted content in order.gpt-4o-miniandgpt-5withstore: false.tool-callsfinish.pnpm lint-fix,pnpm lintandpnpm checkpass.node scripts/clavia-release.mjs packandcheckpass. The packed consumer runs withskipLibCheck: false(6 files, 101 tests) and resolves a singleeffect@4.0.0-rc.115.pnpm jsdocs --checkstill reports three existing issues, all in files this PR does not touch (packages/ai/clavia/src/ToolResult.tsandpackages/effect/src/unstable/ai/McpSchema.ts).Release note: publish
@tardie/ai-openaiand@tardie/ai-openai-compattogether, because their module augmentations must match.🤖 Generated with Claude Code