From b69e51d835b52825afe940f400e0c59484fa8e38 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" <219766164+opencode-agent[bot]@users.noreply.github.com> Date: Fri, 31 Jul 2026 13:27:32 -0500 Subject: [PATCH] refactor(ai): simplify provider options (#39924) Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> --- packages/ai/src/providers/openrouter.ts | 25 ++++++------- packages/ai/test/provider/openrouter.test.ts | 37 ++++++++++++++++++++ packages/core/src/session/generate-node.ts | 5 +-- packages/core/src/session/model-request.ts | 5 +-- 4 files changed, 52 insertions(+), 20 deletions(-) diff --git a/packages/ai/src/providers/openrouter.ts b/packages/ai/src/providers/openrouter.ts index a3681b096f1..bacb355e1ad 100644 --- a/packages/ai/src/providers/openrouter.ts +++ b/packages/ai/src/providers/openrouter.ts @@ -143,23 +143,24 @@ const cacheControl = () => { const bodyOptions = (input: unknown) => { const openrouter = isRecord(input) ? input : {} - const { promptCacheKey, ...options } = openrouter + const { usage, models, provider, plugins, web_search_options, debug, user, reasoning, promptCacheKey, ...options } = + openrouter return { ...options, - ...(openrouter.usage === undefined || openrouter.usage === true + ...(usage === undefined || usage === true ? { usage: { include: true } } - : openrouter.usage === false + : usage === false ? { usage: { include: false } } - : isRecord(openrouter.usage) - ? { usage: openrouter.usage } + : isRecord(usage) + ? { usage } : {}), - ...(Array.isArray(openrouter.models) ? { models: openrouter.models } : {}), - ...(isRecord(openrouter.provider) ? { provider: openrouter.provider } : {}), - ...(Array.isArray(openrouter.plugins) ? { plugins: openrouter.plugins } : {}), - ...(isRecord(openrouter.web_search_options) ? { web_search_options: openrouter.web_search_options } : {}), - ...(isRecord(openrouter.debug) ? { debug: openrouter.debug } : {}), - ...(typeof openrouter.user === "string" ? { user: openrouter.user } : {}), - ...(isRecord(openrouter.reasoning) ? { reasoning: openrouter.reasoning } : {}), + ...(Array.isArray(models) ? { models } : {}), + ...(isRecord(provider) ? { provider } : {}), + ...(Array.isArray(plugins) ? { plugins } : {}), + ...(isRecord(web_search_options) ? { web_search_options } : {}), + ...(isRecord(debug) ? { debug } : {}), + ...(typeof user === "string" ? { user } : {}), + ...(isRecord(reasoning) ? { reasoning } : {}), ...(typeof promptCacheKey === "string" ? { prompt_cache_key: promptCacheKey } : {}), } } diff --git a/packages/ai/test/provider/openrouter.test.ts b/packages/ai/test/provider/openrouter.test.ts index 1121631b3e7..9f6efe5bfce 100644 --- a/packages/ai/test/provider/openrouter.test.ts +++ b/packages/ai/test/provider/openrouter.test.ts @@ -192,6 +192,43 @@ describe("OpenRouter", () => { }), ) + it.effect("filters invalid known OpenRouter options while preserving extensions", () => + Effect.gen(function* () { + const invalid: Record = { + usage: "yes", + models: "anthropic/claude-sonnet-4.6", + provider: [], + plugins: {}, + web_search_options: [], + debug: [], + user: 123, + reasoning: [], + promptCacheKey: 123, + future_option: { enabled: true }, + } + const prepared = yield* compileRequest( + LLM.request({ + model: OpenRouter.configure({ + apiKey: "test-key", + providerOptions: { openrouter: invalid }, + }).model("openai/gpt-4o-mini"), + prompt: "Hello", + }), + ) + + expect(prepared.body).toMatchObject({ future_option: { enabled: true } }) + expect(prepared.body).not.toHaveProperty("usage") + expect(prepared.body).not.toHaveProperty("models") + expect(prepared.body).not.toHaveProperty("provider") + expect(prepared.body).not.toHaveProperty("plugins") + expect(prepared.body).not.toHaveProperty("web_search_options") + expect(prepared.body).not.toHaveProperty("debug") + expect(prepared.body).not.toHaveProperty("user") + expect(prepared.body).not.toHaveProperty("reasoning") + expect(prepared.body).not.toHaveProperty("prompt_cache_key") + }), + ) + it.effect("preserves the upstream provider finish reason", () => Effect.gen(function* () { const model = OpenRouter.configure({ apiKey: "test-key" }).model("anthropic/claude-sonnet-4.6") diff --git a/packages/core/src/session/generate-node.ts b/packages/core/src/session/generate-node.ts index 35943097d25..eed4d9763d6 100644 --- a/packages/core/src/session/generate-node.ts +++ b/packages/core/src/session/generate-node.ts @@ -71,10 +71,7 @@ export const layer = Layer.effect( LLM.request({ model: model.model, http: { headers: SessionModelHeaders.make(selection.session, app) }, - providerOptions: - model.model.route.id === "openrouter" - ? { openrouter: { promptCacheKey } } - : { openai: { promptCacheKey } }, + providerOptions: { [providerMetadataKey]: { promptCacheKey } }, system: contextEvent.system, messages: contextEvent.messages, tools: hookedTools, diff --git a/packages/core/src/session/model-request.ts b/packages/core/src/session/model-request.ts index ba87995b63a..57ea5b33eb6 100644 --- a/packages/core/src/session/model-request.ts +++ b/packages/core/src/session/model-request.ts @@ -158,10 +158,7 @@ export const layer = Layer.effect( http: { headers: SessionModelHeaders.make(session, app), }, - providerOptions: - model.route.id === "openrouter" - ? { openrouter: { promptCacheKey } } - : { openai: { promptCacheKey } }, + providerOptions: { [providerMetadataKey]: { promptCacheKey } }, system: contextEvent.system, messages: unsupportedParts(contextEvent.messages, resolved.capabilities), tools: hookedTools,