diff --git a/src/proxy/__tests__/codex-api-headers.test.ts b/src/proxy/__tests__/codex-api-headers.test.ts index 263f88a..8b73930 100644 --- a/src/proxy/__tests__/codex-api-headers.test.ts +++ b/src/proxy/__tests__/codex-api-headers.test.ts @@ -112,13 +112,37 @@ describe("codex-api headers", () => { expect(transport.lastHeaders!["x-codex-turn-state"]).toBeUndefined(); }); - it("excludes turnState and service_tier from JSON body", async () => { + it("excludes turnState from JSON body", async () => { const api = await createApi(); await api.createResponse( - makeRequest({ turnState: "abc", service_tier: "fast" }), + makeRequest({ turnState: "abc" }), ); const body = JSON.parse(transport.lastBody!) as Record; expect(body.turnState).toBeUndefined(); + }); + + it("maps service_tier 'fast' to 'priority' in JSON body", async () => { + const api = await createApi(); + await api.createResponse( + makeRequest({ service_tier: "fast" }), + ); + const body = JSON.parse(transport.lastBody!) as Record; + expect(body.service_tier).toBe("priority"); + }); + + it("passes non-fast service_tier through unchanged", async () => { + const api = await createApi(); + await api.createResponse( + makeRequest({ service_tier: "flex" }), + ); + const body = JSON.parse(transport.lastBody!) as Record; + expect(body.service_tier).toBe("flex"); + }); + + it("omits service_tier from body when null", async () => { + const api = await createApi(); + await api.createResponse(makeRequest({ service_tier: null })); + const body = JSON.parse(transport.lastBody!) as Record; expect(body.service_tier).toBeUndefined(); }); }); @@ -148,5 +172,45 @@ describe("codex-api headers", () => { ); expect(headers["x-codex-turn-state"]).toBe("ws_turn_abc"); }); + + it("maps service_tier 'fast' to 'priority' in WS request", async () => { + mockCreateWebSocketResponse.mockResolvedValue( + new Response("data: {}\n\n", { + headers: { "content-type": "text/event-stream" }, + }), + ); + + const api = await createApi(); + await api.createResponse( + makeRequest({ + previous_response_id: "resp_prev", + useWebSocket: true, + service_tier: "fast", + }), + ); + + const wsRequest = mockCreateWebSocketResponse.mock.calls[0][2] as Record; + expect(wsRequest.service_tier).toBe("priority"); + }); + + it("passes non-fast service_tier through in WS request", async () => { + mockCreateWebSocketResponse.mockResolvedValue( + new Response("data: {}\n\n", { + headers: { "content-type": "text/event-stream" }, + }), + ); + + const api = await createApi(); + await api.createResponse( + makeRequest({ + previous_response_id: "resp_prev", + useWebSocket: true, + service_tier: "flex", + }), + ); + + const wsRequest = mockCreateWebSocketResponse.mock.calls[0][2] as Record; + expect(wsRequest.service_tier).toBe("flex"); + }); }); }); diff --git a/src/proxy/codex-api.ts b/src/proxy/codex-api.ts index 8846a8b..c706cf4 100644 --- a/src/proxy/codex-api.ts +++ b/src/proxy/codex-api.ts @@ -215,7 +215,7 @@ export class CodexApi { if (request.tools?.length) wsRequest.tools = request.tools; if (request.tool_choice) wsRequest.tool_choice = request.tool_choice; if (request.text) wsRequest.text = request.text; - // service_tier is stripped — Codex backend rejects it ("Unsupported service_tier") + if (request.service_tier) wsRequest.service_tier = request.service_tier === "fast" ? "priority" : request.service_tier; if (request.prompt_cache_key) wsRequest.prompt_cache_key = request.prompt_cache_key; if (request.include?.length) wsRequest.include = request.include; @@ -243,8 +243,10 @@ export class CodexApi { headers["x-client-request-id"] = crypto.randomUUID(); if (request.turnState) headers["x-codex-turn-state"] = request.turnState; - const { previous_response_id: _pid, useWebSocket: _ws, turnState: _ts, service_tier: _st, ...bodyFields } = request; - const body = JSON.stringify(bodyFields); + const { previous_response_id: _pid, useWebSocket: _ws, turnState: _ts, service_tier, ...bodyFields } = request; + const body = JSON.stringify( + service_tier ? { ...bodyFields, service_tier: service_tier === "fast" ? "priority" : service_tier } : bodyFields, + ); let transportRes; try { diff --git a/src/proxy/ws-transport.ts b/src/proxy/ws-transport.ts index 1618648..3d93e08 100644 --- a/src/proxy/ws-transport.ts +++ b/src/proxy/ws-transport.ts @@ -51,6 +51,7 @@ export interface WsCreateRequest { strict?: boolean; }; }; + service_tier?: string; prompt_cache_key?: string; include?: string[]; // NOTE: `store` and `stream` are intentionally omitted.