@mastra/langfuse 0.0.0-sidebar-window-undefined-fix-20251029233656 → 0.0.0-standard-schema-20260123120255

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/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,9 +66,19 @@ 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
84
  - **Model generation support**: `MODEL_GENERATION` spans become Langfuse generations with token usage
@@ -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,177 +1,132 @@
1
1
  'use strict';
2
2
 
3
- var aiTracing = require('@mastra/core/ai-tracing');
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/ai-tracing.ts
7
- var LangfuseExporter = class extends aiTracing.BaseExporter {
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?.cacheWrite !== void 0) {
17
+ metrics.cache_write_input_tokens = usage.inputDetails.cacheWrite;
18
+ metrics.input -= metrics.cache_write_input_tokens;
19
+ }
20
+ }
21
+ if (usage.inputDetails?.cacheRead !== void 0) {
22
+ metrics.cache_read_input_tokens = usage.inputDetails.cacheRead;
23
+ }
24
+ if (usage.outputTokens !== void 0) {
25
+ metrics.output = usage.outputTokens;
26
+ }
27
+ if (usage.outputDetails?.reasoning !== void 0) {
28
+ metrics.reasoning = usage.outputDetails.reasoning;
29
+ }
30
+ if (metrics.input != null && metrics.output != null) {
31
+ metrics.total = metrics.input + metrics.output;
32
+ if (metrics.cache_write_input_tokens != null) {
33
+ metrics.total += metrics.cache_write_input_tokens;
34
+ }
35
+ }
36
+ return metrics;
37
+ }
38
+
39
+ // src/tracing.ts
40
+ var LangfuseExporter = class extends observability.TrackingExporter {
8
41
  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) {
42
+ #client;
43
+ #realtime;
44
+ constructor(config = {}) {
45
+ const publicKey = config.publicKey ?? process.env.LANGFUSE_PUBLIC_KEY;
46
+ const secretKey = config.secretKey ?? process.env.LANGFUSE_SECRET_KEY;
47
+ const baseUrl = config.baseUrl ?? process.env.LANGFUSE_BASE_URL;
48
+ super({
49
+ ...config,
50
+ publicKey,
51
+ secretKey,
52
+ baseUrl
53
+ });
54
+ this.#realtime = config.realtime ?? false;
55
+ if (!publicKey || !secretKey) {
56
+ const publicKeySource = config.publicKey ? "from config" : process.env.LANGFUSE_PUBLIC_KEY ? "from env" : "missing";
57
+ const secretKeySource = config.secretKey ? "from config" : process.env.LANGFUSE_SECRET_KEY ? "from env" : "missing";
16
58
  this.setDisabled(
17
- `Missing required credentials (publicKey: ${!!config.publicKey}, secretKey: ${!!config.secretKey})`
59
+ `Missing required credentials (publicKey: ${publicKeySource}, secretKey: ${secretKeySource}). Set LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY environment variables or pass them in config.`
18
60
  );
19
- this.client = null;
20
61
  return;
21
62
  }
22
- this.client = new langfuse.Langfuse({
23
- publicKey: config.publicKey,
24
- secretKey: config.secretKey,
25
- baseUrl: config.baseUrl,
63
+ this.#client = new langfuse.Langfuse({
64
+ publicKey,
65
+ secretKey,
66
+ baseUrl,
26
67
  ...config.options
27
68
  });
28
69
  }
29
- async _exportEvent(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();
70
+ async _postExportTracingEvent() {
71
+ if (this.#realtime) {
72
+ await this.#client?.flushAsync();
47
73
  }
48
74
  }
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) {
56
- return;
57
- }
58
- const langfuseParent = this.getLangfuseParent({ traceData, span, method });
75
+ async _buildRoot(args) {
76
+ const { span } = args;
77
+ return this.#client?.trace(this.buildTracePayload(span));
78
+ }
79
+ async _buildEvent(args) {
80
+ const { span, traceData } = args;
81
+ const langfuseParent = traceData.getParentOrRoot({ span });
59
82
  if (!langfuseParent) {
60
83
  return;
61
84
  }
62
- const payload = this.buildSpanPayload(span, true);
63
- const langfuseSpan = span.type === aiTracing.AISpanType.MODEL_GENERATION ? langfuseParent.generation(payload) : langfuseParent.span(payload);
64
- traceData.spans.set(span.id, langfuseSpan);
65
- traceData.activeSpans.add(span.id);
85
+ const payload = this.buildSpanPayload(span, true, traceData);
86
+ return langfuseParent.event(payload);
66
87
  }
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", {
83
- 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
90
- });
88
+ async _buildSpan(args) {
89
+ const { span, traceData } = args;
90
+ const langfuseParent = traceData.getParentOrRoot({ span });
91
+ if (!langfuseParent) {
91
92
  return;
92
93
  }
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
- }
102
- }
94
+ const payload = this.buildSpanPayload(span, true, traceData);
95
+ const langfuseSpan = span.type === observability$1.SpanType.MODEL_GENERATION ? langfuseParent.generation(payload) : langfuseParent.span(payload);
96
+ this.logger.debug(`${this.name}: built span`, {
97
+ traceId: span.traceId,
98
+ spanId: payload.id,
99
+ method: "_buildSpan"
100
+ });
101
+ return langfuseSpan;
103
102
  }
