@mastra/langfuse 1.0.0-beta.10 → 1.0.0-beta.12
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 +60 -0
- package/README.md +48 -6
- package/dist/index.cjs +94 -191
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +96 -192
- 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 +55 -32
- package/dist/tracing.d.ts.map +1 -1
- package/package.json +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,65 @@
|
|
|
1
1
|
# @mastra/langfuse
|
|
2
2
|
|
|
3
|
+
## 1.0.0-beta.12
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Added `TrackingExporter` base class with improved handling for: ([#11870](https://github.com/mastra-ai/mastra/pull/11870))
|
|
8
|
+
- **Out-of-order span processing**: Spans that arrive before their parents are now queued and processed once dependencies are available
|
|
9
|
+
- **Delayed cleanup**: Trace data is retained briefly after spans end to handle late-arriving updates
|
|
10
|
+
- **Memory management**: Configurable limits on pending and total traces to prevent memory leaks
|
|
11
|
+
|
|
12
|
+
New configuration options on `TrackingExporterConfig`:
|
|
13
|
+
- `earlyQueueMaxAttempts` - Max retry attempts for queued events (default: 5)
|
|
14
|
+
- `earlyQueueTTLMs` - TTL for queued events in ms (default: 30000)
|
|
15
|
+
- `traceCleanupDelayMs` - Delay before cleaning up completed traces (default: 30000)
|
|
16
|
+
- `maxPendingCleanupTraces` - Soft cap on traces awaiting cleanup (default: 100)
|
|
17
|
+
- `maxTotalTraces` - Hard cap on total traces (default: 500)
|
|
18
|
+
|
|
19
|
+
Updated @mastra/braintrust, @mastra/langfuse, @mastra/langsmith, @mastra/posthog to use the new TrackingExporter
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- Updated dependencies [[`ebae12a`](https://github.com/mastra-ai/mastra/commit/ebae12a2dd0212e75478981053b148a2c246962d), [`c61a0a5`](https://github.com/mastra-ai/mastra/commit/c61a0a5de4904c88fd8b3718bc26d1be1c2ec6e7), [`69136e7`](https://github.com/mastra-ai/mastra/commit/69136e748e32f57297728a4e0f9a75988462f1a7), [`449aed2`](https://github.com/mastra-ai/mastra/commit/449aed2ba9d507b75bf93d427646ea94f734dfd1), [`eb648a2`](https://github.com/mastra-ai/mastra/commit/eb648a2cc1728f7678768dd70cd77619b448dab9), [`0131105`](https://github.com/mastra-ai/mastra/commit/0131105532e83bdcbb73352fc7d0879eebf140dc), [`9d5059e`](https://github.com/mastra-ai/mastra/commit/9d5059eae810829935fb08e81a9bb7ecd5b144a7), [`ef756c6`](https://github.com/mastra-ai/mastra/commit/ef756c65f82d16531c43f49a27290a416611e526), [`b00ccd3`](https://github.com/mastra-ai/mastra/commit/b00ccd325ebd5d9e37e34dd0a105caae67eb568f), [`3bdfa75`](https://github.com/mastra-ai/mastra/commit/3bdfa7507a91db66f176ba8221aa28dd546e464a), [`e770de9`](https://github.com/mastra-ai/mastra/commit/e770de941a287a49b1964d44db5a5763d19890a6), [`52e2716`](https://github.com/mastra-ai/mastra/commit/52e2716b42df6eff443de72360ae83e86ec23993), [`27b4040`](https://github.com/mastra-ai/mastra/commit/27b4040bfa1a95d92546f420a02a626b1419a1d6), [`610a70b`](https://github.com/mastra-ai/mastra/commit/610a70bdad282079f0c630e0d7bb284578f20151), [`8dc7f55`](https://github.com/mastra-ai/mastra/commit/8dc7f55900395771da851dc7d78d53ae84fe34ec), [`8379099`](https://github.com/mastra-ai/mastra/commit/8379099fc467af6bef54dd7f80c9bd75bf8bbddf), [`b06be72`](https://github.com/mastra-ai/mastra/commit/b06be7223d5ef23edc98c01a67ef713c6cc039f9), [`8c0ec25`](https://github.com/mastra-ai/mastra/commit/8c0ec25646c8a7df253ed1e5ff4863a0d3f1316c), [`ff4d9a6`](https://github.com/mastra-ai/mastra/commit/ff4d9a6704fc87b31a380a76ed22736fdedbba5a), [`69821ef`](https://github.com/mastra-ai/mastra/commit/69821ef806482e2c44e2197ac0b050c3fe3a5285), [`1ed5716`](https://github.com/mastra-ai/mastra/commit/1ed5716830867b3774c4a1b43cc0d82935f32b96), [`4186bdd`](https://github.com/mastra-ai/mastra/commit/4186bdd00731305726fa06adba0b076a1d50b49f), [`7aaf973`](https://github.com/mastra-ai/mastra/commit/7aaf973f83fbbe9521f1f9e7a4fd99b8de464617)]:
|
|
24
|
+
- @mastra/core@1.0.0-beta.22
|
|
25
|
+
- @mastra/observability@1.0.0-beta.11
|
|
26
|
+
|
|
27
|
+
## 1.0.0-beta.11
|
|
28
|
+
|
|
29
|
+
### Minor Changes
|
|
30
|
+
|
|
31
|
+
- feat(observability): add zero-config environment variable support for all exporters ([#11686](https://github.com/mastra-ai/mastra/pull/11686))
|
|
32
|
+
|
|
33
|
+
All observability exporters now support zero-config setup via environment variables. Set the appropriate environment variables and instantiate exporters with no configuration:
|
|
34
|
+
- **Langfuse**: `LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY`, `LANGFUSE_BASE_URL`
|
|
35
|
+
- **Braintrust**: `BRAINTRUST_API_KEY`, `BRAINTRUST_ENDPOINT`
|
|
36
|
+
- **PostHog**: `POSTHOG_API_KEY`, `POSTHOG_HOST`
|
|
37
|
+
- **Arize/Phoenix**: `ARIZE_SPACE_ID`, `ARIZE_API_KEY`, `ARIZE_PROJECT_NAME`, `PHOENIX_ENDPOINT`, `PHOENIX_API_KEY`, `PHOENIX_PROJECT_NAME`
|
|
38
|
+
- **OTEL Providers**:
|
|
39
|
+
- Dash0: `DASH0_API_KEY`, `DASH0_ENDPOINT`, `DASH0_DATASET`
|
|
40
|
+
- SigNoz: `SIGNOZ_API_KEY`, `SIGNOZ_REGION`, `SIGNOZ_ENDPOINT`
|
|
41
|
+
- New Relic: `NEW_RELIC_LICENSE_KEY`, `NEW_RELIC_ENDPOINT`
|
|
42
|
+
- Traceloop: `TRACELOOP_API_KEY`, `TRACELOOP_DESTINATION_ID`, `TRACELOOP_ENDPOINT`
|
|
43
|
+
- Laminar: `LMNR_PROJECT_API_KEY`, `LAMINAR_ENDPOINT`
|
|
44
|
+
|
|
45
|
+
Example usage:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
// Zero-config - reads from environment variables
|
|
49
|
+
new LangfuseExporter();
|
|
50
|
+
new BraintrustExporter();
|
|
51
|
+
new PosthogExporter();
|
|
52
|
+
new ArizeExporter();
|
|
53
|
+
new OtelExporter({ provider: { signoz: {} } });
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Explicit configuration still works and takes precedence over environment variables.
|
|
57
|
+
|
|
58
|
+
### Patch Changes
|
|
59
|
+
|
|
60
|
+
- Updated dependencies [[`08766f1`](https://github.com/mastra-ai/mastra/commit/08766f15e13ac0692fde2a8bd366c2e16e4321df), [`ae8baf7`](https://github.com/mastra-ai/mastra/commit/ae8baf7d8adcb0ff9dac11880400452bc49b33ff), [`cfabdd4`](https://github.com/mastra-ai/mastra/commit/cfabdd4aae7a726b706942d6836eeca110fb6267), [`a0e437f`](https://github.com/mastra-ai/mastra/commit/a0e437fac561b28ee719e0302d72b2f9b4c138f0), [`bec5efd`](https://github.com/mastra-ai/mastra/commit/bec5efde96653ccae6604e68c696d1bc6c1a0bf5), [`9eedf7d`](https://github.com/mastra-ai/mastra/commit/9eedf7de1d6e0022a2f4e5e9e6fe1ec468f9b43c)]:
|
|
61
|
+
- @mastra/core@1.0.0-beta.21
|
|
62
|
+
|
|
3
63
|
## 1.0.0-beta.10
|
|
4
64
|
|
|
5
65
|
### Patch Changes
|
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:
|
|
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,6 +66,16 @@ 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
|
package/dist/index.cjs
CHANGED
|
@@ -6,6 +6,8 @@ var observability = require('@mastra/observability');
|
|
|
6
6
|
var langfuse = require('langfuse');
|
|
7
7
|
|
|
8
8
|
// src/tracing.ts
|
|
9
|
+
|
|
10
|
+
// src/metrics.ts
|
|
9
11
|
function formatUsageMetrics(usage) {
|
|
10
12
|
if (!usage) return {};
|
|
11
13
|
const metrics = {};
|
|
@@ -25,204 +27,106 @@ function formatUsageMetrics(usage) {
|
|
|
25
27
|
if (usage.outputDetails?.reasoning !== void 0) {
|
|
26
28
|
metrics.reasoning = usage.outputDetails.reasoning;
|
|
27
29
|
}
|
|
28
|
-
if (metrics.input && metrics.output) {
|
|
30
|
+
if (metrics.input != null && metrics.output != null) {
|
|
29
31
|
metrics.total = metrics.input + metrics.output;
|
|
30
|
-
if (metrics.cache_write_input_tokens) {
|
|
32
|
+
if (metrics.cache_write_input_tokens != null) {
|
|
31
33
|
metrics.total += metrics.cache_write_input_tokens;
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
36
|
return metrics;
|
|
35
37
|
}
|
|
36
|
-
|
|
38
|
+
|
|
39
|
+
// src/tracing.ts
|
|
40
|
+
var LangfuseExporter = class extends observability.TrackingExporter {
|
|
37
41
|
name = "langfuse";
|
|
38
|
-
client;
|
|
39
|
-
realtime;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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";
|
|
45
58
|
this.setDisabled(
|
|
46
|
-
`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.`
|
|
47
60
|
);
|
|
48
|
-
this.client = null;
|
|
49
61
|
return;
|
|
50
62
|
}
|
|
51
|
-
this
|
|
52
|
-
publicKey
|
|
53
|
-
secretKey
|
|
54
|
-
baseUrl
|
|
63
|
+
this.#client = new langfuse.Langfuse({
|
|
64
|
+
publicKey,
|
|
65
|
+
secretKey,
|
|
66
|
+
baseUrl,
|
|
55
67
|
...config.options
|
|
56
68
|
});
|
|
57
69
|
}
|
|
58
|
-
async
|
|
59
|
-
if (
|
|
60
|
-
await this
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
switch (event.type) {
|
|
64
|
-
case "span_started":
|
|
65
|
-
await this.handleSpanStarted(event.exportedSpan);
|
|
66
|
-
break;
|
|
67
|
-
case "span_updated":
|
|
68
|
-
await this.handleSpanUpdateOrEnd(event.exportedSpan, false);
|
|
69
|
-
break;
|
|
70
|
-
case "span_ended":
|
|
71
|
-
await this.handleSpanUpdateOrEnd(event.exportedSpan, true);
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
if (this.realtime) {
|
|
75
|
-
await this.client.flushAsync();
|
|
70
|
+
async _postExportTracingEvent() {
|
|
71
|
+
if (this.#realtime) {
|
|
72
|
+
await this.#client?.flushAsync();
|
|
76
73
|
}
|
|
77
74
|
}
|
|
78
|
-
async
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
if (!span.isRootSpan) {
|
|
88
|
-
const langfuseData = span.metadata?.langfuse;
|
|
89
|
-
traceData.spanMetadata.set(span.id, {
|
|
90
|
-
parentSpanId: span.parentSpanId,
|
|
91
|
-
langfusePrompt: langfuseData?.prompt
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
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 });
|
|
95
82
|
if (!langfuseParent) {
|
|
96
83
|
return;
|
|
97
84
|
}
|
|
98
85
|
const payload = this.buildSpanPayload(span, true, traceData);
|
|
99
|
-
|
|
100
|
-
traceData.spans.set(span.id, langfuseSpan);
|
|
101
|
-
traceData.activeSpans.add(span.id);
|
|
102
|
-
}
|
|
103
|
-
async handleSpanUpdateOrEnd(span, isEnd) {
|
|
104
|
-
const method = isEnd ? "handleSpanEnd" : "handleSpanUpdate";
|
|
105
|
-
const traceData = this.getTraceData({ span, method });
|
|
106
|
-
if (!traceData) {
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
const langfuseSpan = traceData.spans.get(span.id);
|
|
110
|
-
if (!langfuseSpan) {
|
|
111
|
-
if (isEnd && span.isEvent) {
|
|
112
|
-
traceData.activeSpans.delete(span.id);
|
|
113
|
-
if (traceData.activeSpans.size === 0) {
|
|
114
|
-
this.traceMap.delete(span.traceId);
|
|
115
|
-
}
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
this.logger.warn("Langfuse exporter: No Langfuse span found for span update/end", {
|
|
119
|
-
traceId: span.traceId,
|
|
120
|
-
spanId: span.id,
|
|
121
|
-
spanName: span.name,
|
|
122
|
-
spanType: span.type,
|
|
123
|
-
isRootSpan: span.isRootSpan,
|
|
124
|
-
parentSpanId: span.parentSpanId,
|
|
125
|
-
method
|
|
126
|
-
});
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
langfuseSpan.update(this.buildSpanPayload(span, false, traceData));
|
|
130
|
-
if (isEnd) {
|
|
131
|
-
traceData.activeSpans.delete(span.id);
|
|
132
|
-
if (span.isRootSpan) {
|
|
133
|
-
traceData.trace.update({ output: span.output });
|
|
134
|
-
}
|
|
135
|
-
if (traceData.activeSpans.size === 0) {
|
|
136
|
-
this.traceMap.delete(span.traceId);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
86
|
+
return langfuseParent.event(payload);
|
|
139
87
|
}
|
|
140
|
-
async
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
traceId: span.traceId,
|
|
144
|
-
spanId: span.id,
|
|
145
|
-
spanName: span.name,
|
|
146
|
-
method: "handleEventSpan"
|
|
147
|
-
});
|
|
148
|
-
this.initTrace(span);
|
|
149
|
-
}
|
|
150
|
-
const method = "handleEventSpan";
|
|
151
|
-
const traceData = this.getTraceData({ span, method });
|
|
152
|
-
if (!traceData) {
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
const langfuseParent = this.getLangfuseParent({ traceData, span, method });
|
|
88
|
+
async _buildSpan(args) {
|
|
89
|
+
const { span, traceData } = args;
|
|
90
|
+
const langfuseParent = traceData.getParentOrRoot({ span });
|
|
156
91
|
if (!langfuseParent) {
|
|
157
92
|
return;
|
|
158
93
|
}
|
|
159
94
|
const payload = this.buildSpanPayload(span, true, traceData);
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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;
|
|
165
102
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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`, {
|
|
169
108
|
traceId: span.traceId,
|
|
170
|
-
spanId:
|
|
171
|
-
|
|
109
|
+
spanId: langfuseSpan.id,
|
|
110
|
+
method: "_updateSpan"
|
|
172
111
|
});
|
|
173
|
-
|
|
112
|
+
const updatePayload = this.buildSpanPayload(span, false, traceData);
|
|
113
|
+
langfuseSpan.update(updatePayload);
|
|
174
114
|
}
|
|
175
|
-
const trace = this.client.trace(this.buildTracePayload(span));
|
|
176
|
-
const langfuseData = span.metadata?.langfuse;
|
|
177
|
-
const spanMetadata = /* @__PURE__ */ new Map();
|
|
178
|
-
spanMetadata.set(span.id, {
|
|
179
|
-
parentSpanId: void 0,
|
|
180
|
-
langfusePrompt: langfuseData?.prompt
|
|
181
|
-
});
|
|
182
|
-
this.traceMap.set(span.traceId, {
|
|
183
|
-
trace,
|
|
184
|
-
spans: /* @__PURE__ */ new Map(),
|
|
185
|
-
spanMetadata,
|
|
186
|
-
events: /* @__PURE__ */ new Map(),
|
|
187
|
-
activeSpans: /* @__PURE__ */ new Set(),
|
|
188
|
-
rootSpanId: span.id
|
|
189
|
-
});
|
|
190
115
|
}
|
|
191
|
-
|
|
192
|
-
const { span,
|
|
193
|
-
|
|
194
|
-
|
|
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 });
|
|
195
123
|
}
|
|
196
|
-
this.logger.warn("Langfuse exporter: No trace data found for span", {
|
|
197
|
-
traceId: span.traceId,
|
|
198
|
-
spanId: span.id,
|
|
199
|
-
spanName: span.name,
|
|
200
|
-
spanType: span.type,
|
|
201
|
-
isRootSpan: span.isRootSpan,
|
|
202
|
-
parentSpanId: span.parentSpanId,
|
|
203
|
-
method
|
|
204
|
-
});
|
|
205
124
|
}
|
|
206
|
-
|
|
207
|
-
const {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
if (traceData.spans.has(parentId)) {
|
|
213
|
-
return traceData.spans.get(parentId);
|
|
214
|
-
}
|
|
215
|
-
if (traceData.events.has(parentId)) {
|
|
216
|
-
return traceData.events.get(parentId);
|
|
217
|
-
}
|
|
218
|
-
this.logger.warn("Langfuse exporter: No parent data found for span", {
|
|
219
|
-
traceId: span.traceId,
|
|
220
|
-
spanId: span.id,
|
|
221
|
-
spanName: span.name,
|
|
222
|
-
spanType: span.type,
|
|
223
|
-
isRootSpan: span.isRootSpan,
|
|
224
|
-
parentSpanId: span.parentSpanId,
|
|
225
|
-
method
|
|
125
|
+
async _abortSpan(args) {
|
|
126
|
+
const { span, reason } = args;
|
|
127
|
+
span.end({
|
|
128
|
+
level: "ERROR",
|
|
129
|
+
statusMessage: reason.message
|
|
226
130
|
});
|
|
227
131
|
}
|
|
228
132
|
buildTracePayload(span) {
|
|
@@ -243,18 +147,27 @@ var LangfuseExporter = class extends observability.BaseExporter {
|
|
|
243
147
|
return payload;
|
|
244
148
|
}
|
|
245
149
|
/**
|
|
246
|
-
* Look up the Langfuse prompt from the closest
|
|
150
|
+
* Look up the Langfuse prompt from the closest span that has one.
|
|
247
151
|
* This enables prompt inheritance for MODEL_GENERATION spans when the prompt
|
|
248
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
|
|
249
157
|
*/
|
|
250
|
-
|
|
251
|
-
let currentSpanId = span.
|
|
158
|
+
findLangfusePrompt(traceData, span) {
|
|
159
|
+
let currentSpanId = span.id;
|
|
252
160
|
while (currentSpanId) {
|
|
253
|
-
const
|
|
254
|
-
if (
|
|
255
|
-
|
|
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;
|
|
256
169
|
}
|
|
257
|
-
currentSpanId =
|
|
170
|
+
currentSpanId = traceData.getParentId({ spanId: currentSpanId });
|
|
258
171
|
}
|
|
259
172
|
return void 0;
|
|
260
173
|
}
|
|
@@ -264,19 +177,13 @@ var LangfuseExporter = class extends observability.BaseExporter {
|
|
|
264
177
|
payload.id = span.id;
|
|
265
178
|
payload.name = span.name;
|
|
266
179
|
payload.startTime = span.startTime;
|
|
267
|
-
if (span.input !== void 0) payload.input = span.input;
|
|
268
180
|
}
|
|
181
|
+
if (span.input !== void 0) payload.input = span.input;
|
|
269
182
|
if (span.output !== void 0) payload.output = span.output;
|
|
270
183
|
if (span.endTime !== void 0) payload.endTime = span.endTime;
|
|
271
184
|
const attributes = span.attributes ?? {};
|
|
272
|
-
const resolvedTraceData = traceData ?? this.traceMap.get(span.traceId);
|
|
273
|
-
let inheritedLangfusePrompt;
|
|
274
|
-
if (span.type === observability$1.SpanType.MODEL_GENERATION && !span.metadata?.langfuse && resolvedTraceData) {
|
|
275
|
-
inheritedLangfusePrompt = this.findParentLangfusePrompt(resolvedTraceData, span);
|
|
276
|
-
}
|
|
277
185
|
const metadata = {
|
|
278
|
-
...span.metadata
|
|
279
|
-
...inheritedLangfusePrompt ? { langfuse: { prompt: inheritedLangfusePrompt } } : {}
|
|
186
|
+
...span.metadata
|
|
280
187
|
};
|
|
281
188
|
const attributesToOmit = [];
|
|
282
189
|
const metadataToOmit = [];
|
|
@@ -294,8 +201,7 @@ var LangfuseExporter = class extends observability.BaseExporter {
|
|
|
294
201
|
payload.modelParameters = modelAttr.parameters;
|
|
295
202
|
attributesToOmit.push("parameters");
|
|
296
203
|
}
|
|
297
|
-
const
|
|
298
|
-
const promptData = langfuseData?.prompt;
|
|
204
|
+
const promptData = this.findLangfusePrompt(traceData, span);
|
|
299
205
|
const hasNameAndVersion = promptData?.name !== void 0 && promptData?.version !== void 0;
|
|
300
206
|
const hasId = promptData?.id !== void 0;
|
|
301
207
|
if (hasNameAndVersion || hasId) {
|
|
@@ -329,9 +235,9 @@ var LangfuseExporter = class extends observability.BaseExporter {
|
|
|
329
235
|
scorerName,
|
|
330
236
|
metadata
|
|
331
237
|
}) {
|
|
332
|
-
if (!this
|
|
238
|
+
if (!this.#client) return;
|
|
333
239
|
try {
|
|
334
|
-
await this
|
|
240
|
+
await this.#client.score({
|
|
335
241
|
id: `${traceId}-${scorerName}`,
|
|
336
242
|
traceId,
|
|
337
243
|
observationId: spanId,
|
|
@@ -350,12 +256,10 @@ var LangfuseExporter = class extends observability.BaseExporter {
|
|
|
350
256
|
});
|
|
351
257
|
}
|
|
352
258
|
}
|
|
353
|
-
async
|
|
354
|
-
if (this
|
|
355
|
-
await this
|
|
259
|
+
async _postShutdown() {
|
|
260
|
+
if (this.#client) {
|
|
261
|
+
await this.#client.shutdownAsync();
|
|
356
262
|
}
|
|
357
|
-
this.traceMap.clear();
|
|
358
|
-
await super.shutdown();
|
|
359
263
|
}
|
|
360
264
|
};
|
|
361
265
|
|
|
@@ -378,7 +282,6 @@ function withLangfusePrompt(prompt) {
|
|
|
378
282
|
}
|
|
379
283
|
|
|
380
284
|
exports.LangfuseExporter = LangfuseExporter;
|
|
381
|
-
exports.formatUsageMetrics = formatUsageMetrics;
|
|
382
285
|
exports.withLangfusePrompt = withLangfusePrompt;
|
|
383
286
|
//# sourceMappingURL=index.cjs.map
|
|
384
287
|
//# sourceMappingURL=index.cjs.map
|