diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 73caf5ab6bfe..46ce4d9d3d2f 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -1611,17 +1611,13 @@ function effortVariants(model: Provider.Model, values: readonly unknown[]) { } function budgetVariants(model: Provider.Model, min?: number, max?: number) { - const limit = model.limit.output - 1 - if (limit <= 0) return {} - const high = Math.min( - max === undefined ? Math.max(min ?? 0, 16_000) : Math.min(Math.max(min ?? 0, 16_000), max), - limit, - ) - const maximum = max === undefined ? undefined : Math.min(max, limit) + const maximum = Math.min(max ?? OUTPUT_TOKEN_MAX - 1, model.limit.output - 1, OUTPUT_TOKEN_MAX - 1) + if (maximum <= 0) return {} + const high = Math.min(Math.max(min ?? 0, Math.floor((maximum + 1) / 2)), maximum) return Object.fromEntries( [ { id: "high", budget: high }, - ...(maximum === undefined || maximum === high ? [] : [{ id: "max", budget: maximum }]), + { id: "max", budget: maximum }, ].flatMap((item) => { const settings = reasoningBudget(model, item.budget) return settings ? [[item.id, settings]] : [] diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index a144997405a4..a6ebad986e94 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -3111,9 +3111,9 @@ describe("ProviderTransform.reasoningVariants", () => { ["@ai-sdk/cohere", { thinking: { type: "enabled", tokenBudget: 16_000 } }], ["@ai-sdk/alibaba", { enableThinking: true, thinkingBudget: 16_000 }], ])("converts token budgets for %s", (npm, high) => { - expect( - ProviderTransform.reasoningVariants(model([{ type: "budget_tokens", min: 1_024, max: 16_000 }]), target(npm)), - ).toEqual({ high }) + const variants = ProviderTransform.reasoningVariants(model([{ type: "budget_tokens", min: 1_024 }]), target(npm)) + expect(variants?.high).toEqual(high) + expect(Object.keys(variants ?? {})).toEqual(["high", "max"]) }) test("maps null effort to none", () => { @@ -3150,6 +3150,7 @@ describe("ProviderTransform.reasoningVariants", () => { ).toEqual({ none: { thinking: { type: "disabled" } }, high: { thinking: { type: "enabled", tokenBudget: 16_000 } }, + max: { thinking: { type: "enabled", tokenBudget: 31_999 } }, }) }) @@ -3161,7 +3162,7 @@ describe("ProviderTransform.reasoningVariants", () => { ), ).toEqual({ high: { thinking: { type: "enabled", budgetTokens: 16_000 } }, - max: { thinking: { type: "enabled", budgetTokens: 63_999 } }, + max: { thinking: { type: "enabled", budgetTokens: 31_999 } }, }) }) @@ -3171,7 +3172,32 @@ describe("ProviderTransform.reasoningVariants", () => { expect( ProviderTransform.reasoningVariants(model([{ type: "budget_tokens", min: 1_024, max: 64_000 }]), anthropic), ).toEqual({ - high: { thinking: { type: "enabled", budgetTokens: 4_999 } }, + high: { thinking: { type: "enabled", budgetTokens: 2_500 } }, + max: { thinking: { type: "enabled", budgetTokens: 4_999 } }, + }) + }) + + test("derives high and max budgets when models.dev omits max", () => { + expect( + ProviderTransform.reasoningVariants( + model([{ type: "budget_tokens", min: 1_024 }]), + target("@ai-sdk/anthropic", "claude-haiku-4-5"), + ), + ).toEqual({ + high: { thinking: { type: "enabled", budgetTokens: 16_000 } }, + max: { thinking: { type: "enabled", budgetTokens: 31_999 } }, + }) + }) + + test("preserves explicit inclusive budget maxima", () => { + expect( + ProviderTransform.reasoningVariants( + model([{ type: "budget_tokens", min: 1_024, max: 24_576 }]), + target("@ai-sdk/google", "gemini-2.5-pro"), + ), + ).toEqual({ + high: { thinkingConfig: { includeThoughts: true, thinkingBudget: 12_288 } }, + max: { thinkingConfig: { includeThoughts: true, thinkingBudget: 24_576 } }, }) })