From 60961a7f551d035e2ec780eff90806f6beee239f Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Tue, 14 Apr 2026 16:25:30 +0100 Subject: [PATCH] fix(openai): share responses transport defaults --- extensions/openai/openai-codex-provider.ts | 12 +----- extensions/openai/openai-provider.test.ts | 45 ++++++++++++++++++++++ extensions/openai/openai-provider.ts | 17 ++------ extensions/openai/shared.ts | 24 ++++++++++++ 4 files changed, 74 insertions(+), 24 deletions(-) diff --git a/extensions/openai/openai-codex-provider.ts b/extensions/openai/openai-codex-provider.ts index c6d0f8191fc..09e29c1e50d 100644 --- a/extensions/openai/openai-codex-provider.ts +++ b/extensions/openai/openai-codex-provider.ts @@ -28,6 +28,7 @@ import { buildOpenAIReplayPolicy } from "./replay-policy.js"; import { buildOpenAISyntheticCatalogEntry, cloneFirstTemplateModel, + defaultOpenAIResponsesExtraParams, findCatalogTemplate, isOpenAIApiBaseUrl, isOpenAICodexBaseUrl, @@ -336,16 +337,7 @@ export function buildOpenAICodexProviderPlugin(): ProviderPlugin { return id === OPENAI_CODEX_GPT_54_MODEL_ID || id === OPENAI_CODEX_GPT_54_PRO_MODEL_ID; }, buildReplayPolicy: buildOpenAIReplayPolicy, - prepareExtraParams: (ctx) => { - const transport = ctx.extraParams?.transport; - if (transport === "auto" || transport === "sse" || transport === "websocket") { - return ctx.extraParams; - } - return { - ...ctx.extraParams, - transport: "auto", - }; - }, + prepareExtraParams: (ctx) => defaultOpenAIResponsesExtraParams(ctx.extraParams), ...OPENAI_RESPONSES_STREAM_HOOKS, resolveTransportTurnState: (ctx) => resolveOpenAITransportTurnState(ctx), resolveWebSocketSessionPolicy: (ctx) => resolveOpenAIWebSocketSessionPolicy(ctx), diff --git a/extensions/openai/openai-provider.test.ts b/extensions/openai/openai-provider.test.ts index 0eedf68f325..54007b99f13 100644 --- a/extensions/openai/openai-provider.test.ts +++ b/extensions/openai/openai-provider.test.ts @@ -353,6 +353,51 @@ describe("buildOpenAIProvider", () => { expect(result.payload.reasoning).toEqual({ effort: "none" }); }); + it("preserves explicit OpenAI responses transport and warmup overrides", () => { + const provider = buildOpenAIProvider(); + + const explicit = { + transport: "websocket", + openaiWsWarmup: false, + fastMode: true, + }; + + expect( + provider.prepareExtraParams?.({ + provider: "openai", + modelId: "gpt-5.4", + extraParams: explicit, + } as never), + ).toBe(explicit); + }); + + it("defaults Codex responses transport without forcing warmup flags", () => { + const provider = buildOpenAICodexProviderPlugin(); + + expect( + provider.prepareExtraParams?.({ + provider: "openai-codex", + modelId: "gpt-5.4", + extraParams: { effort: "high" }, + } as never), + ).toEqual({ + effort: "high", + transport: "auto", + }); + + const explicit = { + transport: "sse", + openaiWsWarmup: false, + }; + expect( + provider.prepareExtraParams?.({ + provider: "openai-codex", + modelId: "gpt-5.4", + extraParams: explicit, + } as never), + ).toBe(explicit); + }); + it("owns Azure OpenAI reasoning compatibility without forcing OpenAI transport defaults", () => { const provider = buildOpenAIProvider(); const wrap = provider.wrapStreamFn; diff --git a/extensions/openai/openai-provider.ts b/extensions/openai/openai-provider.ts index 0678c20c241..8127e222fa9 100644 --- a/extensions/openai/openai-provider.ts +++ b/extensions/openai/openai-provider.ts @@ -16,6 +16,7 @@ import { buildOpenAIReplayPolicy } from "./replay-policy.js"; import { buildOpenAISyntheticCatalogEntry, cloneFirstTemplateModel, + defaultOpenAIResponsesExtraParams, findCatalogTemplate, isOpenAIApiBaseUrl, matchesExactOrPrefix, @@ -222,20 +223,8 @@ export function buildOpenAIProvider(): ProviderPlugin { ? { api: "openai-responses", baseUrl } : undefined, buildReplayPolicy: buildOpenAIReplayPolicy, - prepareExtraParams: (ctx) => { - const transport = ctx.extraParams?.transport; - const hasSupportedTransport = - transport === "auto" || transport === "sse" || transport === "websocket"; - const hasExplicitWarmup = typeof ctx.extraParams?.openaiWsWarmup === "boolean"; - if (hasSupportedTransport && hasExplicitWarmup) { - return ctx.extraParams; - } - return { - ...ctx.extraParams, - ...(hasSupportedTransport ? {} : { transport: "auto" }), - ...(hasExplicitWarmup ? {} : { openaiWsWarmup: true }), - }; - }, + prepareExtraParams: (ctx) => + defaultOpenAIResponsesExtraParams(ctx.extraParams, { openaiWsWarmup: true }), ...OPENAI_RESPONSES_STREAM_HOOKS, matchesContextOverflowError: ({ errorMessage }) => /content_filter.*(?:prompt|input).*(?:too long|exceed)/i.test(errorMessage), diff --git a/extensions/openai/shared.ts b/extensions/openai/shared.ts index 5f280699c5c..26e1ad7772c 100644 --- a/extensions/openai/shared.ts +++ b/extensions/openai/shared.ts @@ -50,6 +50,30 @@ export function isOpenAICodexBaseUrl(baseUrl?: string): boolean { return /^https?:\/\/chatgpt\.com\/backend-api\/?$/i.test(trimmed); } +function hasSupportedOpenAIResponsesTransport( + transport: unknown, +): transport is "auto" | "sse" | "websocket" { + return transport === "auto" || transport === "sse" || transport === "websocket"; +} + +export function defaultOpenAIResponsesExtraParams( + extraParams: Record | undefined, + options?: { openaiWsWarmup?: boolean }, +): Record | undefined { + const hasSupportedTransport = hasSupportedOpenAIResponsesTransport(extraParams?.transport); + const hasExplicitWarmup = typeof extraParams?.openaiWsWarmup === "boolean"; + const shouldDefaultWarmup = options?.openaiWsWarmup === true; + if (hasSupportedTransport && (!shouldDefaultWarmup || hasExplicitWarmup)) { + return extraParams; + } + + return { + ...extraParams, + ...(hasSupportedTransport ? {} : { transport: "auto" }), + ...(shouldDefaultWarmup && !hasExplicitWarmup ? { openaiWsWarmup: true } : {}), + }; +} + export function buildOpenAISyntheticCatalogEntry( template: ReturnType, entry: {