diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index c14587ba8a4e..948299cb8870 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -433,6 +433,7 @@ export function message(msgs: ModelMessage[], model: Provider.Model, options: Re if ( (model.providerID === "anthropic" || model.providerID === "google-vertex-anthropic" || + (model.api.npm === "@openrouter/ai-sdk-provider" && model.api.id.includes("gemini")) || model.api.id.includes("anthropic") || model.api.id.includes("claude") || model.id.includes("anthropic") || @@ -1221,6 +1222,7 @@ export function options(input: { if (input.model.providerID === "openrouter") { result["prompt_cache_key"] = input.sessionID + result["session_id"] = input.sessionID } if (input.model.api.npm === "@ai-sdk/gateway") { result["gateway"] = { diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index e69dab70f65d..e637a3954d5f 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -172,6 +172,26 @@ describe("ProviderTransform.options - setCacheKey", () => { }) expect(result.store).toBe(false) }) + + test("should set session affinity for openrouter", () => { + const result = ProviderTransform.options({ + model: { + ...mockModel, + id: "google/gemini-3.1-pro-preview", + providerID: "openrouter", + api: { + id: "google/gemini-3.1-pro-preview", + url: "https://openrouter.ai/api/v1", + npm: "@openrouter/ai-sdk-provider", + }, + }, + sessionID, + providerOptions: {}, + }) + + expect(result.prompt_cache_key).toBe(sessionID) + expect(result.session_id).toBe(sessionID) + }) }) describe("ProviderTransform.options - zai/zhipuai thinking", () => { @@ -2821,7 +2841,7 @@ describe("ProviderTransform.message - bedrock caching with non-bedrock providerI }) }) -describe("ProviderTransform.message - cache control on gateway", () => { +describe("ProviderTransform.message - cache control", () => { const createModel = (overrides: Partial = {}) => ({ id: "anthropic/claude-sonnet-4", @@ -2868,6 +2888,37 @@ describe("ProviderTransform.message - cache control on gateway", () => { expect(result[0].providerOptions).toBeUndefined() }) + test("openrouter gemini applies explicit cache control", () => { + const model = createModel({ + id: "google/gemini-3.1-pro-preview", + providerID: "openrouter", + api: { + id: "google/gemini-3.1-pro-preview", + url: "https://openrouter.ai/api/v1", + npm: "@openrouter/ai-sdk-provider", + }, + }) + const msgs = [ + { + role: "system", + content: "You are a helpful assistant", + }, + { + role: "user", + content: [{ type: "text", text: "Hello" }], + }, + ] as any[] + + const result = ProviderTransform.message(msgs, model, {}) as any[] + + expect(result[0].providerOptions?.openrouter).toEqual({ + cacheControl: { type: "ephemeral" }, + }) + expect(result[1].content[0].providerOptions?.openrouter).toEqual({ + cacheControl: { type: "ephemeral" }, + }) + }) + test("non-gateway anthropic keeps existing cache control behavior", () => { const model = createModel({ providerID: "anthropic",