From 415686244a8bbc7c52b546a8ca82e4a84b3a45b5 Mon Sep 17 00:00:00 2001 From: Mitsuyuki Osabe <24588751+carrotRakko@users.noreply.github.com> Date: Mon, 23 Feb 2026 02:11:04 +0900 Subject: [PATCH] feat: pass through OpenRouter provider routing params (#17148) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit extraParams.provider was silently dropped by createStreamFnWithExtraParams(). This change injects it into model.compat.openRouterRouting so pi-ai's buildParams includes params.provider in the API request body. Enables OpenRouter provider routing options (only, order, allow_fallbacks, data_collection, ignore, sort, quantizations) via model config: ```jsonc "openrouter/model-name": { "params": { "provider": { "only": ["deepinfra", "fireworks"], "allow_fallbacks": false } } } ``` Closes #10869 ✍️ Author: Claude Code with @carrotRakko (AI-written, human-approved) --- src/agents/pi-embedded-runner/extra-params.ts | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/agents/pi-embedded-runner/extra-params.ts b/src/agents/pi-embedded-runner/extra-params.ts index 553dccd5752..9b5587b9d15 100644 --- a/src/agents/pi-embedded-runner/extra-params.ts +++ b/src/agents/pi-embedded-runner/extra-params.ts @@ -95,18 +95,43 @@ function createStreamFnWithExtraParams( streamParams.cacheRetention = cacheRetention; } - if (Object.keys(streamParams).length === 0) { + // Extract OpenRouter provider routing preferences from extraParams.provider. + // Injected into model.compat.openRouterRouting so pi-ai's buildParams sets + // params.provider in the API request body (openai-completions.js L359-362). + // pi-ai's OpenRouterRouting type only declares { only?, order? }, but at + // runtime the full object is forwarded — enabling allow_fallbacks, + // data_collection, ignore, sort, quantizations, etc. + const providerRouting = + provider === "openrouter" && + extraParams.provider != null && + typeof extraParams.provider === "object" + ? (extraParams.provider as Record) + : undefined; + + if (Object.keys(streamParams).length === 0 && !providerRouting) { return undefined; } log.debug(`creating streamFn wrapper with params: ${JSON.stringify(streamParams)}`); + if (providerRouting) { + log.debug(`OpenRouter provider routing: ${JSON.stringify(providerRouting)}`); + } const underlying = baseStreamFn ?? streamSimple; - const wrappedStreamFn: StreamFn = (model, context, options) => - underlying(model, context, { + const wrappedStreamFn: StreamFn = (model, context, options) => { + // When provider routing is configured, inject it into model.compat so + // pi-ai picks it up via model.compat.openRouterRouting. + const effectiveModel = providerRouting + ? ({ + ...model, + compat: { ...model.compat, openRouterRouting: providerRouting }, + } as unknown as typeof model) + : model; + return underlying(effectiveModel, context, { ...streamParams, ...options, }); + }; return wrappedStreamFn; }