@mastra/langfuse 0.0.0-fix-backport-setserver-20251201151948 → 0.0.0-fix-request-context-as-query-key-20251209093005
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.
- package/CHANGELOG.md +153 -48
- package/README.md +1 -1
- package/dist/helpers.d.ts +79 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/index.cjs +97 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +95 -11
- package/dist/index.js.map +1 -1
- package/dist/{ai-tracing.d.ts → tracing.d.ts} +13 -6
- package/dist/tracing.d.ts.map +1 -0
- package/package.json +14 -15
- package/dist/ai-tracing.d.ts.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var observability$1 = require('@mastra/core/observability');
|
|
4
|
+
var utils = require('@mastra/core/utils');
|
|
5
|
+
var observability = require('@mastra/observability');
|
|
4
6
|
var langfuse = require('langfuse');
|
|
5
7
|
|
|
6
|
-
// src/
|
|
7
|
-
var LangfuseExporter = class extends
|
|
8
|
+
// src/tracing.ts
|
|
9
|
+
var LangfuseExporter = class extends observability.BaseExporter {
|
|
8
10
|
name = "langfuse";
|
|
9
11
|
client;
|
|
10
12
|
realtime;
|
|
@@ -26,7 +28,7 @@ var LangfuseExporter = class extends aiTracing.BaseExporter {
|
|
|
26
28
|
...config.options
|
|
27
29
|
});
|
|
28
30
|
}
|
|
29
|
-
async
|
|
31
|
+
async _exportTracingEvent(event) {
|
|
30
32
|
if (event.exportedSpan.isEvent) {
|
|
31
33
|
await this.handleEventSpan(event.exportedSpan);
|
|
32
34
|
return;
|
|
@@ -55,12 +57,19 @@ var LangfuseExporter = class extends aiTracing.BaseExporter {
|
|
|
55
57
|
if (!traceData) {
|
|
56
58
|
return;
|
|
57
59
|
}
|
|
60
|
+
if (!span.isRootSpan) {
|
|
61
|
+
const langfuseData = span.metadata?.langfuse;
|
|
62
|
+
traceData.spanMetadata.set(span.id, {
|
|
63
|
+
parentSpanId: span.parentSpanId,
|
|
64
|
+
langfusePrompt: langfuseData?.prompt
|
|
65
|
+
});
|
|
66
|
+
}
|
|
58
67
|
const langfuseParent = this.getLangfuseParent({ traceData, span, method });
|
|
59
68
|
if (!langfuseParent) {
|
|
60
69
|
return;
|
|
61
70
|
}
|
|
62
|
-
const payload = this.buildSpanPayload(span, true);
|
|
63
|
-
const langfuseSpan = span.type ===
|
|
71
|
+
const payload = this.buildSpanPayload(span, true, traceData);
|
|
72
|
+
const langfuseSpan = span.type === observability$1.SpanType.MODEL_GENERATION ? langfuseParent.generation(payload) : langfuseParent.span(payload);
|
|
64
73
|
traceData.spans.set(span.id, langfuseSpan);
|
|
65
74
|
traceData.activeSpans.add(span.id);
|
|
66
75
|
}
|
|
@@ -90,7 +99,7 @@ var LangfuseExporter = class extends aiTracing.BaseExporter {
|
|
|
90
99
|
});
|
|
91
100
|
return;
|
|
92
101
|
}
|
|
93
|
-
langfuseSpan.update(this.buildSpanPayload(span, false));
|
|
102
|
+
langfuseSpan.update(this.buildSpanPayload(span, false, traceData));
|
|
94
103
|
if (isEnd) {
|
|
95
104
|
traceData.activeSpans.delete(span.id);
|
|
96
105
|
if (span.isRootSpan) {
|
|
@@ -120,7 +129,7 @@ var LangfuseExporter = class extends aiTracing.BaseExporter {
|
|
|
120
129
|
if (!langfuseParent) {
|
|
121
130
|
return;
|
|
122
131
|
}
|
|
123
|
-
const payload = this.buildSpanPayload(span, true);
|
|
132
|
+
const payload = this.buildSpanPayload(span, true, traceData);
|
|
124
133
|
const langfuseEvent = langfuseParent.event(payload);
|
|
125
134
|
traceData.events.set(span.id, langfuseEvent);
|
|
126
135
|
if (!span.endTime) {
|
|
@@ -128,10 +137,25 @@ var LangfuseExporter = class extends aiTracing.BaseExporter {
|
|
|
128
137
|
}
|
|
129
138
|
}
|
|
130
139
|
initTrace(span) {
|
|
140
|
+
if (this.traceMap.has(span.traceId)) {
|
|
141
|
+
this.logger.debug("Langfuse exporter: Reusing existing trace from local map", {
|
|
142
|
+
traceId: span.traceId,
|
|
143
|
+
spanId: span.id,
|
|
144
|
+
spanName: span.name
|
|
145
|
+
});
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
131
148
|
const trace = this.client.trace(this.buildTracePayload(span));
|
|
149
|
+
const langfuseData = span.metadata?.langfuse;
|
|
150
|
+
const spanMetadata = /* @__PURE__ */ new Map();
|
|
151
|
+
spanMetadata.set(span.id, {
|
|
152
|
+
parentSpanId: void 0,
|
|
153
|
+
langfusePrompt: langfuseData?.prompt
|
|
154
|
+
});
|
|
132
155
|
this.traceMap.set(span.traceId, {
|
|
133
156
|
trace,
|
|
134
157
|
spans: /* @__PURE__ */ new Map(),
|
|
158
|
+
spanMetadata,
|
|
135
159
|
events: /* @__PURE__ */ new Map(),
|
|
136
160
|
activeSpans: /* @__PURE__ */ new Set(),
|
|
137
161
|
rootSpanId: span.id
|
|
@@ -183,6 +207,7 @@ var LangfuseExporter = class extends aiTracing.BaseExporter {
|
|
|
183
207
|
if (userId) payload.userId = userId;
|
|
184
208
|
if (sessionId) payload.sessionId = sessionId;
|
|
185
209
|
if (span.input) payload.input = span.input;
|
|
210
|
+
if (span.tags?.length) payload.tags = span.tags;
|
|
186
211
|
payload.metadata = {
|
|
187
212
|
spanType: span.type,
|
|
188
213
|
...span.attributes,
|
|
@@ -232,7 +257,23 @@ var LangfuseExporter = class extends aiTracing.BaseExporter {
|
|
|
232
257
|
}
|
|
233
258
|
return Object.keys(normalized).length > 0 ? normalized : void 0;
|
|
234
259
|
}
|
|
235
|
-
|
|
260
|
+
/**
|
|
261
|
+
* Look up the Langfuse prompt from the closest parent span that has one.
|
|
262
|
+
* This enables prompt inheritance for MODEL_GENERATION spans when the prompt
|
|
263
|
+
* is set on a parent span (e.g., AGENT_RUN) rather than directly on the generation.
|
|
264
|
+
*/
|
|
265
|
+
findParentLangfusePrompt(traceData, span) {
|
|
266
|
+
let currentSpanId = span.parentSpanId;
|
|
267
|
+
while (currentSpanId) {
|
|
268
|
+
const parentMetadata = traceData.spanMetadata.get(currentSpanId);
|
|
269
|
+
if (parentMetadata?.langfusePrompt) {
|
|
270
|
+
return parentMetadata.langfusePrompt;
|
|
271
|
+
}
|
|
272
|
+
currentSpanId = parentMetadata?.parentSpanId;
|
|
273
|
+
}
|
|
274
|
+
return void 0;
|
|
275
|
+
}
|
|
276
|
+
buildSpanPayload(span, isCreate, traceData) {
|
|
236
277
|
const payload = {};
|
|
237
278
|
if (isCreate) {
|
|
238
279
|
payload.id = span.id;
|
|
@@ -243,8 +284,18 @@ var LangfuseExporter = class extends aiTracing.BaseExporter {
|
|
|
243
284
|
if (span.output !== void 0) payload.output = span.output;
|
|
244
285
|
if (span.endTime !== void 0) payload.endTime = span.endTime;
|
|
245
286
|
const attributes = span.attributes ?? {};
|
|
287
|
+
const resolvedTraceData = traceData ?? this.traceMap.get(span.traceId);
|
|
288
|
+
let inheritedLangfusePrompt;
|
|
289
|
+
if (span.type === observability$1.SpanType.MODEL_GENERATION && !span.metadata?.langfuse && resolvedTraceData) {
|
|
290
|
+
inheritedLangfusePrompt = this.findParentLangfusePrompt(resolvedTraceData, span);
|
|
291
|
+
}
|
|
292
|
+
const metadata = {
|
|
293
|
+
...span.metadata,
|
|
294
|
+
...inheritedLangfusePrompt ? { langfuse: { prompt: inheritedLangfusePrompt } } : {}
|
|
295
|
+
};
|
|
246
296
|
const attributesToOmit = [];
|
|
247
|
-
|
|
297
|
+
const metadataToOmit = [];
|
|
298
|
+
if (span.type === observability$1.SpanType.MODEL_GENERATION) {
|
|
248
299
|
const modelAttr = attributes;
|
|
249
300
|
if (modelAttr.model !== void 0) {
|
|
250
301
|
payload.model = modelAttr.model;
|
|
@@ -261,11 +312,26 @@ var LangfuseExporter = class extends aiTracing.BaseExporter {
|
|
|
261
312
|
payload.modelParameters = modelAttr.parameters;
|
|
262
313
|
attributesToOmit.push("parameters");
|
|
263
314
|
}
|
|
315
|
+
const langfuseData = metadata.langfuse;
|
|
316
|
+
const promptData = langfuseData?.prompt;
|
|
317
|
+
const hasNameAndVersion = promptData?.name !== void 0 && promptData?.version !== void 0;
|
|
318
|
+
const hasId = promptData?.id !== void 0;
|
|
319
|
+
if (hasNameAndVersion || hasId) {
|
|
320
|
+
payload.prompt = {};
|
|
321
|
+
if (promptData?.name !== void 0) payload.prompt.name = promptData.name;
|
|
322
|
+
if (promptData?.version !== void 0) payload.prompt.version = promptData.version;
|
|
323
|
+
if (promptData?.id !== void 0) payload.prompt.id = promptData.id;
|
|
324
|
+
metadataToOmit.push("langfuse");
|
|
325
|
+
}
|
|
326
|
+
if (modelAttr.completionStartTime !== void 0) {
|
|
327
|
+
payload.completionStartTime = modelAttr.completionStartTime;
|
|
328
|
+
attributesToOmit.push("completionStartTime");
|
|
329
|
+
}
|
|
264
330
|
}
|
|
265
331
|
payload.metadata = {
|
|
266
332
|
spanType: span.type,
|
|
267
|
-
...
|
|
268
|
-
...
|
|
333
|
+
...utils.omitKeys(attributes, attributesToOmit),
|
|
334
|
+
...utils.omitKeys(metadata, metadataToOmit)
|
|
269
335
|
};
|
|
270
336
|
if (span.errorInfo) {
|
|
271
337
|
payload.level = "ERROR";
|
|
@@ -311,6 +377,25 @@ var LangfuseExporter = class extends aiTracing.BaseExporter {
|
|
|
311
377
|
}
|
|
312
378
|
};
|
|
313
379
|
|
|
380
|
+
// src/helpers.ts
|
|
381
|
+
function withLangfusePrompt(prompt) {
|
|
382
|
+
return (opts) => ({
|
|
383
|
+
...opts,
|
|
384
|
+
metadata: {
|
|
385
|
+
...opts.metadata,
|
|
386
|
+
langfuse: {
|
|
387
|
+
...opts.metadata?.langfuse,
|
|
388
|
+
prompt: {
|
|
389
|
+
...prompt.name !== void 0 && { name: prompt.name },
|
|
390
|
+
...prompt.version !== void 0 && { version: prompt.version },
|
|
391
|
+
...prompt.id !== void 0 && { id: prompt.id }
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
314
398
|
exports.LangfuseExporter = LangfuseExporter;
|
|
399
|
+
exports.withLangfusePrompt = withLangfusePrompt;
|
|
315
400
|
//# sourceMappingURL=index.cjs.map
|
|
316
401
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ai-tracing.ts"],"names":["BaseExporter","Langfuse","AISpanType","omitKeys"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Langfuse Exporter for Mastra AI Tracing\n *\n * This exporter sends tracing data to Langfuse for AI observability.\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 {\n AITracingEvent,\n AnyExportedAISpan,\n ModelGenerationAttributes,\n BaseExporterConfig,\n} from '@mastra/core/ai-tracing';\nimport { AISpanType, omitKeys, BaseExporter } from '@mastra/core/ai-tracing';\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 _exportEvent(event: AITracingEvent): 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: AnyExportedAISpan): 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 === AISpanType.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: AnyExportedAISpan, 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: AnyExportedAISpan): 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: AnyExportedAISpan): 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: AnyExportedAISpan; 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: AnyExportedAISpan;\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: AnyExportedAISpan): 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: AnyExportedAISpan, 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 === AISpanType.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/tracing.ts","../src/helpers.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 LangfusePromptData = { name?: string; version?: number; id?: string };\n\ntype SpanMetadata = {\n parentSpanId?: string;\n langfusePrompt?: LangfusePromptData;\n};\n\ntype TraceData = {\n trace: LangfuseTraceClient; // Langfuse trace object\n spans: Map<string, LangfuseSpanClient | LangfuseGenerationClient>; // Maps span.id to Langfuse span/generation\n spanMetadata: Map<string, SpanMetadata>; // Maps span.id to span metadata for prompt inheritance\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 // Store span metadata for prompt inheritance lookup (for non-root spans)\n if (!span.isRootSpan) {\n const langfuseData = span.metadata?.langfuse as { prompt?: LangfusePromptData } | undefined;\n traceData.spanMetadata.set(span.id, {\n parentSpanId: span.parentSpanId,\n langfusePrompt: langfuseData?.prompt,\n });\n }\n\n const langfuseParent = this.getLangfuseParent({ traceData, span, method });\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 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, traceData));\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, traceData);\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 // Check if trace already exists in our local traceMap\n // This allows multiple root spans (e.g., from multiple agent.stream calls)\n // to be grouped under the same Langfuse trace\n if (this.traceMap.has(span.traceId)) {\n this.logger.debug('Langfuse exporter: Reusing existing trace from local map', {\n traceId: span.traceId,\n spanId: span.id,\n spanName: span.name,\n });\n return; // Reuse existing trace\n }\n\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 const trace = this.client.trace(this.buildTracePayload(span));\n\n // Extract langfuse prompt data from root span\n const langfuseData = span.metadata?.langfuse as { prompt?: LangfusePromptData } | undefined;\n const spanMetadata = new Map<string, SpanMetadata>();\n\n // Store root span metadata for prompt inheritance\n spanMetadata.set(span.id, {\n parentSpanId: undefined,\n langfusePrompt: langfuseData?.prompt,\n });\n\n this.traceMap.set(span.traceId, {\n trace,\n spans: new Map(),\n spanMetadata,\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 // 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 * 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 /**\n * Look up the Langfuse prompt from the closest parent 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 */\n private findParentLangfusePrompt(traceData: TraceData, span: AnyExportedSpan): LangfusePromptData | undefined {\n let currentSpanId = span.parentSpanId;\n\n while (currentSpanId) {\n const parentMetadata = traceData.spanMetadata.get(currentSpanId);\n if (parentMetadata?.langfusePrompt) {\n return parentMetadata.langfusePrompt;\n }\n currentSpanId = parentMetadata?.parentSpanId;\n }\n\n return undefined;\n }\n\n private buildSpanPayload(span: AnyExportedSpan, isCreate: boolean, traceData?: TraceData): 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 // For MODEL_GENERATION spans without langfuse metadata, look up the closest\n // parent span that has langfuse prompt data. 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 const resolvedTraceData = traceData ?? this.traceMap.get(span.traceId);\n let inheritedLangfusePrompt: LangfusePromptData | undefined;\n\n if (span.type === SpanType.MODEL_GENERATION && !span.metadata?.langfuse && resolvedTraceData) {\n inheritedLangfusePrompt = this.findParentLangfusePrompt(resolvedTraceData, span);\n }\n\n const metadata: Record<string, any> = {\n ...span.metadata,\n ...(inheritedLangfusePrompt ? { langfuse: { prompt: inheritedLangfusePrompt } } : {}),\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 // 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 // Handle Langfuse prompt linking\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 langfuseData = metadata.langfuse as\n | { prompt?: { name?: string; version?: number; id?: string } }\n | undefined;\n const promptData = langfuseData?.prompt;\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 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","/**\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
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Langfuse Observability Provider for Mastra
|
|
3
3
|
*
|
|
4
4
|
* This package provides Langfuse-specific observability features for Mastra applications.
|
|
5
|
-
* Currently includes
|
|
5
|
+
* Currently includes tracing support with plans for additional observability features.
|
|
6
6
|
*/
|
|
7
|
-
export * from './
|
|
7
|
+
export * from './tracing.js';
|
|
8
|
+
export * from './helpers.js';
|
|
8
9
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,cAAc,cAAc,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,7 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SpanType } from '@mastra/core/observability';
|
|
2
|
+
import { omitKeys } from '@mastra/core/utils';
|
|
3
|
+
import { BaseExporter } from '@mastra/observability';
|
|
2
4
|
import { Langfuse } from 'langfuse';
|
|
3
5
|
|
|
4
|
-
// src/
|
|
6
|
+
// src/tracing.ts
|
|
5
7
|
var LangfuseExporter = class extends BaseExporter {
|
|
6
8
|
name = "langfuse";
|
|
7
9
|
client;
|
|
@@ -24,7 +26,7 @@ var LangfuseExporter = class extends BaseExporter {
|
|
|
24
26
|
...config.options
|
|
25
27
|
});
|
|
26
28
|
}
|
|
27
|
-
async
|
|
29
|
+
async _exportTracingEvent(event) {
|
|
28
30
|
if (event.exportedSpan.isEvent) {
|
|
29
31
|
await this.handleEventSpan(event.exportedSpan);
|
|
30
32
|
return;
|
|
@@ -53,12 +55,19 @@ var LangfuseExporter = class extends BaseExporter {
|
|
|
53
55
|
if (!traceData) {
|
|
54
56
|
return;
|
|
55
57
|
}
|
|
58
|
+
if (!span.isRootSpan) {
|
|
59
|
+
const langfuseData = span.metadata?.langfuse;
|
|
60
|
+
traceData.spanMetadata.set(span.id, {
|
|
61
|
+
parentSpanId: span.parentSpanId,
|
|
62
|
+
langfusePrompt: langfuseData?.prompt
|
|
63
|
+
});
|
|
64
|
+
}
|
|
56
65
|
const langfuseParent = this.getLangfuseParent({ traceData, span, method });
|
|
57
66
|
if (!langfuseParent) {
|
|
58
67
|
return;
|
|
59
68
|
}
|
|
60
|
-
const payload = this.buildSpanPayload(span, true);
|
|
61
|
-
const langfuseSpan = span.type ===
|
|
69
|
+
const payload = this.buildSpanPayload(span, true, traceData);
|
|
70
|
+
const langfuseSpan = span.type === SpanType.MODEL_GENERATION ? langfuseParent.generation(payload) : langfuseParent.span(payload);
|
|
62
71
|
traceData.spans.set(span.id, langfuseSpan);
|
|
63
72
|
traceData.activeSpans.add(span.id);
|
|
64
73
|
}
|
|
@@ -88,7 +97,7 @@ var LangfuseExporter = class extends BaseExporter {
|
|
|
88
97
|
});
|
|
89
98
|
return;
|
|
90
99
|
}
|
|
91
|
-
langfuseSpan.update(this.buildSpanPayload(span, false));
|
|
100
|
+
langfuseSpan.update(this.buildSpanPayload(span, false, traceData));
|
|
92
101
|
if (isEnd) {
|
|
93
102
|
traceData.activeSpans.delete(span.id);
|
|
94
103
|
if (span.isRootSpan) {
|
|
@@ -118,7 +127,7 @@ var LangfuseExporter = class extends BaseExporter {
|
|
|
118
127
|
if (!langfuseParent) {
|
|
119
128
|
return;
|
|
120
129
|
}
|
|
121
|
-
const payload = this.buildSpanPayload(span, true);
|
|
130
|
+
const payload = this.buildSpanPayload(span, true, traceData);
|
|
122
131
|
const langfuseEvent = langfuseParent.event(payload);
|
|
123
132
|
traceData.events.set(span.id, langfuseEvent);
|
|
124
133
|
if (!span.endTime) {
|
|
@@ -126,10 +135,25 @@ var LangfuseExporter = class extends BaseExporter {
|
|
|
126
135
|
}
|
|
127
136
|
}
|
|
128
137
|
initTrace(span) {
|
|
138
|
+
if (this.traceMap.has(span.traceId)) {
|
|
139
|
+
this.logger.debug("Langfuse exporter: Reusing existing trace from local map", {
|
|
140
|
+
traceId: span.traceId,
|
|
141
|
+
spanId: span.id,
|
|
142
|
+
spanName: span.name
|
|
143
|
+
});
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
129
146
|
const trace = this.client.trace(this.buildTracePayload(span));
|
|
147
|
+
const langfuseData = span.metadata?.langfuse;
|
|
148
|
+
const spanMetadata = /* @__PURE__ */ new Map();
|
|
149
|
+
spanMetadata.set(span.id, {
|
|
150
|
+
parentSpanId: void 0,
|
|
151
|
+
langfusePrompt: langfuseData?.prompt
|
|
152
|
+
});
|
|
130
153
|
this.traceMap.set(span.traceId, {
|
|
131
154
|
trace,
|
|
132
155
|
spans: /* @__PURE__ */ new Map(),
|
|
156
|
+
spanMetadata,
|
|
133
157
|
events: /* @__PURE__ */ new Map(),
|
|
134
158
|
activeSpans: /* @__PURE__ */ new Set(),
|
|
135
159
|
rootSpanId: span.id
|
|
@@ -181,6 +205,7 @@ var LangfuseExporter = class extends BaseExporter {
|
|
|
181
205
|
if (userId) payload.userId = userId;
|
|
182
206
|
if (sessionId) payload.sessionId = sessionId;
|
|
183
207
|
if (span.input) payload.input = span.input;
|
|
208
|
+
if (span.tags?.length) payload.tags = span.tags;
|
|
184
209
|
payload.metadata = {
|
|
185
210
|
spanType: span.type,
|
|
186
211
|
...span.attributes,
|
|
@@ -230,7 +255,23 @@ var LangfuseExporter = class extends BaseExporter {
|
|
|
230
255
|
}
|
|
231
256
|
return Object.keys(normalized).length > 0 ? normalized : void 0;
|
|
232
257
|
}
|
|
233
|
-
|
|
258
|
+
/**
|
|
259
|
+
* Look up the Langfuse prompt from the closest parent span that has one.
|
|
260
|
+
* This enables prompt inheritance for MODEL_GENERATION spans when the prompt
|
|
261
|
+
* is set on a parent span (e.g., AGENT_RUN) rather than directly on the generation.
|
|
262
|
+
*/
|
|
263
|
+
findParentLangfusePrompt(traceData, span) {
|
|
264
|
+
let currentSpanId = span.parentSpanId;
|
|
265
|
+
while (currentSpanId) {
|
|
266
|
+
const parentMetadata = traceData.spanMetadata.get(currentSpanId);
|
|
267
|
+
if (parentMetadata?.langfusePrompt) {
|
|
268
|
+
return parentMetadata.langfusePrompt;
|
|
269
|
+
}
|
|
270
|
+
currentSpanId = parentMetadata?.parentSpanId;
|
|
271
|
+
}
|
|
272
|
+
return void 0;
|
|
273
|
+
}
|
|
274
|
+
buildSpanPayload(span, isCreate, traceData) {
|
|
234
275
|
const payload = {};
|
|
235
276
|
if (isCreate) {
|
|
236
277
|
payload.id = span.id;
|
|
@@ -241,8 +282,18 @@ var LangfuseExporter = class extends BaseExporter {
|
|
|
241
282
|
if (span.output !== void 0) payload.output = span.output;
|
|
242
283
|
if (span.endTime !== void 0) payload.endTime = span.endTime;
|
|
243
284
|
const attributes = span.attributes ?? {};
|
|
285
|
+
const resolvedTraceData = traceData ?? this.traceMap.get(span.traceId);
|
|
286
|
+
let inheritedLangfusePrompt;
|
|
287
|
+
if (span.type === SpanType.MODEL_GENERATION && !span.metadata?.langfuse && resolvedTraceData) {
|
|
288
|
+
inheritedLangfusePrompt = this.findParentLangfusePrompt(resolvedTraceData, span);
|
|
289
|
+
}
|
|
290
|
+
const metadata = {
|
|
291
|
+
...span.metadata,
|
|
292
|
+
...inheritedLangfusePrompt ? { langfuse: { prompt: inheritedLangfusePrompt } } : {}
|
|
293
|
+
};
|
|
244
294
|
const attributesToOmit = [];
|
|
245
|
-
|
|
295
|
+
const metadataToOmit = [];
|
|
296
|
+
if (span.type === SpanType.MODEL_GENERATION) {
|
|
246
297
|
const modelAttr = attributes;
|
|
247
298
|
if (modelAttr.model !== void 0) {
|
|
248
299
|
payload.model = modelAttr.model;
|
|
@@ -259,11 +310,26 @@ var LangfuseExporter = class extends BaseExporter {
|
|
|
259
310
|
payload.modelParameters = modelAttr.parameters;
|
|
260
311
|
attributesToOmit.push("parameters");
|
|
261
312
|
}
|
|
313
|
+
const langfuseData = metadata.langfuse;
|
|
314
|
+
const promptData = langfuseData?.prompt;
|
|
315
|
+
const hasNameAndVersion = promptData?.name !== void 0 && promptData?.version !== void 0;
|
|
316
|
+
const hasId = promptData?.id !== void 0;
|
|
317
|
+
if (hasNameAndVersion || hasId) {
|
|
318
|
+
payload.prompt = {};
|
|
319
|
+
if (promptData?.name !== void 0) payload.prompt.name = promptData.name;
|
|
320
|
+
if (promptData?.version !== void 0) payload.prompt.version = promptData.version;
|
|
321
|
+
if (promptData?.id !== void 0) payload.prompt.id = promptData.id;
|
|
322
|
+
metadataToOmit.push("langfuse");
|
|
323
|
+
}
|
|
324
|
+
if (modelAttr.completionStartTime !== void 0) {
|
|
325
|
+
payload.completionStartTime = modelAttr.completionStartTime;
|
|
326
|
+
attributesToOmit.push("completionStartTime");
|
|
327
|
+
}
|
|
262
328
|
}
|
|
263
329
|
payload.metadata = {
|
|
264
330
|
spanType: span.type,
|
|
265
331
|
...omitKeys(attributes, attributesToOmit),
|
|
266
|
-
...
|
|
332
|
+
...omitKeys(metadata, metadataToOmit)
|
|
267
333
|
};
|
|
268
334
|
if (span.errorInfo) {
|
|
269
335
|
payload.level = "ERROR";
|
|
@@ -309,6 +375,24 @@ var LangfuseExporter = class extends BaseExporter {
|
|
|
309
375
|
}
|
|
310
376
|
};
|
|
311
377
|
|
|
312
|
-
|
|
378
|
+
// src/helpers.ts
|
|
379
|
+
function withLangfusePrompt(prompt) {
|
|
380
|
+
return (opts) => ({
|
|
381
|
+
...opts,
|
|
382
|
+
metadata: {
|
|
383
|
+
...opts.metadata,
|
|
384
|
+
langfuse: {
|
|
385
|
+
...opts.metadata?.langfuse,
|
|
386
|
+
prompt: {
|
|
387
|
+
...prompt.name !== void 0 && { name: prompt.name },
|
|
388
|
+
...prompt.version !== void 0 && { version: prompt.version },
|
|
389
|
+
...prompt.id !== void 0 && { id: prompt.id }
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export { LangfuseExporter, withLangfusePrompt };
|
|
313
397
|
//# sourceMappingURL=index.js.map
|
|
314
398
|
//# sourceMappingURL=index.js.map
|