@mastra/langfuse 0.0.0-error-handler-fix-20251020202607 → 0.0.0-execa-dynamic-import-20260304221256

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/LICENSE.md CHANGED
@@ -1,3 +1,18 @@
1
+ Portions of this software are licensed as follows:
2
+
3
+ - All content that resides under any directory named "ee/" within this
4
+ repository, including but not limited to:
5
+ - `packages/core/src/auth/ee/`
6
+ - `packages/server/src/server/auth/ee/`
7
+ is licensed under the license defined in `ee/LICENSE`.
8
+
9
+ - All third-party components incorporated into the Mastra Software are
10
+ licensed under the original license provided by the owner of the
11
+ applicable component.
12
+
13
+ - Content outside of the above-mentioned directories or restrictions is
14
+ available under the "Apache License 2.0" as defined below.
15
+
1
16
  # Apache License 2.0
2
17
 
3
18
  Copyright (c) 2025 Kepler Software, Inc.
package/README.md CHANGED
@@ -10,22 +10,54 @@ npm install @mastra/langfuse
10
10
 
11
11
  ## Usage
12
12
 
13
+ ### Zero-Config Setup
14
+
15
+ The exporter automatically reads credentials from environment variables:
16
+
17
+ ```bash
18
+ # Required
19
+ LANGFUSE_PUBLIC_KEY=pk-lf-...
20
+ LANGFUSE_SECRET_KEY=sk-lf-...
21
+
22
+ # Optional - defaults to Langfuse cloud
23
+ LANGFUSE_BASE_URL=https://cloud.langfuse.com
24
+ ```
25
+
26
+ ```typescript
27
+ import { LangfuseExporter } from '@mastra/langfuse';
28
+
29
+ const mastra = new Mastra({
30
+ ...,
31
+ observability: {
32
+ configs: {
33
+ langfuse: {
34
+ serviceName: 'my-service',
35
+ exporters: [new LangfuseExporter()],
36
+ },
37
+ },
38
+ },
39
+ });
40
+ ```
41
+
42
+ ### Explicit Configuration
43
+
44
+ You can also pass credentials directly:
45
+
13
46
  ```typescript
14
47
  import { LangfuseExporter } from '@mastra/langfuse';
15
48
 
16
- // Use with Mastra
17
49
  const mastra = new Mastra({
18
50
  ...,
19
51
  observability: {
20
52
  configs: {
21
53
  langfuse: {
22
- serviceName: 'service',
54
+ serviceName: 'my-service',
23
55
  exporters: [
24
56
  new LangfuseExporter({
25
- publicKey: process.env.LANGFUSE_PUBLIC_KEY,
26
- secretKey: process.env.LANGFUSE_SECRET_KEY,
27
- baseUrl: process.env.LANGFUSE_BASE_URL, // Optional - defaults to Langfuse cloud
28
- realtime: true,
57
+ publicKey: 'pk-lf-...',
58
+ secretKey: 'sk-lf-...',
59
+ baseUrl: 'https://cloud.langfuse.com', // Optional
60
+ realtime: true, // Optional - flush after each event
29
61
  }),
30
62
  ],
31
63
  },
@@ -34,12 +66,22 @@ const mastra = new Mastra({
34
66
  });
35
67
  ```
36
68
 
69
+ ### Configuration Options
70
+
71
+ | Option | Type | Description |
72
+ | ----------- | --------- | ---------------------------------------------------------------------------- |
73
+ | `publicKey` | `string` | Langfuse public key. Defaults to `LANGFUSE_PUBLIC_KEY` env var |
74
+ | `secretKey` | `string` | Langfuse secret key. Defaults to `LANGFUSE_SECRET_KEY` env var |
75
+ | `baseUrl` | `string` | Langfuse host URL. Defaults to `LANGFUSE_BASE_URL` env var or Langfuse cloud |
76
+ | `realtime` | `boolean` | Flush after each event for immediate visibility. Defaults to `false` |
77
+ | `options` | `object` | Additional options to pass to the Langfuse client |
78
+
37
79
  ## Features
38
80
 
39
- ### AI Tracing
81
+ ### Tracing
40
82
 
