@mastra/langfuse 0.0.0-trace-timeline-update-20251121114225 → 0.0.0-type-testing-20260120105120
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 +309 -12
- package/README.md +48 -6
- package/dist/helpers.d.ts +79 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/index.cjs +173 -196
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +174 -198
- package/dist/index.js.map +1 -1
- package/dist/metrics.d.ts +17 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/tracing.d.ts +63 -34
- package/dist/tracing.d.ts.map +1 -1
- package/package.json +9 -9
|
@@ -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
|
@@ -6,174 +6,127 @@ var observability = require('@mastra/observability');
|
|
|
6
6
|
var langfuse = require('langfuse');
|
|
7
7
|
|
|
8
8
|
// src/tracing.ts
|
|
9
|
-
|
|
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 {
|
|
10
41
|
name = "langfuse";
|
|
11
|
-
client;
|
|
12
|
-
realtime;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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";
|
|
18
58
|
this.setDisabled(
|
|
19
|
-
`Missing required credentials (publicKey: ${
|
|
59
|
+
`Missing required credentials (publicKey: ${publicKeySource}, secretKey: ${secretKeySource}). Set LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY environment variables or pass them in config.`
|
|
20
60
|
);
|
|
21
|
-
this.client = null;
|
|
22
61
|
return;
|
|
23
62
|
}
|
|
24
|
-
this
|
|
25
|
-
publicKey
|
|
26
|
-
secretKey
|
|
27
|
-
baseUrl
|
|
63
|
+
this.#client = new langfuse.Langfuse({
|
|
64
|
+
publicKey,
|
|
65
|
+
secretKey,
|
|
66
|
+
baseUrl,
|
|
28
67
|
...config.options
|
|
29
68
|
});
|
|
30
69
|
}
|
|
31
|
-
async
|
|
32
|
-
if (
|
|
33
|
-
await this
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
switch (event.type) {
|
|
37
|
-
case "span_started":
|
|
38
|
-
await this.handleSpanStarted(event.exportedSpan);
|
|
39
|
-
break;
|
|
40
|
-
case "span_updated":
|
|
41
|
-
await this.handleSpanUpdateOrEnd(event.exportedSpan, false);
|
|
42
|
-
break;
|
|
43
|
-
case "span_ended":
|
|
44
|
-
await this.handleSpanUpdateOrEnd(event.exportedSpan, true);
|
|
45
|
-
break;
|
|
46
|
-
}
|
|
47
|
-
if (this.realtime) {
|
|
48
|
-
await this.client.flushAsync();
|
|
70
|
+
async _postExportTracingEvent() {
|
|
71
|
+
if (this.#realtime) {
|
|
72
|
+
await this.#client?.flushAsync();
|
|
49
73
|
}
|
|
50
74
|
}
|
|
51
|
-
async
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
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 });
|
|
82
|
+
if (!langfuseParent) {
|
|
58
83
|
return;
|
|
59
84
|
}
|
|
60
|
-
const
|
|
85
|
+
const payload = this.buildSpanPayload(span, true, traceData);
|
|
86
|
+
return langfuseParent.event(payload);
|
|
87
|
+
}
|
|
88
|
+
async _buildSpan(args) {
|
|
89
|
+
const { span, traceData } = args;
|
|
90
|
+
const langfuseParent = traceData.getParentOrRoot({ span });
|
|
61
91
|
if (!langfuseParent) {
|
|
62
92
|
return;
|
|
63
93
|
}
|
|
64
|
-
const payload = this.buildSpanPayload(span, true);
|
|
94
|
+
const payload = this.buildSpanPayload(span, true, traceData);
|
|
65
95
|
const langfuseSpan = span.type === observability$1.SpanType.MODEL_GENERATION ? langfuseParent.generation(payload) : langfuseParent.span(payload);
|
|
66
|
-
|
|
67
|
-
|
|
96
|
+
this.logger.debug(`${this.name}: built span`, {
|
|
97
|
+
traceId: span.traceId,
|
|
98
|
+
spanId: payload.id,
|
|
99
|
+
method: "_buildSpan"
|
|
100
|
+
});
|
|
101
|
+
return langfuseSpan;
|
|
68
102
|
}
|
|
69
|
-
async
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
const langfuseSpan = traceData.spans.get(span.id);
|
|
76
|
-
if (!langfuseSpan) {
|
|
77
|
-
if (isEnd && span.isEvent) {
|
|
78
|
-
traceData.activeSpans.delete(span.id);
|
|
79
|
-
if (traceData.activeSpans.size === 0) {
|
|
80
|
-
this.traceMap.delete(span.traceId);
|
|
81
|
-
}
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
this.logger.warn("Langfuse exporter: No Langfuse span found for span update/end", {
|
|
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`, {
|
|
85
108
|
traceId: span.traceId,
|
|
86
|
-
spanId:
|
|
87
|
-
|
|
88
|
-
spanType: span.type,
|
|
89
|
-
isRootSpan: span.isRootSpan,
|
|
90
|
-
parentSpanId: span.parentSpanId,
|
|
91
|
-
method
|
|
109
|
+
spanId: langfuseSpan.id,
|
|
110
|
+
method: "_updateSpan"
|
|
92
111
|
});
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
langfuseSpan.update(this.buildSpanPayload(span, false));
|
|
96
|
-
if (isEnd) {
|
|
97
|
-
traceData.activeSpans.delete(span.id);
|
|
98
|
-
if (span.isRootSpan) {
|
|
99
|
-
traceData.trace.update({ output: span.output });
|
|
100
|
-
}
|
|
101
|
-
if (traceData.activeSpans.size === 0) {
|
|
102
|
-
this.traceMap.delete(span.traceId);
|
|
103
|
-
}
|
|
112
|
+
const updatePayload = this.buildSpanPayload(span, false, traceData);
|
|
113
|
+
langfuseSpan.update(updatePayload);
|
|
104
114
|
}
|
|
105
115
|
}
|
|
106
|
-
async
|
|
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));
|
|
107
120
|
if (span.isRootSpan) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
spanId: span.id,
|
|
111
|
-
spanName: span.name,
|
|
112
|
-
method: "handleEventSpan"
|
|
113
|
-
});
|
|
114
|
-
this.initTrace(span);
|
|
115
|
-
}
|
|
116
|
-
const method = "handleEventSpan";
|
|
117
|
-
const traceData = this.getTraceData({ span, method });
|
|
118
|
-
if (!traceData) {
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
const langfuseParent = this.getLangfuseParent({ traceData, span, method });
|
|
122
|
-
if (!langfuseParent) {
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
const payload = this.buildSpanPayload(span, true);
|
|
126
|
-
const langfuseEvent = langfuseParent.event(payload);
|
|
127
|
-
traceData.events.set(span.id, langfuseEvent);
|
|
128
|
-
if (!span.endTime) {
|
|
129
|
-
traceData.activeSpans.add(span.id);
|
|
121
|
+
const langfuseRoot = traceData.getRoot();
|
|
122
|
+
langfuseRoot?.update({ output: span.output });
|
|
130
123
|
}
|
|
131
124
|
}
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
events: /* @__PURE__ */ new Map(),
|
|
138
|
-
activeSpans: /* @__PURE__ */ new Set(),
|
|
139
|
-
rootSpanId: span.id
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
getTraceData(options) {
|
|
143
|
-
const { span, method } = options;
|
|
144
|
-
if (this.traceMap.has(span.traceId)) {
|
|
145
|
-
return this.traceMap.get(span.traceId);
|
|
146
|
-
}
|
|
147
|
-
this.logger.warn("Langfuse exporter: No trace data found for span", {
|
|
148
|
-
traceId: span.traceId,
|
|
149
|
-
spanId: span.id,
|
|
150
|
-
spanName: span.name,
|
|
151
|
-
spanType: span.type,
|
|
152
|
-
isRootSpan: span.isRootSpan,
|
|
153
|
-
parentSpanId: span.parentSpanId,
|
|
154
|
-
method
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
getLangfuseParent(options) {
|
|
158
|
-
const { traceData, span, method } = options;
|
|
159
|
-
const parentId = span.parentSpanId;
|
|
160
|
-
if (!parentId) {
|
|
161
|
-
return traceData.trace;
|
|
162
|
-
}
|
|
163
|
-
if (traceData.spans.has(parentId)) {
|
|
164
|
-
return traceData.spans.get(parentId);
|
|
165
|
-
}
|
|
166
|
-
if (traceData.events.has(parentId)) {
|
|
167
|
-
return traceData.events.get(parentId);
|
|
168
|
-
}
|
|
169
|
-
this.logger.warn("Langfuse exporter: No parent data found for span", {
|
|
170
|
-
traceId: span.traceId,
|
|
171
|
-
spanId: span.id,
|
|
172
|
-
spanName: span.name,
|
|
173
|
-
spanType: span.type,
|
|
174
|
-
isRootSpan: span.isRootSpan,
|
|
175
|
-
parentSpanId: span.parentSpanId,
|
|
176
|
-
method
|
|
125
|
+
async _abortSpan(args) {
|
|
126
|
+
const { span, reason } = args;
|
|
127
|
+
span.end({
|
|
128
|
+
level: "ERROR",
|
|
129
|
+
statusMessage: reason.message
|
|
177
130
|
});
|
|
178
131
|
}
|
|
179
132
|
buildTracePayload(span) {
|
|
@@ -185,6 +138,7 @@ var LangfuseExporter = class extends observability.BaseExporter {
|
|
|
185
138
|
if (userId) payload.userId = userId;
|
|
186
139
|
if (sessionId) payload.sessionId = sessionId;
|
|
187
140
|
if (span.input) payload.input = span.input;
|
|
141
|
+
if (span.tags?.length) payload.tags = span.tags;
|
|
188
142
|
payload.metadata = {
|
|
189
143
|
spanType: span.type,
|
|
190
144
|
...span.attributes,
|
|
@@ -193,59 +147,46 @@ var LangfuseExporter = class extends observability.BaseExporter {
|
|
|
193
147
|
return payload;
|
|
194
148
|
}
|
|
195
149
|
/**
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
* @param usage - Token usage data from AI SDK (v4 or v5 format)
|
|
205
|
-
* @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
|
|
206
157
|
*/
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
} else if (normalized.input !== void 0 && normalized.output !== void 0) {
|
|
221
|
-
normalized.total = normalized.input + normalized.output;
|
|
222
|
-
}
|
|
223
|
-
if (usage.reasoningTokens !== void 0) {
|
|
224
|
-
normalized.reasoning = usage.reasoningTokens;
|
|
225
|
-
}
|
|
226
|
-
if (usage.cachedInputTokens !== void 0) {
|
|
227
|
-
normalized.cachedInput = usage.cachedInputTokens;
|
|
228
|
-
}
|
|
229
|
-
if (usage.promptCacheHitTokens !== void 0) {
|
|
230
|
-
normalized.promptCacheHit = usage.promptCacheHitTokens;
|
|
231
|
-
}
|
|
232
|
-
if (usage.promptCacheMissTokens !== void 0) {
|
|
233
|
-
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 });
|
|
234
171
|
}
|
|
235
|
-
return
|
|
172
|
+
return void 0;
|
|
236
173
|
}
|
|
237
|
-
buildSpanPayload(span, isCreate) {
|
|
174
|
+
buildSpanPayload(span, isCreate, traceData) {
|
|
238
175
|
const payload = {};
|
|
239
176
|
if (isCreate) {
|
|
240
177
|
payload.id = span.id;
|
|
241
178
|
payload.name = span.name;
|
|
242
179
|
payload.startTime = span.startTime;
|
|
243
|
-
if (span.input !== void 0) payload.input = span.input;
|
|
244
180
|
}
|
|
181
|
+
if (span.input !== void 0) payload.input = span.input;
|
|
245
182
|
if (span.output !== void 0) payload.output = span.output;
|
|
246
183
|
if (span.endTime !== void 0) payload.endTime = span.endTime;
|
|
247
184
|
const attributes = span.attributes ?? {};
|
|
185
|
+
const metadata = {
|
|
186
|
+
...span.metadata
|
|
187
|
+
};
|
|
248
188
|
const attributesToOmit = [];
|
|
189
|
+
const metadataToOmit = [];
|
|
249
190
|
if (span.type === observability$1.SpanType.MODEL_GENERATION) {
|
|
250
191
|
const modelAttr = attributes;
|
|
251
192
|
if (modelAttr.model !== void 0) {
|
|
@@ -253,21 +194,32 @@ var LangfuseExporter = class extends observability.BaseExporter {
|
|
|
253
194
|
attributesToOmit.push("model");
|
|
254
195
|
}
|
|
255
196
|
if (modelAttr.usage !== void 0) {
|
|
256
|
-
|
|
257
|
-
if (normalizedUsage) {
|
|
258
|
-
payload.usage = normalizedUsage;
|
|
259
|
-
}
|
|
197
|
+
payload.usageDetails = formatUsageMetrics(modelAttr.usage);
|
|
260
198
|
attributesToOmit.push("usage");
|
|
261
199
|
}
|
|
262
200
|
if (modelAttr.parameters !== void 0) {
|
|
263
201
|
payload.modelParameters = modelAttr.parameters;
|
|
264
202
|
attributesToOmit.push("parameters");
|
|
265
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
|
+
}
|
|
266
218
|
}
|
|
267
219
|
payload.metadata = {
|
|
268
220
|
spanType: span.type,
|
|
269
221
|
...utils.omitKeys(attributes, attributesToOmit),
|
|
270
|
-
...
|
|
222
|
+
...utils.omitKeys(metadata, metadataToOmit)
|
|
271
223
|
};
|
|
272
224
|
if (span.errorInfo) {
|
|
273
225
|
payload.level = "ERROR";
|
|
@@ -283,9 +235,9 @@ var LangfuseExporter = class extends observability.BaseExporter {
|
|
|
283
235
|
scorerName,
|
|
284
236
|
metadata
|
|
285
237
|
}) {
|
|
286
|
-
if (!this
|
|
238
|
+
if (!this.#client) return;
|
|
287
239
|
try {
|
|
288
|
-
await this
|
|
240
|
+
await this.#client.score({
|
|
289
241
|
id: `${traceId}-${scorerName}`,
|
|
290
242
|
traceId,
|
|
291
243
|
observationId: spanId,
|
|
@@ -304,15 +256,40 @@ var LangfuseExporter = class extends observability.BaseExporter {
|
|
|
304
256
|
});
|
|
305
257
|
}
|
|
306
258
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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();
|
|
310
270
|
}
|
|
311
|
-
this.traceMap.clear();
|
|
312
|
-
await super.shutdown();
|
|
313
271
|
}
|
|
314
272
|
};
|
|
315
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
|
+
|
|
316
292
|
exports.LangfuseExporter = LangfuseExporter;
|
|
293
|
+
exports.withLangfusePrompt = withLangfusePrompt;
|
|
317
294
|
//# sourceMappingURL=index.cjs.map
|
|
318
295
|
//# sourceMappingURL=index.cjs.map
|