From 9889458d93848524e09fcfd1c71ccaf4050dedc8 Mon Sep 17 00:00:00 2001 From: VastCosmic <137017677@qq.com> Date: Fri, 3 Apr 2026 17:04:36 +0800 Subject: [PATCH 1/3] fix: gate OpenAI Responses API params for openai-compatible providers Closes #8261 reasoningSummary and textVerbosity are OpenAI Responses API parameters unsupported by generic openai-compatible endpoints. Adds supportsOpenAIResponsesParams() to gate them by model.api.npm. reasoningEffort remains universal. --- packages/opencode/src/provider/transform.ts | 23 ++++- .../opencode/test/provider/transform.test.ts | 89 +++++++++++++++++++ 2 files changed, 110 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 9e1ba8290a8..a8d3d3889c9 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -46,6 +46,20 @@ export namespace ProviderTransform { return undefined } + // kilocode_change start - gate OpenAI Responses API params for openai-compatible providers + function supportsOpenAIResponsesParams(model: Provider.Model): boolean { + switch (model.api.npm) { + case "@ai-sdk/openai": + case "@ai-sdk/azure": + case "@ai-sdk/github-copilot": + case "@openrouter/ai-sdk-provider": + case "@kilocode/kilo-gateway": + return true + } + return false + } + // kilocode_change end + function normalizeMessages( msgs: ModelMessage[], model: Provider.Model, @@ -826,12 +840,17 @@ export namespace ProviderTransform { if (input.model.api.id.includes("gpt-5") && !input.model.api.id.includes("gpt-5-chat")) { if (!input.model.api.id.includes("gpt-5-pro")) { result["reasoningEffort"] = "medium" + } + + const nativeOpenAI = supportsOpenAIResponsesParams(input.model) + + if (!input.model.api.id.includes("gpt-5-pro") && nativeOpenAI) { result["reasoningSummary"] = "auto" } - // Only set textVerbosity for non-chat gpt-5.x models - // Chat models (e.g. gpt-5.2-chat-latest) only support "medium" verbosity + // textVerbosity is also an OpenAI Responses API parameter if ( + nativeOpenAI && input.model.api.id.includes("gpt-5.") && !input.model.api.id.includes("codex") && !input.model.api.id.includes("-chat") && diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index c70acf260c8..f0face32cbb 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -2737,3 +2737,92 @@ describe("ProviderTransform.variants", () => { }) }) // kilocode_change end + +// kilocode_change start - tests for reasoningSummary guard +describe("ProviderTransform.options - OpenAI Responses API params guard", () => { + const sessionID = "test-session" + + const gpt5Model = (npm: string, providerID: string, apiId = "gpt-5.4"): any => ({ + id: `${'${providerID}/${apiId}'}`, + providerID, + api: { id: apiId, npm, url: "" }, + name: apiId, + capabilities: { + temperature: true, + reasoning: true, + attachment: true, + toolcall: true, + input: { text: true, audio: false, image: false, video: false, pdf: false }, + output: { text: true, audio: false, image: false, video: false, pdf: false }, + interleaved: false, + }, + cost: { input: 0, output: 0 }, + limit: { context: 200000, output: 32000 }, + options: {}, + headers: {}, + }) + + test("includes reasoningSummary and textVerbosity for @ai-sdk/openai", () => { + const result = ProviderTransform.options({ + model: gpt5Model("@ai-sdk/openai", "openai"), + sessionID, + }) + expect(result.reasoningSummary).toBe("auto") + expect(result.reasoningEffort).toBe("medium") + expect(result.textVerbosity).toBe("low") + }) + + test("includes reasoningSummary for @ai-sdk/azure", () => { + const result = ProviderTransform.options({ + model: gpt5Model("@ai-sdk/azure", "azure"), + sessionID, + }) + expect(result.reasoningSummary).toBe("auto") + }) + + test("excludes reasoningSummary and textVerbosity for @ai-sdk/openai-compatible", () => { + const result = ProviderTransform.options({ + model: gpt5Model("@ai-sdk/openai-compatible", "my-proxy"), + sessionID, + }) + expect(result.reasoningSummary).toBeUndefined() + expect(result.textVerbosity).toBeUndefined() + expect(result.reasoningEffort).toBe("medium") + }) + + test("excludes reasoningSummary for unknown SDK packages", () => { + const result = ProviderTransform.options({ + model: gpt5Model("@ai-sdk/xai", "xai"), + sessionID, + }) + expect(result.reasoningSummary).toBeUndefined() + expect(result.reasoningEffort).toBe("medium") + }) + + test("includes reasoningSummary for @openrouter/ai-sdk-provider", () => { + const result = ProviderTransform.options({ + model: gpt5Model("@openrouter/ai-sdk-provider", "openrouter"), + sessionID, + }) + expect(result.reasoningSummary).toBe("auto") + }) + + test("includes reasoningSummary for @kilocode/kilo-gateway", () => { + const result = ProviderTransform.options({ + model: gpt5Model("@kilocode/kilo-gateway", "kilo"), + sessionID, + }) + expect(result.reasoningSummary).toBe("auto") + }) + + test("reasoningEffort remains universal across all providers", () => { + for (const npm of ["@ai-sdk/openai-compatible", "@ai-sdk/xai", "@ai-sdk/deepinfra"]) { + const result = ProviderTransform.options({ + model: gpt5Model(npm, "test"), + sessionID, + }) + expect(result.reasoningEffort).toBe("medium") + } + }) +}) +// kilocode_change end From eede3e46c8eeec0c679cba6fecce044b2f8df94d Mon Sep 17 00:00:00 2001 From: VastCosmic <137017677@qq.com> Date: Wed, 6 May 2026 18:36:22 +0800 Subject: [PATCH 2/3] fix: minimize OpenAI responses params conflict resolution --- packages/opencode/src/provider/transform.ts | 31 ++++++------------- .../opencode/test/provider/transform.test.ts | 2 +- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 2617ea33ee3..1883732b9d3 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -49,20 +49,6 @@ function sdkKey(npm: string): string | undefined { return undefined } -// kilocode_change start - gate OpenAI Responses API params for openai-compatible providers -function supportsOpenAIResponsesParams(model: Provider.Model): boolean { - switch (model.api.npm) { - case "@ai-sdk/openai": - case "@ai-sdk/azure": - case "@ai-sdk/github-copilot": - case "@openrouter/ai-sdk-provider": - case "@kilocode/kilo-gateway": - return true - } - return false -} -// kilocode_change end - function normalizeMessages( msgs: ModelMessage[], model: Provider.Model, @@ -250,7 +236,7 @@ function normalizeMessages( const filteredContent = msg.content.filter((part: any) => part.type !== "reasoning") // Include reasoning_content | reasoning_details directly on the message for all assistant messages. - // Always set the field even when empty 鈥?some providers (e.g. DeepSeek) may return empty + // Always set the field even when empty — some providers (e.g. DeepSeek) may return empty // reasoning_content which still needs to be sent back in subsequent requests. return { ...msg, @@ -999,20 +985,21 @@ export function options(input: { } if (input.model.api.id.includes("gpt-5") && !input.model.api.id.includes("gpt-5-chat")) { - const nativeOpenAI = supportsOpenAIResponsesParams(input.model) + const nativeOpenAI = [ + "@ai-sdk/openai", + "@ai-sdk/azure", + "@ai-sdk/github-copilot", + "@openrouter/ai-sdk-provider", + "@kilocode/kilo-gateway", + ].includes(input.model.api.npm) if (!input.model.api.id.includes("gpt-5-pro")) { result["reasoningEffort"] = "medium" - // Only inject reasoningSummary for providers that support OpenAI Responses API params. - // @ai-sdk/openai-compatible proxies (e.g. LiteLLM) do not understand this - // parameter and return "Unknown parameter: 'reasoningSummary'". if (nativeOpenAI) { result["reasoningSummary"] = "auto" } } - // Only set textVerbosity for non-chat gpt-5.x models that support OpenAI Responses API params. - // Chat models (e.g. gpt-5.2-chat-latest) only support "medium" verbosity if ( nativeOpenAI && input.model.api.id.includes("gpt-5.") && @@ -1086,7 +1073,7 @@ export function smallOptions(model: Provider.Model) { } // Maps model ID prefix to provider slug used in providerOptions. -// Example: "amazon/nova-2-lite" 鈫?"bedrock" +// Example: "amazon/nova-2-lite" → "bedrock" const SLUG_OVERRIDES: Record = { amazon: "bedrock", } diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index e10fda640f0..4fb83641eba 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -3617,7 +3617,7 @@ describe("ProviderTransform.options - OpenAI Responses API params guard", () => const sessionID = "test-session" const gpt5Model = (npm: string, providerID: string, apiId = "gpt-5.4"): any => ({ - id: `${'${providerID}/${apiId}'}`, + id: `${providerID}/${apiId}`, providerID, api: { id: apiId, npm, url: "" }, name: apiId, From 7f8d2911bd3decab9088238e25683d699552e171 Mon Sep 17 00:00:00 2001 From: VastCosmic <137017677@qq.com> Date: Wed, 6 May 2026 19:40:07 +0800 Subject: [PATCH 3/3] fix: annotate OpenAI responses params guard --- packages/opencode/src/provider/transform.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 1883732b9d3..d1e871d170c 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -985,6 +985,7 @@ export function options(input: { } if (input.model.api.id.includes("gpt-5") && !input.model.api.id.includes("gpt-5-chat")) { + // kilocode_change start - guard OpenAI Responses-only params for compatible providers const nativeOpenAI = [ "@ai-sdk/openai", "@ai-sdk/azure", @@ -1009,6 +1010,7 @@ export function options(input: { ) { result["textVerbosity"] = "low" } + // kilocode_change end if (input.model.providerID.startsWith("opencode")) { result["promptCacheKey"] = input.sessionID