41
83
  - **Automatic span mapping**: Root spans become Langfuse traces
42
- - **LLM generation support**: `LLM_GENERATION` spans become Langfuse generations with token usage
84
+ - **Model generation support**: `MODEL_GENERATION` spans become Langfuse generations with token usage
43
85
  - **Type-specific metadata**: Extracts relevant metadata for each span type (agents, tools, workflows)
44
86
  - **Error tracking**: Automatic error status and message tracking
45
87
  - **Hierarchical traces**: Maintains parent-child relationships
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Langfuse Tracing Options Helpers
3
+ *
4
+ * These helpers integrate with the `buildTracingOptions` pattern from
5
+ * `@mastra/observability` to add Langfuse-specific tracing features.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { buildTracingOptions } from '@mastra/observability';
10
+ * import { withLangfusePrompt } from '@mastra/langfuse';
11
+ *
12
+ * const prompt = await langfuse.getPrompt('my-prompt');
13
+ *
14
+ * const agent = new Agent({
15
+ * defaultGenerateOptions: {
16
+ * tracingOptions: buildTracingOptions(withLangfusePrompt(prompt)),
17
+ * },
18
+ * });
19
+ * ```
20
+ */
21
+ import type { TracingOptionsUpdater } from '@mastra/observability';
22
+ /**
23
+ * Langfuse prompt input - accepts either a Langfuse SDK prompt object
24
+ * or manual fields.
25
+ */
26
+ export interface LangfusePromptInput {
27
+ /** Prompt name */
28
+ name?: string;
29
+ /** Prompt version */
30
+ version?: number;
31
+ /** Prompt UUID */
32
+ id?: string;
33
+ }
34
+ /**
35
+ * Adds Langfuse prompt metadata to the tracing options
36
+ * to enable Langfuse Prompt Tracing.
37
+ *
38
+ * The metadata is added under `metadata.langfuse.prompt` and includes:
39
+ * - `name` - Prompt name
40
+ * - `version` - Prompt version
41
+ * - `id` - Prompt UUID
42
+ *
43
+ * All fields are deeply merged with any existing metadata.
44
+ *
45
+ * @param prompt - A Langfuse prompt object (from `langfuse.getPrompt()`) or manual fields
46
+ * @returns A TracingOptionsUpdater function for use with `buildTracingOptions`
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * import { buildTracingOptions } from '@mastra/observability';
51
+ * import { withLangfusePrompt } from '@mastra/langfuse';
52
+ * import { Langfuse } from 'langfuse';
53
+ *
54
+ * const langfuse = new Langfuse();
55
+ * const prompt = await langfuse.getPrompt('customer-support');
56
+ *
57
+ * // Use with buildTracingOptions
58
+ * const tracingOptions = buildTracingOptions(
59
+ * withLangfusePrompt(prompt),
60
+ * );
61
+ *
62
+ * // Or directly in agent config
63
+ * const agent = new Agent({
64
+ * name: 'support-agent',
65
+ * instructions: prompt.prompt,
66
+ * model: openai('gpt-4o'),
67
+ * defaultGenerateOptions: {
68
+ * tracingOptions: buildTracingOptions(withLangfusePrompt(prompt)),
69
+ * },
70
+ * });
71
+ *
72
+ * // Manual fields also work
73
+ * const tracingOptions = buildTracingOptions(
74
+ * withLangfusePrompt({ name: 'my-prompt', version: 1 }),
75
+ * );
76
+ * ```
77
+ */
78
+ export declare function withLangfusePrompt(prompt: LangfusePromptInput): TracingOptionsUpdater;
79
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAEnE;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,kBAAkB;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,mBAAmB,GAAG,qBAAqB,CAerF"}
package/dist/index.cjs CHANGED
@@ -1,183 +1,137 @@
1
1
  'use strict';
2
2
 
3
- var aiTracing = require('@mastra/core/ai-tracing');
4
- var logger = require('@mastra/core/logger');
3
+ var observability$1 = require('@mastra/core/observability');
4
+ var utils = require('@mastra/core/utils');
5
+ var observability = require('@mastra/observability');
5
6
  var langfuse = require('langfuse');
6
7
 
