From c1deaface2d1669eaf8880151394c7385d764a8f Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 3 May 2026 15:26:13 +0100 Subject: [PATCH] fix(agents): omit gpt-5.4-mini chat reasoning effort --- CHANGELOG.md | 1 + src/agents/openai-reasoning-effort.test.ts | 12 ++++++++ src/agents/openai-reasoning-effort.ts | 8 +++++ src/agents/openai-transport-stream.test.ts | 34 ++++++++++++++++++++++ 4 files changed, 55 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48d652a9bf6..22b68e57b87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ Docs: https://docs.openclaw.ai ### Fixes - Discord/status: honor explicit `messages.statusReactions.enabled: true` in tool-only guild channels so queued ack reactions can progress through thinking/done lifecycle reactions instead of stopping at the initial emoji. Thanks @Marvinthebored. +- Agents/OpenAI: omit Chat Completions `reasoning_effort` for `gpt-5.4-mini` while preserving Responses reasoning support, preventing Telegram-routed fallback runs from hanging after OpenAI rejects tool payloads. Fixes #76176. Thanks @ThisIsAdilah and @chinar-amrutkar. - Agents/models: forward model `maxTokens` as the default output-token limit for OpenAI-compatible Responses and Completions transports when no runtime override is provided, preventing provider defaults from silently truncating larger outputs. (#76645) Thanks @joeyfrasier. - Control UI/Skills: fix skill detail modal silently failing to open in all browsers by deferring `showModal()` until the dialog element is connected to the DOM; the Lit `ref` callback fired before connection causing a `DOMException: HTMLDialogElement.showModal: Dialog element is not connected` on every skill click. Thanks @nickmopen. - Gateway/update: run `doctor --non-interactive --fix` after Control UI global package updates before reporting success, so legacy config is migrated before the gateway restart. Thanks @stevenchouai. diff --git a/src/agents/openai-reasoning-effort.test.ts b/src/agents/openai-reasoning-effort.test.ts index ab5c0afa4d5..583be9d32b8 100644 --- a/src/agents/openai-reasoning-effort.test.ts +++ b/src/agents/openai-reasoning-effort.test.ts @@ -13,6 +13,18 @@ describe("OpenAI reasoning effort support", () => { expect(resolveOpenAIReasoningEffortForModel({ model, effort: "xhigh" })).toBe("xhigh"); }); + it("omits reasoning_effort for gpt-5.4-mini in Chat Completions", () => { + const model = { provider: "openai", id: "gpt-5.4-mini", api: "openai-completions" }; + expect(resolveOpenAISupportedReasoningEfforts(model)).toHaveLength(0); + expect(resolveOpenAIReasoningEffortForModel({ model, effort: "medium" })).toBeUndefined(); + }); + + it("preserves reasoning_effort for gpt-5.4-mini in Responses", () => { + const model = { provider: "openai", id: "gpt-5.4-mini", api: "openai-responses" }; + expect(resolveOpenAISupportedReasoningEfforts(model)).toContain("medium"); + expect(resolveOpenAIReasoningEffortForModel({ model, effort: "medium" })).toBe("medium"); + }); + it("does not downgrade xhigh when Pi compat metadata declares it explicitly", () => { const model = { provider: "openai-codex", diff --git a/src/agents/openai-reasoning-effort.ts b/src/agents/openai-reasoning-effort.ts index 1bb2148ea79..47f50cd3e7d 100644 --- a/src/agents/openai-reasoning-effort.ts +++ b/src/agents/openai-reasoning-effort.ts @@ -80,6 +80,14 @@ export function resolveOpenAISupportedReasoningEfforts( if (/^gpt-5\.[2-9](?:\.\d+)?-pro(?:-|$)/u.test(id)) { return GPT_PRO_REASONING_EFFORTS; } + const api = normalizeLowercaseStringOrEmpty(typeof model.api === "string" ? model.api : ""); + if (api === "openai-responses" || api === "openai-codex-responses") { + if (/^gpt-5\.4-mini(?:-|$)/u.test(id)) { + return GPT_52_REASONING_EFFORTS; + } + } else if (/^gpt-5\.4-mini(?:-|$)/u.test(id)) { + return []; + } if (/^gpt-5\.[2-9](?:\.\d+)?(?:-|$)/u.test(id)) { return GPT_52_REASONING_EFFORTS; } diff --git a/src/agents/openai-transport-stream.test.ts b/src/agents/openai-transport-stream.test.ts index 73dc31415f9..ce75accc091 100644 --- a/src/agents/openai-transport-stream.test.ts +++ b/src/agents/openai-transport-stream.test.ts @@ -2050,6 +2050,40 @@ describe("openai transport stream", () => { expect(params.reasoning_effort).toBe("high"); }); + it("omits reasoning_effort for gpt-5.4-mini Chat Completions tool payloads", () => { + const params = buildOpenAICompletionsParams( + { + id: "gpt-5.4-mini", + name: "GPT-5.4 mini", + api: "openai-completions", + provider: "openai", + baseUrl: "https://api.openai.com/v1", + reasoning: true, + input: ["text"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 400000, + maxTokens: 128000, + } satisfies Model<"openai-completions">, + { + systemPrompt: "system", + messages: [], + tools: [ + { + name: "lookup_weather", + description: "Get forecast", + parameters: { type: "object", properties: {}, additionalProperties: false }, + }, + ], + } as never, + { + reasoning: "medium", + } as never, + ) as { reasoning_effort?: unknown; tools?: unknown }; + + expect(params.tools).toBeDefined(); + expect(params).not.toHaveProperty("reasoning_effort"); + }); + it("uses provider-native reasoning effort values declared by model compat", () => { const baseModel = { id: "qwen/qwen3-32b",