[PR #12544] fix(provider): convert thinking.budgetTokens to snake_case for openai-compatible SDK #14260

Open
opened 2026-02-16 18:19:04 -05:00 by yindo · 0 comments
Owner

Original Pull Request: https://github.com/anomalyco/opencode/pull/12544

State: open
Merged: No


Summary

When users configure thinking.budgetTokens for Claude models behind OpenAI-compatible proxies (LiteLLM, OpenRouter, etc.), the @ai-sdk/openai-compatible SDK passes the camelCase key as-is to the HTTP request. This fix adds a conversion in providerOptions() — the function that wraps options right before they're passed to the AI SDK. If thinking.budgetTokens (camelCase) is there and the SDK is @ai-sdk/openai-compatible, it rewrites the key to budget_tokens (snake_case).

Example config (opencode.json):

{
  "provider": {
    "litellm": {
      "npm": "@ai-sdk/openai-compatible",
      "options": {
        "baseURL": "http://localhost:4001/v1",
        "apiKey": "sk-1234"
      },
      "models": {
        "claude-sonnet-4-5": {
          "id": "claude-sonnet",
          "name": "Claude Sonnet 4.5 (LiteLLM)",
          "limit": { "context": 200000, "output": 64000 },
          "reasoning": true,
          "variants": {
            "high": {
              "thinking": { "type": "enabled", "budgetTokens": 16000 }
            },
            "max": {
              "thinking": { "type": "enabled", "budgetTokens": 31999 }
            }
          }
        }
      }
    }
  }
}

What @ai-sdk/openai-compatible sent on the HTTP request (broken):

{ "thinking": { "type": "enabled", "budgetTokens": 16000 } }

After this fix:

{ "thinking": { "type": "enabled", "budget_tokens": 16000 } }

This fix:

  • Does not modify variants() or change what options are available to users
  • Does not use string matching on model names
  • Only converts the format, not the values — users control what is sent
  • Leaves @ai-sdk/anthropic untouched (it handles conversion internally)
  • Does not touch maxOutputTokens() — the @ai-sdk/openai-compatible SDK sends max_tokens directly without summing budgetTokens on top (unlike @ai-sdk/anthropic), so the existing subtraction logic does not apply

Tests added

  • Unit tests for providerOptions() snake_case conversion (4 tests)
  • All 102 tests pass
**Original Pull Request:** https://github.com/anomalyco/opencode/pull/12544 **State:** open **Merged:** No --- ## Summary When users configure `thinking.budgetTokens` for Claude models behind OpenAI-compatible proxies (LiteLLM, OpenRouter, etc.), the `@ai-sdk/openai-compatible` SDK passes the camelCase key as-is to the HTTP request. This fix adds a conversion in `providerOptions()` — the function that wraps options right before they're passed to the AI SDK. If `thinking.budgetTokens` (camelCase) is there and the SDK is `@ai-sdk/openai-compatible`, it rewrites the key to `budget_tokens` (snake_case). **Example config (opencode.json):** ```json { "provider": { "litellm": { "npm": "@ai-sdk/openai-compatible", "options": { "baseURL": "http://localhost:4001/v1", "apiKey": "sk-1234" }, "models": { "claude-sonnet-4-5": { "id": "claude-sonnet", "name": "Claude Sonnet 4.5 (LiteLLM)", "limit": { "context": 200000, "output": 64000 }, "reasoning": true, "variants": { "high": { "thinking": { "type": "enabled", "budgetTokens": 16000 } }, "max": { "thinking": { "type": "enabled", "budgetTokens": 31999 } } } } } } } } ``` **What `@ai-sdk/openai-compatible` sent on the HTTP request (broken):** ```json { "thinking": { "type": "enabled", "budgetTokens": 16000 } } ``` **After this fix:** ```json { "thinking": { "type": "enabled", "budget_tokens": 16000 } } ``` This fix: - Does **not** modify `variants()` or change what options are available to users - Does **not** use string matching on model names - Only converts the **format**, not the **values** — users control what is sent - Leaves `@ai-sdk/anthropic` untouched (it handles conversion internally) - Does **not** touch `maxOutputTokens()` — the `@ai-sdk/openai-compatible` SDK sends `max_tokens` directly without summing `budgetTokens` on top (unlike `@ai-sdk/anthropic`), so the existing subtraction logic does not apply ## Tests added - Unit tests for `providerOptions()` snake_case conversion (4 tests) - All 102 tests pass
yindo added the pull-request label 2026-02-16 18:19:04 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#14260