@mastra/langfuse 0.0.0-unified-sidebar-20251010130811 → 0.0.0-unified-workspace-snapshot-20260128233410

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,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,132 @@
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?.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 {
9
41
  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;
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";
58
+ this.setDisabled(
59
+ `Missing required credentials (publicKey: ${publicKeySource}, secretKey: ${secretKeySource}). Set LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY environment variables or pass them in config.`
60
+ );
23
61
  return;
24
62
  }
25
- this.client = new langfuse.Langfuse({
26
- publicKey: config.publicKey,
27
- secretKey: config.secretKey,
28
- baseUrl: config.baseUrl,
63
+ this.#client = new langfuse.Langfuse({
64
+ publicKey,
65
+ secretKey,
66
+ baseUrl,
29
67
  ...config.options
30
68
  });
31
69
  }
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();
70
+ async _postExportTracingEvent() {
71
+ if (this.#realtime) {
72
+ await this.#client?.flushAsync();
53
73
  }
54
74
  }
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 });
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 });
65
82
  if (!langfuseParent) {
66
83
  return;
67
84
  }
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);
85
+ const payload = this.buildSpanPayload(span, true, traceData);
86
+ return langfuseParent.event(payload);
72
87
  }
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
- });
88
+ async _buildSpan(args) {
89
+ const { span, traceData } = args;
90
+ const langfuseParent = traceData.getParentOrRoot({ span });
91
+ if (!langfuseParent) {
97
92
  return;
98
93
  }
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
- }
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;
109
102
  }
110
- async handleEventSpan(span) {
111
- if (span.isRootSpan) {
112
- 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`, {
113
108
  traceId: span.traceId,
114
- spanId: span.id,
115
- spanName: span.name,
116
- method: "handleEventSpan"
109
+ spanId: langfuseSpan.id,
110
+ method: "_updateSpan"
117
111
  });
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;
112
+ const updatePayload = this.buildSpanPayload(span, false, traceData);
113
+ langfuseSpan.update(updatePayload);
128
114
  }
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);
134
- }
135
- }
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
115
  }
146
- getTraceData(options) {
147
- const { span, method } = options;
148
- if (this.traceMap.has(span.traceId)) {
149
- 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 });
150
123
  }
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
124
  }
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
125
+ async _abortSpan(args) {
126
+ const { span, reason } = args;
127
+ span.end({
128
+ level: "ERROR",
129
+ statusMessage: reason.message
181
130
  });
182
131
  }
183
132
  buildTracePayload(span) {
@@ -189,6 +138,7 @@ var LangfuseExporter = class {
189
138
  if (userId) payload.userId = userId;
190
139
  if (sessionId) payload.sessionId = sessionId;
191
140
  if (span.input) payload.input = span.input;
141
+ if (span.tags?.length) payload.tags = span.tags;
192
142
  payload.metadata = {
193
143
  spanType: span.type,
194
144
  ...span.attributes,
@@ -196,37 +146,80 @@ var LangfuseExporter = class {
196
146
  };
197
147
  return payload;
198
148
  }
199
- buildSpanPayload(span, isCreate) {
149
+ /**
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
157
+ */
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 });
171
+ }
172
+ return void 0;
173
+ }
174
+ buildSpanPayload(span, isCreate, traceData) {
200
175
  const payload = {};
201
176
  if (isCreate) {
202
177
  payload.id = span.id;
203
178
  payload.name = span.name;
204
179
  payload.startTime = span.startTime;
205
- if (span.input !== void 0) payload.input = span.input;
206
180
  }
181
+ if (span.input !== void 0) payload.input = span.input;
207
182
  if (span.output !== void 0) payload.output = span.output;
208
183
  if (span.endTime !== void 0) payload.endTime = span.endTime;
209
184
  const attributes = span.attributes ?? {};
185
+ const metadata = {
186
+ ...span.metadata
187
+ };
210
188
  const attributesToOmit = [];
211
- if (span.type === aiTracing.AISpanType.LLM_GENERATION) {
212
- const llmAttr = attributes;
213
- if (llmAttr.model !== void 0) {
214
- payload.model = llmAttr.model;
189
+ const metadataToOmit = [];
190
+ if (span.type === observability$1.SpanType.MODEL_GENERATION) {
191
+ const modelAttr = attributes;
192
+ if (modelAttr.model !== void 0) {
193
+ payload.model = modelAttr.model;
215
194
  attributesToOmit.push("model");
216
195
  }
217
- if (llmAttr.usage !== void 0) {
218
- payload.usage = llmAttr.usage;
196
+ if (modelAttr.usage !== void 0) {
197
+ payload.usageDetails = formatUsageMetrics(modelAttr.usage);
219
198
  attributesToOmit.push("usage");
220
199
  }
221
- if (llmAttr.parameters !== void 0) {
222
- payload.modelParameters = llmAttr.parameters;
200
+ if (modelAttr.parameters !== void 0) {
201
+ payload.modelParameters = modelAttr.parameters;
223
202
  attributesToOmit.push("parameters");
224
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
+ }
225
218
  }
226
219
  payload.metadata = {
227
220
  spanType: span.type,
228
- ...aiTracing.omitKeys(attributes, attributesToOmit),
229
- ...span.metadata
221
+ ...utils.omitKeys(attributes, attributesToOmit),
222
+ ...utils.omitKeys(metadata, metadataToOmit)
230
223
  };
231
224
  if (span.errorInfo) {
232
225
  payload.level = "ERROR";
@@ -242,9 +235,9 @@ var LangfuseExporter = class {
242
235
  scorerName,
243
236
  metadata
244
237
  }) {
245
- if (!this.client) return;
238
+ if (!this.#client) return;
246
239
  try {
247
- await this.client.score({
240
+ await this.#client.score({
248
241
  id: `${traceId}-${scorerName}`,
249
242
  traceId,
250
243
  observationId: spanId,
@@ -263,14 +256,40 @@ var LangfuseExporter = class {
263
256
  });
264
257
  }
265
258
  }
266
- async shutdown() {
267
- if (this.client) {
268
- 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();
269
270
  }
270
- this.traceMap.clear();
271
271
  }
272
272
  };
273
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
+
274
292
  exports.LangfuseExporter = LangfuseExporter;
293
+ exports.withLangfusePrompt = withLangfusePrompt;
275
294
  //# sourceMappingURL=index.cjs.map
276
295
  //# sourceMappingURL=index.cjs.map