@mastra/langfuse 0.0.0-stream-vnext-usage-20250908171242 → 0.0.0-testing-cloud-studios-20260114234039
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 +564 -3
- package/README.md +51 -13
- package/dist/helpers.d.ts +79 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/index.cjs +211 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +209 -47
- package/dist/index.js.map +1 -1
- package/dist/tracing.d.ts +69 -0
- package/dist/tracing.d.ts.map +1 -0
- package/package.json +17 -12
- package/dist/ai-tracing.d.ts +0 -41
- package/dist/ai-tracing.d.ts.map +0 -1
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
|
+
|
|
13
26
|
```typescript
|
|
14
27
|
import { LangfuseExporter } from '@mastra/langfuse';
|
|
15
28
|
|
|
16
|
-
// Use with Mastra
|
|
17
29
|
const mastra = new Mastra({
|
|
18
30
|
...,
|
|
19
31
|
observability: {
|
|
20
|
-
|
|
32
|
+
configs: {
|
|
21
33
|
langfuse: {
|
|
22
|
-
serviceName: 'service',
|
|
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
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { LangfuseExporter } from '@mastra/langfuse';
|
|
48
|
+
|
|
49
|
+
const mastra = new Mastra({
|
|
50
|
+
...,
|
|
51
|
+
observability: {
|
|
52
|
+
configs: {
|
|
53
|
+
langfuse: {
|
|
54
|
+
serviceName: 'my-service',
|
|
23
55
|
exporters: [
|
|
24
56
|
new LangfuseExporter({
|
|
25
|
-
publicKey:
|
|
26
|
-
secretKey:
|
|
27
|
-
baseUrl:
|
|
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,16 +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
|
-
###
|
|
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
|
|
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
|
|
46
|
-
|
|
47
|
-
## License
|
|
48
|
-
|
|
49
|
-
Apache 2.0
|
|
@@ -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,51 +1,79 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
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/
|
|
8
|
-
|
|
8
|
+
// src/tracing.ts
|
|
9
|
+
function formatUsageMetrics(usage) {
|
|
10
|
+
if (!usage) return {};
|
|
11
|
+
const metrics = {};
|
|
12
|
+
if (usage.inputTokens !== void 0) {
|
|
13
|
+
metrics.input = usage.inputTokens;
|
|
14
|
+
if (usage.inputDetails?.cacheWrite !== void 0) {
|
|
15
|
+
metrics.cache_write_input_tokens = usage.inputDetails.cacheWrite;
|
|
16
|
+
metrics.input -= metrics.cache_write_input_tokens;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (usage.inputDetails?.cacheRead !== void 0) {
|
|
20
|
+
metrics.cache_read_input_tokens = usage.inputDetails.cacheRead;
|
|
21
|
+
}
|
|
22
|
+
if (usage.outputTokens !== void 0) {
|
|
23
|
+
metrics.output = usage.outputTokens;
|
|
24
|
+
}
|
|
25
|
+
if (usage.outputDetails?.reasoning !== void 0) {
|
|
26
|
+
metrics.reasoning = usage.outputDetails.reasoning;
|
|
27
|
+
}
|
|
28
|
+
if (metrics.input && metrics.output) {
|
|
29
|
+
metrics.total = metrics.input + metrics.output;
|
|
30
|
+
if (metrics.cache_write_input_tokens) {
|
|
31
|
+
metrics.total += metrics.cache_write_input_tokens;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return metrics;
|
|
35
|
+
}
|
|
36
|
+
var LangfuseExporter = class extends observability.BaseExporter {
|
|
9
37
|
name = "langfuse";
|
|
10
38
|
client;
|
|
11
39
|
realtime;
|
|
12
40
|
traceMap = /* @__PURE__ */ new Map();
|
|
13
|
-
|
|
14
|
-
|
|
41
|
+
constructor(config = {}) {
|
|
42
|
+
super(config);
|
|
15
43
|
this.realtime = config.realtime ?? false;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
44
|
+
const publicKey = config.publicKey ?? process.env.LANGFUSE_PUBLIC_KEY;
|
|
45
|
+
const secretKey = config.secretKey ?? process.env.LANGFUSE_SECRET_KEY;
|
|
46
|
+
const baseUrl = config.baseUrl ?? process.env.LANGFUSE_BASE_URL;
|
|
47
|
+
if (!publicKey || !secretKey) {
|
|
48
|
+
const publicKeySource = config.publicKey ? "from config" : process.env.LANGFUSE_PUBLIC_KEY ? "from env" : "missing";
|
|
49
|
+
const secretKeySource = config.secretKey ? "from config" : process.env.LANGFUSE_SECRET_KEY ? "from env" : "missing";
|
|
50
|
+
this.setDisabled(
|
|
51
|
+
`Missing required credentials (publicKey: ${publicKeySource}, secretKey: ${secretKeySource}). Set LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY environment variables or pass them in config.`
|
|
52
|
+
);
|
|
22
53
|
this.client = null;
|
|
23
54
|
return;
|
|
24
55
|
}
|
|
25
56
|
this.client = new langfuse.Langfuse({
|
|
26
|
-
publicKey
|
|
27
|
-
secretKey
|
|
28
|
-
baseUrl
|
|
57
|
+
publicKey,
|
|
58
|
+
secretKey,
|
|
59
|
+
baseUrl,
|
|
29
60
|
...config.options
|
|
30
61
|
});
|
|
31
62
|
}
|
|
32
|
-
async
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
if (event.span.isEvent) {
|
|
37
|
-
await this.handleEventSpan(event.span);
|
|
63
|
+
async _exportTracingEvent(event) {
|
|
64
|
+
if (event.exportedSpan.isEvent) {
|
|
65
|
+
await this.handleEventSpan(event.exportedSpan);
|
|
38
66
|
return;
|
|
39
67
|
}
|
|
40
68
|
switch (event.type) {
|
|
41
69
|
case "span_started":
|
|
42
|
-
await this.handleSpanStarted(event.
|
|
70
|
+
await this.handleSpanStarted(event.exportedSpan);
|
|
43
71
|
break;
|
|
44
72
|
case "span_updated":
|
|
45
|
-
await this.handleSpanUpdateOrEnd(event.
|
|
73
|
+
await this.handleSpanUpdateOrEnd(event.exportedSpan, false);
|
|
46
74
|
break;
|
|
47
75
|
case "span_ended":
|
|
48
|
-
await this.handleSpanUpdateOrEnd(event.
|
|
76
|
+
await this.handleSpanUpdateOrEnd(event.exportedSpan, true);
|
|
49
77
|
break;
|
|
50
78
|
}
|
|
51
79
|
if (this.realtime) {
|
|
@@ -61,13 +89,21 @@ var LangfuseExporter = class {
|
|
|
61
89
|
if (!traceData) {
|
|
62
90
|
return;
|
|
63
91
|
}
|
|
92
|
+
if (!span.isRootSpan) {
|
|
93
|
+
const langfuseData = span.metadata?.langfuse;
|
|
94
|
+
traceData.spanMetadata.set(span.id, {
|
|
95
|
+
parentSpanId: span.parentSpanId,
|
|
96
|
+
langfusePrompt: langfuseData?.prompt
|
|
97
|
+
});
|
|
98
|
+
}
|
|
64
99
|
const langfuseParent = this.getLangfuseParent({ traceData, span, method });
|
|
65
100
|
if (!langfuseParent) {
|
|
66
101
|
return;
|
|
67
102
|
}
|
|
68
|
-
const payload = this.buildSpanPayload(span, true);
|
|
69
|
-
const langfuseSpan = span.type ===
|
|
103
|
+
const payload = this.buildSpanPayload(span, true, traceData);
|
|
104
|
+
const langfuseSpan = span.type === observability$1.SpanType.MODEL_GENERATION ? langfuseParent.generation(payload) : langfuseParent.span(payload);
|
|
70
105
|
traceData.spans.set(span.id, langfuseSpan);
|
|
106
|
+
traceData.activeSpans.add(span.id);
|
|
71
107
|
}
|
|
72
108
|
async handleSpanUpdateOrEnd(span, isEnd) {
|
|
73
109
|
const method = isEnd ? "handleSpanEnd" : "handleSpanUpdate";
|
|
@@ -77,21 +113,33 @@ var LangfuseExporter = class {
|
|
|
77
113
|
}
|
|
78
114
|
const langfuseSpan = traceData.spans.get(span.id);
|
|
79
115
|
if (!langfuseSpan) {
|
|
116
|
+
if (isEnd && span.isEvent) {
|
|
117
|
+
traceData.activeSpans.delete(span.id);
|
|
118
|
+
if (traceData.activeSpans.size === 0) {
|
|
119
|
+
this.traceMap.delete(span.traceId);
|
|
120
|
+
}
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
80
123
|
this.logger.warn("Langfuse exporter: No Langfuse span found for span update/end", {
|
|
81
124
|
traceId: span.traceId,
|
|
82
125
|
spanId: span.id,
|
|
83
126
|
spanName: span.name,
|
|
84
127
|
spanType: span.type,
|
|
85
128
|
isRootSpan: span.isRootSpan,
|
|
86
|
-
parentSpanId: span.
|
|
129
|
+
parentSpanId: span.parentSpanId,
|
|
87
130
|
method
|
|
88
131
|
});
|
|
89
132
|
return;
|
|
90
133
|
}
|
|
91
|
-
langfuseSpan.update(this.buildSpanPayload(span, false));
|
|
92
|
-
if (isEnd
|
|
93
|
-
traceData.
|
|
94
|
-
|
|
134
|
+
langfuseSpan.update(this.buildSpanPayload(span, false, traceData));
|
|
135
|
+
if (isEnd) {
|
|
136
|
+
traceData.activeSpans.delete(span.id);
|
|
137
|
+
if (span.isRootSpan) {
|
|
138
|
+
traceData.trace.update({ output: span.output });
|
|
139
|
+
}
|
|
140
|
+
if (traceData.activeSpans.size === 0) {
|
|
141
|
+
this.traceMap.delete(span.traceId);
|
|
142
|
+
}
|
|
95
143
|
}
|
|
96
144
|
}
|
|
97
145
|
async handleEventSpan(span) {
|
|
@@ -113,13 +161,37 @@ var LangfuseExporter = class {
|
|
|
113
161
|
if (!langfuseParent) {
|
|
114
162
|
return;
|
|
115
163
|
}
|
|
116
|
-
const payload = this.buildSpanPayload(span, true);
|
|
164
|
+
const payload = this.buildSpanPayload(span, true, traceData);
|
|
117
165
|
const langfuseEvent = langfuseParent.event(payload);
|
|
118
166
|
traceData.events.set(span.id, langfuseEvent);
|
|
167
|
+
if (!span.endTime) {
|
|
168
|
+
traceData.activeSpans.add(span.id);
|
|
169
|
+
}
|
|
119
170
|
}
|
|
120
171
|
initTrace(span) {
|
|
172
|
+
if (this.traceMap.has(span.traceId)) {
|
|
173
|
+
this.logger.debug("Langfuse exporter: Reusing existing trace from local map", {
|
|
174
|
+
traceId: span.traceId,
|
|
175
|
+
spanId: span.id,
|
|
176
|
+
spanName: span.name
|
|
177
|
+
});
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
121
180
|
const trace = this.client.trace(this.buildTracePayload(span));
|
|
122
|
-
|
|
181
|
+
const langfuseData = span.metadata?.langfuse;
|
|
182
|
+
const spanMetadata = /* @__PURE__ */ new Map();
|
|
183
|
+
spanMetadata.set(span.id, {
|
|
184
|
+
parentSpanId: void 0,
|
|
185
|
+
langfusePrompt: langfuseData?.prompt
|
|
186
|
+
});
|
|
187
|
+
this.traceMap.set(span.traceId, {
|
|
188
|
+
trace,
|
|
189
|
+
spans: /* @__PURE__ */ new Map(),
|
|
190
|
+
spanMetadata,
|
|
191
|
+
events: /* @__PURE__ */ new Map(),
|
|
192
|
+
activeSpans: /* @__PURE__ */ new Set(),
|
|
193
|
+
rootSpanId: span.id
|
|
194
|
+
});
|
|
123
195
|
}
|
|
124
196
|
getTraceData(options) {
|
|
125
197
|
const { span, method } = options;
|
|
@@ -132,13 +204,13 @@ var LangfuseExporter = class {
|
|
|
132
204
|
spanName: span.name,
|
|
133
205
|
spanType: span.type,
|
|
134
206
|
isRootSpan: span.isRootSpan,
|
|
135
|
-
parentSpanId: span.
|
|
207
|
+
parentSpanId: span.parentSpanId,
|
|
136
208
|
method
|
|
137
209
|
});
|
|
138
210
|
}
|
|
139
211
|
getLangfuseParent(options) {
|
|
140
212
|
const { traceData, span, method } = options;
|
|
141
|
-
const parentId = span.
|
|
213
|
+
const parentId = span.parentSpanId;
|
|
142
214
|
if (!parentId) {
|
|
143
215
|
return traceData.trace;
|
|
144
216
|
}
|
|
@@ -154,7 +226,7 @@ var LangfuseExporter = class {
|
|
|
154
226
|
spanName: span.name,
|
|
155
227
|
spanType: span.type,
|
|
156
228
|
isRootSpan: span.isRootSpan,
|
|
157
|
-
parentSpanId: span.
|
|
229
|
+
parentSpanId: span.parentSpanId,
|
|
158
230
|
method
|
|
159
231
|
});
|
|
160
232
|
}
|
|
@@ -167,6 +239,7 @@ var LangfuseExporter = class {
|
|
|
167
239
|
if (userId) payload.userId = userId;
|
|
168
240
|
if (sessionId) payload.sessionId = sessionId;
|
|
169
241
|
if (span.input) payload.input = span.input;
|
|
242
|
+
if (span.tags?.length) payload.tags = span.tags;
|
|
170
243
|
payload.metadata = {
|
|
171
244
|
spanType: span.type,
|
|
172
245
|
...span.attributes,
|
|
@@ -174,7 +247,23 @@ var LangfuseExporter = class {
|
|
|
174
247
|
};
|
|
175
248
|
return payload;
|
|
176
249
|
}
|
|
177
|
-
|
|
250
|
+
/**
|
|
251
|
+
* Look up the Langfuse prompt from the closest parent span that has one.
|
|
252
|
+
* This enables prompt inheritance for MODEL_GENERATION spans when the prompt
|
|
253
|
+
* is set on a parent span (e.g., AGENT_RUN) rather than directly on the generation.
|
|
254
|
+
*/
|
|
255
|
+
findParentLangfusePrompt(traceData, span) {
|
|
256
|
+
let currentSpanId = span.parentSpanId;
|
|
257
|
+
while (currentSpanId) {
|
|
258
|
+
const parentMetadata = traceData.spanMetadata.get(currentSpanId);
|
|
259
|
+
if (parentMetadata?.langfusePrompt) {
|
|
260
|
+
return parentMetadata.langfusePrompt;
|
|
261
|
+
}
|
|
262
|
+
currentSpanId = parentMetadata?.parentSpanId;
|
|
263
|
+
}
|
|
264
|
+
return void 0;
|
|
265
|
+
}
|
|
266
|
+
buildSpanPayload(span, isCreate, traceData) {
|
|
178
267
|
const payload = {};
|
|
179
268
|
if (isCreate) {
|
|
180
269
|
payload.id = span.id;
|
|
@@ -185,26 +274,51 @@ var LangfuseExporter = class {
|
|
|
185
274
|
if (span.output !== void 0) payload.output = span.output;
|
|
186
275
|
if (span.endTime !== void 0) payload.endTime = span.endTime;
|
|
187
276
|
const attributes = span.attributes ?? {};
|
|
277
|
+
const resolvedTraceData = traceData ?? this.traceMap.get(span.traceId);
|
|
278
|
+
let inheritedLangfusePrompt;
|
|
279
|
+
if (span.type === observability$1.SpanType.MODEL_GENERATION && !span.metadata?.langfuse && resolvedTraceData) {
|
|
280
|
+
inheritedLangfusePrompt = this.findParentLangfusePrompt(resolvedTraceData, span);
|
|
281
|
+
}
|
|
282
|
+
const metadata = {
|
|
283
|
+
...span.metadata,
|
|
284
|
+
...inheritedLangfusePrompt ? { langfuse: { prompt: inheritedLangfusePrompt } } : {}
|
|
285
|
+
};
|
|
188
286
|
const attributesToOmit = [];
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
287
|
+
const metadataToOmit = [];
|
|
288
|
+
if (span.type === observability$1.SpanType.MODEL_GENERATION) {
|
|
289
|
+
const modelAttr = attributes;
|
|
290
|
+
if (modelAttr.model !== void 0) {
|
|
291
|
+
payload.model = modelAttr.model;
|
|
193
292
|
attributesToOmit.push("model");
|
|
194
293
|
}
|
|
195
|
-
if (
|
|
196
|
-
payload.
|
|
294
|
+
if (modelAttr.usage !== void 0) {
|
|
295
|
+
payload.usageDetails = formatUsageMetrics(modelAttr.usage);
|
|
197
296
|
attributesToOmit.push("usage");
|
|
198
297
|
}
|
|
199
|
-
if (
|
|
200
|
-
payload.modelParameters =
|
|
298
|
+
if (modelAttr.parameters !== void 0) {
|
|
299
|
+
payload.modelParameters = modelAttr.parameters;
|
|
201
300
|
attributesToOmit.push("parameters");
|
|
202
301
|
}
|
|
302
|
+
const langfuseData = metadata.langfuse;
|
|
303
|
+
const promptData = langfuseData?.prompt;
|
|
304
|
+
const hasNameAndVersion = promptData?.name !== void 0 && promptData?.version !== void 0;
|
|
305
|
+
const hasId = promptData?.id !== void 0;
|
|
306
|
+
if (hasNameAndVersion || hasId) {
|
|
307
|
+
payload.prompt = {};
|
|
308
|
+
if (promptData?.name !== void 0) payload.prompt.name = promptData.name;
|
|
309
|
+
if (promptData?.version !== void 0) payload.prompt.version = promptData.version;
|
|
310
|
+
if (promptData?.id !== void 0) payload.prompt.id = promptData.id;
|
|
311
|
+
metadataToOmit.push("langfuse");
|
|
312
|
+
}
|
|
313
|
+
if (modelAttr.completionStartTime !== void 0) {
|
|
314
|
+
payload.completionStartTime = modelAttr.completionStartTime;
|
|
315
|
+
attributesToOmit.push("completionStartTime");
|
|
316
|
+
}
|
|
203
317
|
}
|
|
204
318
|
payload.metadata = {
|
|
205
319
|
spanType: span.type,
|
|
206
|
-
...
|
|
207
|
-
...
|
|
320
|
+
...utils.omitKeys(attributes, attributesToOmit),
|
|
321
|
+
...utils.omitKeys(metadata, metadataToOmit)
|
|
208
322
|
};
|
|
209
323
|
if (span.errorInfo) {
|
|
210
324
|
payload.level = "ERROR";
|
|
@@ -212,14 +326,64 @@ var LangfuseExporter = class {
|
|
|
212
326
|
}
|
|
213
327
|
return payload;
|
|
214
328
|
}
|
|
329
|
+
async addScoreToTrace({
|
|
330
|
+
traceId,
|
|
331
|
+
spanId,
|
|
332
|
+
score,
|
|
333
|
+
reason,
|
|
334
|
+
scorerName,
|
|
335
|
+
metadata
|
|
336
|
+
}) {
|
|
337
|
+
if (!this.client) return;
|
|
338
|
+
try {
|
|
339
|
+
await this.client.score({
|
|
340
|
+
id: `${traceId}-${scorerName}`,
|
|
341
|
+
traceId,
|
|
342
|
+
observationId: spanId,
|
|
343
|
+
name: scorerName,
|
|
344
|
+
value: score,
|
|
345
|
+
...metadata?.sessionId ? { sessionId: metadata.sessionId } : {},
|
|
346
|
+
metadata: { ...reason ? { reason } : {} },
|
|
347
|
+
dataType: "NUMERIC"
|
|
348
|
+
});
|
|
349
|
+
} catch (error) {
|
|
350
|
+
this.logger.error("Langfuse exporter: Error adding score to trace", {
|
|
351
|
+
error,
|
|
352
|
+
traceId,
|
|
353
|
+
spanId,
|
|
354
|
+
scorerName
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
215
358
|
async shutdown() {
|
|
216
359
|
if (this.client) {
|
|
217
360
|
await this.client.shutdownAsync();
|
|
218
361
|
}
|
|
219
362
|
this.traceMap.clear();
|
|
363
|
+
await super.shutdown();
|
|
220
364
|
}
|
|
221
365
|
};
|
|
222
366
|
|
|
367
|
+
// src/helpers.ts
|
|
368
|
+
function withLangfusePrompt(prompt) {
|
|
369
|
+
return (opts) => ({
|
|
370
|
+
...opts,
|
|
371
|
+
metadata: {
|
|
372
|
+
...opts.metadata,
|
|
373
|
+
langfuse: {
|
|
374
|
+
...opts.metadata?.langfuse,
|
|
375
|
+
prompt: {
|
|
376
|
+
...prompt.name !== void 0 && { name: prompt.name },
|
|
377
|
+
...prompt.version !== void 0 && { version: prompt.version },
|
|
378
|
+
...prompt.id !== void 0 && { id: prompt.id }
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
|
|
223
385
|
exports.LangfuseExporter = LangfuseExporter;
|
|
386
|
+
exports.formatUsageMetrics = formatUsageMetrics;
|
|
387
|
+
exports.withLangfusePrompt = withLangfusePrompt;
|
|
224
388
|
//# sourceMappingURL=index.cjs.map
|
|
225
389
|
//# sourceMappingURL=index.cjs.map
|