summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAiden Cline <[email protected]>2026-02-01 14:50:49 -0600
committerGitHub <[email protected]>2026-02-01 14:50:49 -0600
commitf6948d0ffaa4b9d7e727bf2963c943563d72788f (patch)
tree6324da7cb0a03a840ceaa2f1c07f5314f96b4e1b
parentd52ee41b3aa2148c58d94bda75e83654dd38892a (diff)
downloadopencode-f6948d0ffaa4b9d7e727bf2963c943563d72788f.tar.gz
opencode-f6948d0ffaa4b9d7e727bf2963c943563d72788f.zip
fix: variant logic for anthropic models through openai compat endpoint (#11665)
-rw-r--r--packages/opencode/src/provider/transform.ts41
-rw-r--r--packages/opencode/test/provider/transform.test.ts131
2 files changed, 2 insertions, 170 deletions
diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts
index 68220b90c..ded416e66 100644
--- a/packages/opencode/src/provider/transform.ts
+++ b/packages/opencode/src/provider/transform.ts
@@ -395,31 +395,6 @@ export namespace ProviderTransform {
case "@ai-sdk/deepinfra":
// https://v5.ai-sdk.dev/providers/ai-sdk-providers/deepinfra
case "@ai-sdk/openai-compatible":
- // When using openai-compatible SDK with Claude/Anthropic models,
- // we must use snake_case (budget_tokens) as the SDK doesn't convert parameter names
- // and the OpenAI-compatible API spec uses snake_case
- if (
- model.providerID === "anthropic" ||
- model.api.id.includes("anthropic") ||
- model.api.id.includes("claude") ||
- model.id.includes("anthropic") ||
- model.id.includes("claude")
- ) {
- return {
- high: {
- thinking: {
- type: "enabled",
- budget_tokens: 16000,
- },
- },
- max: {
- thinking: {
- type: "enabled",
- budget_tokens: 31999,
- },
- },
- }
- }
return Object.fromEntries(WIDELY_SUPPORTED_EFFORTS.map((effort) => [effort, { reasoningEffort: effort }]))
case "@ai-sdk/azure":
@@ -719,21 +694,9 @@ export namespace ProviderTransform {
const modelCap = modelLimit || globalLimit
const standardLimit = Math.min(modelCap, globalLimit)
- // Handle thinking mode for @ai-sdk/anthropic, @ai-sdk/google-vertex/anthropic (budgetTokens)
- // and @ai-sdk/openai-compatible with Claude (budget_tokens)
- if (
- npm === "@ai-sdk/anthropic" ||
- npm === "@ai-sdk/google-vertex/anthropic" ||
- npm === "@ai-sdk/openai-compatible"
- ) {
+ if (npm === "@ai-sdk/anthropic" || npm === "@ai-sdk/google-vertex/anthropic") {
const thinking = options?.["thinking"]
- // Support both camelCase (for @ai-sdk/anthropic) and snake_case (for openai-compatible)
- const budgetTokens =
- typeof thinking?.["budgetTokens"] === "number"
- ? thinking["budgetTokens"]
- : typeof thinking?.["budget_tokens"] === "number"
- ? thinking["budget_tokens"]
- : 0
+ const budgetTokens = typeof thinking?.["budgetTokens"] === "number" ? thinking["budgetTokens"] : 0
const enabled = thinking?.["type"] === "enabled"
if (enabled && budgetTokens > 0) {
// Return text tokens so that text + thinking <= model cap, preferring 32k text when possible.
diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts
index cbb9ddbc4..8e28f1209 100644
--- a/packages/opencode/test/provider/transform.test.ts
+++ b/packages/opencode/test/provider/transform.test.ts
@@ -267,76 +267,6 @@ describe("ProviderTransform.maxOutputTokens", () => {
expect(result).toBe(OUTPUT_TOKEN_MAX)
})
})
-
- describe("openai-compatible with thinking options (snake_case)", () => {
- test("returns 32k when budget_tokens + 32k <= modelLimit", () => {
- const modelLimit = 100000
- const options = {
- thinking: {
- type: "enabled",
- budget_tokens: 10000,
- },
- }
- const result = ProviderTransform.maxOutputTokens(
- "@ai-sdk/openai-compatible",
- options,
- modelLimit,
- OUTPUT_TOKEN_MAX,
- )
- expect(result).toBe(OUTPUT_TOKEN_MAX)
- })
-
- test("returns modelLimit - budget_tokens when budget_tokens + 32k > modelLimit", () => {
- const modelLimit = 50000
- const options = {
- thinking: {
- type: "enabled",
- budget_tokens: 30000,
- },
- }
- const result = ProviderTransform.maxOutputTokens(
- "@ai-sdk/openai-compatible",
- options,
- modelLimit,
- OUTPUT_TOKEN_MAX,
- )
- expect(result).toBe(20000)
- })
-
- test("returns 32k when thinking type is not enabled", () => {
- const modelLimit = 100000
- const options = {
- thinking: {
- type: "disabled",
- budget_tokens: 10000,
- },
- }
- const result = ProviderTransform.maxOutputTokens(
- "@ai-sdk/openai-compatible",
- options,
- modelLimit,
- OUTPUT_TOKEN_MAX,
- )
- expect(result).toBe(OUTPUT_TOKEN_MAX)
- })
-
- test("returns 32k when budget_tokens is 0", () => {
- const modelLimit = 100000
- const options = {
- thinking: {
- type: "enabled",
- budget_tokens: 0,
- },
- }
- const result = ProviderTransform.maxOutputTokens(
- "@ai-sdk/openai-compatible",
- options,
- modelLimit,
- OUTPUT_TOKEN_MAX,
- )
- expect(result).toBe(OUTPUT_TOKEN_MAX)
- })
- })
})
describe("ProviderTransform.schema - gemini array items", () => {
@@ -1564,67 +1494,6 @@ describe("ProviderTransform.variants", () => {
expect(result.low).toEqual({ reasoningEffort: "low" })
expect(result.high).toEqual({ reasoningEffort: "high" })
})
-
- test("Claude via LiteLLM returns thinking with snake_case budget_tokens", () => {
- const model = createMockModel({
- id: "anthropic/claude-sonnet-4-5",
- providerID: "anthropic",
- api: {
- id: "claude-sonnet-4-5-20250929",
- url: "http://localhost:4000",
- npm: "@ai-sdk/openai-compatible",
- },
- })
- const result = ProviderTransform.variants(model)
- expect(Object.keys(result)).toEqual(["high", "max"])
- expect(result.high).toEqual({
- thinking: {
- type: "enabled",
- budget_tokens: 16000,
- },
- })
- expect(result.max).toEqual({
- thinking: {
- type: "enabled",
- budget_tokens: 31999,
- },
- })
- })
-
- test("Claude model (by model.id) via openai-compatible uses snake_case", () => {
- const model = createMockModel({
- id: "litellm/claude-3-opus",
- providerID: "litellm",
- api: {
- id: "claude-3-opus-20240229",
- url: "http://localhost:4000",
- npm: "@ai-sdk/openai-compatible",
- },
- })
- const result = ProviderTransform.variants(model)
- expect(Object.keys(result)).toEqual(["high", "max"])
- expect(result.high).toEqual({
- thinking: {
- type: "enabled",
- budget_tokens: 16000,
- },
- })
- })
-
- test("Anthropic model (by model.api.id) via openai-compatible uses snake_case", () => {
- const model = createMockModel({
- id: "custom/my-model",
- providerID: "custom",
- api: {
- id: "anthropic.claude-sonnet",
- url: "http://localhost:4000",
- npm: "@ai-sdk/openai-compatible",
- },
- })
- const result = ProviderTransform.variants(model)
- expect(Object.keys(result)).toEqual(["high", "max"])
- expect(result.high.thinking.budget_tokens).toBe(16000)
- })
})
describe("@ai-sdk/azure", () => {