diff --git a/src/agents/model-selection.test.ts b/src/agents/model-selection.test.ts index c98fe91fcae..c8f1bc75f7d 100644 --- a/src/agents/model-selection.test.ts +++ b/src/agents/model-selection.test.ts @@ -2529,6 +2529,72 @@ describe("model-selection", () => { expect(resolveAnthropicOpusThinking(cfg)).toBe("adaptive"); }); + it("treats params.thinking=false as off (#74374)", () => { + const cfg = { + agents: { + defaults: { + models: { + "deepseek/deepseek-v4-pro": { + params: { thinking: false }, + }, + }, + }, + }, + } as OpenClawConfig; + + expect( + resolveThinkingDefault({ + cfg, + provider: "deepseek", + model: "deepseek-v4-pro", + }), + ).toBe("off"); + }); + + it('treats params.thinking="disabled" as off (#74374)', () => { + const cfg = { + agents: { + defaults: { + models: { + "deepseek/deepseek-v4-pro": { + params: { thinking: "disabled" }, + }, + }, + }, + }, + } as OpenClawConfig; + + expect( + resolveThinkingDefault({ + cfg, + provider: "deepseek", + model: "deepseek-v4-pro", + }), + ).toBe("off"); + }); + + it('treats params.thinking="none" as off', () => { + const cfg = { + agents: { + defaults: { + models: { + "deepseek/deepseek-v4-pro": { + params: { thinking: "none" }, + }, + }, + }, + }, + } as OpenClawConfig; + + expect( + resolveThinkingDefault({ + cfg, + provider: "deepseek", + model: "deepseek-v4-pro", + }), + ).toBe("off"); + }); + it("keeps thinking off by default for explicitly configured Anthropic Opus 4.7", () => { const cfg = { agents: { diff --git a/src/agents/model-thinking-default.ts b/src/agents/model-thinking-default.ts index e3cbb766a52..f0a67361a29 100644 --- a/src/agents/model-thinking-default.ts +++ b/src/agents/model-thinking-default.ts @@ -41,6 +41,14 @@ export function resolveThinkingDefault(params: { const perModelThinking = configuredModels?.[canonicalKey]?.params?.thinking ?? (legacyKey ? configuredModels?.[legacyKey]?.params?.thinking : undefined); + // Accept boolean false and common disable aliases as "off". + if ( + perModelThinking === false || + perModelThinking === "disabled" || + perModelThinking === "none" + ) { + return "off"; + } if ( perModelThinking === "off" || perModelThinking === "minimal" || diff --git a/src/auto-reply/reply/model-selection.test.ts b/src/auto-reply/reply/model-selection.test.ts index 7f3ad02c737..67399d1ddf2 100644 --- a/src/auto-reply/reply/model-selection.test.ts +++ b/src/auto-reply/reply/model-selection.test.ts @@ -145,6 +145,43 @@ describe("createModelSelectionState catalog loading", () => { expect(loadModelCatalog).not.toHaveBeenCalled(); }); + it("keeps per-model disabled params.thinking ahead of global thinkingDefault", async () => { + vi.mocked(loadModelCatalog).mockClear(); + const cfg = { + agents: { + defaults: { + thinkingDefault: "low", + models: { + "deepseek/deepseek-v4-pro": { + params: { thinking: false }, + }, + }, + }, + }, + models: { + providers: { + deepseek: { + baseUrl: "https://api.deepseek.com/v1", + models: [makeConfiguredModel({ id: "deepseek-v4-pro", name: "DeepSeek V4 Pro" })], + }, + }, + }, + } as OpenClawConfig; + + const state = await createModelSelectionState({ + cfg, + agentCfg: cfg.agents?.defaults, + defaultProvider: "deepseek", + defaultModel: "deepseek-v4-pro", + provider: "deepseek", + model: "deepseek-v4-pro", + hasModelDirective: false, + }); + + await expect(state.resolveDefaultThinkingLevel()).resolves.toBe("off"); + expect(loadModelCatalog).not.toHaveBeenCalled(); + }); + it("uses the implicit model default when no global thinking default is configured", async () => { vi.mocked(loadModelCatalog).mockClear(); const cfg = { diff --git a/src/auto-reply/reply/model-selection.ts b/src/auto-reply/reply/model-selection.ts index 1631d010187..954eb04df2e 100644 --- a/src/auto-reply/reply/model-selection.ts +++ b/src/auto-reply/reply/model-selection.ts @@ -469,6 +469,14 @@ export async function createModelSelectionState(params: { const configuredModelThinkingDefault = configuredModels?.[canonicalKey]?.params?.thinking ?? (legacyKey ? configuredModels?.[legacyKey]?.params?.thinking : undefined); + if ( + configuredModelThinkingDefault === false || + configuredModelThinkingDefault === "disabled" || + configuredModelThinkingDefault === "none" + ) { + defaultThinkingLevel = "off"; + return defaultThinkingLevel; + } if ( configuredModelThinkingDefault === "off" || configuredModelThinkingDefault === "minimal" ||