feat(protocols): ✨ Preserve encrypted reasoning content across multi-turn replay#14
Merged
Conversation
Upgrade the Responses codec to handle Codex CLI's non-standard
input/output item types and reasoning extensions on top of the
reasoning-id-encrypted baseline, enabling TiyGate to sit between
standard Responses clients and Codex providers without semantic
loss.
Codex tool call items:
- local_shell_call / local_shell_call_output mapped to IR
ToolCall (name="local_shell", action as arguments, call_id
set for Responses round-trip fidelity) and ToolResult
- custom_tool_call / custom_tool_call_output mapped to ToolCall
(input wrapped as {"input": "..."} JSON arguments) and
ToolResult
Codex opaque items preserved for same-protocol replay:
- tool_search_call, tool_search_output, agent_message,
compaction, compaction_trigger, context_compaction stored
in extensions["codex_opaque_items"] and restored on
encode_request; silently dropped on cross-protocol egress
Reasoning extensions:
- Parse reasoning.summary into IR ThinkingConfig.summary
- Store full reasoning object in extensions["reasoning_full"]
for verbatim same-protocol replay (preserves summary etc.)
- Re-emit reasoning.summary on encode when no full replay
path is taken
Other passthrough:
- Add client_metadata to responses_extra preserve list
Streaming:
- Handle local_shell_call / custom_tool_call in
response.output_item.added so saw_function_call is set,
producing ToolCalls finish_reason instead of Stop
All changes are additive: existing function_call, reasoning,
and message handling is unchanged. No breaking changes to
standard Responses protocol behavior.
Guard against `"usage": null` in both streaming and non-streaming decoders to prevent zero-valued Usage structs from poisoning cross-protocol encoders. OpenAI sends `"usage": null` on every non-final chunk when `stream_options.include_usage` is true. Without this guard, the decoder would emit a zero-valued `StreamPart::Usage` that causes downstream encoders (e.g. Responses) to trigger `response.completed` prematurely, discarding the real usage that arrives later. Added regression tests verifying null usage is correctly ignored in both streaming and non-streaming code paths.
Allow overriding the multimodal request body size limit through the `TIYGATE_MAX_MULTIMODAL_BODY_BYTES` environment variable. Also increase the default limit from 32 MiB to 64 MiB to accommodate larger payloads.
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.
Summary
StreamPart::ReasoningDeltawith optionalidandencrypted_contentfields so provider-issued reasoning item ids and encrypted reasoning payloads survive through the IR and can be replayed verbatim on subsequent turns.encrypted_contentacross request decode, response decode, and streaming encode/decode, including empty-summary encrypted-only reasoning items (summary: []).None(no behavioral change).output_item.donesummary shape.Test Plan
cargo test -p tiygate-protocols --all-features— 314 passedcargo clippy -p tiygate-protocols --all-targets --all-features -- -D warnings— cleantest_stream_encoder_encrypted_only_reasoning_empty_summaryassertssummary: [],encrypted_content, and providerrs_...id onoutput_item.done🤖 Generated with TiyCode