mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-10-05 23:31:05 +08:00
fix(claude): gate fallback-credit beta on fallback tokens or explicit fallbacks
This commit is contained in:
@@ -135,7 +135,7 @@ func isManagedClaudeBeta(beta string) bool {
|
||||
// 11 advanced-tool-use-2025-11-20 requests using tool search or another advanced tool-use feature
|
||||
// 12 effort-2025-11-24 effort-supporting models with active thinking
|
||||
// 13 server-side-fallback-2026-06-01 requests with fallbacks or requested
|
||||
// 14 fallback-credit-2026-06-01 OAuth credentials
|
||||
// 14 fallback-credit-2026-06-01 requests with fallback tokens, fallbacks, or requested
|
||||
// 15 structured-outputs-2025-12-15 structured output requests
|
||||
// 16 thinking-display-updates-2026-08-18 requests with thinking.display=updates
|
||||
// 17 fast-mode-2026-02-01 speed:fast requests only
|
||||
@@ -177,7 +177,10 @@ func claudeCodeCLIBetas(body []byte, requested map[string]bool, oauthToken bool)
|
||||
if !isProbeOrHelper && (requested[claudeServerSideFallbackBeta] || gjson.GetBytes(body, "fallbacks").Exists()) {
|
||||
betas = append(betas, claudeServerSideFallbackBeta)
|
||||
}
|
||||
if requested[claudeFallbackCreditBeta] || oauthToken {
|
||||
shouldIncludeFallbackCredit := requested[claudeFallbackCreditBeta] ||
|
||||
gjson.GetBytes(body, "fallback_credit_token").Exists() ||
|
||||
(oauthToken && gjson.GetBytes(body, "fallbacks").Exists())
|
||||
if shouldIncludeFallbackCredit {
|
||||
betas = append(betas, claudeFallbackCreditBeta)
|
||||
}
|
||||
for _, beta := range claudeCodeTrailingBetas {
|
||||
|
||||
@@ -7990,8 +7990,7 @@ func TestClaudeCodeCLIBetas_MatchesObservedClientMatrix(t *testing.T) {
|
||||
"interleaved-thinking-2025-05-14,redact-thinking-2026-02-12," +
|
||||
"thinking-token-count-2026-05-13,context-management-2025-06-27," +
|
||||
"prompt-caching-scope-2026-01-05,advanced-tool-use-2025-11-20," +
|
||||
"effort-2025-11-24,fallback-credit-2026-06-01," +
|
||||
"extended-cache-ttl-2025-04-11",
|
||||
"effort-2025-11-24,extended-cache-ttl-2025-04-11",
|
||||
},
|
||||
{
|
||||
name: "oauth precedes context-1m",
|
||||
@@ -8122,7 +8121,7 @@ func TestClaudeCodeCLIBetas_MatchesObservedClientMatrix(t *testing.T) {
|
||||
"interleaved-thinking-2025-05-14,redact-thinking-2026-02-12," +
|
||||
"thinking-token-count-2026-05-13,context-management-2025-06-27," +
|
||||
"prompt-caching-scope-2026-01-05,mid-conversation-system-2026-04-07," +
|
||||
"advisor-tool-2026-03-01,effort-2025-11-24,fallback-credit-2026-06-01," +
|
||||
"advisor-tool-2026-03-01,effort-2025-11-24," +
|
||||
"afk-mode-2026-01-31,extended-cache-ttl-2025-04-11",
|
||||
},
|
||||
{
|
||||
@@ -8134,7 +8133,7 @@ func TestClaudeCodeCLIBetas_MatchesObservedClientMatrix(t *testing.T) {
|
||||
"interleaved-thinking-2025-05-14,redact-thinking-2026-02-12," +
|
||||
"thinking-token-count-2026-05-13,context-management-2025-06-27," +
|
||||
"prompt-caching-scope-2026-01-05,mid-conversation-system-2026-04-07," +
|
||||
"effort-2025-11-24,fallback-credit-2026-06-01,fast-mode-2026-02-01," +
|
||||
"effort-2025-11-24,fast-mode-2026-02-01," +
|
||||
"afk-mode-2026-01-31,extended-cache-ttl-2025-04-11",
|
||||
},
|
||||
{
|
||||
@@ -8145,7 +8144,7 @@ func TestClaudeCodeCLIBetas_MatchesObservedClientMatrix(t *testing.T) {
|
||||
"interleaved-thinking-2025-05-14,redact-thinking-2026-02-12," +
|
||||
"thinking-token-count-2026-05-13,context-management-2025-06-27," +
|
||||
"prompt-caching-scope-2026-01-05,mid-conversation-system-2026-04-07," +
|
||||
"effort-2025-11-24,fallback-credit-2026-06-01,extended-cache-ttl-2025-04-11",
|
||||
"effort-2025-11-24,extended-cache-ttl-2025-04-11",
|
||||
},
|
||||
{
|
||||
name: "thinking display updates emits thinking-display-updates beta and drops redact-thinking",
|
||||
@@ -8168,7 +8167,7 @@ func TestClaudeCodeCLIBetas_MatchesObservedClientMatrix(t *testing.T) {
|
||||
"interleaved-thinking-2025-05-14,redact-thinking-2026-02-12," +
|
||||
"thinking-token-count-2026-05-13,context-management-2025-06-27," +
|
||||
"prompt-caching-scope-2026-01-05,mid-conversation-system-2026-04-07," +
|
||||
"effort-2025-11-24,fallback-credit-2026-06-01",
|
||||
"effort-2025-11-24",
|
||||
},
|
||||
{
|
||||
name: "probe request max_tokens=1 omits effort and extended-cache-ttl betas",
|
||||
@@ -8177,8 +8176,7 @@ func TestClaudeCodeCLIBetas_MatchesObservedClientMatrix(t *testing.T) {
|
||||
want: "claude-code-20250219,oauth-2025-04-20," +
|
||||
"interleaved-thinking-2025-05-14,redact-thinking-2026-02-12," +
|
||||
"thinking-token-count-2026-05-13,context-management-2025-06-27," +
|
||||
"prompt-caching-scope-2026-01-05,mid-conversation-system-2026-04-07," +
|
||||
"fallback-credit-2026-06-01",
|
||||
"prompt-caching-scope-2026-01-05,mid-conversation-system-2026-04-07",
|
||||
},
|
||||
{
|
||||
name: "haiku model omits effort beta even if requested",
|
||||
@@ -8189,7 +8187,7 @@ func TestClaudeCodeCLIBetas_MatchesObservedClientMatrix(t *testing.T) {
|
||||
"interleaved-thinking-2025-05-14,redact-thinking-2026-02-12," +
|
||||
"thinking-token-count-2026-05-13,context-management-2025-06-27," +
|
||||
"prompt-caching-scope-2026-01-05," +
|
||||
"fallback-credit-2026-06-01,extended-cache-ttl-2025-04-11",
|
||||
"extended-cache-ttl-2025-04-11",
|
||||
},
|
||||
{
|
||||
name: "disabled thinking omits effort beta even if requested",
|
||||
@@ -8200,7 +8198,39 @@ func TestClaudeCodeCLIBetas_MatchesObservedClientMatrix(t *testing.T) {
|
||||
"interleaved-thinking-2025-05-14,redact-thinking-2026-02-12," +
|
||||
"thinking-token-count-2026-05-13,context-management-2025-06-27," +
|
||||
"prompt-caching-scope-2026-01-05,mid-conversation-system-2026-04-07," +
|
||||
"fallback-credit-2026-06-01,extended-cache-ttl-2025-04-11",
|
||||
"extended-cache-ttl-2025-04-11",
|
||||
},
|
||||
{
|
||||
name: "body with fallback_credit_token automatically adds fallback-credit beta",
|
||||
body: `{"model":"claude-sonnet-5","fallback_credit_token":"fct_12345"}`,
|
||||
oauth: true,
|
||||
want: "claude-code-20250219,oauth-2025-04-20," +
|
||||
"interleaved-thinking-2025-05-14,redact-thinking-2026-02-12," +
|
||||
"thinking-token-count-2026-05-13,context-management-2025-06-27," +
|
||||
"prompt-caching-scope-2026-01-05,mid-conversation-system-2026-04-07," +
|
||||
"effort-2025-11-24,fallback-credit-2026-06-01,extended-cache-ttl-2025-04-11",
|
||||
},
|
||||
{
|
||||
name: "oauth body with fallbacks automatically adds fallback-credit beta",
|
||||
body: `{"model":"claude-fable-5-1","fallbacks":[{"model":"claude-opus-5"}]}`,
|
||||
oauth: true,
|
||||
want: "claude-code-20250219,oauth-2025-04-20," +
|
||||
"interleaved-thinking-2025-05-14,redact-thinking-2026-02-12," +
|
||||
"thinking-token-count-2026-05-13,context-management-2025-06-27," +
|
||||
"prompt-caching-scope-2026-01-05,mid-conversation-system-2026-04-07," +
|
||||
"effort-2025-11-24,server-side-fallback-2026-06-01,fallback-credit-2026-06-01," +
|
||||
"extended-cache-ttl-2025-04-11",
|
||||
},
|
||||
{
|
||||
name: "requested fallback-credit beta is honored",
|
||||
body: `{"model":"claude-sonnet-5"}`,
|
||||
requested: map[string]bool{claudeFallbackCreditBeta: true},
|
||||
oauth: true,
|
||||
want: "claude-code-20250219,oauth-2025-04-20," +
|
||||
"interleaved-thinking-2025-05-14,redact-thinking-2026-02-12," +
|
||||
"thinking-token-count-2026-05-13,context-management-2025-06-27," +
|
||||
"prompt-caching-scope-2026-01-05,mid-conversation-system-2026-04-07," +
|
||||
"effort-2025-11-24,fallback-credit-2026-06-01,extended-cache-ttl-2025-04-11",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -235,7 +235,8 @@ func TestApplyClaudeHeaders_ClaudeCodeCLIProfileUsesOAuthBetasWithoutPretendingT
|
||||
if errReq != nil {
|
||||
t.Fatalf("NewRequest() error = %v", errReq)
|
||||
}
|
||||
if errHeaders := applyClaudeHeaders(req, auth, "key-third-party", false, nil, []byte(`{"model":"claude-sonnet-5"}`), &config.Config{}, nil, false, "11111111-2222-4333-8444-555555555555"); errHeaders != nil {
|
||||
payload := []byte(`{"model":"claude-sonnet-5"}`)
|
||||
if errHeaders := applyClaudeHeaders(req, auth, "key-third-party", false, nil, payload, &config.Config{}, nil, false, "11111111-2222-4333-8444-555555555555"); errHeaders != nil {
|
||||
t.Fatalf("applyClaudeHeaders() error = %v", errHeaders)
|
||||
}
|
||||
if got := req.Header.Get("Authorization"); got != "Bearer key-third-party" {
|
||||
@@ -245,14 +246,8 @@ func TestApplyClaudeHeaders_ClaudeCodeCLIProfileUsesOAuthBetasWithoutPretendingT
|
||||
t.Fatalf("x-api-key = %q, want empty on third-party gateway", got)
|
||||
}
|
||||
betas := req.Header.Get("Anthropic-Beta")
|
||||
if !strings.Contains(betas, "oauth-2025-04-20") {
|
||||
t.Fatalf("Anthropic-Beta = %q, want oauth beta", betas)
|
||||
}
|
||||
if !strings.Contains(betas, "extended-cache-ttl-2025-04-11") {
|
||||
t.Fatalf("Anthropic-Beta = %q, want extended-cache-ttl", betas)
|
||||
}
|
||||
if !strings.Contains(betas, "fallback-credit-2026-06-01") {
|
||||
t.Fatalf("Anthropic-Beta = %q, want fallback-credit", betas)
|
||||
if want := claudeCodeCLIBetas(payload, nil, true); betas != want {
|
||||
t.Fatalf("Anthropic-Beta = %q, want %q", betas, want)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user