feat(ai): use zstd compression for codex sse transport

This commit is contained in:
Vegard Stikbakke
2026-06-30 14:29:51 +02:00
parent a3cc169d97
commit 0ac3cfe09b
3 changed files with 138 additions and 4 deletions
+47 -1
View File
@@ -1,4 +1,5 @@
import type * as NodeOs from "node:os";
import type * as NodeZlib from "node:zlib";
import type {
Tool as OpenAITool,
ResponseCreateParamsStreaming,
@@ -58,6 +59,9 @@ const DEFAULT_MAX_RETRIES = 0;
const BASE_DELAY_MS = 1000;
const DEFAULT_MAX_RETRY_DELAY_MS = 60_000;
const DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS = 15_000;
// The Codex backend accepts zstd-compressed request bodies on the SSE responses
// endpoint (the same endpoint the official Codex client compresses against).
const REQUEST_COMPRESSION_ZSTD_LEVEL = 3;
const CODEX_TOOL_CALL_PROVIDERS = new Set(["openai", "openai-codex", "opencode"]);
const WEBSOCKET_MESSAGE_TOO_BIG_CLOSE_CODE = 1009;
const WEBSOCKET_CONNECTION_LIMIT_REACHED_CODE = "websocket_connection_limit_reached";
@@ -177,6 +181,39 @@ function normalizeTimeoutMs(value: number | undefined): number | undefined {
return Math.floor(value);
}
// ============================================================================
// Request Compression
// ============================================================================
type ProcessWithBuiltinModule = typeof process & {
getBuiltinModule?: (id: "node:zlib") => typeof NodeZlib;
};
function loadNodeZlib(): typeof NodeZlib | null {
if (typeof process === "undefined" || !(process.versions?.node || process.versions?.bun)) {
return null;
}
return (process as ProcessWithBuiltinModule).getBuiltinModule?.("node:zlib") ?? null;
}
// Returns the zstd-compressed body bytes, or null when compression is
// unavailable (browser/Vite builds). Callers fall back to sending the
// uncompressed JSON when this returns null.
function compressRequestBodyZstd(bodyJson: string): Uint8Array | null {
const zlib = loadNodeZlib();
if (!zlib || typeof zlib.zstdCompressSync !== "function") {
return null;
}
try {
const compressed = zlib.zstdCompressSync(bodyJson, {
params: { [zlib.constants.ZSTD_c_compressionLevel]: REQUEST_COMPRESSION_ZSTD_LEVEL },
});
return new Uint8Array(compressed.buffer, compressed.byteOffset, compressed.byteLength);
} catch {
return null;
}
}
// ============================================================================
// Main Stream Function
// ============================================================================
@@ -298,6 +335,15 @@ export const stream: StreamFunction<"openai-codex-responses", OpenAICodexRespons
}
}
// Compress the request body once for the SSE path. The Codex backend
// decodes Content-Encoding: zstd; the WebSocket transport above sends the
// uncompressed JSON frame, matching the official Codex client.
const compressedBody = compressRequestBodyZstd(bodyJson);
if (compressedBody) {
sseHeaders.set("content-encoding", "zstd");
}
const sseBody: Uint8Array | string = compressedBody ?? bodyJson;
// Fetch with retry logic for rate limits and transient errors
let response: Response | undefined;
let lastError: Error | undefined;
@@ -316,7 +362,7 @@ export const stream: StreamFunction<"openai-codex-responses", OpenAICodexRespons
response = await fetch(resolveCodexUrl(model.baseUrl), {
method: "POST",
headers: sseHeaders,
body: bodyJson,
body: sseBody,
signal: combinedSignal.signal,
});
} catch (error) {