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:
committed by
Peter Steinberger
parent
eae13d9367
commit
bc52d4a459
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user