mirror of
https://github.com/openclaw/openclaw.git
synced 2026-02-19 18:39:20 -05:00
Fix: Honor /think off for reasoning-capable models
Problem: When users execute `/think off`, they still receive `reasoning_content` from models configured with `reasoning: true` (e.g., GLM-4.7, GLM-4.6, Kimi K2.5, MiniMax-M2.1). Expected: `/think off` should completely disable reasoning content. Actual: Reasoning content is still returned. Root Cause: The directive handlers delete `sessionEntry.thinkingLevel` when user executes `/think off`. This causes the thinking level to become undefined, and the system falls back to `resolveThinkingDefault()`, which checks the model catalog and returns "low" for reasoning-capable models, ignoring the user's explicit intent. Why We Must Persist "off" (Design Rationale): 1. **Model-dependent defaults**: Unlike other directives where "off" means use a global default, `thinkingLevel` has model-dependent defaults: - Reasoning-capable models (GLM-4.7, etc.) → default "low" - Other models → default "off" 2. **Existing pattern**: The codebase already follows this pattern for `elevatedLevel`, which persists "off" explicitly to override defaults that may be "on". The comment explains: "Persist 'off' explicitly so `/elevated off` actually overrides defaults." 3. **User intent**: When a user explicitly executes `/think off`, they want to disable thinking regardless of the model's capabilities. Deleting the field breaks this intent by falling back to the model's default. Solution: Persist "off" value instead of deleting the field in all internal directive handlers: - `src/auto-reply/reply/directive-handling.impl.ts`: Directive-only messages - `src/auto-reply/reply/directive-handling.persist.ts`: Inline directives - `src/commands/agent.ts`: CLI command-line flags Gateway API Backward Compatibility: The original implementation incorrectly mapped `null` to "off" in `sessions-patch.ts` for consistency with internal handlers. This was a breaking change because: - Previously, `null` cleared the override (deleted the field) - API clients lost the ability to "clear to default" via `null` - This contradicts standard JSON semantics where `null` means "no value" Restored original null semantics in `src/gateway/sessions-patch.ts`: - `null` → delete field, fall back to model default (clear override) - `"off"` → persist explicit override - Other values → normalize and persist This ensures backward compatibility for API clients while fixing the `/think off` issue in internal handlers. Signed-off-by: Liu Yuan <namei.unix@gmail.com>
This commit is contained in:
@@ -309,11 +309,7 @@ export async function handleDirectiveOnly(params: {
|
||||
let reasoningChanged =
|
||||
directives.hasReasoningDirective && directives.reasoningLevel !== undefined;
|
||||
if (directives.hasThinkDirective && directives.thinkLevel) {
|
||||
if (directives.thinkLevel === "off") {
|
||||
delete sessionEntry.thinkingLevel;
|
||||
} else {
|
||||
sessionEntry.thinkingLevel = directives.thinkLevel;
|
||||
}
|
||||
sessionEntry.thinkingLevel = directives.thinkLevel;
|
||||
}
|
||||
if (shouldDowngradeXHigh) {
|
||||
sessionEntry.thinkingLevel = "high";
|
||||
|
||||
@@ -82,11 +82,7 @@ export async function persistInlineDirectives(params: {
|
||||
let updated = false;
|
||||
|
||||
if (directives.hasThinkDirective && directives.thinkLevel) {
|
||||
if (directives.thinkLevel === "off") {
|
||||
delete sessionEntry.thinkingLevel;
|
||||
} else {
|
||||
sessionEntry.thinkingLevel = directives.thinkLevel;
|
||||
}
|
||||
sessionEntry.thinkingLevel = directives.thinkLevel;
|
||||
updated = true;
|
||||
}
|
||||
if (directives.hasVerboseDirective && directives.verboseLevel) {
|
||||
|
||||
@@ -222,11 +222,7 @@ export async function agentCommand(
|
||||
sessionEntry ?? { sessionId, updatedAt: Date.now() };
|
||||
const next: SessionEntry = { ...entry, sessionId, updatedAt: Date.now() };
|
||||
if (thinkOverride) {
|
||||
if (thinkOverride === "off") {
|
||||
delete next.thinkingLevel;
|
||||
} else {
|
||||
next.thinkingLevel = thinkOverride;
|
||||
}
|
||||
next.thinkingLevel = thinkOverride;
|
||||
}
|
||||
applyVerboseOverride(next, verboseOverride);
|
||||
sessionStore[sessionKey] = next;
|
||||
|
||||
@@ -124,6 +124,7 @@ export async function applySessionsPatchToStore(params: {
|
||||
if ("thinkingLevel" in patch) {
|
||||
const raw = patch.thinkingLevel;
|
||||
if (raw === null) {
|
||||
// Clear the override and fall back to model default
|
||||
delete next.thinkingLevel;
|
||||
} else if (raw !== undefined) {
|
||||
const normalized = normalizeThinkLevel(String(raw));
|
||||
@@ -134,11 +135,7 @@ export async function applySessionsPatchToStore(params: {
|
||||
`invalid thinkingLevel (use ${formatThinkingLevels(hintProvider, hintModel, "|")})`,
|
||||
);
|
||||
}
|
||||
if (normalized === "off") {
|
||||
delete next.thinkingLevel;
|
||||
} else {
|
||||
next.thinkingLevel = normalized;
|
||||
}
|
||||
next.thinkingLevel = normalized;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user