diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 705af3d5c1b5..303ff11fdfd8 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -516,12 +516,20 @@ export function message(msgs: ModelMessage[], model: Provider.Model, options: Re return msgs } +const GEMINI_MODELS_WITH_SAMPLING_DEFAULTS = [ + /gemini-2[.-]5(?:[.-]|$)/, + /gemini-3-(?:flash|pro)(?:[.-]|$)/, + /gemini-3[.-]1(?:[.-]|$)/, + /gemini-3[.-]5-flash(?!-lite)(?:[.-]|$)/, +] + export function temperature(model: Provider.Model) { - const id = model.id.toLowerCase() + const id = model.api.id.toLowerCase() if (id.includes("north-mini-code")) return 1.0 if (id.includes("qwen")) return 0.55 if (id.includes("claude")) return undefined - if (id.includes("gemini")) return 1.0 + if (id.includes("gemini")) + return GEMINI_MODELS_WITH_SAMPLING_DEFAULTS.some((model) => model.test(id)) ? 1.0 : undefined if (id.includes("glm-4.6")) return 1.0 if (id.includes("glm-4.7")) return 1.0 if (id.includes("minimax-m2")) return 1.0 @@ -536,21 +544,24 @@ export function temperature(model: Provider.Model) { } export function topP(model: Provider.Model) { - const id = model.id.toLowerCase() + const id = model.api.id.toLowerCase() if (id.includes("qwen")) return 1 - if (["minimax-m2", "gemini", "kimi-k2.5", "kimi-k2p5", "kimi-k2-5"].some((s) => id.includes(s))) { + if (id.includes("gemini")) + return GEMINI_MODELS_WITH_SAMPLING_DEFAULTS.some((model) => model.test(id)) ? 0.95 : undefined + if (["minimax-m2", "kimi-k2.5", "kimi-k2p5", "kimi-k2-5"].some((s) => id.includes(s))) { return 0.95 } return undefined } export function topK(model: Provider.Model) { - const id = model.id.toLowerCase() + const id = model.api.id.toLowerCase() if (id.includes("minimax-m2")) { if (["m2.", "m25", "m21"].some((s) => id.includes(s))) return 40 return 20 } - if (id.includes("gemini")) return 64 + if (id.includes("gemini")) + return GEMINI_MODELS_WITH_SAMPLING_DEFAULTS.some((model) => model.test(id)) ? 64 : undefined return undefined } diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index 93e166c4a8c9..0b88fa59dff8 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -3186,7 +3186,69 @@ describe("ProviderTransform.message - cache control on gateway", () => { describe("ProviderTransform.temperature - Cohere North", () => { test("defaults north-mini-code models to 1.0", () => { - expect(ProviderTransform.temperature({ id: "cohere/North-Mini-Code-1-0-latest" } as any)).toBe(1.0) + expect( + ProviderTransform.temperature({ + id: "cohere/North-Mini-Code-1-0-latest", + api: { id: "North-Mini-Code-1-0-latest" }, + } as any), + ).toBe(1.0) + }) +}) + +describe("ProviderTransform sampling defaults - Gemini", () => { + const model = (id: string) => + ({ + id: `google/${id}`, + api: { id }, + }) as any + + const alias = (id: string, apiID: string) => + ({ + id, + api: { id: apiID }, + }) as any + + test.each([ + "gemini-3.5-flash-lite", + "gemini-3-5-flash-lite", + "gemini-3.6-flash", + "gemini-3-6-flash", + "gemini-4-pro", + "gemini-future", + ])("omits deprecated sampling controls for %s", (id) => { + expect(ProviderTransform.temperature(model(id))).toBeUndefined() + expect(ProviderTransform.topP(model(id))).toBeUndefined() + expect(ProviderTransform.topK(model(id))).toBeUndefined() + }) + + test.each([ + "gemini-2.5-flash", + "gemini-2.5-pro", + "gemini-2.5-flash-lite", + "gemini-2-5-flash-lite", + "gemini-3-flash-preview", + "gemini-3-pro-image", + "gemini-3.1-flash-lite", + "gemini-3.1-pro-preview", + "gemini-3-1-pro-preview", + "gemini-3.5-flash", + "gemini-3-5-flash", + ])("preserves sampling defaults for %s", (id) => { + expect(ProviderTransform.temperature(model(id))).toBe(1) + expect(ProviderTransform.topP(model(id))).toBe(0.95) + expect(ProviderTransform.topK(model(id))).toBe(64) + }) + + test("uses the API model ID for configured aliases", () => { + const deprecated = alias("google/gemini-3.5-flash", "google/gemini-3.6-flash") + expect(ProviderTransform.temperature(deprecated)).toBeUndefined() + expect(ProviderTransform.topP(deprecated)).toBeUndefined() + expect(ProviderTransform.topK(deprecated)).toBeUndefined() + + const supported = alias("my-gemini", "google/gemini-2.5-flash") + expect(ProviderTransform.temperature(supported)).toBe(1) + expect(ProviderTransform.topP(supported)).toBe(0.95) + expect(ProviderTransform.topK(supported)).toBe(64) }) })