From 18043d416bc4e3d71718e8bc3283700fac22c03d Mon Sep 17 00:00:00 2001 From: claude-code-best Date: Mon, 20 Apr 2026 10:07:51 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=B8=BAsideQuery=E6=B7=BB=E5=8A=A0Lan?= =?UTF-8?q?gfuse=E8=BF=BD=E8=B8=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sideQuery 绕过了 claude.ts 的主 API 路径,导致所有走 sideQuery 的调用 (auto mode classifier、permission explainer、session search 等)都没有 Langfuse 记录。现在为每次 sideQuery 调用创建独立 trace 并记录 LLM observation, 未配置 Langfuse 时全部 no-op。 Co-Authored-By: Claude Opus 4.6 --- src/utils/sideQuery.ts | 70 +++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 18 deletions(-) diff --git a/src/utils/sideQuery.ts b/src/utils/sideQuery.ts index 4e6d4d731..9455a83e2 100644 --- a/src/utils/sideQuery.ts +++ b/src/utils/sideQuery.ts @@ -2,6 +2,7 @@ import type Anthropic from '@anthropic-ai/sdk' import type { BetaToolUnion } from '@anthropic-ai/sdk/resources/beta/messages.js' import { getLastApiCompletionTimestamp, + getSessionId, setLastApiCompletionTimestamp, } from '../bootstrap/state.js' import { STRUCTURED_OUTPUTS_BETA_HEADER } from '../constants/betas.js' @@ -14,8 +15,10 @@ import { logEvent } from '../services/analytics/index.js' import type { AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS } from '../services/analytics/metadata.js' import { getAPIMetadata } from '../services/api/claude.js' import { getAnthropicClient } from '../services/api/client.js' +import { createTrace, endTrace, recordLLMObservation } from '../services/langfuse/index.js' import { getModelBetas, modelSupportsStructuredOutputs } from './betas.js' import { computeFingerprint } from './fingerprint.js' +import { getAPIProvider } from './model/providers.js' import { normalizeModelStringForAPI } from './model/model.js' type MessageParam = Anthropic.MessageParam @@ -177,25 +180,39 @@ export async function sideQuery(opts: SideQueryOptions): Promise { } const normalizedModel = normalizeModelStringForAPI(model) + const provider = getAPIProvider() const start = Date.now() - // biome-ignore lint/plugin: this IS the wrapper that handles OAuth attribution - const response = await client.beta.messages.create( - { - model: normalizedModel, - max_tokens, - system: systemBlocks, - messages, - ...(tools && { tools }), - ...(tool_choice && { tool_choice }), - ...(output_format && { output_config: { format: output_format } }), - ...(temperature !== undefined && { temperature }), - ...(stop_sequences && { stop_sequences }), - ...(thinkingConfig && { thinking: thinkingConfig }), - ...(betas.length > 0 && { betas }), - metadata: getAPIMetadata(), - }, - { signal }, - ) + const langfuseTrace = createTrace({ + sessionId: getSessionId(), + model: normalizedModel, + provider, + name: `side-query:${opts.querySource}`, + querySource: opts.querySource, + }) + + let response: BetaMessage + try { + response = await client.beta.messages.create( + { + model: normalizedModel, + max_tokens, + system: systemBlocks, + messages, + ...(tools && { tools }), + ...(tool_choice && { tool_choice }), + ...(output_format && { output_config: { format: output_format } }), + ...(temperature !== undefined && { temperature }), + ...(stop_sequences && { stop_sequences }), + ...(thinkingConfig && { thinking: thinkingConfig }), + ...(betas.length > 0 && { betas }), + metadata: getAPIMetadata(), + }, + { signal }, + ) + } catch (error) { + endTrace(langfuseTrace, undefined, 'error') + throw error + } const requestId = (response as { _request_id?: string | null })._request_id ?? undefined @@ -218,5 +235,22 @@ export async function sideQuery(opts: SideQueryOptions): Promise { }) setLastApiCompletionTimestamp(now) + // Record LLM observation in Langfuse (no-op if not configured) + recordLLMObservation(langfuseTrace, { + model: normalizedModel, + provider, + input: messages, + output: response.content, + usage: { + input_tokens: response.usage.input_tokens, + output_tokens: response.usage.output_tokens, + cache_creation_input_tokens: response.usage.cache_creation_input_tokens ?? undefined, + cache_read_input_tokens: response.usage.cache_read_input_tokens ?? undefined, + }, + startTime: new Date(start), + endTime: new Date(), + }) + endTrace(langfuseTrace) + return response }