104
- async handleEventSpan(span) {
105
- if (span.isRootSpan) {
106
- this.logger.debug("Langfuse exporter: Creating trace", {
103
+ async _updateSpan(args) {
104
+ const { span, traceData } = args;
105
+ const langfuseSpan = traceData.getSpan({ spanId: span.id });
106
+ if (langfuseSpan) {
107
+ this.logger.debug(`${this.name}: found span for update`, {
107
108
  traceId: span.traceId,
108
- spanId: span.id,
109
- spanName: span.name,
110
- method: "handleEventSpan"
109
+ spanId: langfuseSpan.id,
110
+ method: "_updateSpan"
111
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);
112
+ const updatePayload = this.buildSpanPayload(span, false, traceData);
113
+ langfuseSpan.update(updatePayload);
128
114
  }
129
115
  }
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);
116
+ async _finishSpan(args) {
117
+ const { span, traceData } = args;
118
+ const langfuseSpan = traceData.getSpan({ spanId: span.id });
119
+ langfuseSpan?.update(this.buildSpanPayload(span, false, traceData));
120
+ if (span.isRootSpan) {
121
+ const langfuseRoot = traceData.getRoot();
122
+ langfuseRoot?.update({ output: span.output });
144
123
  }
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
124
  }
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
125
+ async _abortSpan(args) {
126
+ const { span, reason } = args;
127
+ span.end({
128
+ level: "ERROR",
129
+ statusMessage: reason.message
175
130
  });
176
131
  }
177
132
  buildTracePayload(span) {
@@ -183,6 +138,7 @@ var LangfuseExporter = class extends aiTracing.BaseExporter {
183
138
  if (userId) payload.userId = userId;
184
139
  if (sessionId) payload.sessionId = sessionId;
185
140
  if (span.input) payload.input = span.input;
141
+ if (span.tags?.length) payload.tags = span.tags;
186
142
  payload.metadata = {
187
143
  spanType: span.type,
188
144
  ...span.attributes,
@@ -191,81 +147,79 @@ var LangfuseExporter = class extends aiTracing.BaseExporter {
191
147
  return payload;
192
148
  }
193
149
  /**
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
150
+ * Look up the Langfuse prompt from the closest span that has one.
151
+ * This enables prompt inheritance for MODEL_GENERATION spans when the prompt
152
+ * is set on a parent span (e.g., AGENT_RUN) rather than directly on the generation.
153
+ * This enables prompt linking when:
154
+ * - A workflow calls multiple agents, each with different prompts
155
+ * - Nested agents have different prompts
156
+ * - The prompt is set on AGENT_RUN but MODEL_GENERATION inherits it
204
157
  */
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;
158
+ findLangfusePrompt(traceData, span) {
159
+ let currentSpanId = span.id;
160
+ while (currentSpanId) {
161
+ const providerMetadata = traceData.getMetadata({ spanId: currentSpanId });
162
+ if (providerMetadata?.prompt) {
163
+ this.logger.debug(`${this.name}: found prompt in provider metadata`, {
164
+ traceId: span.traceId,
165
+ spanId: span.id,
166
+ prompt: providerMetadata?.prompt
167
+ });
168
+ return providerMetadata.prompt;
169
+ }
170
+ currentSpanId = traceData.getParentId({ spanId: currentSpanId });
232
171
  }
233
- return Object.keys(normalized).length > 0 ? normalized : void 0;
172
+ return void 0;
234
173
  }
235
- buildSpanPayload(span, isCreate) {
174
+ buildSpanPayload(span, isCreate, traceData) {
236
175
  const payload = {};
237
176
  if (isCreate) {
238
177
  payload.id = span.id;
239
178
  payload.name = span.name;
240
179
  payload.startTime = span.startTime;
241
- if (span.input !== void 0) payload.input = span.input;
242
180
  }
181
+ if (span.input !== void 0) payload.input = span.input;
243
182
  if (span.output !== void 0) payload.output = span.output;
244
183
  if (span.endTime !== void 0) payload.endTime = span.endTime;
245
184
  const attributes = span.attributes ?? {};
185
+ const metadata = {
186
+ ...span.metadata
187
+ };
246
188
  const attributesToOmit = [];
247
- if (span.type === aiTracing.AISpanType.MODEL_GENERATION) {
189
+ const metadataToOmit = [];
190
+ if (span.type === observability$1.SpanType.MODEL_GENERATION) {
248
191
  const modelAttr = attributes;
249
192
  if (modelAttr.model !== void 0) {
250
193
  payload.model = modelAttr.model;
251
194
  attributesToOmit.push("model");
252
195
  }
253
196
  if (modelAttr.usage !== void 0) {
254
- const normalizedUsage = this.normalizeUsage(modelAttr.usage);
255
- if (normalizedUsage) {
256
- payload.usage = normalizedUsage;
257
- }
197
+ payload.usageDetails = formatUsageMetrics(modelAttr.usage);
258
198
  attributesToOmit.push("usage");
259
199
  }
260
200
  if (modelAttr.parameters !== void 0) {
261
201
  payload.modelParameters = modelAttr.parameters;
262
202
  attributesToOmit.push("parameters");
263
203
  }
204
+ const promptData = this.findLangfusePrompt(traceData, span);
205
+ const hasNameAndVersion = promptData?.name !== void 0 && promptData?.version !== void 0;
206
+ const hasId = promptData?.id !== void 0;
207
+ if (hasNameAndVersion || hasId) {
208
+ payload.prompt = {};
209
+ if (promptData?.name !== void 0) payload.prompt.name = promptData.name;
210
+ if (promptData?.version !== void 0) payload.prompt.version = promptData.version;
211
+ if (promptData?.id !== void 0) payload.prompt.id = promptData.id;
212
+ metadataToOmit.push("langfuse");
213
+ }
214
+ if (modelAttr.completionStartTime !== void 0) {
215
+ payload.completionStartTime = modelAttr.completionStartTime;
216
+ attributesToOmit.push("completionStartTime");
217
+ }
264
218
  }
265
219
  payload.metadata = {
266
220
  spanType: span.type,
267
- ...aiTracing.omitKeys(attributes, attributesToOmit),
268
- ...span.metadata
221
+ ...utils.omitKeys(attributes, attributesToOmit),
222
+ ...utils.omitKeys(metadata, metadataToOmit)
269
223
  };
270
224
  if (span.errorInfo) {
271
225
  payload.level = "ERROR";
@@ -281,9 +235,9 @@ var LangfuseExporter = class extends aiTracing.BaseExporter {
281
235
  scorerName,
282
236
  metadata
283
237
  }) {
284
- if (!this.client) return;
238
+ if (!this.#client) return;
285
239
  try {
286
- await this.client.score({
240
+ await this.#client.score({
287
241
  id: `${traceId}-${scorerName}`,
288
242
  traceId,
289
243
  observationId: spanId,
@@ -302,15 +256,40 @@ var LangfuseExporter = class extends aiTracing.BaseExporter {
302
256
  });
303
257
  }
304
258
  }
305
- async shutdown() {
306
- if (this.client) {
307
- await this.client.shutdownAsync();
259
+ /**
260
+ * Force flush any buffered data to Langfuse without shutting down.
261
+ */
262
+ async _flush() {
263
+ if (this.#client) {
264
+ await this.#client.flushAsync();
265
+ }
266
+ }
267
+ async _postShutdown() {
268
+ if (this.#client) {
269
+ await this.#client.shutdownAsync();
308
270
  }
309
- this.traceMap.clear();
310
- await super.shutdown();
311
271
  }
312
272
  };
313
273
 
274
+ // src/helpers.ts
275
+ function withLangfusePrompt(prompt) {
276
+ return (opts) => ({
277
+ ...opts,
278
+ metadata: {
279
+ ...opts.metadata,
280
+ langfuse: {
281
+ ...opts.metadata?.langfuse,
282
+ prompt: {
283
+ ...prompt.name !== void 0 && { name: prompt.name },
284
+ ...prompt.version !== void 0 && { version: prompt.version },
285
+ ...prompt.id !== void 0 && { id: prompt.id }
286
+ }
287
+ }
288
+ }
289
+ });
290
+ }
291
+
314
292
  exports.LangfuseExporter = LangfuseExporter;
293
+ exports.withLangfusePrompt = withLangfusePrompt;
315
294
  //# sourceMappingURL=index.cjs.map
316
295
  //# sourceMappingURL=index.cjs.map