@mastra/langfuse 1.4.5 → 1.4.6
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 +18 -0
- package/dist/index.cjs +296 -291
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +294 -288
- package/dist/index.js.map +1 -1
- package/package.json +10 -9
package/dist/index.js
CHANGED
|
@@ -1,298 +1,304 @@
|
|
|
1
|
-
import { LangfuseClient } from
|
|
2
|
-
import { LangfuseSpanProcessor } from
|
|
3
|
-
import {
|
|
4
|
-
import { BaseExporter } from
|
|
5
|
-
import { SpanConverter } from
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { LangfuseClient } from "@langfuse/client";
|
|
2
|
+
import { LangfuseSpanProcessor } from "@langfuse/otel";
|
|
3
|
+
import { SpanType, TracingEventType } from "@mastra/core/observability";
|
|
4
|
+
import { BaseExporter } from "@mastra/observability";
|
|
5
|
+
import { SpanConverter } from "@mastra/otel-exporter";
|
|
6
|
+
//#region src/tracing.ts
|
|
7
|
+
/**
|
|
8
|
+
* Langfuse Exporter for Mastra Observability
|
|
9
|
+
*
|
|
10
|
+
* Sends observability data to Langfuse using the official @langfuse/otel span processor
|
|
11
|
+
* and @langfuse/client for non-tracing features (scoring, prompt management, evaluations).
|
|
12
|
+
*
|
|
13
|
+
* @see https://langfuse.com/docs/observability/sdk/typescript/overview
|
|
14
|
+
*/
|
|
15
|
+
const LOG_PREFIX = "[LangfuseExporter]";
|
|
16
|
+
const LANGFUSE_DEFAULT_BASE_URL = "https://cloud.langfuse.com";
|
|
10
17
|
var LangfuseExporter = class extends BaseExporter {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
spanId,
|
|
145
|
-
name: scorerName,
|
|
146
|
-
value: score,
|
|
147
|
-
comment: reason,
|
|
148
|
-
metadata
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
async flush() {
|
|
152
|
-
await Promise.all([this.#processor?.forceFlush(), this.#client?.flush()]);
|
|
153
|
-
}
|
|
154
|
-
async shutdown() {
|
|
155
|
-
await Promise.all([this.#processor?.shutdown(), this.#client?.shutdown()]);
|
|
156
|
-
}
|
|
18
|
+
name = "langfuse";
|
|
19
|
+
#processor;
|
|
20
|
+
#client;
|
|
21
|
+
#spanConverter;
|
|
22
|
+
#realtime;
|
|
23
|
+
#environment;
|
|
24
|
+
#release;
|
|
25
|
+
constructor(config = {}) {
|
|
26
|
+
super(config);
|
|
27
|
+
const publicKey = config.publicKey ?? process.env.LANGFUSE_PUBLIC_KEY;
|
|
28
|
+
const secretKey = config.secretKey ?? process.env.LANGFUSE_SECRET_KEY;
|
|
29
|
+
const baseUrl = stripTrailingSlashes(config.baseUrl ?? process.env.LANGFUSE_BASE_URL ?? "https://cloud.langfuse.com");
|
|
30
|
+
this.#realtime = config.realtime ?? false;
|
|
31
|
+
if (!publicKey || !secretKey) {
|
|
32
|
+
const publicKeySource = config.publicKey ? "from config" : process.env.LANGFUSE_PUBLIC_KEY ? "from env" : "missing";
|
|
33
|
+
const secretKeySource = config.secretKey ? "from config" : process.env.LANGFUSE_SECRET_KEY ? "from env" : "missing";
|
|
34
|
+
this.setDisabled(`${LOG_PREFIX} Missing required credentials (publicKey: ${publicKeySource}, secretKey: ${secretKeySource}). Set LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY environment variables or pass them in config.`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
this.#processor = new LangfuseSpanProcessor({
|
|
38
|
+
publicKey,
|
|
39
|
+
secretKey,
|
|
40
|
+
baseUrl,
|
|
41
|
+
environment: config.environment,
|
|
42
|
+
release: config.release,
|
|
43
|
+
exportMode: this.#realtime ? "immediate" : "batched",
|
|
44
|
+
flushAt: config.flushAt,
|
|
45
|
+
flushInterval: config.flushInterval,
|
|
46
|
+
shouldExportSpan: () => true
|
|
47
|
+
});
|
|
48
|
+
this.#client = new LangfuseClient({
|
|
49
|
+
publicKey,
|
|
50
|
+
secretKey,
|
|
51
|
+
baseUrl
|
|
52
|
+
});
|
|
53
|
+
this.#environment = config.environment ?? process.env.LANGFUSE_TRACING_ENVIRONMENT;
|
|
54
|
+
this.#release = config.release ?? process.env.LANGFUSE_RELEASE;
|
|
55
|
+
}
|
|
56
|
+
init(options) {
|
|
57
|
+
this.#spanConverter = new SpanConverter({
|
|
58
|
+
packageName: "@mastra/langfuse",
|
|
59
|
+
serviceName: options.config?.serviceName,
|
|
60
|
+
format: "GenAI_v1_38_0"
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
async _exportTracingEvent(event) {
|
|
64
|
+
if (event.type !== TracingEventType.SPAN_ENDED) return;
|
|
65
|
+
if (!this.#processor) return;
|
|
66
|
+
await this.exportSpan(event.exportedSpan);
|
|
67
|
+
}
|
|
68
|
+
async exportSpan(span) {
|
|
69
|
+
if (!this.#spanConverter) this.#spanConverter = new SpanConverter({
|
|
70
|
+
packageName: "@mastra/langfuse",
|
|
71
|
+
serviceName: "mastra-service",
|
|
72
|
+
format: "GenAI_v1_38_0"
|
|
73
|
+
});
|
|
74
|
+
try {
|
|
75
|
+
const otelSpan = await this.#spanConverter.convertSpan(span);
|
|
76
|
+
mapMastraToLangfuseAttributes(otelSpan.attributes, span, this.#environment, this.#release);
|
|
77
|
+
this.#processor.onEnd(otelSpan);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
this.logger.error(`${LOG_PREFIX} Failed to export span ${span.id}:`, error);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The LangfuseClient instance for advanced Langfuse features.
|
|
84
|
+
* Use this for prompt management, evaluations, datasets, and direct API access.
|
|
85
|
+
*/
|
|
86
|
+
get client() {
|
|
87
|
+
return this.#client;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Submit a score to Langfuse. Used by both the new `onScoreEvent` path and the
|
|
91
|
+
* deprecated `addScoreToTrace` wrapper.
|
|
92
|
+
*/
|
|
93
|
+
submitScore(args) {
|
|
94
|
+
if (!this.#client) return;
|
|
95
|
+
const { id, traceId, spanId, name, value, comment, metadata } = args;
|
|
96
|
+
try {
|
|
97
|
+
this.#client.score.create({
|
|
98
|
+
id,
|
|
99
|
+
traceId,
|
|
100
|
+
...spanId ? { observationId: spanId } : {},
|
|
101
|
+
name,
|
|
102
|
+
value,
|
|
103
|
+
...comment ? { comment } : {},
|
|
104
|
+
...metadata ? { metadata } : {},
|
|
105
|
+
dataType: "NUMERIC"
|
|
106
|
+
});
|
|
107
|
+
} catch (error) {
|
|
108
|
+
this.logger.error(`${LOG_PREFIX} Error submitting score`, {
|
|
109
|
+
error,
|
|
110
|
+
traceId,
|
|
111
|
+
spanId,
|
|
112
|
+
name
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async onScoreEvent(event) {
|
|
117
|
+
const { score } = event;
|
|
118
|
+
if (!score.traceId) return;
|
|
119
|
+
this.submitScore({
|
|
120
|
+
id: score.scoreId,
|
|
121
|
+
traceId: score.traceId,
|
|
122
|
+
spanId: score.spanId,
|
|
123
|
+
name: score.scorerName ?? score.scorerId,
|
|
124
|
+
value: score.score,
|
|
125
|
+
comment: score.reason,
|
|
126
|
+
metadata: score.metadata
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* @deprecated Use the observability score event pipeline (`mastra.observability.addScore`)
|
|
131
|
+
* instead. This method is preserved for backwards compatibility and forwards to the same
|
|
132
|
+
* underlying client call as `onScoreEvent`.
|
|
133
|
+
*/
|
|
134
|
+
async addScoreToTrace({ traceId, spanId, score, reason, scorerName, metadata }) {
|
|
135
|
+
this.submitScore({
|
|
136
|
+
id: `${traceId}-${spanId || ""}-${scorerName}`,
|
|
137
|
+
traceId,
|
|
138
|
+
spanId,
|
|
139
|
+
name: scorerName,
|
|
140
|
+
value: score,
|
|
141
|
+
comment: reason,
|
|
142
|
+
metadata
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
async flush() {
|
|
146
|
+
await Promise.all([this.#processor?.forceFlush(), this.#client?.flush()]);
|
|
147
|
+
}
|
|
148
|
+
async shutdown() {
|
|
149
|
+
await Promise.all([this.#processor?.shutdown(), this.#client?.shutdown()]);
|
|
150
|
+
}
|
|
157
151
|
};
|
|
152
|
+
/**
|
|
153
|
+
* Maps Mastra-specific OTel attributes to the langfuse.* namespace that
|
|
154
|
+
* Langfuse's OTLP endpoint reads for prompt linking, TTFT, and other features.
|
|
155
|
+
*
|
|
156
|
+
* SpanConverter produces attributes like mastra.metadata.*, mastra.completion_start_time, etc.
|
|
157
|
+
* Langfuse's OTLP server only reads langfuse.observation.prompt.name, langfuse.observation.completion_start_time, etc.
|
|
158
|
+
*
|
|
159
|
+
* This function mutates the attributes object in place.
|
|
160
|
+
* @see https://langfuse.com/integrations/native/opentelemetry#property-mapping
|
|
161
|
+
*/
|
|
158
162
|
function mapMastraToLangfuseAttributes(attributes, span, environment, release) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
if (span.entityId) {
|
|
234
|
-
attributes["langfuse.trace.metadata.workflowId"] = span.entityId;
|
|
235
|
-
}
|
|
236
|
-
if (span.entityName) {
|
|
237
|
-
attributes["langfuse.trace.metadata.workflowName"] = span.entityName;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
if (attributes["gen_ai.agent.id"]) {
|
|
242
|
-
attributes["langfuse.observation.metadata.agentId"] = attributes["gen_ai.agent.id"];
|
|
243
|
-
}
|
|
244
|
-
if (attributes["gen_ai.agent.name"]) {
|
|
245
|
-
attributes["langfuse.observation.metadata.agentName"] = attributes["gen_ai.agent.name"];
|
|
246
|
-
}
|
|
247
|
-
if (attributes["mastra.span.type"]) {
|
|
248
|
-
attributes["langfuse.observation.metadata.spanType"] = attributes["mastra.span.type"];
|
|
249
|
-
}
|
|
250
|
-
if (attributes["gen_ai.operation.name"]) {
|
|
251
|
-
attributes["langfuse.observation.metadata.operationName"] = attributes["gen_ai.operation.name"];
|
|
252
|
-
}
|
|
253
|
-
if (!attributes["gen_ai.input.messages"] && !attributes["gen_ai.tool.call.arguments"]) {
|
|
254
|
-
for (const key of Object.keys(attributes)) {
|
|
255
|
-
if (key.startsWith("mastra.") && key.endsWith(".input")) {
|
|
256
|
-
attributes["langfuse.observation.input"] = attributes[key];
|
|
257
|
-
break;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
if (!attributes["gen_ai.output.messages"] && !attributes["gen_ai.tool.call.result"]) {
|
|
262
|
-
for (const key of Object.keys(attributes)) {
|
|
263
|
-
if (key.startsWith("mastra.") && key.endsWith(".output")) {
|
|
264
|
-
attributes["langfuse.observation.output"] = attributes[key];
|
|
265
|
-
break;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
163
|
+
if (environment) attributes["langfuse.environment"] = environment;
|
|
164
|
+
if (release) attributes["langfuse.release"] = release;
|
|
165
|
+
const langfuseMetadata = attributes["mastra.metadata.langfuse"];
|
|
166
|
+
if (langfuseMetadata) {
|
|
167
|
+
try {
|
|
168
|
+
const parsed = typeof langfuseMetadata === "string" ? JSON.parse(langfuseMetadata) : langfuseMetadata;
|
|
169
|
+
if (parsed && typeof parsed === "object") {
|
|
170
|
+
if (parsed.prompt) {
|
|
171
|
+
if (parsed.prompt.name !== void 0) attributes["langfuse.observation.prompt.name"] = parsed.prompt.name;
|
|
172
|
+
if (parsed.prompt.version !== void 0) attributes["langfuse.observation.prompt.version"] = parsed.prompt.version;
|
|
173
|
+
}
|
|
174
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
175
|
+
if (key === "prompt" || value === null || value === void 0) continue;
|
|
176
|
+
const traceKey = `langfuse.trace.metadata.${key}`;
|
|
177
|
+
if (attributes[traceKey] === void 0) attributes[traceKey] = typeof value === "string" ? value : JSON.stringify(value);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
} catch {}
|
|
181
|
+
delete attributes["mastra.metadata.langfuse"];
|
|
182
|
+
}
|
|
183
|
+
if (attributes["mastra.completion_start_time"]) {
|
|
184
|
+
attributes["langfuse.observation.completion_start_time"] = attributes["mastra.completion_start_time"];
|
|
185
|
+
delete attributes["mastra.completion_start_time"];
|
|
186
|
+
}
|
|
187
|
+
if (attributes["mastra.metadata.userId"]) {
|
|
188
|
+
attributes["user.id"] = attributes["mastra.metadata.userId"];
|
|
189
|
+
delete attributes["mastra.metadata.userId"];
|
|
190
|
+
}
|
|
191
|
+
const sessionId = attributes["mastra.metadata.sessionId"] ?? attributes["mastra.metadata.threadId"];
|
|
192
|
+
if (sessionId) {
|
|
193
|
+
attributes["session.id"] = sessionId;
|
|
194
|
+
delete attributes["mastra.metadata.sessionId"];
|
|
195
|
+
delete attributes["mastra.metadata.threadId"];
|
|
196
|
+
}
|
|
197
|
+
if (attributes["mastra.tags"]) {
|
|
198
|
+
attributes["langfuse.trace.tags"] = attributes["mastra.tags"];
|
|
199
|
+
delete attributes["mastra.tags"];
|
|
200
|
+
}
|
|
201
|
+
if (attributes["mastra.metadata.traceName"]) {
|
|
202
|
+
attributes["langfuse.trace.name"] = attributes["mastra.metadata.traceName"];
|
|
203
|
+
delete attributes["mastra.metadata.traceName"];
|
|
204
|
+
}
|
|
205
|
+
if (attributes["mastra.metadata.version"]) {
|
|
206
|
+
attributes["langfuse.trace.version"] = attributes["mastra.metadata.version"];
|
|
207
|
+
delete attributes["mastra.metadata.version"];
|
|
208
|
+
}
|
|
209
|
+
if (span.isRootSpan) {
|
|
210
|
+
if (span.type === SpanType.AGENT_RUN) {
|
|
211
|
+
if (!attributes["langfuse.trace.name"] && (span.entityName || span.entityId)) attributes["langfuse.trace.name"] = span.entityName ?? span.entityId;
|
|
212
|
+
if (span.entityId) attributes["langfuse.trace.metadata.agentId"] = span.entityId;
|
|
213
|
+
if (span.entityName) attributes["langfuse.trace.metadata.agentName"] = span.entityName;
|
|
214
|
+
} else if (span.type === SpanType.WORKFLOW_RUN) {
|
|
215
|
+
if (!attributes["langfuse.trace.name"] && (span.entityName || span.entityId)) attributes["langfuse.trace.name"] = span.entityName ?? span.entityId;
|
|
216
|
+
if (span.entityId) attributes["langfuse.trace.metadata.workflowId"] = span.entityId;
|
|
217
|
+
if (span.entityName) attributes["langfuse.trace.metadata.workflowName"] = span.entityName;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (attributes["gen_ai.agent.id"]) attributes["langfuse.observation.metadata.agentId"] = attributes["gen_ai.agent.id"];
|
|
221
|
+
if (attributes["gen_ai.agent.name"]) attributes["langfuse.observation.metadata.agentName"] = attributes["gen_ai.agent.name"];
|
|
222
|
+
if (attributes["mastra.span.type"]) attributes["langfuse.observation.metadata.spanType"] = attributes["mastra.span.type"];
|
|
223
|
+
if (attributes["gen_ai.operation.name"]) attributes["langfuse.observation.metadata.operationName"] = attributes["gen_ai.operation.name"];
|
|
224
|
+
if (!attributes["gen_ai.input.messages"] && !attributes["gen_ai.tool.call.arguments"]) {
|
|
225
|
+
for (const key of Object.keys(attributes)) if (key.startsWith("mastra.") && key.endsWith(".input")) {
|
|
226
|
+
attributes["langfuse.observation.input"] = attributes[key];
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (!attributes["gen_ai.output.messages"] && !attributes["gen_ai.tool.call.result"]) {
|
|
231
|
+
for (const key of Object.keys(attributes)) if (key.startsWith("mastra.") && key.endsWith(".output")) {
|
|
232
|
+
attributes["langfuse.observation.output"] = attributes[key];
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
269
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Remove trailing "/" characters procedurally. Avoids polynomial
|
|
239
|
+
* backtracking that a greedy regex like `/\/+$/` can exhibit when the
|
|
240
|
+
* input is attacker-controlled.
|
|
241
|
+
*/
|
|
270
242
|
function stripTrailingSlashes(s) {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
return end === s.length ? s : s.slice(0, end);
|
|
243
|
+
let end = s.length;
|
|
244
|
+
while (end > 0 && s.charCodeAt(end - 1) === 47) end--;
|
|
245
|
+
return end === s.length ? s : s.slice(0, end);
|
|
276
246
|
}
|
|
277
|
-
|
|
278
|
-
|
|
247
|
+
//#endregion
|
|
248
|
+
//#region src/helpers.ts
|
|
249
|
+
/**
|
|
250
|
+
* Adds Langfuse prompt metadata to the tracing options
|
|
251
|
+
* to enable Langfuse Prompt Tracing.
|
|
252
|
+
*
|
|
253
|
+
* The metadata is added under `metadata.langfuse.prompt` and includes:
|
|
254
|
+
* - `name` - Prompt name (required for Langfuse v5)
|
|
255
|
+
* - `version` - Prompt version (required for Langfuse v5)
|
|
256
|
+
*
|
|
257
|
+
* All fields are deeply merged with any existing metadata.
|
|
258
|
+
*
|
|
259
|
+
* @param prompt - Prompt fields for linking (`name` and `version` required)
|
|
260
|
+
* @returns A TracingOptionsUpdater function for use with `buildTracingOptions`
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* ```typescript
|
|
264
|
+
* import { buildTracingOptions } from '@mastra/observability';
|
|
265
|
+
* import { withLangfusePrompt } from '@mastra/langfuse';
|
|
266
|
+
*
|
|
267
|
+
* // Link a generation to a Langfuse prompt by name and version
|
|
268
|
+
* const tracingOptions = buildTracingOptions(
|
|
269
|
+
* withLangfusePrompt({ name: 'customer-support', version: 1 }),
|
|
270
|
+
* );
|
|
271
|
+
*
|
|
272
|
+
* // Or directly in agent config
|
|
273
|
+
* const agent = new Agent({
|
|
274
|
+
* name: 'support-agent',
|
|
275
|
+
* instructions: 'You are a helpful assistant',
|
|
276
|
+
* model: openai('gpt-4o'),
|
|
277
|
+
* defaultGenerateOptions: {
|
|
278
|
+
* tracingOptions: buildTracingOptions(
|
|
279
|
+
* withLangfusePrompt({ name: 'my-prompt', version: 1 }),
|
|
280
|
+
* ),
|
|
281
|
+
* },
|
|
282
|
+
* });
|
|
283
|
+
* ```
|
|
284
|
+
*/
|
|
279
285
|
function withLangfusePrompt(prompt) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
286
|
+
return (opts) => ({
|
|
287
|
+
...opts,
|
|
288
|
+
metadata: {
|
|
289
|
+
...opts.metadata,
|
|
290
|
+
langfuse: {
|
|
291
|
+
...opts.metadata?.langfuse,
|
|
292
|
+
prompt: {
|
|
293
|
+
...prompt.name !== void 0 && { name: prompt.name },
|
|
294
|
+
...prompt.version !== void 0 && { version: prompt.version },
|
|
295
|
+
...prompt.id !== void 0 && { id: prompt.id }
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
});
|
|
294
300
|
}
|
|
295
|
-
|
|
301
|
+
//#endregion
|
|
296
302
|
export { LANGFUSE_DEFAULT_BASE_URL, LangfuseExporter, withLangfusePrompt };
|
|
297
|
-
|
|
303
|
+
|
|
298
304
|
//# sourceMappingURL=index.js.map
|