基于 Claude Code 2.1.88 反编译源码,完成全面的反追踪指纹还原: 1. 版本升级 2.1.87 → 2.1.88(constants.go, identity_service.go, proxy.js) 2. 新增 6 个 beta header 常量(task-budgets, token-efficient-tools, structured-outputs, advisor, web-search) 3. 更新所有组合 beta header 字符串,加入 context-1m, redact-thinking, effort 等 4. 注入 x-anthropic-billing-header attribution block 到 system prompt 首位 - 完整复刻 fingerprint 算法: SHA256(salt + msg[4,7,20] + version)[:3] - 正确省略 cch 字段(npm 版行为,非原生二进制) 5. X-Claude-Code-Session-Id: 有则同步,无则按 account 生成 6. x-client-request-id: 每请求自动生成 UUID 7. Bootstrap 预热: 模拟 GET /api/claude_cli/bootstrap(per-account, 1h cooldown) 8. 停止无条件剥离 temperature/tool_choice(与真实 CLI 行为一致) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
74 lines
3.2 KiB
Go
74 lines
3.2 KiB
Go
package service
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/Wei-Shaw/sub2api/internal/pkg/claude"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func assertJSONTokenOrder(t *testing.T, body string, tokens ...string) {
|
|
t.Helper()
|
|
|
|
last := -1
|
|
for _, token := range tokens {
|
|
pos := strings.Index(body, token)
|
|
require.NotEqualf(t, -1, pos, "missing token %s in body %s", token, body)
|
|
require.Greaterf(t, pos, last, "token %s should appear after previous tokens in body %s", token, body)
|
|
last = pos
|
|
}
|
|
}
|
|
|
|
func TestReplaceModelInBody_PreservesTopLevelFieldOrder(t *testing.T) {
|
|
svc := &GatewayService{}
|
|
body := []byte(`{"alpha":1,"model":"claude-3-5-sonnet-latest","messages":[],"omega":2}`)
|
|
|
|
result := svc.replaceModelInBody(body, "claude-3-5-sonnet-20241022")
|
|
resultStr := string(result)
|
|
|
|
assertJSONTokenOrder(t, resultStr, `"alpha"`, `"model"`, `"messages"`, `"omega"`)
|
|
require.Contains(t, resultStr, `"model":"claude-3-5-sonnet-20241022"`)
|
|
}
|
|
|
|
func TestNormalizeClaudeOAuthRequestBody_PreservesTopLevelFieldOrder(t *testing.T) {
|
|
body := []byte(`{"alpha":1,"model":"claude-3-5-sonnet-latest","temperature":0.2,"system":"You are OpenCode, the best coding agent on the planet.","messages":[],"tool_choice":{"type":"auto"},"omega":2}`)
|
|
|
|
result, modelID := normalizeClaudeOAuthRequestBody(body, "claude-3-5-sonnet-latest", claudeOAuthNormalizeOptions{
|
|
injectMetadata: true,
|
|
metadataUserID: "user-1",
|
|
})
|
|
resultStr := string(result)
|
|
|
|
require.Equal(t, claude.NormalizeModelID("claude-3-5-sonnet-latest"), modelID)
|
|
assertJSONTokenOrder(t, resultStr, `"alpha"`, `"model"`, `"temperature"`, `"system"`, `"messages"`, `"tool_choice"`, `"omega"`, `"tools"`, `"metadata"`)
|
|
// temperature 和 tool_choice 不再剥离,透传客户端原始值(与真实 CLI 行为一致)
|
|
require.Contains(t, resultStr, `"temperature"`)
|
|
require.Contains(t, resultStr, `"tool_choice"`)
|
|
require.Contains(t, resultStr, `"system":"`+claudeCodeSystemPrompt+`"`)
|
|
require.Contains(t, resultStr, `"tools":[]`)
|
|
require.Contains(t, resultStr, `"metadata":{"user_id":"user-1"}`)
|
|
}
|
|
|
|
func TestInjectClaudeCodePrompt_PreservesFieldOrder(t *testing.T) {
|
|
body := []byte(`{"alpha":1,"system":[{"id":"block-1","type":"text","text":"Custom"}],"messages":[],"omega":2}`)
|
|
|
|
result := injectClaudeCodePrompt(body, []any{
|
|
map[string]any{"id": "block-1", "type": "text", "text": "Custom"},
|
|
})
|
|
resultStr := string(result)
|
|
|
|
assertJSONTokenOrder(t, resultStr, `"alpha"`, `"system"`, `"messages"`, `"omega"`)
|
|
require.Contains(t, resultStr, `{"id":"block-1","type":"text","text":"`+claudeCodeSystemPrompt+`\n\nCustom"}`)
|
|
}
|
|
|
|
func TestEnforceCacheControlLimit_PreservesTopLevelFieldOrder(t *testing.T) {
|
|
body := []byte(`{"alpha":1,"system":[{"type":"text","text":"s1","cache_control":{"type":"ephemeral"}},{"type":"text","text":"s2","cache_control":{"type":"ephemeral"}}],"messages":[{"role":"user","content":[{"type":"text","text":"m1","cache_control":{"type":"ephemeral"}},{"type":"text","text":"m2","cache_control":{"type":"ephemeral"}},{"type":"text","text":"m3","cache_control":{"type":"ephemeral"}}]}],"omega":2}`)
|
|
|
|
result := enforceCacheControlLimit(body)
|
|
resultStr := string(result)
|
|
|
|
assertJSONTokenOrder(t, resultStr, `"alpha"`, `"system"`, `"messages"`, `"omega"`)
|
|
require.Equal(t, 4, strings.Count(resultStr, `"cache_control"`))
|
|
}
|