fix(langfuse): enrich agentflow trace metadata, tags, and cost tracking#1021
Conversation
…/user/billing context - Compute billingStripeCustomerId before analytics init block - Pass chatflowid, chatflowName, user, sessionId, messageId, billingStripeCustomerId, trackingMetadata into AnalyticHandler.getInstance options - Improve analytics catch block to log full error stack trace
…a, add ID tags
- Replace hardcoded metadata: { tags: ['openai-assistant'] } in onChainStart
with rich metadata from this.options (chatflowid, chatflowName, chatId,
userId, organizationId, messageId, sessionId, stripeCustomerId)
- Add chatflow_id, chat_id, chatmessage_id tags to onChainStart traces
- Add chatflow_id, chat_id, chatmessage_id tags to additionalCallbacks
handlerConfig and parentLangfuseTrace.update
- Applies to both new-trace and fallback trace paths in onChainStart
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR Review: fix(langfuse): enrich agentflow trace metadata, tags, and cost trackingSummary: This PR fixes a real gap — agentflow traces in Langfuse were getting minimal metadata compared to chatflow traces. The approach is correct and follows existing patterns. Two issues need attention before merging: a redundant database query on every agentflow execution, and a tag inconsistency between the two code paths. Critical Issues1. Redundant database query on every agentflow execution Location: The new code adds an unconditional const billedUserId = user?.id || chatflow.userId
const billedUser = await appDataSource.getRepository(User).findOne({ where: { id: billedUserId } })
const billingStripeCustomerId = OVERRIDE_CUSTOMER_ID ? DEFAULT_CUSTOMER_ID : billedUser?.stripeCustomerIdThis runs on every agentflow execution, even when analytics are disabled ( Impact: One extra DB round-trip per agentflow call, regardless of whether analytics or Suggestion: Move the three billing lines inside the if (isAnalyticsEnabled(chatflow.analytic)) {
const billedUserId = user?.id || chatflow.userId
const billedUser = await appDataSource.getRepository(User).findOne({ where: { id: billedUserId } })
const billingStripeCustomerId = OVERRIDE_CUSTOMER_ID ? DEFAULT_CUSTOMER_ID : billedUser?.stripeCustomerId
// ... rest of analytics init
}Major Concerns2. Tag inconsistency: Location: The new tags added to tags: [
`Name:${chatflow.name}`,
`chatflow_id:${options.chatflowid}`, // no guard — prints "chatflow_id:undefined" if missing
`chat_id:${options.chatId}`, // same issue
...(options.messageId ? [`chatmessage_id:${options.messageId}`] : [])
],In ...(this.options.chatflowid ? [`chatflow_id:${this.options.chatflowid}`] : []),
...(this.options.chatId ? [`chat_id:${this.options.chatId}`] : []),For callers that do not supply Suggestion: Apply the same conditional spread pattern used in tags: [
`Name:${chatflow.name}`,
...(options.chatflowid ? [`chatflow_id:${options.chatflowid}`] : []),
...(options.chatId ? [`chat_id:${options.chatId}`] : []),
...(options.messageId ? [`chatmessage_id:${options.messageId}`] : [])
],This applies to both the 3. Duplicate Location: chatflowid: chatflow.id,
chatflowId: chatflow.id, // duplicate — same value, different casingThis is intentional based on the PR description ("copies existing pattern"), but it is worth documenting why both are needed (i.e., which consumers use which casing). A comment here would prevent future confusion. The Minor Issues and Suggestions4. Location: The existing 5. Location: ].filter(Boolean),After using conditional spreads ( 6. Location: logger.error(`[server]: Analytics stack trace:`, error)The standard Positive Observations
Checklist
Status: Request Changes — Two issues should be addressed before merge: the unconditional DB query (correctness/performance) and the missing null guards on tags in |
Summary
AnalyticHandler.getInstanceoptions inbuildAgentflow.tswith chatflow/user/billing context so agentflow Langfuse traces get the same rich metadata as chatflowsmetadata: { tags: ['openai-assistant'] }inonChainStartwith metadata built fromthis.optionschatflow_id,chat_id,chatmessage_idtags to all Langfuse traces (agentflows via onChainStart, chatflows/legacy via additionalCallbacks)Problem
Agentflow traces exist in Langfuse but are bare — no chatflowid, userId, organizationId, billing metadata, and empty tags. This makes them unfindable and unfilterable compared to chatflow traces which have rich metadata.
Root Cause
AnalyticHandler.getInstanceinbuildAgentflow.tsreceived minimal options (only chatId, analytic config) — missing chatflowid, user, messageId, sessionId, billingAnalyticHandler.onChainStartinhandler.tscreated traces with hardcodedmetadata: { tags: ['openai-assistant'] }ignoring enriched optionsChanges
buildAgentflow.ts(15 lines)billingStripeCustomerIdbefore analytics init block (copies existing pattern from follow-up prompts section)AnalyticHandler.getInstanceoptions: chatflowid, chatflowId, chatflowName, user, sessionId, messageId, billingStripeCustomerId, trackingMetadatahandler.ts(48 lines net)metadata: { tags: ['openai-assistant'] }in onChainStart (2 trace creation paths) with rich metadata objectchatflow_id:,chat_id:,chatmessage_id:tags to onChainStart tracesadditionalCallbackshandlerConfig andparentLangfuseTrace.updateAfter Deploy
Agentflow traces will have:
Chatflow/legacy traces also get the 3 new ID tags via the additionalCallbacks change.