From b285fb7b2fba8f97668e60fdb460e96528757b97 Mon Sep 17 00:00:00 2001 From: win Date: Wed, 1 Apr 2026 08:53:39 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=AF=B9=E9=BD=90=20Claude=20Code=202.1?= =?UTF-8?q?.88=20=E6=BA=90=E7=A0=81=E6=8C=87=E7=BA=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 超时对齐) --- backend/internal/pkg/telemetry/telemetry.go | 53 +++++++++++--------- backend/internal/repository/http_upstream.go | 2 +- backend/internal/service/gateway_service.go | 4 +- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/backend/internal/pkg/telemetry/telemetry.go b/backend/internal/pkg/telemetry/telemetry.go index 271d1104..045017cf 100644 --- a/backend/internal/pkg/telemetry/telemetry.go +++ b/backend/internal/pkg/telemetry/telemetry.go @@ -322,7 +322,7 @@ func buildEvent(eventName string, session *sessionState, model, betas string, ex var httpClient = &http.Client{Timeout: telemetryTimeout} -func sendTelemetryEvents(events []eventWrapper, session *sessionState) { +func sendTelemetryEvents(events []eventWrapper, session *sessionState, authToken string) { if len(events) == 0 { return } @@ -341,6 +341,9 @@ func sendTelemetryEvents(events []eventWrapper, session *sessionState) { req.Header.Set("Content-Type", "application/json") req.Header.Set("User-Agent", "claude-code/"+claude.DefaultCLIVersion) req.Header.Set("x-service-name", "claude-code") + if authToken != "" { + req.Header.Set("Authorization", "Bearer "+authToken) + } resp, err := httpClient.Do(req) 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), "message": eventName, "service": "claude-code", - "hostname": hostID.Hostname, + "hostname": "claude-code", "env": "external", "model": model, "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("Content-Type", "application/json") 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) 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. // 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). +// 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"). // betaHeader is the anthropic-beta header value. -func EmitPreRequest(accountSeed, authHeader, model, betaHeader string) { +func EmitPreRequest(accountSeed, authHeader, authToken, model, betaHeader string) { authSuffix := authHeader if len(authSuffix) > 16 { authSuffix = authSuffix[len(authSuffix)-16:] @@ -479,29 +483,28 @@ func EmitPreRequest(accountSeed, authHeader, model, betaHeader string) { } session.RipgrepReported = true - go sendTelemetryEvents(batch1, session) + go sendTelemetryEvents(batch1, session, authToken) go sendDatadogLog("tengu_started", session, model) go sendDatadogLog("tengu_init", session, model) // Delayed batch (~25-35s later, matches real CLI timing) go func() { time.Sleep(time.Duration(25000+rand.Intn(10000)) * time.Millisecond) - batch2 := []eventWrapper{ + sendTelemetryEvents([]eventWrapper{ buildEvent("tengu_session_init", session, model, betas, nil, ""), buildEvent("tengu_context_loaded", session, model, betas, nil, ""), - } - sendTelemetryEvents(batch2, session) + }, session, authToken) }() } - // Every request: request_started + // Every request: tengu_api_query (real CLI event name) go sendTelemetryEvents([]eventWrapper{ - buildEvent("tengu_api_request_started", session, model, betas, nil, ""), - }, session) + buildEvent("tengu_api_query", session, model, betas, nil, ""), + }, session, authToken) } // 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 if len(authSuffix) > 16 { authSuffix = authSuffix[len(authSuffix)-16:] @@ -517,15 +520,14 @@ func EmitPostRequest(accountSeed, authHeader, model, betaHeader string, statusCo betas = claude.DefaultBetaHeader } - events := []eventWrapper{ - buildEvent("tengu_api_request_completed", session, model, betas, nil, ""), - buildEvent("tengu_conversation_turn_completed", session, model, betas, nil, ""), - } - go sendTelemetryEvents(events, session) - go sendDatadogLog("tengu_api_request_completed", session, model) - - // Error telemetry - if statusCode >= 400 && rand.Float64() < 0.5 { + // Real CLI uses tengu_api_success on success, tengu_api_error on failure + if statusCode < 400 { + events := []eventWrapper{ + buildEvent("tengu_api_success", session, model, betas, nil, ""), + } + go sendTelemetryEvents(events, session, authToken) + go sendDatadogLog("tengu_api_success", session, model) + } else { var errMsg string switch { case statusCode == 429: @@ -537,12 +539,13 @@ func EmitPostRequest(accountSeed, authHeader, model, betaHeader string, statusCo default: 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_code": statusCode, "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) @@ -550,8 +553,8 @@ func EmitPostRequest(accountSeed, authHeader, model, betaHeader string, statusCo go func() { time.Sleep(time.Duration(2000+rand.Intn(5000)) * time.Millisecond) sendTelemetryEvents([]eventWrapper{ - buildEvent("tengu_tool_use_completed", session, model, betas, nil, ""), - }, session) + buildEvent("tengu_tool_use_success", session, model, betas, nil, ""), + }, session, authToken) }() } } diff --git a/backend/internal/repository/http_upstream.go b/backend/internal/repository/http_upstream.go index 728c76bf..cc9ae847 100644 --- a/backend/internal/repository/http_upstream.go +++ b/backend/internal/repository/http_upstream.go @@ -46,7 +46,7 @@ const ( defaultIdleConnTimeout = 90 * time.Second // defaultResponseHeaderTimeout: 默认等待响应头超时时间(5分钟) // LLM 请求可能排队较久,需要较长超时 - defaultResponseHeaderTimeout = 300 * time.Second + defaultResponseHeaderTimeout = 600 * time.Second // defaultMaxUpstreamClients: 默认最大客户端缓存数量 // 超出后会淘汰最久未使用的客户端 defaultMaxUpstreamClients = 5000 diff --git a/backend/internal/service/gateway_service.go b/backend/internal/service/gateway_service.go index d51e2f6d..5d4031b2 100644 --- a/backend/internal/service/gateway_service.go +++ b/backend/internal/service/gateway_service.go @@ -4228,10 +4228,11 @@ func (s *GatewayService) Forward(ctx context.Context, c *gin.Context, account *A // 真实 CLI 在首次 messages 请求前 fire-and-forget 调用此端点。 if tokenType == "oauth" && 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( fmt.Sprintf("%d", account.ID), token, + token, reqModel, getHeaderRaw(c.Request.Header, "anthropic-beta"), ) @@ -4657,6 +4658,7 @@ func (s *GatewayService) Forward(ctx context.Context, c *gin.Context, account *A go telemetry.EmitPostRequest( fmt.Sprintf("%d", account.ID), token, + token, reqModel, getHeaderRaw(c.Request.Header, "anthropic-beta"), resp.StatusCode,