fix: 对齐 Claude Code 2.1.88 源码指纹
- 1P event_logging/batch 添加 OAuth Bearer auth header - DD hostname 改为固定 "claude-code"(与真实 CLI 一致) - 事件名对齐真实 CLI: tengu_api_query/tengu_api_success/tengu_api_error/tengu_tool_use_success - DD header 大小写改为 DD-API-KEY - ResponseHeaderTimeout 300s → 600s(与真实 CLI 10min 超时对齐)
This commit is contained in:
parent
d8d8adb37f
commit
58ad47ba80
@ -322,7 +322,7 @@ func buildEvent(eventName string, session *sessionState, model, betas string, ex
|
|||||||
|
|
||||||
var httpClient = &http.Client{Timeout: telemetryTimeout}
|
var httpClient = &http.Client{Timeout: telemetryTimeout}
|
||||||
|
|
||||||
func sendTelemetryEvents(events []eventWrapper, session *sessionState) {
|
func sendTelemetryEvents(events []eventWrapper, session *sessionState, authToken string) {
|
||||||
if len(events) == 0 {
|
if len(events) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -341,6 +341,9 @@ func sendTelemetryEvents(events []eventWrapper, session *sessionState) {
|
|||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
req.Header.Set("User-Agent", "claude-code/"+claude.DefaultCLIVersion)
|
req.Header.Set("User-Agent", "claude-code/"+claude.DefaultCLIVersion)
|
||||||
req.Header.Set("x-service-name", "claude-code")
|
req.Header.Set("x-service-name", "claude-code")
|
||||||
|
if authToken != "" {
|
||||||
|
req.Header.Set("Authorization", "Bearer "+authToken)
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := httpClient.Do(req)
|
resp, err := httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -383,7 +386,7 @@ func sendDatadogLog(eventName string, session *sessionState, model string) {
|
|||||||
"ddtags": fmt.Sprintf("event:%s,arch:%s,client_type:cli,model:%s,platform:darwin,user_type:external,version:%s,version_base:%s", eventName, hostID.Arch, model, claude.DefaultCLIVersion, claude.DefaultCLIVersion),
|
"ddtags": fmt.Sprintf("event:%s,arch:%s,client_type:cli,model:%s,platform:darwin,user_type:external,version:%s,version_base:%s", eventName, hostID.Arch, model, claude.DefaultCLIVersion, claude.DefaultCLIVersion),
|
||||||
"message": eventName,
|
"message": eventName,
|
||||||
"service": "claude-code",
|
"service": "claude-code",
|
||||||
"hostname": hostID.Hostname,
|
"hostname": "claude-code",
|
||||||
"env": "external",
|
"env": "external",
|
||||||
"model": model,
|
"model": model,
|
||||||
"session_id": session.SessionID,
|
"session_id": session.SessionID,
|
||||||
@ -415,7 +418,7 @@ func sendDatadogLog(eventName string, session *sessionState, model string) {
|
|||||||
req.Header.Set("Accept", "application/json, text/plain, */*")
|
req.Header.Set("Accept", "application/json, text/plain, */*")
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
req.Header.Set("User-Agent", "axios/1.13.6")
|
req.Header.Set("User-Agent", "axios/1.13.6")
|
||||||
req.Header.Set("dd-api-key", ddAPIKey)
|
req.Header.Set("DD-API-KEY", ddAPIKey)
|
||||||
|
|
||||||
resp, err := httpClient.Do(req)
|
resp, err := httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -429,9 +432,10 @@ func sendDatadogLog(eventName string, session *sessionState, model string) {
|
|||||||
// EmitPreRequest fires pre-request telemetry events for a /v1/messages request.
|
// EmitPreRequest fires pre-request telemetry events for a /v1/messages request.
|
||||||
// accountSeed should be a stable identifier for the account (e.g. account ID or OAuth token suffix).
|
// accountSeed should be a stable identifier for the account (e.g. account ID or OAuth token suffix).
|
||||||
// authHeader is the Authorization header value (used for device ID derivation).
|
// authHeader is the Authorization header value (used for device ID derivation).
|
||||||
|
// authToken is the raw OAuth token (without "Bearer " prefix) for 1P auth.
|
||||||
// model is the model name from the request body (e.g. "claude-sonnet-4-6").
|
// model is the model name from the request body (e.g. "claude-sonnet-4-6").
|
||||||
// betaHeader is the anthropic-beta header value.
|
// betaHeader is the anthropic-beta header value.
|
||||||
func EmitPreRequest(accountSeed, authHeader, model, betaHeader string) {
|
func EmitPreRequest(accountSeed, authHeader, authToken, model, betaHeader string) {
|
||||||
authSuffix := authHeader
|
authSuffix := authHeader
|
||||||
if len(authSuffix) > 16 {
|
if len(authSuffix) > 16 {
|
||||||
authSuffix = authSuffix[len(authSuffix)-16:]
|
authSuffix = authSuffix[len(authSuffix)-16:]
|
||||||
@ -479,29 +483,28 @@ func EmitPreRequest(accountSeed, authHeader, model, betaHeader string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
session.RipgrepReported = true
|
session.RipgrepReported = true
|
||||||
go sendTelemetryEvents(batch1, session)
|
go sendTelemetryEvents(batch1, session, authToken)
|
||||||
go sendDatadogLog("tengu_started", session, model)
|
go sendDatadogLog("tengu_started", session, model)
|
||||||
go sendDatadogLog("tengu_init", session, model)
|
go sendDatadogLog("tengu_init", session, model)
|
||||||
|
|
||||||
// Delayed batch (~25-35s later, matches real CLI timing)
|
// Delayed batch (~25-35s later, matches real CLI timing)
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(time.Duration(25000+rand.Intn(10000)) * time.Millisecond)
|
time.Sleep(time.Duration(25000+rand.Intn(10000)) * time.Millisecond)
|
||||||
batch2 := []eventWrapper{
|
sendTelemetryEvents([]eventWrapper{
|
||||||
buildEvent("tengu_session_init", session, model, betas, nil, ""),
|
buildEvent("tengu_session_init", session, model, betas, nil, ""),
|
||||||
buildEvent("tengu_context_loaded", session, model, betas, nil, ""),
|
buildEvent("tengu_context_loaded", session, model, betas, nil, ""),
|
||||||
}
|
}, session, authToken)
|
||||||
sendTelemetryEvents(batch2, session)
|
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Every request: request_started
|
// Every request: tengu_api_query (real CLI event name)
|
||||||
go sendTelemetryEvents([]eventWrapper{
|
go sendTelemetryEvents([]eventWrapper{
|
||||||
buildEvent("tengu_api_request_started", session, model, betas, nil, ""),
|
buildEvent("tengu_api_query", session, model, betas, nil, ""),
|
||||||
}, session)
|
}, session, authToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EmitPostRequest fires post-request telemetry events after upstream response.
|
// EmitPostRequest fires post-request telemetry events after upstream response.
|
||||||
func EmitPostRequest(accountSeed, authHeader, model, betaHeader string, statusCode int) {
|
func EmitPostRequest(accountSeed, authHeader, authToken, model, betaHeader string, statusCode int) {
|
||||||
authSuffix := authHeader
|
authSuffix := authHeader
|
||||||
if len(authSuffix) > 16 {
|
if len(authSuffix) > 16 {
|
||||||
authSuffix = authSuffix[len(authSuffix)-16:]
|
authSuffix = authSuffix[len(authSuffix)-16:]
|
||||||
@ -517,15 +520,14 @@ func EmitPostRequest(accountSeed, authHeader, model, betaHeader string, statusCo
|
|||||||
betas = claude.DefaultBetaHeader
|
betas = claude.DefaultBetaHeader
|
||||||
}
|
}
|
||||||
|
|
||||||
events := []eventWrapper{
|
// Real CLI uses tengu_api_success on success, tengu_api_error on failure
|
||||||
buildEvent("tengu_api_request_completed", session, model, betas, nil, ""),
|
if statusCode < 400 {
|
||||||
buildEvent("tengu_conversation_turn_completed", session, model, betas, nil, ""),
|
events := []eventWrapper{
|
||||||
}
|
buildEvent("tengu_api_success", session, model, betas, nil, ""),
|
||||||
go sendTelemetryEvents(events, session)
|
}
|
||||||
go sendDatadogLog("tengu_api_request_completed", session, model)
|
go sendTelemetryEvents(events, session, authToken)
|
||||||
|
go sendDatadogLog("tengu_api_success", session, model)
|
||||||
// Error telemetry
|
} else {
|
||||||
if statusCode >= 400 && rand.Float64() < 0.5 {
|
|
||||||
var errMsg string
|
var errMsg string
|
||||||
switch {
|
switch {
|
||||||
case statusCode == 429:
|
case statusCode == 429:
|
||||||
@ -537,12 +539,13 @@ func EmitPostRequest(accountSeed, authHeader, model, betaHeader string, statusCo
|
|||||||
default:
|
default:
|
||||||
errMsg = "client_error"
|
errMsg = "client_error"
|
||||||
}
|
}
|
||||||
errEvent := buildEvent("tengu_api_request_error", session, model, betas, map[string]any{
|
errEvent := buildEvent("tengu_api_error", session, model, betas, map[string]any{
|
||||||
"error_type": "TelemetrySafeError",
|
"error_type": "TelemetrySafeError",
|
||||||
"error_code": statusCode,
|
"error_code": statusCode,
|
||||||
"error_message": errMsg,
|
"error_message": errMsg,
|
||||||
}, "")
|
}, "")
|
||||||
go sendTelemetryEvents([]eventWrapper{errEvent}, session)
|
go sendTelemetryEvents([]eventWrapper{errEvent}, session, authToken)
|
||||||
|
go sendDatadogLog("tengu_api_error", session, model)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Random tool_use event (30% probability, 2-7s delay)
|
// Random tool_use event (30% probability, 2-7s delay)
|
||||||
@ -550,8 +553,8 @@ func EmitPostRequest(accountSeed, authHeader, model, betaHeader string, statusCo
|
|||||||
go func() {
|
go func() {
|
||||||
time.Sleep(time.Duration(2000+rand.Intn(5000)) * time.Millisecond)
|
time.Sleep(time.Duration(2000+rand.Intn(5000)) * time.Millisecond)
|
||||||
sendTelemetryEvents([]eventWrapper{
|
sendTelemetryEvents([]eventWrapper{
|
||||||
buildEvent("tengu_tool_use_completed", session, model, betas, nil, ""),
|
buildEvent("tengu_tool_use_success", session, model, betas, nil, ""),
|
||||||
}, session)
|
}, session, authToken)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,7 +46,7 @@ const (
|
|||||||
defaultIdleConnTimeout = 90 * time.Second
|
defaultIdleConnTimeout = 90 * time.Second
|
||||||
// defaultResponseHeaderTimeout: 默认等待响应头超时时间(5分钟)
|
// defaultResponseHeaderTimeout: 默认等待响应头超时时间(5分钟)
|
||||||
// LLM 请求可能排队较久,需要较长超时
|
// LLM 请求可能排队较久,需要较长超时
|
||||||
defaultResponseHeaderTimeout = 300 * time.Second
|
defaultResponseHeaderTimeout = 600 * time.Second
|
||||||
// defaultMaxUpstreamClients: 默认最大客户端缓存数量
|
// defaultMaxUpstreamClients: 默认最大客户端缓存数量
|
||||||
// 超出后会淘汰最久未使用的客户端
|
// 超出后会淘汰最久未使用的客户端
|
||||||
defaultMaxUpstreamClients = 5000
|
defaultMaxUpstreamClients = 5000
|
||||||
|
|||||||
@ -4165,10 +4165,11 @@ func (s *GatewayService) Forward(ctx context.Context, c *gin.Context, account *A
|
|||||||
// 真实 CLI 在首次 messages 请求前 fire-and-forget 调用此端点。
|
// 真实 CLI 在首次 messages 请求前 fire-and-forget 调用此端点。
|
||||||
if tokenType == "oauth" && token != "" {
|
if tokenType == "oauth" && token != "" {
|
||||||
TriggerBootstrapIfNeeded(account.ID, token)
|
TriggerBootstrapIfNeeded(account.ID, token)
|
||||||
// OTEL telemetry: emit pre-request events (tengu_started, tengu_api_request_started etc.)
|
// OTEL telemetry: emit pre-request events (tengu_started, tengu_api_query etc.)
|
||||||
go telemetry.EmitPreRequest(
|
go telemetry.EmitPreRequest(
|
||||||
fmt.Sprintf("%d", account.ID),
|
fmt.Sprintf("%d", account.ID),
|
||||||
token,
|
token,
|
||||||
|
token,
|
||||||
reqModel,
|
reqModel,
|
||||||
getHeaderRaw(c.Request.Header, "anthropic-beta"),
|
getHeaderRaw(c.Request.Header, "anthropic-beta"),
|
||||||
)
|
)
|
||||||
@ -4594,6 +4595,7 @@ func (s *GatewayService) Forward(ctx context.Context, c *gin.Context, account *A
|
|||||||
go telemetry.EmitPostRequest(
|
go telemetry.EmitPostRequest(
|
||||||
fmt.Sprintf("%d", account.ID),
|
fmt.Sprintf("%d", account.ID),
|
||||||
token,
|
token,
|
||||||
|
token,
|
||||||
reqModel,
|
reqModel,
|
||||||
getHeaderRaw(c.Request.Header, "anthropic-beta"),
|
getHeaderRaw(c.Request.Header, "anthropic-beta"),
|
||||||
resp.StatusCode,
|
resp.StatusCode,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user