From f04209e2ea5956ecb960ceda52dba9980f83117b Mon Sep 17 00:00:00 2001 From: MasterSwords1 <142840981+MasterSwords1@users.noreply.github.com> Date: Sat, 19 Sep 2026 10:42:11 +0100 Subject: [PATCH] fix: thinking controls offer unsupported provider-native efforts (#145248) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Related: #144866, #90703 ## What Problem This Solves Fixes thinking controls offering and accepting advanced levels from provider-native labels such as `MAX` or `XHIGH`, even though the actual model request silently uses a lower level. ## User Impact The model picker, session validation, and execution now agree on which advanced levels a custom provider supports. Lowercase canonical efforts still work, and explicit mappings such as `max: "MAX"` preserve the provider's exact wire value. Unsupported selections fail without changing the session or its runtime/account selection. ## Why This Change Was Made The shared thinking profile incorrectly applied user-input aliases to provider-native capability labels. It now reads those labels exactly, preserving the [existing explicit-mapping policy](https://github.com/openclaw/openclaw/pull/102993#issuecomment-4931608378). Provider-owned profiles, explicit maps, and null opt-outs are unchanged; no new aliases or transport policy are introduced. The earlier lowercase capability-admission bug is already fixed on main. This update addresses the remaining inconsistent advertisement rather than restoring the older transport changes. ## Evidence A real isolated Gateway and secretless loopback HTTP provider exercised `models.list → sessions.patch → chat.send → chat.history`: | Case | Before | After | | --- | --- | --- | | Unmapped native `MAX` / `XHIGH` | Advertised and accepted `max` / `xhigh`; actual wire used `high` | Not advertised; unsupported selection rejected; accepted `high` sends `high` | | Canonical lowercase `max` | Session and wire `max` | Unchanged | | Explicit `max → MAX` mapping | Session `max`, wire `MAX` | Unchanged | | Explicit null opt-out | Selection rejected | Unchanged | Rejected candidate selections left the complete persisted session row unchanged. A saved synthetic account selected through `/model`, its explicit OpenClaw runtime, and model identity survived both rejection and a subsequent real HTTP turn. A copied pre-fix session now displays its effective supported level instead of the unavailable Maximum choice. - [Inspected before/after screenshots from the real Gateway UI](https://github.com/openclaw/openclaw/pull/145248#issuecomment-5740680770). - New regression fails on the original shared profile; 137 focused policy/admission/wire-contract tests pass after the fix. - Focused formatting and diff checks pass. Full static and matrix checks remain in hosted CI. - Local provider responses were synthetic; no external provider or live-account behavior is claimed. Co-authored-by: Ayaan Zaidi --- src/auto-reply/thinking.levels.test.ts | 29 ++++++++++++++++++++++++++ src/auto-reply/thinking.ts | 3 +-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/auto-reply/thinking.levels.test.ts b/src/auto-reply/thinking.levels.test.ts index 62243377c1ea..21f3d6d83c4f 100644 --- a/src/auto-reply/thinking.levels.test.ts +++ b/src/auto-reply/thinking.levels.test.ts @@ -708,6 +708,35 @@ describe("listThinkingLevels", () => { ).toBe(true); }); + it("does not treat provider-native effort labels as user thinking aliases", () => { + const catalog = [ + { + provider: "custom", + id: "native-efforts", + api: "openai-completions", + reasoning: true, + compat: { supportedReasoningEfforts: ["high", "XHIGH", "MAX", "extra-high", "auto"] }, + }, + ]; + + expect(listThinkingLevels("custom", "native-efforts", catalog, "openclaw")).toEqual([ + "off", + "minimal", + "low", + "medium", + "high", + ]); + expect( + isThinkingLevelSupported({ + provider: "custom", + model: "native-efforts", + level: "max", + catalog, + agentRuntime: "openclaw", + }), + ).toBe(false); + }); + it("uses advanced catalog efforts and derives OpenClaw Ultra from Max", () => { const catalog = [ { diff --git a/src/auto-reply/thinking.ts b/src/auto-reply/thinking.ts index f0d26542838b..84566b69fe5d 100644 --- a/src/auto-reply/thinking.ts +++ b/src/auto-reply/thinking.ts @@ -240,8 +240,7 @@ function appendCatalogAdvancedThinkingLevels( } } let supportsMax = profile.levels.some(({ id }) => id === "max"); - for (const effort of compat?.supportedReasoningEfforts ?? []) { - const level = normalizeThinkLevel(effort); + for (const level of compat?.supportedReasoningEfforts ?? []) { if ( level === "ultra" || ((level === "adaptive" || level === "xhigh" || level === "max") &&