7
- // src/ai-tracing.ts
8
- var LangfuseExporter = class {
8
+ // src/tracing.ts
9
+
10
+ // src/metrics.ts
11
+ function formatUsageMetrics(usage) {
12
+ if (!usage) return {};
13
+ const metrics = {};
14
+ if (usage.inputTokens !== void 0) {
15
+ metrics.input = usage.inputTokens;
16
+ if (usage.inputDetails?.cacheRead !== void 0) {
17
+ metrics.cache_read_input_tokens = usage.inputDetails.cacheRead;
18
+ metrics.input -= metrics.cache_read_input_tokens;
19
+ }
20
+ if (usage.inputDetails?.cacheWrite !== void 0) {
21
+ metrics.cache_write_input_tokens = usage.inputDetails.cacheWrite;
22
+ metrics.input -= metrics.cache_write_input_tokens;
23
+ }
24
+ if (metrics.input < 0) metrics.input = 0;
25
+ }
26
+ if (usage.outputTokens !== void 0) {
27
+ metrics.output = usage.outputTokens;
28
+ }
29
+ if (usage.outputDetails?.reasoning !== void 0) {
30
+ metrics.reasoning = usage.outputDetails.reasoning;
31
+ }
32
+ if (metrics.input != null && metrics.output != null) {
33
+ metrics.total = metrics.input + metrics.output;
34
+ if (metrics.cache_read_input_tokens != null) {
35
+ metrics.total += metrics.cache_read_input_tokens;
36
+ }
37
+ if (metrics.cache_write_input_tokens != null) {
38
+ metrics.total += metrics.cache_write_input_tokens;
39
+ }
40
+ }
41
+ return metrics;
42
+ }
43
+
44
+ // src/tracing.ts
45
+ var LangfuseExporter = class extends observability.TrackingExporter {
9
46
  name = "langfuse";
10
- client;
11
- realtime;
12
- traceMap = /* @__PURE__ */ new Map();
13
- logger;
14
- constructor(config) {
15
- this.realtime = config.realtime ?? false;
16
- this.logger = new logger.ConsoleLogger({ level: config.logLevel ?? "warn" });
17
- if (!config.publicKey || !config.secretKey) {
18
- this.logger.error("LangfuseExporter: Missing required credentials, exporter will be disabled", {
19
- hasPublicKey: !!config.publicKey,
20
- hasSecretKey: !!config.secretKey
21
- });
22
- this.client = null;
47
+ #client;
48
+ #realtime;
49
+ constructor(config = {}) {
50
+ const publicKey = config.publicKey ?? process.env.LANGFUSE_PUBLIC_KEY;
51
+ const secretKey = config.secretKey ?? process.env.LANGFUSE_SECRET_KEY;
52
+ const baseUrl = config.baseUrl ?? process.env.LANGFUSE_BASE_URL;
53
+ super({
54
+ ...config,
55
+ publicKey,
56
+ secretKey,
57
+ baseUrl
58
+ });
59
+ this.#realtime = config.realtime ?? false;
60
+ if (!publicKey || !secretKey) {
61
+ const publicKeySource = config.publicKey ? "from config" : process.env.LANGFUSE_PUBLIC_KEY ? "from env" : "missing";
62
+ const secretKeySource = config.secretKey ? "from config" : process.env.LANGFUSE_SECRET_KEY ? "from env" : "missing";
63
+ this.setDisabled(
64
+ `Missing required credentials (publicKey: ${publicKeySource}, secretKey: ${secretKeySource}). Set LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY environment variables or pass them in config.`
65
+ );
23
66
  return;
24
67
  }
25
- this.client = new langfuse.Langfuse({
26
- publicKey: config.publicKey,
27
- secretKey: config.secretKey,
28
- baseUrl: config.baseUrl,
68
+ this.#client = new langfuse.Langfuse({
69
+ publicKey,
70
+ secretKey,
71
+ baseUrl,
29
72
  ...config.options
30
73
  });
31
74
  }
32
- async exportEvent(event) {
33
- if (!this.client) {
34
- return;
35
- }
36
- if (event.exportedSpan.isEvent) {
37
- await this.handleEventSpan(event.exportedSpan);
38
- return;
39
- }
40
- switch (event.type) {
41
- case "span_started":
42
- await this.handleSpanStarted(event.exportedSpan);
43
- break;
44
- case "span_updated":
45
- await this.handleSpanUpdateOrEnd(event.exportedSpan, false);
46
- break;
47
- case "span_ended":
48
- await this.handleSpanUpdateOrEnd(event.exportedSpan, true);
49
- break;
50
- }
51
- if (this.realtime) {
52
- await this.client.flushAsync();
75
+ async _postExportTracingEvent() {
76
+ if (this.#realtime) {
77
+ await this.#client?.flushAsync();
53
78
  }
54
79
  }
55
- async handleSpanStarted(span) {
56
- if (span.isRootSpan) {
57
- this.initTrace(span);
58
- }
59
- const method = "handleSpanStarted";
60
- const traceData = this.getTraceData({ span, method });
61
- if (!traceData) {
62
- return;
63
- }
64
- const langfuseParent = this.getLangfuseParent({ traceData, span, method });
80
+ async _buildRoot(args) {
81
+ const { span } = args;
82
+ return this.#client?.trace(this.buildTracePayload(span));
83
+ }
84
+ async _buildEvent(args) {
85
+ const { span, traceData } = args;
86
+ const langfuseParent = traceData.getParentOrRoot({ span });
65
87
  if (!langfuseParent) {
66
88
  return;
67
89
  }
68
- const payload = this.buildSpanPayload(span, true);
69
- const langfuseSpan = span.type === aiTracing.AISpanType.LLM_GENERATION ? langfuseParent.generation(payload) : langfuseParent.span(payload);
70
- traceData.spans.set(span.id, langfuseSpan);
71
- traceData.activeSpans.add(span.id);
90
+ const payload = this.buildSpanPayload(span, true, traceData);
91
+ return langfuseParent.event(payload);
72
92
  }
73
- async handleSpanUpdateOrEnd(span, isEnd) {
74
- const method = isEnd ? "handleSpanEnd" : "handleSpanUpdate";
75
- const traceData = this.getTraceData({ span, method });
76
- if (!traceData) {
77
- return;
78
- }
79
- const langfuseSpan = traceData.spans.get(span.id);
80
- if (!langfuseSpan) {
81
- if (isEnd && span.isEvent) {
82
- traceData.activeSpans.delete(span.id);
83
- if (traceData.activeSpans.size === 0) {
84
- this.traceMap.delete(span.traceId);
85
- }
86
- return;
87
- }
88
- this.logger.warn("Langfuse exporter: No Langfuse span found for span update/end", {
89
- traceId: span.traceId,
90
- spanId: span.id,
91
- spanName: span.name,
92
- spanType: span.type,
93
- isRootSpan: span.isRootSpan,
94
- parentSpanId: span.parentSpanId,
95
- method
96
- });
93
+ async _buildSpan(args) {
94
+ const { span, traceData } = args;
95
+ const langfuseParent = traceData.getParentOrRoot({ span });
96
+ if (!langfuseParent) {
97
97
  return;
98
98
  }
99
- langfuseSpan.update(this.buildSpanPayload(span, false));
100
- if (isEnd) {
101
- traceData.activeSpans.delete(span.id);
102
- if (span.isRootSpan) {
103
- traceData.trace.update({ output: span.output });
104
- }
105
- if (traceData.activeSpans.size === 0) {
106
- this.traceMap.delete(span.traceId);
107
- }
108
- }
99
+ const payload = this.buildSpanPayload(span, true, traceData);
100
+ const langfuseSpan = span.type === observability$1.SpanType.MODEL_GENERATION ? langfuseParent.generation(payload) : langfuseParent.span(payload);
101
+ this.logger.debug(`${this.name}: built span`, {
102
+ traceId: span.traceId,
103
+ spanId: payload.id,
104
+ method: "_buildSpan"
105
+ });
106
+ return langfuseSpan;
109
107
  }
110
- async handleEventSpan(span) {
111
- if (span.isRootSpan) {
112
- this.logger.debug("Langfuse exporter: Creating trace", {
108
+ async _updateSpan(args) {
109
+ const { span, traceData } = args;
110
+ const langfuseSpan = traceData.getSpan({ spanId: span.id });
111
+ if (langfuseSpan) {
112
+ this.logger.debug(`${this.name}: found span for update`, {
113
113
  traceId: span.traceId,
114
- spanId: span.id,
115
- spanName: span.name,
116
- method: "handleEventSpan"
114
+ spanId: langfuseSpan.id,
115
+ method: "_updateSpan"
117
116
  });
118
- this.initTrace(span);
119
- }
120
- const method = "handleEventSpan";
121
- const traceData = this.getTraceData({ span, method });
122
- if (!traceData) {
123
- return;
124
- }
125
- const langfuseParent = this.getLangfuseParent({ traceData, span, method });
126
- if (!langfuseParent) {
127
- return;
128
- }
129
- const payload = this.buildSpanPayload(span, true);
130
- const langfuseEvent = langfuseParent.event(payload);
131
- traceData.events.set(span.id, langfuseEvent);
132
- if (!span.endTime) {
133
- traceData.activeSpans.add(span.id);
117
+ const updatePayload = this.buildSpanPayload(span, false, traceData);
118
+ langfuseSpan.update(updatePayload);
134
119
  }
135
120
  }
136
- initTrace(span) {
137
- const trace = this.client.trace(this.buildTracePayload(span));
138
- this.traceMap.set(span.traceId, {
139
- trace,
140
- spans: /* @__PURE__ */ new Map(),
141
- events: /* @__PURE__ */ new Map(),
142
- activeSpans: /* @__PURE__ */ new Set(),
143
- rootSpanId: span.id
144
- });
145
- }
146
- getTraceData(options) {
147
- const { span, method } = options;
148
- if (this.traceMap.has(span.traceId)) {
149
- return this.traceMap.get(span.traceId);
121
+ async _finishSpan(args) {
122
+ const { span, traceData } = args;
123
+ const langfuseSpan = traceData.getSpan({ spanId: span.id });
124
+ langfuseSpan?.update(this.buildSpanPayload(span, false, traceData));
125
+ if (span.isRootSpan) {
126
+ const langfuseRoot = traceData.getRoot();
127
+ langfuseRoot?.update({ output: span.output });
150
128
  }
151
- this.logger.warn("Langfuse exporter: No trace data found for span", {
152
- traceId: span.traceId,
153
- spanId: span.id,
154
- spanName: span.name,
155
- spanType: span.type,
156
- isRootSpan: span.isRootSpan,
157
- parentSpanId: span.parentSpanId,
158
- method
159
- });
160
129
  }
161
- getLangfuseParent(options) {
162
- const { traceData, span, method } = options;
163
- const parentId = span.parentSpanId;
164
- if (!parentId) {
165
- return traceData.trace;
166
- }
167
- if (traceData.spans.has(parentId)) {
168
- return traceData.spans.get(parentId);
169
- }
170
- if (traceData.events.has(parentId)) {
171
- return traceData.events.get(parentId);
172
- }
173
- this.logger.warn("Langfuse exporter: No parent data found for span", {
174
- traceId: span.traceId,
175
- spanId: span.id,
176
- spanName: span.name,
177
- spanType: span.type,
178
- isRootSpan: span.isRootSpan,
179
- parentSpanId: span.parentSpanId,
180
- method
130
+ async _abortSpan(args) {
131
+ const { span, reason } = args;
132
+ span.end({
133
+ level: "ERROR",
134
+ statusMessage: reason.message
181
135
  });
182
136
  }
183
137
  buildTracePayload(span) {
@@ -189,6 +143,7 @@ var LangfuseExporter = class {
189
143
  if (userId) payload.userId = userId;
190
144
  if (sessionId) payload.sessionId = sessionId;
191
145
  if (span.input) payload.input = span.input;
146
+ if (span.tags?.length) payload.tags = span.tags;
192
147
  payload.metadata = {
193
148
  spanType: span.type,
194
149
  ...span.attributes,
@@ -197,81 +152,79 @@ var LangfuseExporter = class {
197
152
  return payload;
198
153
  }
199
154
  /**
200
- * Normalize usage data to handle both AI SDK v4 and v5 formats.
201
- *
202
- * AI SDK v4 uses: promptTokens, completionTokens
203
- * AI SDK v5 uses: inputTokens, outputTokens
204
- *
205
- * This function normalizes to a unified format that Langfuse can consume,
206
- * prioritizing v5 format while maintaining backward compatibility.
207
- *
208
- * @param usage - Token usage data from AI SDK (v4 or v5 format)
209
- * @returns Normalized usage object, or undefined if no usage data available
155
+ * Look up the Langfuse prompt from the closest span that has one.
156
+ * This enables prompt inheritance for MODEL_GENERATION spans when the prompt
157
+ * is set on a parent span (e.g., AGENT_RUN) rather than directly on the generation.
158
+ * This enables prompt linking when:
159
+ * - A workflow calls multiple agents, each with different prompts
160
+ * - Nested agents have different prompts
161
+ * - The prompt is set on AGENT_RUN but MODEL_GENERATION inherits it
210
162
  */
211
- normalizeUsage(usage) {
212
- if (!usage) return void 0;
213
- const normalized = {};
214
- const inputTokens = usage.inputTokens ?? usage.promptTokens;
215
- if (inputTokens !== void 0) {
216
- normalized.input = inputTokens;
217
- }
218
- const outputTokens = usage.outputTokens ?? usage.completionTokens;
219
- if (outputTokens !== void 0) {
220
- normalized.output = outputTokens;
221
- }
222
- if (usage.totalTokens !== void 0) {
223
- normalized.total = usage.totalTokens;
224
- } else if (normalized.input !== void 0 && normalized.output !== void 0) {
225
- normalized.total = normalized.input + normalized.output;
226
- }
227
- if (usage.reasoningTokens !== void 0) {
228
- normalized.reasoning = usage.reasoningTokens;
229
- }
230
- if (usage.cachedInputTokens !== void 0) {
231
- normalized.cachedInput = usage.cachedInputTokens;
232
- }
233
- if (usage.promptCacheHitTokens !== void 0) {
234
- normalized.promptCacheHit = usage.promptCacheHitTokens;
235
- }
236
- if (usage.promptCacheMissTokens !== void 0) {
237
- normalized.promptCacheMiss = usage.promptCacheMissTokens;
163
+ findLangfusePrompt(traceData, span) {
164
+ let currentSpanId = span.id;
165
+ while (currentSpanId) {
166
+ const providerMetadata = traceData.getMetadata({ spanId: currentSpanId });
167
+ if (providerMetadata?.prompt) {
168
+ this.logger.debug(`${this.name}: found prompt in provider metadata`, {
169
+ traceId: span.traceId,
170
+ spanId: span.id,
171
+ prompt: providerMetadata?.prompt
172
+ });
173
+ return providerMetadata.prompt;
174
+ }
175
+ currentSpanId = traceData.getParentId({ spanId: currentSpanId });
238
176
  }
239
- return Object.keys(normalized).length > 0 ? normalized : void 0;
177
+ return void 0;
240
178
  }
241
- buildSpanPayload(span, isCreate) {
179
+ buildSpanPayload(span, isCreate, traceData) {
242
180
  const payload = {};
243
181
  if (isCreate) {
244
182
  payload.id = span.id;
245
183
  payload.name = span.name;
246
184
  payload.startTime = span.startTime;
247
- if (span.input !== void 0) payload.input = span.input;
248
185
  }
186
+ if (span.input !== void 0) payload.input = span.input;
249
187
  if (span.output !== void 0) payload.output = span.output;
250
188
  if (span.endTime !== void 0) payload.endTime = span.endTime;
251
189
  const attributes = span.attributes ?? {};
190
+ const metadata = {
191
+ ...span.metadata
192
+ };
252
193
  const attributesToOmit = [];
253
- if (span.type === aiTracing.AISpanType.LLM_GENERATION) {
254
- const llmAttr = attributes;
255
- if (llmAttr.model !== void 0) {
256
- payload.model = llmAttr.model;
194
+ const metadataToOmit = [];
195
+ if (span.type === observability$1.SpanType.MODEL_GENERATION) {
196
+ const modelAttr = attributes;
197
+ if (modelAttr.model !== void 0) {
198
+ payload.model = modelAttr.model;
257
199
  attributesToOmit.push("model");
258
200
  }
259
- if (llmAttr.usage !== void 0) {
260
- const normalizedUsage = this.normalizeUsage(llmAttr.usage);
261
- if (normalizedUsage) {
262
- payload.usage = normalizedUsage;
263
- }
201
+ if (modelAttr.usage !== void 0) {
202
+ payload.usageDetails = formatUsageMetrics(modelAttr.usage);
264
203
  attributesToOmit.push("usage");
265
204
  }
266
- if (llmAttr.parameters !== void 0) {
267
- payload.modelParameters = llmAttr.parameters;
205
+ if (modelAttr.parameters !== void 0) {
206
+ payload.modelParameters = modelAttr.parameters;
268
207
  attributesToOmit.push("parameters");
269
208
  }
209
+ const promptData = this.findLangfusePrompt(traceData, span);
210
+ const hasNameAndVersion = promptData?.name !== void 0 && promptData?.version !== void 0;
211
+ const hasId = promptData?.id !== void 0;
212
+ if (hasNameAndVersion || hasId) {
213
+ payload.prompt = {};
214
+ if (promptData?.name !== void 0) payload.prompt.name = promptData.name;
215
+ if (promptData?.version !== void 0) payload.prompt.version = promptData.version;
216
+ if (promptData?.id !== void 0) payload.prompt.id = promptData.id;
217
+ metadataToOmit.push("langfuse");
218
+ }
219
+ if (modelAttr.completionStartTime !== void 0) {
220
+ payload.completionStartTime = modelAttr.completionStartTime;
221
+ attributesToOmit.push("completionStartTime");
222
+ }
270
223
  }
271
224
  payload.metadata = {
272
225
  spanType: span.type,
273
- ...aiTracing.omitKeys(attributes, attributesToOmit),
274
- ...span.metadata
226
+ ...utils.omitKeys(attributes, attributesToOmit),
227
+ ...utils.omitKeys(metadata, metadataToOmit)
275
228
  };
276
229
  if (span.errorInfo) {
277
230
  payload.level = "ERROR";
@@ -287,9 +240,9 @@ var LangfuseExporter = class {
287
240
  scorerName,
288
241
  metadata
289
242
  }) {
290
- if (!this.client) return;
243
+ if (!this.#client) return;
291
244
  try {
292
- await this.client.score({
245
+ await this.#client.score({
293
246
  id: `${traceId}-${scorerName}`,
294
247
  traceId,
295
248
  observationId: spanId,
@@ -308,14 +261,40 @@ var LangfuseExporter = class {
308
261
  });
309
262
  }
310
263
  }
311
- async shutdown() {
312
- if (this.client) {
313
- await this.client.shutdownAsync();
264
+ /**
265
+ * Force flush any buffered data to Langfuse without shutting down.
266
+ */
267
+ async _flush() {
268
+ if (this.#client) {
269
+ await this.#client.flushAsync();
270
+ }
271
+ }
272
+ async _postShutdown() {
273
+ if (this.#client) {
274
+ await this.#client.shutdownAsync();
314
275
  }
315
- this.traceMap.clear();
316
276
  }
317
277
  };
318
278
 
279
+ // src/helpers.ts
280
+ function withLangfusePrompt(prompt) {
281
+ return (opts) => ({
282
+ ...opts,
283
+ metadata: {
284
+ ...opts.metadata,
285
+ langfuse: {
286
+ ...opts.metadata?.langfuse,
287
+ prompt: {
288
+ ...prompt.name !== void 0 && { name: prompt.name },
289
+ ...prompt.version !== void 0 && { version: prompt.version },
290
+ ...prompt.id !== void 0 && { id: prompt.id }
291
+ }
292
+ }
293
+ }
294
+ });
295
+ }
296
+
319
297
  exports.LangfuseExporter = LangfuseExporter;
298
+ exports.withLangfusePrompt = withLangfusePrompt;
320
299
  //# sourceMappingURL=index.cjs.map
321
300
  //# sourceMappingURL=index.cjs.map