Add an opt-in WebSocket transport for Codex Responses - #523
Closed
mjlbach wants to merge 2 commits into
Closed
Conversation
Upgrade the Codex Responses endpoint to a WebSocket (OpenAI-Beta: responses_websockets=2026-02-06), send the request body as one response.create text frame, and feed each received frame into the same Responses reducer the SSE transport uses. Connections are cached per (session, account) with a five-minute idle and 55-minute age limit, so later turns skip DNS, TCP, and TLS setup; a busy or mismatched entry gets a one-off connection, and a reused connection that fails before any model output is replaced by a fresh one once. Enabled with FX_OPENAI_CODEX_TRANSPORT=websocket; the default transport is unchanged. A WebSocket transport failure before any model output falls back to SSE under the original invocation admission and latches the process to SSE; after output has been emitted the failure is surfaced instead, so a replay can never duplicate tool calls. Verified against the production Codex endpoint, including cross-turn connection reuse in a saved session. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Respect request.deadline across the WebSocket path: it bounds the handshake alongside the connect timeout and bounds the stream phase via the bounded cancel watcher, surfacing error.Timeout. Reject masked server frames (RFC 6455 5.1: only clients mask). Add two e2e tests that drive the built binary against a Bun WebSocket loopback server: a completion streamed over one upgraded connection with zero SSE requests, and pre-output fallback to SSE when the upgrade is rejected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Author
|
This PR has been folded into #521 |
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
src/gateway/websocket.zig, an RFC 6455 client codec (handshake keys, masked frames, fragmentation, control-frame rules) overstd.Ioreader/writer streamssrc/gateway/openai_codex_websocket.zig: upgrade the Codex Responses endpoint to a WebSocket (OpenAI-Beta: responses_websockets=2026-02-06), send the request body as oneresponse.createtext frame, and feed each received frame into the same Responses reducer the SSE transport uses, with identical stream limitsFX_OPENAI_CODEX_TRANSPORT=websocket; the default transport is unchangedrequest.deadlineis respected end to end: it bounds the handshake and the stream phase (via the bounded cancel watcher), surfacingerror.TimeoutThe handshake is
std.http.Client's existing 101/informational path, so TLS, proxies, and the cancel watcher are inherited rather than reimplemented. Protocol and policy mirror Codex (codex-rs/core/src/client.rs) and pi (openai-codex-responses.ts); pi's transport selection, fallback-before-output rule, and cache limits are followed exactly. Prewarm,previous_response_idcontinuation, and zstd compression on the SSE path are follow-ups.Testing
zig fmt --checkon touched fileszig build test— full suite passes; new tests cover RFC 6455 vectors and frame round trips, a loopback WebSocket server proving end-to-end streaming plus connection reuse (one TCP accept serving two requests), fallback-safe handshake rejection, and the fallback error classificationbun test acp.test.ts(116 tests) andtui-auth-source-selection.test.ts(56 tests) pass, including two new e2e tests that drive the built binary against a Bun WebSocket loopback server: a completion streamed over one upgraded connection with zero SSE requests, and pre-output fallback to SSE when the upgrade is rejectedchatgpt.com: single-shot and tool-using turns stream over the WebSocket; a saved session logscodex_ws_connection reused=trueon the second step, removing the ~400 ms connect observed on fresh connections🤖 Generated with Claude Code