From 67b1c9d00aca2356a5ddd9de485e14e117e41378 Mon Sep 17 00:00:00 2001 From: tanishqvec Date: Sat, 29 Aug 2026 23:55:52 +0530 Subject: [PATCH] feat(provider): add none variant that disables thinking for DeepSeek V4 --- packages/opencode/src/provider/transform.ts | 18 ++++++- .../opencode/test/provider/transform.test.ts | 54 ++++++++++++++++++- 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 28a5beb9abac..38dcea658d81 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -1655,10 +1655,15 @@ export function reasoningVariants(model: ModelsDev.Model, target: Provider.Model if (options === undefined) return if (options.length === 0) return {} + const toggle = options.some((option) => option.type === "toggle") const effort = options.find((option) => option.type === "effort") - if (effort) return effortVariants(target, effort.values) + if (effort) { + // A model can declare both a toggle and effort values. Effort values cannot + // express "off", so keep the toggle's none variant rather than dropping it. + const off = toggle ? reasoningToggle(target).none : undefined + return { ...(off ? { none: off } : {}), ...effortVariants(target, effort.values) } + } - const toggle = options.some((option) => option.type === "toggle") const budget = options.find((option) => option.type === "budget_tokens") if (!budget) return toggle ? nonEmptyVariants(reasoningToggle(target)) : undefined @@ -1712,6 +1717,15 @@ function reasoningToggle(model: Provider.Model): NonNullable { describe("ProviderTransform.reasoningVariants", () => { const model = (reasoning_options: ModelsDev.Model["reasoning_options"]) => ({ reasoning_options }) as ModelsDev.Model - const target = (npm: string, id = "test-model") => + const target = (npm: string, id = "test-model", providerID = "test") => ({ id, - providerID: "test", + providerID, api: { id, npm, url: "" }, capabilities: { reasoning: true }, limit: { output: 64_000 }, @@ -3772,6 +3772,56 @@ describe("ProviderTransform.reasoningVariants", () => { ) }, ) + + test("keeps the thinking toggle as a none variant when effort values are also declared", () => { + expect( + ProviderTransform.reasoningVariants( + model([{ type: "toggle" }, { type: "effort", values: ["low", "high", "max"] }]), + target("@ai-sdk/openai-compatible", "deepseek-v4-flash", "deepseek"), + ), + ).toEqual({ + none: { thinking: { type: "disabled" } }, + low: { reasoningEffort: "low" }, + high: { reasoningEffort: "high" }, + max: { reasoningEffort: "max" }, + }) + }) + + test("disables deepseek thinking through reasoning effort on zen", () => { + // zen accepts reasoning_effort "none" and ignores thinking; api.deepseek.com + // is the other way round, so the payload follows the provider. + expect( + ProviderTransform.reasoningVariants( + model([{ type: "toggle" }, { type: "effort", values: ["low", "high", "max"] }]), + target("@ai-sdk/openai-compatible", "deepseek-v4-flash", "opencode"), + ), + ).toEqual({ + none: { reasoningEffort: "none" }, + low: { reasoningEffort: "low" }, + high: { reasoningEffort: "high" }, + max: { reasoningEffort: "max" }, + }) + }) + + test("only offers the deepseek toggle on providers known to support it", () => { + // 30 other providers resell deepseek over openai-compatible and may not + // accept either switch, so matching on the model id alone is too broad. + expect( + ProviderTransform.reasoningVariants( + model([{ type: "toggle" }, { type: "effort", values: ["high"] }]), + target("@ai-sdk/openai-compatible", "deepseek/deepseek-v4-flash", "novita-ai"), + ), + ).toEqual({ high: { reasoningEffort: "high" } }) + }) + + test("does not add a none variant for openai-compatible models that are not deepseek", () => { + expect( + ProviderTransform.reasoningVariants( + model([{ type: "toggle" }, { type: "effort", values: ["high"] }]), + target("@ai-sdk/openai-compatible", "some-other-model"), + ), + ).toEqual({ high: { reasoningEffort: "high" } }) + }) }) describe("ProviderTransform.variants", () => {