fix: strip max_tokens from Anthropic count tokens

This commit is contained in:
jy.liu
2026-07-26 21:20:37 +08:00
parent 2730c1c43b
commit efa5a2240d
3 changed files with 35 additions and 1 deletions
@@ -582,7 +582,8 @@ func TestGatewayService_AnthropicAPIKeyPassthrough_CountTokensFiltersGenerationF
require.Equal(t, "sys", gjson.GetBytes(sentBody, "system.0.text").String())
require.Equal(t, "hello", gjson.GetBytes(sentBody, "messages.0.content").String())
require.Equal(t, "tool", gjson.GetBytes(sentBody, "tools.0.name").String())
require.Equal(t, int64(1024), gjson.GetBytes(sentBody, "max_tokens").Int())
require.False(t, gjson.GetBytes(sentBody, "max_tokens").Exists(),
"count_tokens 请求不得携带生成参数 max_tokens")
require.Equal(t, "enabled", gjson.GetBytes(sentBody, "thinking.type").String())
}
@@ -596,6 +596,35 @@ func TestBuildCountTokensRequest_OAuthMimicHaiku_PreservesContextManagementEndTo
"count_tokens 路径必须含 token-counting beta")
}
func TestBuildCountTokensRequest_OAuthMimic_DropsInjectedMaxTokens(t *testing.T) {
// OAuth mimicry injects max_tokens=128000 for normal messages requests. It is
// invalid for Anthropic's count_tokens endpoint and must be stripped on wire.
gin.SetMode(gin.TestMode)
rec := httptest.NewRecorder()
c, _ := gin.CreateTestContext(rec)
c.Request = httptest.NewRequest(http.MethodPost, "/v1/messages/count_tokens", nil)
account := &Account{ID: 413, Platform: PlatformAnthropic, Type: AccountTypeOAuth,
Credentials: map[string]any{"access_token": "oauth-tok"},
Status: StatusActive, Schedulable: true,
}
normalized, _ := normalizeClaudeOAuthRequestBody(
[]byte(`{"model":"claude-sonnet-4-5","messages":[]}`),
"claude-sonnet-4-5", claudeOAuthNormalizeOptions{},
)
require.Equal(t, int64(128000), gjson.GetBytes(normalized, "max_tokens").Int(),
"precondition: OAuth mimicry injects the Claude Code default")
svc := &GatewayService{cfg: &config.Config{}}
req, _, err := svc.buildCountTokensRequest(
context.Background(), c, account, normalized,
"oauth-tok", "oauth", "claude-sonnet-4-5", true,
)
require.NoError(t, err)
require.False(t, gjson.GetBytes(readUpstreamBodyForTest(t, req), "max_tokens").Exists(),
"count_tokens wire body must not contain max_tokens")
}
func TestBuildCountTokensRequest_APIKeyHaiku_StripsContextManagementEndToEnd(t *testing.T) {
// API-key + haiku + 客户端 header 不带 context-management beta → final beta 不含 → strip
gin.SetMode(gin.TestMode)
@@ -584,6 +584,10 @@ func sanitizeCountTokensRequestBody(body []byte) []byte {
"stream",
"stop_sequences",
"stop",
// Anthropic's /v1/messages/count_tokens accepts request-input fields only.
// max_tokens is a generation parameter; OAuth mimicry may inject it to
// resemble Claude Code messages requests, so it must never reach this endpoint.
"max_tokens",
} {
if gjson.GetBytes(out, path).Exists() {
if next, ok := deleteJSONPathBytes(out, path); ok {