fix(gateway): recognize Baseten reasoning budget errors

This commit is contained in:
Achord Chan
2026-09-20 11:02:06 +08:00
parent 1a9d49e16f
commit 1411b7eb7e
2 changed files with 81 additions and 1 deletions
+13 -1
View File
@@ -1607,12 +1607,16 @@ const (
// isThinkingBudgetConstraintError detects whether an upstream error message indicates
// a budget_tokens constraint violation (e.g. "budget_tokens >= 1024").
// Matches three conditions (all must be true):
// Also recognizes Baseten's final-answer reserve constraint.
// For the original budget constraint, all three conditions must be true:
// 1. Contains "budget_tokens" or "budget tokens"
// 2. Contains "thinking"
// 3. Contains ">= 1024" or "greater than or equal to 1024" or ("1024" + "input should be")
func isThinkingBudgetConstraintError(errMsg string) bool {
m := strings.ToLower(errMsg)
if isFinalAnswerReserveError(m) {
return true
}
// Condition 1: budget_tokens or budget tokens
hasBudget := strings.Contains(m, "budget_tokens") || strings.Contains(m, "budget tokens")
@@ -1636,6 +1640,14 @@ func isThinkingBudgetConstraintError(errMsg string) bool {
return false
}
// isFinalAnswerReserveError matches the specific reserve constraint, rather than
// treating arbitrary reasoning quota or context-length errors as repairable.
func isFinalAnswerReserveError(errMsg string) bool {
m := strings.ToLower(errMsg)
return strings.Contains(m, "must be greater than 1024 to reserve tokens for a final answer") &&
strings.Contains(m, "baseten reasoning is enabled")
}
// RectifyThinkingBudget modifies the request body to fix budget_tokens constraint errors.
// It sets thinking.budget_tokens = 32000, thinking.type = "enabled" (unless adaptive),
// and ensures max_tokens >= 32001.
@@ -0,0 +1,68 @@
//go:build unit
package service
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
"github.com/tidwall/gjson"
)
const basetenBudgetError = "Error from provider (Console Go): Upstream request failed: [invalid_request_error] must be greater than 1024 to reserve tokens for a final answer when Baseten reasoning is enabled"
func TestThinkingBudgetConstraintErrors(t *testing.T) {
for _, tc := range []struct {
message string
want bool
}{
{basetenBudgetError, true},
{"MUST BE GREATER THAN 1024 TO RESERVE TOKENS FOR A FINAL ANSWER WHEN BASETEN REASONING IS ENABLED", true},
{"thinking.budget_tokens must be >= 1024", true},
{"thinking budget tokens must be greater than or equal to 1024", true},
{"thinking.budget_tokens: Input should be greater than 1024", true},
{"reasoning quota exceeded: 1024 tokens", false},
{"must be greater than 1024", false},
{"reserve tokens for a final answer when Baseten reasoning is enabled", false},
{"must be greater than 2048 to reserve tokens for a final answer when Baseten reasoning is enabled", false},
{"", false},
} {
t.Run(tc.message, func(t *testing.T) {
require.Equal(t, tc.want, isThinkingBudgetConstraintError(tc.message))
})
}
}
func TestRectifyThinkingBudgetFinalAnswerReserve(t *testing.T) {
for _, tc := range []struct {
maxTokens int
want int64
changed bool
}{
{1024, 64000, true},
{32000, 64000, true},
{32001, 32001, false},
{40000, 40000, false},
{64000, 64000, false},
{100000, 100000, false},
} {
t.Run(fmt.Sprint(tc.maxTokens), func(t *testing.T) {
body := []byte(fmt.Sprintf(`{"model":"example","max_tokens":%d,"thinking":{"type":"enabled","budget_tokens":32000},"messages":[{"role":"user","content":"hi"}]}`, tc.maxTokens))
require.True(t, isThinkingBudgetConstraintError(basetenBudgetError))
got, changed := RectifyThinkingBudget(body)
require.Equal(t, tc.changed, changed)
require.Equal(t, tc.want, gjson.GetBytes(got, "max_tokens").Int())
require.Equal(t, int64(32000), gjson.GetBytes(got, "thinking.budget_tokens").Int())
require.Equal(t, "hi", gjson.GetBytes(got, "messages.0.content").String())
})
}
adaptive := []byte(`{"max_tokens":1024,"thinking":{"type":"adaptive"}}`)
got, changed := RectifyThinkingBudget(adaptive)
require.False(t, changed)
require.Equal(t, adaptive, got)
legacy := []byte(`{"max_tokens":32001,"thinking":{"type":"enabled","budget_tokens":32000}}`)
got, changed = RectifyThinkingBudget(legacy)
require.False(t, changed)
require.Equal(t, legacy, got)
}