Skip to content

Commit 8a03e3d

Browse files
author
曾文锋0668000834
committed
Default reasoning to on when model has reasoning: true (fix #22456)
What: When a model is configured with reasoning: true in openclaw.json (e.g. OpenRouter x-ai/grok-4.1-fast), the session now defaults reasoningLevel to on if the user has not set it via /reasoning or session store. Why: Users expected setting reasoning: true on the model to enable reasoning; previously only session/directive reasoningLevel was used and it always defaulted to off, so Think stayed off despite the model config.
1 parent 58f7b76 commit 8a03e3d

File tree

4 files changed

+73
-1
lines changed

4 files changed

+73
-1
lines changed

src/agents/model-selection.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,21 @@ export function resolveThinkingDefault(params: {
515515
return "off";
516516
}
517517

518+
/** Default reasoning level when session/directive do not set it: "on" if model supports reasoning, else "off". */
519+
export function resolveReasoningDefault(params: {
520+
provider: string;
521+
model: string;
522+
catalog?: ModelCatalogEntry[];
523+
}): "on" | "off" {
524+
const key = modelKey(params.provider, params.model);
525+
const candidate = params.catalog?.find(
526+
(entry) =>
527+
(entry.provider === params.provider && entry.id === params.model) ||
528+
(entry.provider === key && entry.id === params.model),
529+
);
530+
return candidate?.reasoning === true ? "on" : "off";
531+
}
532+
518533
/**
519534
* Resolve the model configured for Gmail hook processing.
520535
* Returns null if hooks.gmail.model is not set.

src/auto-reply/reply/get-reply-directives.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ export async function resolveReplyDirectives(params: {
345345
directives.verboseLevel ??
346346
(sessionEntry?.verboseLevel as VerboseLevel | undefined) ??
347347
(agentCfg?.verboseDefault as VerboseLevel | undefined);
348-
const resolvedReasoningLevel: ReasoningLevel =
348+
let resolvedReasoningLevel: ReasoningLevel =
349349
directives.reasoningLevel ??
350350
(sessionEntry?.reasoningLevel as ReasoningLevel | undefined) ??
351351
"off";
@@ -389,6 +389,14 @@ export async function resolveReplyDirectives(params: {
389389
provider = modelState.provider;
390390
model = modelState.model;
391391

392+
// When neither directive nor session set reasoning, default to model capability (e.g. OpenRouter with reasoning: true).
393+
const reasoningExplicitlySet =
394+
directives.reasoningLevel !== undefined ||
395+
(sessionEntry?.reasoningLevel !== undefined && sessionEntry?.reasoningLevel !== null);
396+
if (!reasoningExplicitlySet && resolvedReasoningLevel === "off") {
397+
resolvedReasoningLevel = await modelState.resolveDefaultReasoningLevel();
398+
}
399+
392400
let contextTokens = resolveContextTokens({
393401
agentCfg,
394402
model,

src/auto-reply/reply/model-selection.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,3 +264,35 @@ describe("createModelSelectionState respects session model override", () => {
264264
expect(state.model).toBe("deepseek-v3-4bit-mlx");
265265
});
266266
});
267+
268+
describe("createModelSelectionState resolveDefaultReasoningLevel", () => {
269+
it("returns on when catalog model has reasoning true", async () => {
270+
const { loadModelCatalog } = await import("../../agents/model-catalog.js");
271+
vi.mocked(loadModelCatalog).mockResolvedValueOnce([
272+
{ provider: "openrouter", id: "x-ai/grok-4.1-fast", name: "Grok", reasoning: true },
273+
]);
274+
const state = await createModelSelectionState({
275+
cfg: {} as OpenClawConfig,
276+
agentCfg: undefined,
277+
defaultProvider: "openrouter",
278+
defaultModel: "x-ai/grok-4.1-fast",
279+
provider: "openrouter",
280+
model: "x-ai/grok-4.1-fast",
281+
hasModelDirective: false,
282+
});
283+
await expect(state.resolveDefaultReasoningLevel()).resolves.toBe("on");
284+
});
285+
286+
it("returns off when catalog model has no reasoning", async () => {
287+
const state = await createModelSelectionState({
288+
cfg: {} as OpenClawConfig,
289+
agentCfg: undefined,
290+
defaultProvider: "openai",
291+
defaultModel: "gpt-4o-mini",
292+
provider: "openai",
293+
model: "gpt-4o-mini",
294+
hasModelDirective: false,
295+
});
296+
await expect(state.resolveDefaultReasoningLevel()).resolves.toBe("off");
297+
});
298+
});

src/auto-reply/reply/model-selection.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
modelKey,
99
normalizeProviderId,
1010
resolveModelRefFromString,
11+
resolveReasoningDefault,
1112
resolveThinkingDefault,
1213
} from "../../agents/model-selection.js";
1314
import type { OpenClawConfig } from "../../config/config.js";
@@ -32,6 +33,8 @@ type ModelSelectionState = {
3233
allowedModelCatalog: ModelCatalog;
3334
resetModelOverride: boolean;
3435
resolveDefaultThinkingLevel: () => Promise<ThinkLevel>;
36+
/** Default reasoning level from model capability: "on" if model has reasoning, else "off". */
37+
resolveDefaultReasoningLevel: () => Promise<"on" | "off">;
3538
needsModelCatalog: boolean;
3639
};
3740

@@ -397,13 +400,27 @@ export async function createModelSelectionState(params: {
397400
return defaultThinkingLevel;
398401
};
399402

403+
const resolveDefaultReasoningLevel = async (): Promise<"on" | "off"> => {
404+
let catalogForReasoning = modelCatalog ?? allowedModelCatalog;
405+
if (!catalogForReasoning || catalogForReasoning.length === 0) {
406+
modelCatalog = await loadModelCatalog({ config: cfg });
407+
catalogForReasoning = modelCatalog;
408+
}
409+
return resolveReasoningDefault({
410+
provider,
411+
model,
412+
catalog: catalogForReasoning,
413+
});
414+
};
415+
400416
return {
401417
provider,
402418
model,
403419
allowedModelKeys,
404420
allowedModelCatalog,
405421
resetModelOverride,
406422
resolveDefaultThinkingLevel,
423+
resolveDefaultReasoningLevel,
407424
needsModelCatalog,
408425
};
409426
}

0 commit comments

Comments
 (0)