diff --git a/CHANGELOG.md b/CHANGELOG.md index 369d5044135..59ab6491942 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -126,6 +126,7 @@ Docs: https://docs.openclaw.ai - Providers/OpenRouter: allow pass-through OpenRouter and Opencode model IDs in live model filtering so custom routed model IDs are treated as modern refs. (#14312) Thanks @Joly0. - Providers/OpenRouter: default reasoning to enabled when the selected model advertises `reasoning: true` and no session/directive override is set. (#22513) Thanks @zwffff. - Providers/OpenRouter: map `/think` levels to `reasoning.effort` in embedded runs while preserving explicit `reasoning.max_tokens` payloads. (#17236) Thanks @robbyczgw-cla. +- Providers/OpenRouter: remove conflicting top-level `reasoning_effort` when injecting nested `reasoning.effort`, preventing OpenRouter 400 payload-validation failures for reasoning models. (#24120) thanks @tenequm. - Providers/OpenRouter: preserve stored session provider when model IDs are vendor-prefixed (for example, `anthropic/...`) so follow-up turns do not incorrectly route to direct provider APIs. (#22753) Thanks @dndodson. - Providers/OpenRouter: preserve the required `openrouter/` prefix for OpenRouter-native model IDs during model-ref normalization. (#12942) Thanks @omair445. - Providers/OpenRouter: pass through provider routing parameters from model params.provider to OpenRouter request payloads for provider selection controls. (#17148) Thanks @carrotRakko. diff --git a/src/agents/pi-embedded-runner/extra-params.ts b/src/agents/pi-embedded-runner/extra-params.ts index 3ae690c9421..04cd95b4d37 100644 --- a/src/agents/pi-embedded-runner/extra-params.ts +++ b/src/agents/pi-embedded-runner/extra-params.ts @@ -376,6 +376,13 @@ function createOpenRouterWrapper( onPayload: (payload) => { if (thinkingLevel && payload && typeof payload === "object") { const payloadObj = payload as Record; + + // pi-ai may inject a top-level reasoning_effort (OpenAI flat format). + // OpenRouter expects the nested reasoning.effort format instead, and + // rejects payloads containing both fields. Remove the flat field so + // only the nested one is sent. + delete payloadObj.reasoning_effort; + const existingReasoning = payloadObj.reasoning; // OpenRouter treats reasoning.effort and reasoning.max_tokens as