Merge pull request #4926 from Vibeone/fix/oauth-mimicry-cache-prefix-break

fix(gateway): 识别被代理的 Claude Code 流量,避免 mimicry 重写破坏 prompt cache
This commit is contained in:
Wesley Liddick authored and GitHub committed 2026-07-27 11:40:43 +08:00
commit b468e428e9
2 files changed
+36

No files matched your search

@@ -1208,3 +1208,28 @@ func (s *GatewayService) claudeOAuthSystemPromptInjectionSettings(ctx context.Co
}
return s.settingService.GetClaudeOAuthSystemPromptInjectionSettings(ctx)
}
// systemHasBillingAttributionBlock 检查请求体的 system 字段中是否包含真实 Claude Code
// 客户端注入的 billing attribution block。该 block 格式稳定(见 gateway_billing_block.go),
// 仅由真实 Claude Code CLI 生成;第三方客户端(opencode 等)不会生成此 block。
//
// 用于识别被上游 API 网关代理的真实 Claude Code 流量:此类请求的 User-Agent 被网关替换
// 为 Go-http-client,但 body 保留了完整的客户端特征。如果不识别这类请求而走 mimicry
// 重写 system,会破坏 Anthropic prompt cache 的前缀一致性,导致 messages 级缓存永不命中。
func systemHasBillingAttributionBlock(body []byte) bool {
system := gjson.GetBytes(body, "system")
if !system.IsArray() {
return false
}
found := false
system.ForEach(func(_, item gjson.Result) bool {
text := item.Get("text").String()
if strings.HasPrefix(text, claudeCodeBillingHeaderPrefix) &&
strings.Contains(text, claudeCodeEntrypointMarker) {
found = true
return false
}
return true
})
return found
}
@@ -170,6 +170,17 @@ func (s *GatewayService) Forward(ctx context.Context, c *gin.Context, account *A
clientUserAgent = c.GetHeader("User-Agent")
}
isClaudeCode := IsClaudeCodeClient(ctx) || isClaudeCodeClient(clientUserAgent, parsed.MetadataUserID)
// 补充判定:上游 API 网关(如 new-api)转发真实 Claude Code 流量时,
// UA 会变成 Go-http-client 但 body 保留了完整的 Claude Code 特征
// (billing attribution block + metadata.user_id)。此时如果仍走 mimicry
// 重写 system prompt,会破坏 Anthropic prompt cache 的前缀匹配——
// 导致 messages 级缓存永远 miss、cache_creation 每轮全量重写。
// 通过检查 body 中的 billing attribution block 来识别被代理的真实 CC 流量。
if !isClaudeCode && parsed.MetadataUserID != "" {
isClaudeCode = systemHasBillingAttributionBlock(body)
}
shouldMimicClaudeCode := account.IsOAuth() && !isClaudeCode
if shouldMimicClaudeCode {