fix(openrouter): skip reasoning effort injection for 'auto' routing model

The 'auto' model on OpenRouter dynamically routes to any underlying model
OpenRouter selects, including reasoning-required endpoints. Previously,
OpenClaw would unconditionally inject `reasoning.effort: "none"` into
every request when the thinking level was "off", which causes a 400 error
on models where reasoning is mandatory and cannot be disabled.

Root cause:
- openrouter/auto has reasoning: false in the built-in catalog
- With thinking level "off", createOpenRouterWrapper injects
  `reasoning: { effort: "none" }` via mapThinkingLevelToOpenRouterReasoningEffort
- For any OpenRouter-routed model that requires reasoning this results in:
  "400 Reasoning is mandatory for this endpoint and cannot be disabled"
- The reasoning: false is then persisted back to models.json on every
  ensureOpenClawModelsJson call, so manually removing it has no lasting effect

Fix:
- In applyExtraParamsToAgent, when provider is "openrouter" and the model
  id is "auto", pass undefined as thinkingLevel to createOpenRouterWrapper
  so no reasoning.effort is injected at all, letting OpenRouter's upstream
  model handle it natively
- Add an explanatory comment in buildOpenrouterProvider clarifying that the
  reasoning: false catalog value does NOT cause effort injection for "auto"

Users who need explicit reasoning control should target a specific model
id (e.g. openrouter/deepseek/deepseek-r1) rather than the auto router.

Fixes #24851

(cherry picked from commit aa554397980972d917dece09ab03c4cc15f5d100)
This commit is contained in:
zerone0x
2026-02-24 04:36:20 +01:00
committed by Peter Steinberger
parent eae13d9367
commit bc52d4a459
2 changed files with 14 additions and 1 deletions

View File

@@ -685,6 +685,12 @@ function buildOpenrouterProvider(): ProviderConfig {
{
id: OPENROUTER_DEFAULT_MODEL_ID,
name: "OpenRouter Auto",
// reasoning: false here is a catalog default only; it does NOT cause
// `reasoning.effort: "none"` to be sent for the "auto" routing model.
// applyExtraParamsToAgent skips the reasoning effort injection for
// model id "auto" because it dynamically routes to any OpenRouter model
// (including ones where reasoning is mandatory and cannot be disabled).
// See: openclaw/openclaw#24851
reasoning: false,
input: ["text", "image"],
cost: OPENROUTER_DEFAULT_COST,

View File

@@ -546,7 +546,14 @@ export function applyExtraParamsToAgent(
if (provider === "openrouter") {
log.debug(`applying OpenRouter app attribution headers for ${provider}/${modelId}`);
agent.streamFn = createOpenRouterWrapper(agent.streamFn, thinkingLevel);
// "auto" is a dynamic routing model — we don't know which underlying model
// OpenRouter will select, and it may be a reasoning-required endpoint.
// Omit the thinkingLevel so we never inject `reasoning.effort: "none"`,
// which would cause a 400 on models where reasoning is mandatory.
// Users who need reasoning control should target a specific model ID.
// See: openclaw/openclaw#24851
const openRouterThinkingLevel = modelId === "auto" ? undefined : thinkingLevel;
agent.streamFn = createOpenRouterWrapper(agent.streamFn, openRouterThinkingLevel);
agent.streamFn = createOpenRouterSystemCacheWrapper(agent.streamFn);
}