@mastra/langfuse 0.0.0-trace-timeline-update-20251121092347 → 0.0.0-type-testing-20260120105120

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tracing.ts"],"names":["BaseExporter","Langfuse","SpanType","omitKeys"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Langfuse Exporter for Mastra Observability\n *\n * This exporter sends observability data to Langfuse.\n * Root spans start traces in Langfuse.\n * MODEL_GENERATION spans become Langfuse generations, all others become spans.\n *\n * Compatible with both AI SDK v4 and v5:\n * - Handles both legacy token usage format (promptTokens/completionTokens)\n * and v5 format (inputTokens/outputTokens)\n * - Supports v5 reasoning tokens and cache-related metrics\n * - Adapts to v5 streaming protocol changes\n */\n\nimport type { TracingEvent, AnyExportedSpan, ModelGenerationAttributes } from '@mastra/core/observability';\nimport { SpanType } from '@mastra/core/observability';\nimport { omitKeys } from '@mastra/core/utils';\nimport { BaseExporter } from '@mastra/observability';\nimport type { BaseExporterConfig } from '@mastra/observability';\nimport { Langfuse } from 'langfuse';\nimport type { LangfuseTraceClient, LangfuseSpanClient, LangfuseGenerationClient, LangfuseEventClient } from 'langfuse';\n\nexport interface LangfuseExporterConfig extends BaseExporterConfig {\n /** Langfuse API key */\n publicKey?: string;\n /** Langfuse secret key */\n secretKey?: string;\n /** Langfuse host URL */\n baseUrl?: string;\n /** Enable realtime mode - flushes after each event for immediate visibility */\n realtime?: boolean;\n /** Additional options to pass to the Langfuse client */\n options?: any;\n}\n\ntype TraceData = {\n trace: LangfuseTraceClient; // Langfuse trace object\n spans: Map<string, LangfuseSpanClient | LangfuseGenerationClient>; // Maps span.id to Langfuse span/generation\n events: Map<string, LangfuseEventClient>; // Maps span.id to Langfuse event\n activeSpans: Set<string>; // Tracks which spans haven't ended yet\n rootSpanId?: string; // Track the root span ID\n};\n\ntype LangfuseParent = LangfuseTraceClient | LangfuseSpanClient | LangfuseGenerationClient | LangfuseEventClient;\n\n/**\n * Normalized token usage format compatible with Langfuse.\n * This unified format supports both AI SDK v4 and v5 token structures.\n *\n * @example\n * ```typescript\n * // AI SDK v4 format normalizes to:\n * { input: 100, output: 50, total: 150 }\n *\n * // AI SDK v5 format normalizes to:\n * { input: 120, output: 60, total: 180, reasoning: 1000, cachedInput: 50 }\n * ```\n */\ninterface NormalizedUsage {\n /**\n * Input tokens sent to the model\n * @source AI SDK v5: `inputTokens` | AI SDK v4: `promptTokens`\n */\n input?: number;\n\n /**\n * Output tokens received from the model\n * @source AI SDK v5: `outputTokens` | AI SDK v4: `completionTokens`\n */\n output?: number;\n\n /**\n * Total tokens (input + output + reasoning if applicable)\n * @source AI SDK v4 & v5: `totalTokens`\n */\n total?: number;\n\n /**\n * Reasoning tokens used by reasoning models\n * @source AI SDK v5: `reasoningTokens`\n * @since AI SDK v5.0.0\n * @example Models like o1-preview, o1-mini\n */\n reasoning?: number;\n\n /**\n * Cached input tokens (prompt cache hit)\n * @source AI SDK v5: `cachedInputTokens`\n * @since AI SDK v5.0.0\n * @example Anthropic's prompt caching, OpenAI prompt caching\n */\n cachedInput?: number;\n\n /**\n * Prompt cache hit tokens (legacy format)\n * @source AI SDK v4: `promptCacheHitTokens`\n * @deprecated Prefer `cachedInput` from v5 format\n */\n promptCacheHit?: number;\n\n /**\n * Prompt cache miss tokens (legacy format)\n * @source AI SDK v4: `promptCacheMissTokens`\n * @deprecated Prefer v5 format which uses `cachedInputTokens`\n */\n promptCacheMiss?: number;\n}\n\nexport class LangfuseExporter extends BaseExporter {\n name = 'langfuse';\n private client: Langfuse;\n private realtime: boolean;\n private traceMap = new Map<string, TraceData>();\n\n constructor(config: LangfuseExporterConfig) {\n super(config);\n\n this.realtime = config.realtime ?? false;\n\n if (!config.publicKey || !config.secretKey) {\n this.setDisabled(\n `Missing required credentials (publicKey: ${!!config.publicKey}, secretKey: ${!!config.secretKey})`,\n );\n // Create a no-op client to prevent runtime errors\n this.client = null as any;\n return;\n }\n\n this.client = new Langfuse({\n publicKey: config.publicKey,\n secretKey: config.secretKey,\n baseUrl: config.baseUrl,\n ...config.options,\n });\n }\n\n protected async _exportTracingEvent(event: TracingEvent): Promise<void> {\n if (event.exportedSpan.isEvent) {\n await this.handleEventSpan(event.exportedSpan);\n return;\n }\n\n switch (event.type) {\n case 'span_started':\n await this.handleSpanStarted(event.exportedSpan);\n break;\n case 'span_updated':\n await this.handleSpanUpdateOrEnd(event.exportedSpan, false);\n break;\n case 'span_ended':\n await this.handleSpanUpdateOrEnd(event.exportedSpan, true);\n break;\n }\n\n // Flush immediately in realtime mode for instant visibility\n if (this.realtime) {\n await this.client.flushAsync();\n }\n }\n\n private async handleSpanStarted(span: AnyExportedSpan): Promise<void> {\n if (span.isRootSpan) {\n this.initTrace(span);\n }\n const method = 'handleSpanStarted';\n\n const traceData = this.getTraceData({ span, method });\n if (!traceData) {\n return;\n }\n\n const langfuseParent = this.getLangfuseParent({ traceData, span, method });\n if (!langfuseParent) {\n return;\n }\n\n const payload = this.buildSpanPayload(span, true);\n\n const langfuseSpan =\n span.type === SpanType.MODEL_GENERATION ? langfuseParent.generation(payload) : langfuseParent.span(payload);\n\n traceData.spans.set(span.id, langfuseSpan);\n traceData.activeSpans.add(span.id); // Track as active\n }\n\n private async handleSpanUpdateOrEnd(span: AnyExportedSpan, isEnd: boolean): Promise<void> {\n const method = isEnd ? 'handleSpanEnd' : 'handleSpanUpdate';\n\n const traceData = this.getTraceData({ span, method });\n if (!traceData) {\n return;\n }\n\n const langfuseSpan = traceData.spans.get(span.id);\n if (!langfuseSpan) {\n // For event spans that only send SPAN_ENDED, we might not have the span yet\n if (isEnd && span.isEvent) {\n // Just make sure it's not in active spans\n traceData.activeSpans.delete(span.id);\n if (traceData.activeSpans.size === 0) {\n this.traceMap.delete(span.traceId);\n }\n return;\n }\n\n this.logger.warn('Langfuse exporter: No Langfuse span found for span update/end', {\n traceId: span.traceId,\n spanId: span.id,\n spanName: span.name,\n spanType: span.type,\n isRootSpan: span.isRootSpan,\n parentSpanId: span.parentSpanId,\n method,\n });\n return;\n }\n\n // use update for both update & end, so that we can use the\n // end time we set when ending the span.\n langfuseSpan.update(this.buildSpanPayload(span, false));\n\n if (isEnd) {\n // Remove from active spans\n traceData.activeSpans.delete(span.id);\n\n if (span.isRootSpan) {\n traceData.trace.update({ output: span.output });\n }\n\n // Only clean up the trace when ALL spans have ended\n if (traceData.activeSpans.size === 0) {\n this.traceMap.delete(span.traceId);\n }\n }\n }\n\n private async handleEventSpan(span: AnyExportedSpan): Promise<void> {\n if (span.isRootSpan) {\n this.logger.debug('Langfuse exporter: Creating trace', {\n traceId: span.traceId,\n spanId: span.id,\n spanName: span.name,\n method: 'handleEventSpan',\n });\n this.initTrace(span);\n }\n const method = 'handleEventSpan';\n\n const traceData = this.getTraceData({ span, method });\n if (!traceData) {\n return;\n }\n\n const langfuseParent = this.getLangfuseParent({ traceData, span, method });\n if (!langfuseParent) {\n return;\n }\n\n const payload = this.buildSpanPayload(span, true);\n\n const langfuseEvent = langfuseParent.event(payload);\n\n traceData.events.set(span.id, langfuseEvent);\n\n // Event spans are typically immediately ended, but let's track them properly\n if (!span.endTime) {\n traceData.activeSpans.add(span.id);\n }\n }\n\n private initTrace(span: AnyExportedSpan): void {\n const trace = this.client.trace(this.buildTracePayload(span));\n this.traceMap.set(span.traceId, {\n trace,\n spans: new Map(),\n events: new Map(),\n activeSpans: new Set(),\n rootSpanId: span.id,\n });\n }\n\n private getTraceData(options: { span: AnyExportedSpan; method: string }): TraceData | undefined {\n const { span, method } = options;\n\n if (this.traceMap.has(span.traceId)) {\n return this.traceMap.get(span.traceId);\n }\n\n this.logger.warn('Langfuse exporter: No trace data found for span', {\n traceId: span.traceId,\n spanId: span.id,\n spanName: span.name,\n spanType: span.type,\n isRootSpan: span.isRootSpan,\n parentSpanId: span.parentSpanId,\n method,\n });\n }\n\n private getLangfuseParent(options: {\n traceData: TraceData;\n span: AnyExportedSpan;\n method: string;\n }): LangfuseParent | undefined {\n const { traceData, span, method } = options;\n\n const parentId = span.parentSpanId;\n if (!parentId) {\n return traceData.trace;\n }\n if (traceData.spans.has(parentId)) {\n return traceData.spans.get(parentId);\n }\n if (traceData.events.has(parentId)) {\n return traceData.events.get(parentId);\n }\n this.logger.warn('Langfuse exporter: No parent data found for span', {\n traceId: span.traceId,\n spanId: span.id,\n spanName: span.name,\n spanType: span.type,\n isRootSpan: span.isRootSpan,\n parentSpanId: span.parentSpanId,\n method,\n });\n }\n\n private buildTracePayload(span: AnyExportedSpan): Record<string, any> {\n const payload: Record<string, any> = {\n id: span.traceId,\n name: span.name,\n };\n\n const { userId, sessionId, ...remainingMetadata } = span.metadata ?? {};\n\n if (userId) payload.userId = userId;\n if (sessionId) payload.sessionId = sessionId;\n if (span.input) payload.input = span.input;\n\n payload.metadata = {\n spanType: span.type,\n ...span.attributes,\n ...remainingMetadata,\n };\n\n return payload;\n }\n\n /**\n * Normalize usage data to handle both AI SDK v4 and v5 formats.\n *\n * AI SDK v4 uses: promptTokens, completionTokens\n * AI SDK v5 uses: inputTokens, outputTokens\n *\n * This function normalizes to a unified format that Langfuse can consume,\n * prioritizing v5 format while maintaining backward compatibility.\n *\n * @param usage - Token usage data from AI SDK (v4 or v5 format)\n * @returns Normalized usage object, or undefined if no usage data available\n */\n private normalizeUsage(usage: ModelGenerationAttributes['usage']): NormalizedUsage | undefined {\n if (!usage) return undefined;\n\n const normalized: NormalizedUsage = {};\n\n // Handle input tokens (v5 'inputTokens' or v4 'promptTokens')\n // Using ?? to prioritize v5 format while falling back to v4\n const inputTokens = usage.inputTokens ?? usage.promptTokens;\n if (inputTokens !== undefined) {\n normalized.input = inputTokens;\n }\n\n // Handle output tokens (v5 'outputTokens' or v4 'completionTokens')\n const outputTokens = usage.outputTokens ?? usage.completionTokens;\n if (outputTokens !== undefined) {\n normalized.output = outputTokens;\n }\n\n // Total tokens - calculate if not provided\n if (usage.totalTokens !== undefined) {\n normalized.total = usage.totalTokens;\n } else if (normalized.input !== undefined && normalized.output !== undefined) {\n normalized.total = normalized.input + normalized.output;\n }\n\n // AI SDK v5-specific: reasoning tokens\n if (usage.reasoningTokens !== undefined) {\n normalized.reasoning = usage.reasoningTokens;\n }\n\n // AI SDK v5-specific: cached tokens (cache hit)\n if (usage.cachedInputTokens !== undefined) {\n normalized.cachedInput = usage.cachedInputTokens;\n }\n\n // Legacy cache metrics (promptCacheHitTokens/promptCacheMissTokens)\n if (usage.promptCacheHitTokens !== undefined) {\n normalized.promptCacheHit = usage.promptCacheHitTokens;\n }\n if (usage.promptCacheMissTokens !== undefined) {\n normalized.promptCacheMiss = usage.promptCacheMissTokens;\n }\n\n return Object.keys(normalized).length > 0 ? normalized : undefined;\n }\n\n private buildSpanPayload(span: AnyExportedSpan, isCreate: boolean): Record<string, any> {\n const payload: Record<string, any> = {};\n\n if (isCreate) {\n payload.id = span.id;\n payload.name = span.name;\n payload.startTime = span.startTime;\n if (span.input !== undefined) payload.input = span.input;\n }\n\n if (span.output !== undefined) payload.output = span.output;\n if (span.endTime !== undefined) payload.endTime = span.endTime;\n\n const attributes = (span.attributes ?? {}) as Record<string, any>;\n\n // Strip special fields from metadata if used in top-level keys\n const attributesToOmit: string[] = [];\n\n if (span.type === SpanType.MODEL_GENERATION) {\n const modelAttr = attributes as ModelGenerationAttributes;\n\n if (modelAttr.model !== undefined) {\n payload.model = modelAttr.model;\n attributesToOmit.push('model');\n }\n\n if (modelAttr.usage !== undefined) {\n // Normalize usage to handle both v4 and v5 formats\n const normalizedUsage = this.normalizeUsage(modelAttr.usage);\n if (normalizedUsage) {\n payload.usage = normalizedUsage;\n }\n attributesToOmit.push('usage');\n }\n\n if (modelAttr.parameters !== undefined) {\n payload.modelParameters = modelAttr.parameters;\n attributesToOmit.push('parameters');\n }\n }\n\n payload.metadata = {\n spanType: span.type,\n ...omitKeys(attributes, attributesToOmit),\n ...span.metadata,\n };\n\n if (span.errorInfo) {\n payload.level = 'ERROR';\n payload.statusMessage = span.errorInfo.message;\n }\n\n return payload;\n }\n\n async addScoreToTrace({\n traceId,\n spanId,\n score,\n reason,\n scorerName,\n metadata,\n }: {\n traceId: string;\n spanId?: string;\n score: number;\n reason?: string;\n scorerName: string;\n metadata?: Record<string, any>;\n }): Promise<void> {\n if (!this.client) return;\n\n try {\n await this.client.score({\n id: `${traceId}-${scorerName}`,\n traceId,\n observationId: spanId,\n name: scorerName,\n value: score,\n ...(metadata?.sessionId ? { sessionId: metadata.sessionId } : {}),\n metadata: { ...(reason ? { reason } : {}) },\n dataType: 'NUMERIC',\n });\n } catch (error) {\n this.logger.error('Langfuse exporter: Error adding score to trace', {\n error,\n traceId,\n spanId,\n scorerName,\n });\n }\n }\n\n async shutdown(): Promise<void> {\n if (this.client) {\n await this.client.shutdownAsync();\n }\n this.traceMap.clear();\n await super.shutdown();\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/metrics.ts","../src/tracing.ts","../src/helpers.ts"],"names":["TrackingExporter","Langfuse","SpanType","omitKeys"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { UsageStats } from '@mastra/core/observability';\n\n/**\n * Token usage format compatible with Langfuse.\n */\nexport interface LangfuseUsageMetrics {\n input?: number;\n output?: number;\n total?: number;\n reasoning?: number;\n cache_read_input_tokens?: number;\n cache_write_input_tokens?: number;\n}\n\n/**\n * Formats UsageStats to Langfuse's expected format.\n */\nexport function formatUsageMetrics(usage?: UsageStats): LangfuseUsageMetrics {\n if (!usage) return {};\n\n const metrics: LangfuseUsageMetrics = {};\n\n if (usage.inputTokens !== undefined) {\n metrics.input = usage.inputTokens;\n\n if (usage.inputDetails?.cacheWrite !== undefined) {\n metrics.cache_write_input_tokens = usage.inputDetails.cacheWrite;\n metrics.input -= metrics.cache_write_input_tokens;\n }\n }\n\n if (usage.inputDetails?.cacheRead !== undefined) {\n metrics.cache_read_input_tokens = usage.inputDetails.cacheRead;\n }\n\n if (usage.outputTokens !== undefined) {\n metrics.output = usage.outputTokens;\n }\n\n if (usage.outputDetails?.reasoning !== undefined) {\n metrics.reasoning = usage.outputDetails.reasoning;\n }\n\n // Use explicit null checks to handle zero values correctly\n if (metrics.input != null && metrics.output != null) {\n metrics.total = metrics.input + metrics.output;\n if (metrics.cache_write_input_tokens != null) {\n metrics.total += metrics.cache_write_input_tokens;\n }\n }\n\n return metrics;\n}\n","/**\n * Langfuse Exporter for Mastra Observability\n *\n * This exporter sends observability data to Langfuse.\n * Root spans start traces in Langfuse.\n * MODEL_GENERATION spans become Langfuse generations, all others become spans.\n */\n\nimport type { AnyExportedSpan, ModelGenerationAttributes, SpanErrorInfo } from '@mastra/core/observability';\nimport { SpanType } from '@mastra/core/observability';\nimport { omitKeys } from '@mastra/core/utils';\nimport { TrackingExporter } from '@mastra/observability';\nimport type { TrackingExporterConfig, TraceData } from '@mastra/observability';\nimport { Langfuse } from 'langfuse';\nimport type { LangfuseTraceClient, LangfuseSpanClient, LangfuseGenerationClient, LangfuseEventClient } from 'langfuse';\nimport { formatUsageMetrics } from './metrics';\n\nexport interface LangfuseExporterConfig extends TrackingExporterConfig {\n /** Langfuse API key */\n publicKey?: string;\n /** Langfuse secret key */\n secretKey?: string;\n /** Langfuse host URL */\n baseUrl?: string;\n /** Enable realtime mode - flushes after each event for immediate visibility */\n realtime?: boolean;\n /** Additional options to pass to the Langfuse client */\n options?: any;\n}\n\ntype LangfusePromptData = { name?: string; version?: number; id?: string };\n\n/**\n * With Langfuse, data from the root span is stored in both the Root and the\n * first span.\n */\n\ntype LangfuseRoot = LangfuseTraceClient;\ntype LangfuseSpan = LangfuseSpanClient | LangfuseGenerationClient;\ntype LangfuseEvent = LangfuseEventClient;\ntype LangfuseMetadata = { prompt?: LangfusePromptData };\ntype LangfuseTraceData = TraceData<LangfuseRoot, LangfuseSpan, LangfuseEvent, LangfuseMetadata>;\n\nexport class LangfuseExporter extends TrackingExporter<\n LangfuseRoot,\n LangfuseSpan,\n LangfuseEvent,\n LangfuseMetadata,\n LangfuseExporterConfig\n> {\n name = 'langfuse';\n #client: Langfuse | undefined;\n #realtime: boolean;\n\n constructor(config: LangfuseExporterConfig = {}) {\n // Resolve env vars BEFORE calling super (config is readonly in base class)\n const publicKey = config.publicKey ?? process.env.LANGFUSE_PUBLIC_KEY;\n const secretKey = config.secretKey ?? process.env.LANGFUSE_SECRET_KEY;\n const baseUrl = config.baseUrl ?? process.env.LANGFUSE_BASE_URL;\n\n super({\n ...config,\n publicKey,\n secretKey,\n baseUrl,\n });\n\n this.#realtime = config.realtime ?? false;\n\n if (!publicKey || !secretKey) {\n const publicKeySource = config.publicKey\n ? 'from config'\n : process.env.LANGFUSE_PUBLIC_KEY\n ? 'from env'\n : 'missing';\n const secretKeySource = config.secretKey\n ? 'from config'\n : process.env.LANGFUSE_SECRET_KEY\n ? 'from env'\n : 'missing';\n this.setDisabled(\n `Missing required credentials (publicKey: ${publicKeySource}, secretKey: ${secretKeySource}). ` +\n `Set LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY environment variables or pass them in config.`,\n );\n return;\n }\n\n this.#client = new Langfuse({\n publicKey,\n secretKey,\n baseUrl,\n ...config.options,\n });\n }\n\n protected override async _postExportTracingEvent(): Promise<void> {\n // Flush immediately in realtime mode for instant visibility\n if (this.#realtime) {\n await this.#client?.flushAsync();\n }\n }\n\n protected override async _buildRoot(args: {\n span: AnyExportedSpan;\n traceData: LangfuseTraceData;\n }): Promise<LangfuseTraceClient | undefined> {\n const { span } = args;\n // Note: If the traceId already exists in Langfuse (e.g., from a previous server instance\n // or session), the Langfuse SDK handles this gracefully. Calling client.trace() with\n // an existing ID is idempotent - it will update/continue the existing trace rather than\n // failing or creating a duplicate. This is by design for distributed tracing scenarios.\n // See: https://langfuse.com/docs/tracing-features/trace-ids\n return this.#client?.trace(this.buildTracePayload(span));\n }\n\n protected override async _buildEvent(args: {\n span: AnyExportedSpan;\n traceData: LangfuseTraceData;\n }): Promise<LangfuseEvent | undefined> {\n const { span, traceData } = args;\n const langfuseParent = traceData.getParentOrRoot({ span });\n if (!langfuseParent) {\n return;\n }\n\n const payload = this.buildSpanPayload(span, true, traceData);\n return langfuseParent.event(payload);\n }\n\n protected override async _buildSpan(args: {\n span: AnyExportedSpan;\n traceData: LangfuseTraceData;\n }): Promise<LangfuseSpan | undefined> {\n const { span, traceData } = args;\n const langfuseParent = traceData.getParentOrRoot({ span });\n if (!langfuseParent) {\n return;\n }\n\n const payload = this.buildSpanPayload(span, true, traceData);\n\n const langfuseSpan =\n span.type === SpanType.MODEL_GENERATION ? langfuseParent.generation(payload) : langfuseParent.span(payload);\n\n this.logger.debug(`${this.name}: built span`, {\n traceId: span.traceId,\n spanId: payload.id,\n method: '_buildSpan',\n });\n\n return langfuseSpan;\n }\n\n protected override async _updateSpan(args: { span: AnyExportedSpan; traceData: LangfuseTraceData }): Promise<void> {\n const { span, traceData } = args;\n const langfuseSpan = traceData.getSpan({ spanId: span.id });\n if (langfuseSpan) {\n this.logger.debug(`${this.name}: found span for update`, {\n traceId: span.traceId,\n spanId: langfuseSpan.id,\n method: '_updateSpan',\n });\n\n const updatePayload = this.buildSpanPayload(span, false, traceData);\n\n // use update for both update & end, so that we can use the\n // end time we set when ending the span.\n langfuseSpan.update(updatePayload);\n }\n }\n\n protected override async _finishSpan(args: { span: AnyExportedSpan; traceData: LangfuseTraceData }): Promise<void> {\n const { span, traceData } = args;\n const langfuseSpan = traceData.getSpan({ spanId: span.id });\n // use update for both update & end, so that we can use the\n // end time we set when ending the span.\n langfuseSpan?.update(this.buildSpanPayload(span, false, traceData));\n\n if (span.isRootSpan) {\n const langfuseRoot = traceData.getRoot();\n langfuseRoot?.update({ output: span.output });\n }\n }\n\n protected override async _abortSpan(args: { span: LangfuseSpan; reason: SpanErrorInfo }): Promise<void> {\n const { span, reason } = args;\n span.end({\n level: 'ERROR',\n statusMessage: reason.message,\n });\n }\n\n private buildTracePayload(span: AnyExportedSpan): Record<string, any> {\n const payload: Record<string, any> = {\n id: span.traceId,\n name: span.name,\n };\n\n const { userId, sessionId, ...remainingMetadata } = span.metadata ?? {};\n\n if (userId) payload.userId = userId;\n if (sessionId) payload.sessionId = sessionId;\n if (span.input) payload.input = span.input;\n // Include tags if present (only for root spans, which is always the case here)\n if (span.tags?.length) payload.tags = span.tags;\n\n payload.metadata = {\n spanType: span.type,\n ...span.attributes,\n ...remainingMetadata,\n };\n\n return payload;\n }\n\n /**\n * Look up the Langfuse prompt from the closest span that has one.\n * This enables prompt inheritance for MODEL_GENERATION spans when the prompt\n * is set on a parent span (e.g., AGENT_RUN) rather than directly on the generation.\n * This enables prompt linking when:\n * - A workflow calls multiple agents, each with different prompts\n * - Nested agents have different prompts\n * - The prompt is set on AGENT_RUN but MODEL_GENERATION inherits it\n */\n private findLangfusePrompt(traceData: LangfuseTraceData, span: AnyExportedSpan): LangfusePromptData | undefined {\n let currentSpanId: string | undefined = span.id;\n\n while (currentSpanId) {\n const providerMetadata = traceData.getMetadata({ spanId: currentSpanId });\n\n if (providerMetadata?.prompt) {\n this.logger.debug(`${this.name}: found prompt in provider metadata`, {\n traceId: span.traceId,\n spanId: span.id,\n prompt: providerMetadata?.prompt,\n });\n return providerMetadata.prompt;\n }\n currentSpanId = traceData.getParentId({ spanId: currentSpanId });\n }\n\n return undefined;\n }\n\n private buildSpanPayload(\n span: AnyExportedSpan,\n isCreate: boolean,\n traceData: LangfuseTraceData,\n ): Record<string, any> {\n const payload: Record<string, any> = {};\n\n if (isCreate) {\n payload.id = span.id;\n payload.name = span.name;\n payload.startTime = span.startTime;\n }\n\n if (span.input !== undefined) payload.input = span.input;\n if (span.output !== undefined) payload.output = span.output;\n if (span.endTime !== undefined) payload.endTime = span.endTime;\n\n const attributes = (span.attributes ?? {}) as Record<string, any>;\n\n const metadata: Record<string, any> = {\n ...span.metadata,\n };\n\n // Strip special fields from metadata if used in top-level keys\n const attributesToOmit: string[] = [];\n const metadataToOmit: string[] = [];\n\n if (span.type === SpanType.MODEL_GENERATION) {\n const modelAttr = attributes as ModelGenerationAttributes;\n\n if (modelAttr.model !== undefined) {\n payload.model = modelAttr.model;\n attributesToOmit.push('model');\n }\n\n if (modelAttr.usage !== undefined) {\n payload.usageDetails = formatUsageMetrics(modelAttr.usage);\n attributesToOmit.push('usage');\n }\n\n if (modelAttr.parameters !== undefined) {\n payload.modelParameters = modelAttr.parameters;\n attributesToOmit.push('parameters');\n }\n\n // Users can set metadata.langfuse.prompt to link generations to Langfuse Prompt Management\n // Supported formats:\n // - { id } - link by prompt UUID alone\n // - { name, version } - link by name and version\n // - { name, version, id } - link with all fields\n const promptData = this.findLangfusePrompt(traceData, span);\n const hasNameAndVersion = promptData?.name !== undefined && promptData?.version !== undefined;\n const hasId = promptData?.id !== undefined;\n\n if (hasNameAndVersion || hasId) {\n payload.prompt = {};\n\n if (promptData?.name !== undefined) payload.prompt.name = promptData.name;\n if (promptData?.version !== undefined) payload.prompt.version = promptData.version;\n if (promptData?.id !== undefined) payload.prompt.id = promptData.id;\n\n metadataToOmit.push('langfuse');\n }\n\n // completionStartTime is used by Langfuse to calculate time-to-first-token (TTFT)\n if (modelAttr.completionStartTime !== undefined) {\n payload.completionStartTime = modelAttr.completionStartTime;\n attributesToOmit.push('completionStartTime');\n }\n }\n\n payload.metadata = {\n spanType: span.type,\n ...omitKeys(attributes, attributesToOmit),\n ...omitKeys(metadata, metadataToOmit),\n };\n\n if (span.errorInfo) {\n payload.level = 'ERROR';\n payload.statusMessage = span.errorInfo.message;\n }\n\n return payload;\n }\n\n async addScoreToTrace({\n traceId,\n spanId,\n score,\n reason,\n scorerName,\n metadata,\n }: {\n traceId: string;\n spanId?: string;\n score: number;\n reason?: string;\n scorerName: string;\n metadata?: Record<string, any>;\n }): Promise<void> {\n if (!this.#client) return;\n\n try {\n await this.#client.score({\n id: `${traceId}-${scorerName}`,\n traceId,\n observationId: spanId,\n name: scorerName,\n value: score,\n ...(metadata?.sessionId ? { sessionId: metadata.sessionId } : {}),\n metadata: { ...(reason ? { reason } : {}) },\n dataType: 'NUMERIC',\n });\n } catch (error) {\n this.logger.error('Langfuse exporter: Error adding score to trace', {\n error,\n traceId,\n spanId,\n scorerName,\n });\n }\n }\n\n /**\n * Force flush any buffered data to Langfuse without shutting down.\n */\n protected override async _flush(): Promise<void> {\n if (this.#client) {\n await this.#client.flushAsync();\n }\n }\n\n override async _postShutdown(): Promise<void> {\n if (this.#client) {\n await this.#client.shutdownAsync();\n }\n }\n}\n","/**\n * Langfuse Tracing Options Helpers\n *\n * These helpers integrate with the `buildTracingOptions` pattern from\n * `@mastra/observability` to add Langfuse-specific tracing features.\n *\n * @example\n * ```typescript\n * import { buildTracingOptions } from '@mastra/observability';\n * import { withLangfusePrompt } from '@mastra/langfuse';\n *\n * const prompt = await langfuse.getPrompt('my-prompt');\n *\n * const agent = new Agent({\n * defaultGenerateOptions: {\n * tracingOptions: buildTracingOptions(withLangfusePrompt(prompt)),\n * },\n * });\n * ```\n */\n\nimport type { TracingOptionsUpdater } from '@mastra/observability';\n\n/**\n * Langfuse prompt input - accepts either a Langfuse SDK prompt object\n * or manual fields.\n */\nexport interface LangfusePromptInput {\n /** Prompt name */\n name?: string;\n /** Prompt version */\n version?: number;\n /** Prompt UUID */\n id?: string;\n}\n\n/**\n * Adds Langfuse prompt metadata to the tracing options\n * to enable Langfuse Prompt Tracing.\n *\n * The metadata is added under `metadata.langfuse.prompt` and includes:\n * - `name` - Prompt name\n * - `version` - Prompt version\n * - `id` - Prompt UUID\n *\n * All fields are deeply merged with any existing metadata.\n *\n * @param prompt - A Langfuse prompt object (from `langfuse.getPrompt()`) or manual fields\n * @returns A TracingOptionsUpdater function for use with `buildTracingOptions`\n *\n * @example\n * ```typescript\n * import { buildTracingOptions } from '@mastra/observability';\n * import { withLangfusePrompt } from '@mastra/langfuse';\n * import { Langfuse } from 'langfuse';\n *\n * const langfuse = new Langfuse();\n * const prompt = await langfuse.getPrompt('customer-support');\n *\n * // Use with buildTracingOptions\n * const tracingOptions = buildTracingOptions(\n * withLangfusePrompt(prompt),\n * );\n *\n * // Or directly in agent config\n * const agent = new Agent({\n * name: 'support-agent',\n * instructions: prompt.prompt,\n * model: openai('gpt-4o'),\n * defaultGenerateOptions: {\n * tracingOptions: buildTracingOptions(withLangfusePrompt(prompt)),\n * },\n * });\n *\n * // Manual fields also work\n * const tracingOptions = buildTracingOptions(\n * withLangfusePrompt({ name: 'my-prompt', version: 1 }),\n * );\n * ```\n */\nexport function withLangfusePrompt(prompt: LangfusePromptInput): TracingOptionsUpdater {\n return opts => ({\n ...opts,\n metadata: {\n ...opts.metadata,\n langfuse: {\n ...(opts.metadata?.langfuse as Record<string, unknown>),\n prompt: {\n ...(prompt.name !== undefined && { name: prompt.name }),\n ...(prompt.version !== undefined && { version: prompt.version }),\n ...(prompt.id !== undefined && { id: prompt.id }),\n },\n },\n },\n });\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -5,4 +5,5 @@
5
5
  * Currently includes tracing support with plans for additional observability features.
6
6
  */
7
7
  export * from './tracing.js';
8
+ export * from './helpers.js';
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,cAAc,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,cAAc,WAAW,CAAC;AAG1B,cAAc,WAAW,CAAC"}
package/dist/index.js CHANGED
@@ -1,177 +1,130 @@
1
1
  import { SpanType } from '@mastra/core/observability';
2
2
  import { omitKeys } from '@mastra/core/utils';
3
- import { BaseExporter } from '@mastra/observability';
3
+ import { TrackingExporter } from '@mastra/observability';
4
4
  import { Langfuse } from 'langfuse';
5
5
 
6
6
  // src/tracing.ts
7
- var LangfuseExporter = class extends BaseExporter {
7
+
8
+ // src/metrics.ts
9
+ function formatUsageMetrics(usage) {
10
+ if (!usage) return {};
11
+ const metrics = {};
12
+ if (usage.inputTokens !== void 0) {
13
+ metrics.input = usage.inputTokens;
14
+ if (usage.inputDetails?.cacheWrite !== void 0) {
15
+ metrics.cache_write_input_tokens = usage.inputDetails.cacheWrite;
16
+ metrics.input -= metrics.cache_write_input_tokens;
17
+ }
18
+ }
19
+ if (usage.inputDetails?.cacheRead !== void 0) {
20
+ metrics.cache_read_input_tokens = usage.inputDetails.cacheRead;
21
+ }
22
+ if (usage.outputTokens !== void 0) {
23
+ metrics.output = usage.outputTokens;
24
+ }
25
+ if (usage.outputDetails?.reasoning !== void 0) {
26
+ metrics.reasoning = usage.outputDetails.reasoning;
27
+ }
28
+ if (metrics.input != null && metrics.output != null) {
29
+ metrics.total = metrics.input + metrics.output;
30
+ if (metrics.cache_write_input_tokens != null) {
31
+ metrics.total += metrics.cache_write_input_tokens;
32
+ }
33
+ }
34
+ return metrics;
35
+ }
36
+
37
+ // src/tracing.ts
38
+ var LangfuseExporter = class extends TrackingExporter {
8
39
  name = "langfuse";
9
- client;
10
- realtime;
11
- traceMap = /* @__PURE__ */ new Map();
12
- constructor(config) {
13
- super(config);
14
- this.realtime = config.realtime ?? false;
15
- if (!config.publicKey || !config.secretKey) {
40
+ #client;
41
+ #realtime;
42
+ constructor(config = {}) {
43
+ const publicKey = config.publicKey ?? process.env.LANGFUSE_PUBLIC_KEY;
44
+ const secretKey = config.secretKey ?? process.env.LANGFUSE_SECRET_KEY;
45
+ const baseUrl = config.baseUrl ?? process.env.LANGFUSE_BASE_URL;
46
+ super({
47
+ ...config,
48
+ publicKey,
49
+ secretKey,
50
+ baseUrl
51
+ });
52
+ this.#realtime = config.realtime ?? false;
53
+ if (!publicKey || !secretKey) {
54
+ const publicKeySource = config.publicKey ? "from config" : process.env.LANGFUSE_PUBLIC_KEY ? "from env" : "missing";
55
+ const secretKeySource = config.secretKey ? "from config" : process.env.LANGFUSE_SECRET_KEY ? "from env" : "missing";
16
56
  this.setDisabled(
17
- `Missing required credentials (publicKey: ${!!config.publicKey}, secretKey: ${!!config.secretKey})`
57
+ `Missing required credentials (publicKey: ${publicKeySource}, secretKey: ${secretKeySource}). Set LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY environment variables or pass them in config.`
18
58
  );
19
- this.client = null;
20
59
  return;
21
60
  }
22
- this.client = new Langfuse({
23
- publicKey: config.publicKey,
24
- secretKey: config.secretKey,
25
- baseUrl: config.baseUrl,
61
+ this.#client = new Langfuse({
62
+ publicKey,
63
+ secretKey,
64
+ baseUrl,
26
65
  ...config.options
27
66
  });
28
67
  }
29
- async _exportTracingEvent(event) {
30
- if (event.exportedSpan.isEvent) {
31
- await this.handleEventSpan(event.exportedSpan);
32
- return;
33
- }
34
- switch (event.type) {
35
- case "span_started":
36
- await this.handleSpanStarted(event.exportedSpan);
37
- break;
38
- case "span_updated":
39
- await this.handleSpanUpdateOrEnd(event.exportedSpan, false);
40
- break;
41
- case "span_ended":
42
- await this.handleSpanUpdateOrEnd(event.exportedSpan, true);
43
- break;
44
- }
45
- if (this.realtime) {
46
- await this.client.flushAsync();
68
+ async _postExportTracingEvent() {
69
+ if (this.#realtime) {
70
+ await this.#client?.flushAsync();
47
71
  }
48
72
  }
49
- async handleSpanStarted(span) {
50
- if (span.isRootSpan) {
51
- this.initTrace(span);
52
- }
53
- const method = "handleSpanStarted";
54
- const traceData = this.getTraceData({ span, method });
55
- if (!traceData) {
73
+ async _buildRoot(args) {
74
+ const { span } = args;
75
+ return this.#client?.trace(this.buildTracePayload(span));
76
+ }
77
+ async _buildEvent(args) {
78
+ const { span, traceData } = args;
79
+ const langfuseParent = traceData.getParentOrRoot({ span });
80
+ if (!langfuseParent) {
56
81
  return;
57
82
  }
58
- const langfuseParent = this.getLangfuseParent({ traceData, span, method });
83
+ const payload = this.buildSpanPayload(span, true, traceData);
84
+ return langfuseParent.event(payload);
85
+ }
86
+ async _buildSpan(args) {
87
+ const { span, traceData } = args;
88
+ const langfuseParent = traceData.getParentOrRoot({ span });
59
89
  if (!langfuseParent) {
60
90
  return;
61
91
  }
62
- const payload = this.buildSpanPayload(span, true);
92
+ const payload = this.buildSpanPayload(span, true, traceData);
63
93
  const langfuseSpan = span.type === SpanType.MODEL_GENERATION ? langfuseParent.generation(payload) : langfuseParent.span(payload);
64
- traceData.spans.set(span.id, langfuseSpan);
65
- traceData.activeSpans.add(span.id);
94
+ this.logger.debug(`${this.name}: built span`, {
95
+ traceId: span.traceId,
96
+ spanId: payload.id,
97
+ method: "_buildSpan"
98
+ });
99
+ return langfuseSpan;
66
100
  }
67
- async handleSpanUpdateOrEnd(span, isEnd) {
68
- const method = isEnd ? "handleSpanEnd" : "handleSpanUpdate";
69
- const traceData = this.getTraceData({ span, method });
70
- if (!traceData) {
71
- return;
72
- }
73
- const langfuseSpan = traceData.spans.get(span.id);
74
- if (!langfuseSpan) {
75
- if (isEnd && span.isEvent) {
76
- traceData.activeSpans.delete(span.id);
77
- if (traceData.activeSpans.size === 0) {
78
- this.traceMap.delete(span.traceId);
79
- }
80
- return;
81
- }
82
- this.logger.warn("Langfuse exporter: No Langfuse span found for span update/end", {
101
+ async _updateSpan(args) {
102
+ const { span, traceData } = args;
103
+ const langfuseSpan = traceData.getSpan({ spanId: span.id });
104
+ if (langfuseSpan) {
105
+ this.logger.debug(`${this.name}: found span for update`, {
83
106
  traceId: span.traceId,
84
- spanId: span.id,
85
- spanName: span.name,
86
- spanType: span.type,
87
- isRootSpan: span.isRootSpan,
88
- parentSpanId: span.parentSpanId,
89
- method
107
+ spanId: langfuseSpan.id,
108
+ method: "_updateSpan"
90
109
  });
91
- return;
92
- }
93
- langfuseSpan.update(this.buildSpanPayload(span, false));
94
- if (isEnd) {
95
- traceData.activeSpans.delete(span.id);
96
- if (span.isRootSpan) {
97
- traceData.trace.update({ output: span.output });
98
- }
99
- if (traceData.activeSpans.size === 0) {
100
- this.traceMap.delete(span.traceId);
101
- }
110
+ const updatePayload = this.buildSpanPayload(span, false, traceData);
111
+ langfuseSpan.update(updatePayload);
102
112
  }
103
113
  }
104
- async handleEventSpan(span) {
114
+ async _finishSpan(args) {
115
+ const { span, traceData } = args;
116
+ const langfuseSpan = traceData.getSpan({ spanId: span.id });
117
+ langfuseSpan?.update(this.buildSpanPayload(span, false, traceData));
105
118
  if (span.isRootSpan) {
106
- this.logger.debug("Langfuse exporter: Creating trace", {
107
- traceId: span.traceId,
108
- spanId: span.id,
109
- spanName: span.name,
110
- method: "handleEventSpan"
111
- });
112
- this.initTrace(span);
113
- }
114
- const method = "handleEventSpan";
115
- const traceData = this.getTraceData({ span, method });
116
- if (!traceData) {
117
- return;
118
- }
119
- const langfuseParent = this.getLangfuseParent({ traceData, span, method });
120
- if (!langfuseParent) {
121
- return;
122
- }
123
- const payload = this.buildSpanPayload(span, true);
124
- const langfuseEvent = langfuseParent.event(payload);
125
- traceData.events.set(span.id, langfuseEvent);
126
- if (!span.endTime) {
127
- traceData.activeSpans.add(span.id);
119
+ const langfuseRoot = traceData.getRoot();
120
+ langfuseRoot?.update({ output: span.output });
128
121
  }
129
122
  }
130
- initTrace(span) {
131
- const trace = this.client.trace(this.buildTracePayload(span));
132
- this.traceMap.set(span.traceId, {
133
- trace,
134
- spans: /* @__PURE__ */ new Map(),
135
- events: /* @__PURE__ */ new Map(),
136
- activeSpans: /* @__PURE__ */ new Set(),
137
- rootSpanId: span.id
138
- });
139
- }
140
- getTraceData(options) {
141
- const { span, method } = options;
142
- if (this.traceMap.has(span.traceId)) {
143
- return this.traceMap.get(span.traceId);
144
- }
145
- this.logger.warn("Langfuse exporter: No trace data found for span", {
146
- traceId: span.traceId,
147
- spanId: span.id,
148
- spanName: span.name,
149
- spanType: span.type,
150
- isRootSpan: span.isRootSpan,
151
- parentSpanId: span.parentSpanId,
152
- method
153
- });
154
- }
155
- getLangfuseParent(options) {
156
- const { traceData, span, method } = options;
157
- const parentId = span.parentSpanId;
158
- if (!parentId) {
159
- return traceData.trace;
160
- }
161
- if (traceData.spans.has(parentId)) {
162
- return traceData.spans.get(parentId);
163
- }
164
- if (traceData.events.has(parentId)) {
165
- return traceData.events.get(parentId);
166
- }
167
- this.logger.warn("Langfuse exporter: No parent data found for span", {
168
- traceId: span.traceId,
169
- spanId: span.id,
170
- spanName: span.name,
171
- spanType: span.type,
172
- isRootSpan: span.isRootSpan,
173
- parentSpanId: span.parentSpanId,
174
- method
123
+ async _abortSpan(args) {
124
+ const { span, reason } = args;
125
+ span.end({
126
+ level: "ERROR",
127
+ statusMessage: reason.message
175
128
  });
176
129
  }
177
130
  buildTracePayload(span) {
@@ -183,6 +136,7 @@ var LangfuseExporter = class extends BaseExporter {
183
136
  if (userId) payload.userId = userId;
184
137
  if (sessionId) payload.sessionId = sessionId;
185
138
  if (span.input) payload.input = span.input;
139
+ if (span.tags?.length) payload.tags = span.tags;
186
140
  payload.metadata = {
187
141
  spanType: span.type,
188
142
  ...span.attributes,
@@ -191,59 +145,46 @@ var LangfuseExporter = class extends BaseExporter {
191
145
  return payload;
192
146
  }
193
147
  /**
194
- * Normalize usage data to handle both AI SDK v4 and v5 formats.
195
- *
196
- * AI SDK v4 uses: promptTokens, completionTokens
197
- * AI SDK v5 uses: inputTokens, outputTokens
198
- *
199
- * This function normalizes to a unified format that Langfuse can consume,
200
- * prioritizing v5 format while maintaining backward compatibility.
201
- *
202
- * @param usage - Token usage data from AI SDK (v4 or v5 format)
203
- * @returns Normalized usage object, or undefined if no usage data available
148
+ * Look up the Langfuse prompt from the closest span that has one.
149
+ * This enables prompt inheritance for MODEL_GENERATION spans when the prompt
150
+ * is set on a parent span (e.g., AGENT_RUN) rather than directly on the generation.
151
+ * This enables prompt linking when:
152
+ * - A workflow calls multiple agents, each with different prompts
153
+ * - Nested agents have different prompts
154
+ * - The prompt is set on AGENT_RUN but MODEL_GENERATION inherits it
204
155
  */
205
- normalizeUsage(usage) {
206
- if (!usage) return void 0;
207
- const normalized = {};
208
- const inputTokens = usage.inputTokens ?? usage.promptTokens;
209
- if (inputTokens !== void 0) {
210
- normalized.input = inputTokens;
211
- }
212
- const outputTokens = usage.outputTokens ?? usage.completionTokens;
213
- if (outputTokens !== void 0) {
214
- normalized.output = outputTokens;
215
- }
216
- if (usage.totalTokens !== void 0) {
217
- normalized.total = usage.totalTokens;
218
- } else if (normalized.input !== void 0 && normalized.output !== void 0) {
219
- normalized.total = normalized.input + normalized.output;
220
- }
221
- if (usage.reasoningTokens !== void 0) {
222
- normalized.reasoning = usage.reasoningTokens;
223
- }
224
- if (usage.cachedInputTokens !== void 0) {
225
- normalized.cachedInput = usage.cachedInputTokens;
226
- }
227
- if (usage.promptCacheHitTokens !== void 0) {
228
- normalized.promptCacheHit = usage.promptCacheHitTokens;
229
- }
230
- if (usage.promptCacheMissTokens !== void 0) {
231
- normalized.promptCacheMiss = usage.promptCacheMissTokens;
156
+ findLangfusePrompt(traceData, span) {
157
+ let currentSpanId = span.id;
158
+ while (currentSpanId) {
159
+ const providerMetadata = traceData.getMetadata({ spanId: currentSpanId });
160
+ if (providerMetadata?.prompt) {
161
+ this.logger.debug(`${this.name}: found prompt in provider metadata`, {
162
+ traceId: span.traceId,
163
+ spanId: span.id,
164
+ prompt: providerMetadata?.prompt
165
+ });
166
+ return providerMetadata.prompt;
167
+ }
168
+ currentSpanId = traceData.getParentId({ spanId: currentSpanId });
232
169
  }
233
- return Object.keys(normalized).length > 0 ? normalized : void 0;
170
+ return void 0;
234
171
  }
235
- buildSpanPayload(span, isCreate) {
172
+ buildSpanPayload(span, isCreate, traceData) {
236
173
  const payload = {};
237
174
  if (isCreate) {
238
175
  payload.id = span.id;
239
176
  payload.name = span.name;
240
177
  payload.startTime = span.startTime;
241
- if (span.input !== void 0) payload.input = span.input;
242
178
  }
179
+ if (span.input !== void 0) payload.input = span.input;
243
180
  if (span.output !== void 0) payload.output = span.output;
244
181
  if (span.endTime !== void 0) payload.endTime = span.endTime;
245
182
  const attributes = span.attributes ?? {};
183
+ const metadata = {
184
+ ...span.metadata
185
+ };
246
186
  const attributesToOmit = [];
187
+ const metadataToOmit = [];
247
188
  if (span.type === SpanType.MODEL_GENERATION) {
248
189
  const modelAttr = attributes;
249
190
  if (modelAttr.model !== void 0) {
@@ -251,21 +192,32 @@ var LangfuseExporter = class extends BaseExporter {
251
192
  attributesToOmit.push("model");
252
193
  }
253
194
  if (modelAttr.usage !== void 0) {
254
- const normalizedUsage = this.normalizeUsage(modelAttr.usage);
255
- if (normalizedUsage) {
256
- payload.usage = normalizedUsage;
257
- }
195
+ payload.usageDetails = formatUsageMetrics(modelAttr.usage);
258
196
  attributesToOmit.push("usage");
259
197
  }
260
198
  if (modelAttr.parameters !== void 0) {
261
199
  payload.modelParameters = modelAttr.parameters;
262
200
  attributesToOmit.push("parameters");
263
201
  }
202
+ const promptData = this.findLangfusePrompt(traceData, span);
203
+ const hasNameAndVersion = promptData?.name !== void 0 && promptData?.version !== void 0;
204
+ const hasId = promptData?.id !== void 0;
205
+ if (hasNameAndVersion || hasId) {
206
+ payload.prompt = {};
207
+ if (promptData?.name !== void 0) payload.prompt.name = promptData.name;
208
+ if (promptData?.version !== void 0) payload.prompt.version = promptData.version;
209
+ if (promptData?.id !== void 0) payload.prompt.id = promptData.id;
210
+ metadataToOmit.push("langfuse");
211
+ }
212
+ if (modelAttr.completionStartTime !== void 0) {
213
+ payload.completionStartTime = modelAttr.completionStartTime;
214
+ attributesToOmit.push("completionStartTime");
215
+ }
264
216
  }
265
217
  payload.metadata = {
266
218
  spanType: span.type,
267
219
  ...omitKeys(attributes, attributesToOmit),
268
- ...span.metadata
220
+ ...omitKeys(metadata, metadataToOmit)
269
221
  };
270
222
  if (span.errorInfo) {
271
223
  payload.level = "ERROR";
@@ -281,9 +233,9 @@ var LangfuseExporter = class extends BaseExporter {
281
233
  scorerName,
282
234
  metadata
283
235
  }) {
284
- if (!this.client) return;
236
+ if (!this.#client) return;
285
237
  try {
286
- await this.client.score({
238
+ await this.#client.score({
287
239
  id: `${traceId}-${scorerName}`,
288
240
  traceId,
289
241
  observationId: spanId,
@@ -302,15 +254,39 @@ var LangfuseExporter = class extends BaseExporter {
302
254
  });
303
255
  }
304
256
  }
305
- async shutdown() {
306
- if (this.client) {
307
- await this.client.shutdownAsync();
257
+ /**
258
+ * Force flush any buffered data to Langfuse without shutting down.
259
+ */
260
+ async _flush() {
261
+ if (this.#client) {
262
+ await this.#client.flushAsync();
263
+ }
264
+ }
265
+ async _postShutdown() {
266
+ if (this.#client) {
267
+ await this.#client.shutdownAsync();
308
268
  }
309
- this.traceMap.clear();
310
- await super.shutdown();
311
269
  }
312
270
  };
313
271
 
314
- export { LangfuseExporter };
272
+ // src/helpers.ts
273
+ function withLangfusePrompt(prompt) {
274
+ return (opts) => ({
275
+ ...opts,
276
+ metadata: {
277
+ ...opts.metadata,
278
+ langfuse: {
279
+ ...opts.metadata?.langfuse,
280
+ prompt: {
281
+ ...prompt.name !== void 0 && { name: prompt.name },
282
+ ...prompt.version !== void 0 && { version: prompt.version },
283
+ ...prompt.id !== void 0 && { id: prompt.id }
284
+ }
285
+ }
286
+ }
287
+ });
288
+ }
289
+
290
+ export { LangfuseExporter, withLangfusePrompt };
315
291
  //# sourceMappingURL=index.js.map
316
292
  //# sourceMappingURL=index.js.map