Fix Anthropic thinking budgets

Normalize Anthropic thinking budgets while building message parameters so budget_tokens stays below max_tokens with response headroom. When the configured max token budget is too small to support Anthropic's minimum thinking budget, omit thinking for that request instead of sending an invalid payload.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Sacha Al Himdani <SachaProbo@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-07-02 17:29:12 +00:00
parent 180a6a0420
commit 73023fc2b8
2 changed files with 156 additions and 2 deletions

View File

@@ -47,6 +47,11 @@ type (
}
)
const (
anthropicThinkingMinBudgetTokens = 1024
anthropicThinkingOutputTokenReserve = 1024
)
func WithHTTPClient(c *http.Client) Option {
return func(cfg *config) { cfg.httpClient = c }
}
@@ -163,8 +168,8 @@ func buildParams(req *llm.ChatCompletionRequest) (anthropic.MessageNewParams, er
params.ToolChoice = buildToolChoice(req.ToolChoice)
}
if req.Thinking != nil && req.Thinking.Enabled {
params.Thinking = anthropic.ThinkingConfigParamOfEnabled(int64(req.Thinking.BudgetTokens))
if budgetTokens, ok := resolveThinkingBudget(*req.MaxTokens, req.Thinking); ok {
params.Thinking = anthropic.ThinkingConfigParamOfEnabled(int64(budgetTokens))
}
if req.ResponseFormat != nil {
@@ -192,6 +197,28 @@ func buildParams(req *llm.ChatCompletionRequest) (anthropic.MessageNewParams, er
return params, nil
}
func resolveThinkingBudget(maxTokens int, thinking *llm.ThinkingConfig) (int, bool) {
if thinking == nil || !thinking.Enabled || thinking.BudgetTokens <= 0 {
return 0, false
}
if maxTokens <= anthropicThinkingMinBudgetTokens {
return 0, false
}
maxBudgetTokens := maxTokens - anthropicThinkingOutputTokenReserve
if maxBudgetTokens < anthropicThinkingMinBudgetTokens {
maxBudgetTokens = maxTokens - 1
}
budgetTokens := max(min(thinking.BudgetTokens, maxBudgetTokens), anthropicThinkingMinBudgetTokens)
if budgetTokens >= maxTokens {
return 0, false
}
return budgetTokens, true
}
func extractSystem(messages []llm.Message) (system []string, rest []llm.Message) {
for _, msg := range messages {
if msg.Role == llm.RoleSystem {