diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 92d84eac8cb..d6f817b50e4 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -1586,7 +1586,7 @@ export function reasoningVariants(model: ModelsDev.Model, target: Provider.Model if (options.length === 0) return {} const effort = options.find((option) => option.type === "effort") - if (effort) return nonEmptyVariants(effortVariants(target, effort.values)) + if (effort) return effortVariants(target, effort.values) const toggle = options.some((option) => option.type === "toggle") const budget = options.find((option) => option.type === "budget_tokens") @@ -1651,7 +1651,7 @@ function reasoningEffort(model: Provider.Model, effort: string) { return { reasoning: { effort } } case "@ai-sdk/anthropic": case "@ai-sdk/google-vertex/anthropic": - return anthropicEffort(model, effort) + return anthropicEffort(model, effort) ?? { effort } case "@ai-sdk/google": case "@ai-sdk/google-vertex": return { thinkingConfig: { includeThoughts: true, thinkingLevel: effort } } diff --git a/packages/opencode/test/provider/provider.test.ts b/packages/opencode/test/provider/provider.test.ts index e656890b01b..058bad23a1c 100644 --- a/packages/opencode/test/provider/provider.test.ts +++ b/packages/opencode/test/provider/provider.test.ts @@ -1477,7 +1477,7 @@ test("models.dev normalization fills required response fields", () => { expect(model.release_date).toBe("") }) -test("models.dev reasoning options replace generated variants and unsupported options fall back", () => { +test("models.dev reasoning options replace generated variants and unsupported toggles fall back", () => { const provider = { id: "reasoning", name: "Reasoning", @@ -1514,6 +1514,14 @@ test("models.dev reasoning options replace generated variants and unsupported op limit: { context: 128_000, output: 64_000 }, experimental: { modes: { fast: {} } }, }, + anthropicCompatible: { + id: "k3", + name: "Anthropic Compatible", + reasoning: true, + reasoning_options: [{ type: "effort", values: ["max"] }], + provider: { npm: "@ai-sdk/anthropic" }, + limit: { context: 1_048_576, output: 131_072 }, + }, }, } as unknown as ModelsDev.Provider @@ -1530,6 +1538,7 @@ test("models.dev reasoning options replace generated variants and unsupported op expect(models.override.variants).toEqual({ high: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } }, }) + expect(models.anthropicCompatible.variants).toEqual({ max: { effort: "max" } }) expect(models["gemini-3-pro-fast"].variants).toEqual(models.override.variants) }) diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index 4c462bd3dce..81132569223 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -3103,13 +3103,20 @@ describe("ProviderTransform.reasoningVariants", () => { ).toEqual({ high: { effort: "high" } }) }) - test("leaves legacy Anthropic effort options to budget fallback", () => { + test("uses explicit effort metadata for Anthropic-compatible models", () => { expect( ProviderTransform.reasoningVariants( model([{ type: "effort", values: ["high"] }]), target("@ai-sdk/anthropic", "claude-sonnet-4"), ), - ).toBeUndefined() + ).toEqual({ high: { effort: "high" } }) + + expect( + ProviderTransform.reasoningVariants( + model([{ type: "effort", values: ["max"] }]), + target("@ai-sdk/anthropic", "k3"), + ), + ).toEqual({ max: { effort: "max" } }) }) test("uses adaptive reasoning config for Anthropic models on Bedrock", () => { @@ -3129,13 +3136,13 @@ describe("ProviderTransform.reasoningVariants", () => { }) }) - test("leaves legacy Anthropic Bedrock effort options to budget fallback", () => { + test("does not replace unsupported Anthropic Bedrock effort options with token budgets", () => { expect( ProviderTransform.reasoningVariants( model([{ type: "effort", values: ["high"] }]), target("@ai-sdk/amazon-bedrock", "anthropic.claude-sonnet-4-v1:0"), ), - ).toBeUndefined() + ).toEqual({}) }) test.each([ @@ -3256,10 +3263,13 @@ describe("ProviderTransform.reasoningVariants", () => { }) }) - test("leaves unsupported options for heuristic fallback", () => { + test("does not replace unsupported effort options with heuristic variants", () => { expect( ProviderTransform.reasoningVariants(model([{ type: "effort", values: ["high"] }]), target("@ai-sdk/perplexity")), - ).toBeUndefined() + ).toEqual({}) + }) + + test("leaves unsupported toggle options for heuristic fallback", () => { expect(ProviderTransform.reasoningVariants(model([{ type: "toggle" }]), target("@ai-sdk/openai"))).toBeUndefined() }) @@ -3275,7 +3285,7 @@ describe("ProviderTransform.reasoningVariants", () => { }) expect( ProviderTransform.reasoningVariants(effort, target("@ai-sdk/github-copilot", "gemini-3-pro")), - ).toBeUndefined() + ).toEqual({}) }) test.each(["@ai-sdk/cohere", "@ai-sdk/perplexity", "@ai-sdk/vercel", "@ai-sdk/alibaba", "gitlab-ai-provider"])( @@ -3283,7 +3293,7 @@ describe("ProviderTransform.reasoningVariants", () => { (npm) => { expect( ProviderTransform.reasoningVariants(model([{ type: "effort", values: ["high"] }]), target(npm)), - ).toBeUndefined() + ).toEqual({}) }, ) })