Skip to content

feat: per-model thinkingDefault config and /think default directive#20458

Open
kmixter wants to merge 1 commit intoopenclaw:mainfrom
kmixter:jasmine/per-model-thinking-default
Open

feat: per-model thinkingDefault config and /think default directive#20458
kmixter wants to merge 1 commit intoopenclaw:mainfrom
kmixter:jasmine/per-model-thinking-default

Conversation

@kmixter
Copy link

@kmixter kmixter commented Feb 18, 2026

Summary

Add per-model thinkingDefault to resolve thinking level based on which
model is active, so users running different models (e.g. gemini-3-pro
for chat, gemini-3-flash for heartbeats) get appropriate thinking levels
automatically without manually toggling /think per session.

Resolution priority: per-model → global → catalog auto-detect (existing
fallback behavior unchanged). Config keys are normalized via parseModelRef
so aliases like "anthropic/opus-4.6" resolve correctly.

Also adds:

  • /think default directive to undo a session-level thinking override and
    cascade back to the per-model default
  • Re-resolve thinking default after inline /model switch for both
    directive-only and inline-with-content paths
  • Memory flush passes provider/model through for correct resolution

Discussion: #20612
Related: #18152 (built independently, shares the per-model config concept
but adds /think default and model-switch re-resolution)

Config example

{
  "agents": {
    "defaults": {
      "thinkingDefault": "low",
      "models": {
        "google/gemini-2.5-pro":              {},
        "google/gemini-3-flash":              { "thinkingDefault": "high" },
        "google/gemini-flash-lite-latest":    { "thinkingDefault": "off" }
      }
    }
  }
}

Test plan

  • pnpm build passes
  • pnpm check passes (clean on changed files)
  • pnpm test passes (456 tests, 47 files)
  • Manually verified /think default resets level and per-model config resolves correctly via openclaw doctor
  • E2E tested with live gateway

AI-assisted (Claude Code), fully tested.

@openclaw-barnacle openclaw-barnacle bot added agents Agent runtime and tooling size: S labels Feb 18, 2026
@kmixter kmixter force-pushed the jasmine/per-model-thinking-default branch from 04057a8 to 82292ae Compare February 18, 2026 23:51
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines 166 to 174
// Re-resolve thinking default if the model changed (per-model thinkingDefault support).
const newProvider = sessionEntry.providerOverride ?? provider;
const newModel = sessionEntry.modelOverride ?? model;
if (newProvider !== provider || newModel !== model) {
const catalog = await loadModelCatalog({ config: cfg });
resolvedDefaultThinkLevel =
(sessionEntry?.thinkingLevel as ThinkLevel | undefined) ??
resolveThinkingDefault({ cfg, provider: newProvider, model: newModel, catalog });
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

per-model thinking default only re-resolved in directive-only path, not when directives are combined with message content

the re-resolution logic here only applies when the user sends a directive-only message (e.g., just /model openai/gpt-4). when directives are combined with content (e.g., /model openai/gpt-4 what is 2+2?), the code takes the hasAnyDirective path (line 217), which uses applyInlineDirectivesFastLane. that path calls resolveCurrentDirectiveLevels which invokes modelState.resolveDefaultThinkingLevel(), but this closure was created with the original provider/model values and won't reflect the per-model default for the newly switched model.

this means users who switch models inline with their message won't get the correct per-model thinking default until their next message.

consider moving the re-resolution logic to a shared location that both paths use, or updating the resolveDefaultThinkingLevel closure to check for provider/model overrides in the session entry.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/auto-reply/reply/get-reply-directives-apply.ts
Line: 166-174

Comment:
per-model thinking default only re-resolved in directive-only path, not when directives are combined with message content

the re-resolution logic here only applies when the user sends a directive-only message (e.g., just `/model openai/gpt-4`). when directives are combined with content (e.g., `/model openai/gpt-4 what is 2+2?`), the code takes the `hasAnyDirective` path (line 217), which uses `applyInlineDirectivesFastLane`. that path calls `resolveCurrentDirectiveLevels` which invokes `modelState.resolveDefaultThinkingLevel()`, but this closure was created with the *original* provider/model values and won't reflect the per-model default for the newly switched model.

this means users who switch models inline with their message won't get the correct per-model thinking default until their next message.

consider moving the re-resolution logic to a shared location that both paths use, or updating the `resolveDefaultThinkingLevel` closure to check for provider/model overrides in the session entry.

How can I resolve this? If you propose a fix, please make it concise.

Add per-model thinkingDefault to resolve thinking level based on which
model is active, so users running different models (e.g. gemini-3-pro
for chat, gemini-3-flash for heartbeats) get appropriate thinking levels
automatically without manually toggling /think per session.

Resolution priority: per-model → global → catalog auto-detect (existing
fallback behavior unchanged). Config keys are normalized via parseModelRef
so aliases like "anthropic/opus-4.6" resolve correctly.

Also adds:
- /think default directive to undo a session-level thinking override and
  cascade back to the per-model default
- Re-resolve thinking default after inline /model switch for both
  directive-only and inline-with-content paths
- Memory flush passes provider/model through for correct resolution

Related: openclaw#18152 (built independently, shares the per-model config concept
but adds /think default and model-switch re-resolution)

AI-assisted (Claude Code), fully tested. Tested: pnpm build, pnpm check,
pnpm test, and e2e verified on live gateway.
@kmixter kmixter force-pushed the jasmine/per-model-thinking-default branch from 493652c to 240f2b6 Compare February 19, 2026 03:13
@kmixter
Copy link
Author

kmixter commented Feb 19, 2026

The concern raised by the Greptile bot (per-model thinking default only re-resolved in directive-only path) has been addressed in the current revision:

  • Extracted a shared resolveThinkingForActiveModel helper used by both the directive-only path and the inline-with-content (fast lane) path.
  • The fast lane path now passes this resolver via the modelState.resolveDefaultThinkingLevel closure, which reads sessionEntry.providerOverride/modelOverride at call time to pick up model switches.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments