@mastra/langfuse 0.0.0-error-handler-fix-20251020202607 → 0.0.0-execa-dynamic-import-20260304221256
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 +578 -3
- package/LICENSE.md +15 -0
- package/README.md +50 -8
- package/dist/helpers.d.ts +79 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/index.cjs +193 -214
- 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 +192 -214
- 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 +96 -0
- package/dist/tracing.d.ts.map +1 -0
- package/package.json +18 -12
- package/dist/ai-tracing.d.ts +0 -68
- package/dist/ai-tracing.d.ts.map +0 -1
package/LICENSE.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
Portions of this software are licensed as follows:
|
|
2
|
+
|
|
3
|
+
- All content that resides under any directory named "ee/" within this
|
|
4
|
+
repository, including but not limited to:
|
|
5
|
+
- `packages/core/src/auth/ee/`
|
|
6
|
+
- `packages/server/src/server/auth/ee/`
|
|
7
|
+
is licensed under the license defined in `ee/LICENSE`.
|
|
8
|
+
|
|
9
|
+
- All third-party components incorporated into the Mastra Software are
|
|
10
|
+
licensed under the original license provided by the owner of the
|
|
11
|
+
applicable component.
|
|
12
|
+
|
|
13
|
+
- Content outside of the above-mentioned directories or restrictions is
|
|
14
|
+
available under the "Apache License 2.0" as defined below.
|
|
15
|
+
|
|
1
16
|
# Apache License 2.0
|
|
2
17
|
|
|
3
18
|
Copyright (c) 2025 Kepler Software, Inc.
|
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,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
|
-
###
|
|
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
|
|
@@ -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,137 @@
|
|
|
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
|
+
|
|
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?.cacheRead !== void 0) {
|
|
17
|
+
metrics.cache_read_input_tokens = usage.inputDetails.cacheRead;
|
|
18
|
+
metrics.input -= metrics.cache_read_input_tokens;
|
|
19
|
+
}
|
|
20
|
+
if (usage.inputDetails?.cacheWrite !== void 0) {
|
|
21
|
+
metrics.cache_write_input_tokens = usage.inputDetails.cacheWrite;
|
|
22
|
+
metrics.input -= metrics.cache_write_input_tokens;
|
|
23
|
+
}
|
|
24
|
+
if (metrics.input < 0) metrics.input = 0;
|
|
25
|
+
}
|
|
26
|
+
if (usage.outputTokens !== void 0) {
|
|
27
|
+
metrics.output = usage.outputTokens;
|
|
28
|
+
}
|
|
29
|
+
if (usage.outputDetails?.reasoning !== void 0) {
|
|
30
|
+
metrics.reasoning = usage.outputDetails.reasoning;
|
|
31
|
+
}
|
|
32
|
+
if (metrics.input != null && metrics.output != null) {
|
|
33
|
+
metrics.total = metrics.input + metrics.output;
|
|
34
|
+
if (metrics.cache_read_input_tokens != null) {
|
|
35
|
+
metrics.total += metrics.cache_read_input_tokens;
|
|
36
|
+
}
|
|
37
|
+
if (metrics.cache_write_input_tokens != null) {
|
|
38
|
+
metrics.total += metrics.cache_write_input_tokens;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return metrics;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/tracing.ts
|
|
45
|
+
var LangfuseExporter = class extends observability.TrackingExporter {
|
|
9
46
|
name = "langfuse";
|
|
10
|
-
client;
|
|
11
|
-
realtime;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
47
|
+
#client;
|
|
48
|
+
#realtime;
|
|
49
|
+
constructor(config = {}) {
|
|
50
|
+
const publicKey = config.publicKey ?? process.env.LANGFUSE_PUBLIC_KEY;
|
|
51
|
+
const secretKey = config.secretKey ?? process.env.LANGFUSE_SECRET_KEY;
|
|
52
|
+
const baseUrl = config.baseUrl ?? process.env.LANGFUSE_BASE_URL;
|
|
53
|
+
super({
|
|
54
|
+
...config,
|
|
55
|
+
publicKey,
|
|
56
|
+
secretKey,
|
|
57
|
+
baseUrl
|
|
58
|
+
});
|
|
59
|
+
this.#realtime = config.realtime ?? false;
|
|
60
|
+
if (!publicKey || !secretKey) {
|
|
61
|
+
const publicKeySource = config.publicKey ? "from config" : process.env.LANGFUSE_PUBLIC_KEY ? "from env" : "missing";
|
|
62
|
+
const secretKeySource = config.secretKey ? "from config" : process.env.LANGFUSE_SECRET_KEY ? "from env" : "missing";
|
|
63
|
+
this.setDisabled(
|
|
64
|
+
`Missing required credentials (publicKey: ${publicKeySource}, secretKey: ${secretKeySource}). Set LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY environment variables or pass them in config.`
|
|
65
|
+
);
|
|
23
66
|
return;
|
|
24
67
|
}
|
|
25
|
-
this
|
|
26
|
-
publicKey
|
|
27
|
-
secretKey
|
|
28
|
-
baseUrl
|
|
68
|
+
this.#client = new langfuse.Langfuse({
|
|
69
|
+
publicKey,
|
|
70
|
+
secretKey,
|
|
71
|
+
baseUrl,
|
|
29
72
|
...config.options
|
|
30
73
|
});
|
|
31
74
|
}
|
|
32
|
-
async
|
|
33
|
-
if (
|
|
34
|
-
|
|
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();
|
|
75
|
+
async _postExportTracingEvent() {
|
|
76
|
+
if (this.#realtime) {
|
|
77
|
+
await this.#client?.flushAsync();
|
|
53
78
|
}
|
|
54
79
|
}
|
|
55
|
-
async
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
const langfuseParent = this.getLangfuseParent({ traceData, span, method });
|
|
80
|
+
async _buildRoot(args) {
|
|
81
|
+
const { span } = args;
|
|
82
|
+
return this.#client?.trace(this.buildTracePayload(span));
|
|
83
|
+
}
|
|
84
|
+
async _buildEvent(args) {
|
|
85
|
+
const { span, traceData } = args;
|
|
86
|
+
const langfuseParent = traceData.getParentOrRoot({ span });
|
|
65
87
|
if (!langfuseParent) {
|
|
66
88
|
return;
|
|
67
89
|
}
|
|
68
|
-
const payload = this.buildSpanPayload(span, true);
|
|
69
|
-
|
|
70
|
-
traceData.spans.set(span.id, langfuseSpan);
|
|
71
|
-
traceData.activeSpans.add(span.id);
|
|
90
|
+
const payload = this.buildSpanPayload(span, true, traceData);
|
|
91
|
+
return langfuseParent.event(payload);
|
|
72
92
|
}
|
|
73
|
-
async
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
if (!
|
|
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
|
-
});
|
|
93
|
+
async _buildSpan(args) {
|
|
94
|
+
const { span, traceData } = args;
|
|
95
|
+
const langfuseParent = traceData.getParentOrRoot({ span });
|
|
96
|
+
if (!langfuseParent) {
|
|
97
97
|
return;
|
|
98
98
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
}
|
|
99
|
+
const payload = this.buildSpanPayload(span, true, traceData);
|
|
100
|
+
const langfuseSpan = span.type === observability$1.SpanType.MODEL_GENERATION ? langfuseParent.generation(payload) : langfuseParent.span(payload);
|
|
101
|
+
this.logger.debug(`${this.name}: built span`, {
|
|
102
|
+
traceId: span.traceId,
|
|
103
|
+
spanId: payload.id,
|
|
104
|
+
method: "_buildSpan"
|
|
105
|
+
});
|
|
106
|
+
return langfuseSpan;
|
|
109
107
|
}
|
|
110
|
-
async
|
|
111
|
-
|
|
112
|
-
|
|
108
|
+
async _updateSpan(args) {
|
|
109
|
+
const { span, traceData } = args;
|
|
110
|
+
const langfuseSpan = traceData.getSpan({ spanId: span.id });
|
|
111
|
+
if (langfuseSpan) {
|
|
112
|
+
this.logger.debug(`${this.name}: found span for update`, {
|
|
113
113
|
traceId: span.traceId,
|
|
114
|
-
spanId:
|
|
115
|
-
|
|
116
|
-
method: "handleEventSpan"
|
|
114
|
+
spanId: langfuseSpan.id,
|
|
115
|
+
method: "_updateSpan"
|
|
117
116
|
});
|
|
118
|
-
this.
|
|
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;
|
|
128
|
-
}
|
|
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);
|
|
117
|
+
const updatePayload = this.buildSpanPayload(span, false, traceData);
|
|
118
|
+
langfuseSpan.update(updatePayload);
|
|
134
119
|
}
|
|
135
120
|
}
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
rootSpanId: span.id
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
getTraceData(options) {
|
|
147
|
-
const { span, method } = options;
|
|
148
|
-
if (this.traceMap.has(span.traceId)) {
|
|
149
|
-
return this.traceMap.get(span.traceId);
|
|
121
|
+
async _finishSpan(args) {
|
|
122
|
+
const { span, traceData } = args;
|
|
123
|
+
const langfuseSpan = traceData.getSpan({ spanId: span.id });
|
|
124
|
+
langfuseSpan?.update(this.buildSpanPayload(span, false, traceData));
|
|
125
|
+
if (span.isRootSpan) {
|
|
126
|
+
const langfuseRoot = traceData.getRoot();
|
|
127
|
+
langfuseRoot?.update({ output: span.output });
|
|
150
128
|
}
|
|
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
129
|
}
|
|
161
|
-
|
|
162
|
-
const {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
|
130
|
+
async _abortSpan(args) {
|
|
131
|
+
const { span, reason } = args;
|
|
132
|
+
span.end({
|
|
133
|
+
level: "ERROR",
|
|
134
|
+
statusMessage: reason.message
|
|
181
135
|
});
|
|
182
136
|
}
|
|
183
137
|
buildTracePayload(span) {
|
|
@@ -189,6 +143,7 @@ var LangfuseExporter = class {
|
|
|
189
143
|
if (userId) payload.userId = userId;
|
|
190
144
|
if (sessionId) payload.sessionId = sessionId;
|
|
191
145
|
if (span.input) payload.input = span.input;
|
|
146
|
+
if (span.tags?.length) payload.tags = span.tags;
|
|
192
147
|
payload.metadata = {
|
|
193
148
|
spanType: span.type,
|
|
194
149
|
...span.attributes,
|
|
@@ -197,81 +152,79 @@ var LangfuseExporter = class {
|
|
|
197
152
|
return payload;
|
|
198
153
|
}
|
|
199
154
|
/**
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
* @param usage - Token usage data from AI SDK (v4 or v5 format)
|
|
209
|
-
* @returns Normalized usage object, or undefined if no usage data available
|
|
155
|
+
* Look up the Langfuse prompt from the closest span that has one.
|
|
156
|
+
* This enables prompt inheritance for MODEL_GENERATION spans when the prompt
|
|
157
|
+
* is set on a parent span (e.g., AGENT_RUN) rather than directly on the generation.
|
|
158
|
+
* This enables prompt linking when:
|
|
159
|
+
* - A workflow calls multiple agents, each with different prompts
|
|
160
|
+
* - Nested agents have different prompts
|
|
161
|
+
* - The prompt is set on AGENT_RUN but MODEL_GENERATION inherits it
|
|
210
162
|
*/
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
} else if (normalized.input !== void 0 && normalized.output !== void 0) {
|
|
225
|
-
normalized.total = normalized.input + normalized.output;
|
|
226
|
-
}
|
|
227
|
-
if (usage.reasoningTokens !== void 0) {
|
|
228
|
-
normalized.reasoning = usage.reasoningTokens;
|
|
229
|
-
}
|
|
230
|
-
if (usage.cachedInputTokens !== void 0) {
|
|
231
|
-
normalized.cachedInput = usage.cachedInputTokens;
|
|
232
|
-
}
|
|
233
|
-
if (usage.promptCacheHitTokens !== void 0) {
|
|
234
|
-
normalized.promptCacheHit = usage.promptCacheHitTokens;
|
|
235
|
-
}
|
|
236
|
-
if (usage.promptCacheMissTokens !== void 0) {
|
|
237
|
-
normalized.promptCacheMiss = usage.promptCacheMissTokens;
|
|
163
|
+
findLangfusePrompt(traceData, span) {
|
|
164
|
+
let currentSpanId = span.id;
|
|
165
|
+
while (currentSpanId) {
|
|
166
|
+
const providerMetadata = traceData.getMetadata({ spanId: currentSpanId });
|
|
167
|
+
if (providerMetadata?.prompt) {
|
|
168
|
+
this.logger.debug(`${this.name}: found prompt in provider metadata`, {
|
|
169
|
+
traceId: span.traceId,
|
|
170
|
+
spanId: span.id,
|
|
171
|
+
prompt: providerMetadata?.prompt
|
|
172
|
+
});
|
|
173
|
+
return providerMetadata.prompt;
|
|
174
|
+
}
|
|
175
|
+
currentSpanId = traceData.getParentId({ spanId: currentSpanId });
|
|
238
176
|
}
|
|
239
|
-
return
|
|
177
|
+
return void 0;
|
|
240
178
|
}
|
|
241
|
-
buildSpanPayload(span, isCreate) {
|
|
179
|
+
buildSpanPayload(span, isCreate, traceData) {
|
|
242
180
|
const payload = {};
|
|
243
181
|
if (isCreate) {
|
|
244
182
|
payload.id = span.id;
|
|
245
183
|
payload.name = span.name;
|
|
246
184
|
payload.startTime = span.startTime;
|
|
247
|
-
if (span.input !== void 0) payload.input = span.input;
|
|
248
185
|
}
|
|
186
|
+
if (span.input !== void 0) payload.input = span.input;
|
|
249
187
|
if (span.output !== void 0) payload.output = span.output;
|
|
250
188
|
if (span.endTime !== void 0) payload.endTime = span.endTime;
|
|
251
189
|
const attributes = span.attributes ?? {};
|
|
190
|
+
const metadata = {
|
|
191
|
+
...span.metadata
|
|
192
|
+
};
|
|
252
193
|
const attributesToOmit = [];
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
194
|
+
const metadataToOmit = [];
|
|
195
|
+
if (span.type === observability$1.SpanType.MODEL_GENERATION) {
|
|
196
|
+
const modelAttr = attributes;
|
|
197
|
+
if (modelAttr.model !== void 0) {
|
|
198
|
+
payload.model = modelAttr.model;
|
|
257
199
|
attributesToOmit.push("model");
|
|
258
200
|
}
|
|
259
|
-
if (
|
|
260
|
-
|
|
261
|
-
if (normalizedUsage) {
|
|
262
|
-
payload.usage = normalizedUsage;
|
|
263
|
-
}
|
|
201
|
+
if (modelAttr.usage !== void 0) {
|
|
202
|
+
payload.usageDetails = formatUsageMetrics(modelAttr.usage);
|
|
264
203
|
attributesToOmit.push("usage");
|
|
265
204
|
}
|
|
266
|
-
if (
|
|
267
|
-
payload.modelParameters =
|
|
205
|
+
if (modelAttr.parameters !== void 0) {
|
|
206
|
+
payload.modelParameters = modelAttr.parameters;
|
|
268
207
|
attributesToOmit.push("parameters");
|
|
269
208
|
}
|
|
209
|
+
const promptData = this.findLangfusePrompt(traceData, span);
|
|
210
|
+
const hasNameAndVersion = promptData?.name !== void 0 && promptData?.version !== void 0;
|
|
211
|
+
const hasId = promptData?.id !== void 0;
|
|
212
|
+
if (hasNameAndVersion || hasId) {
|
|
213
|
+
payload.prompt = {};
|
|
214
|
+
if (promptData?.name !== void 0) payload.prompt.name = promptData.name;
|
|
215
|
+
if (promptData?.version !== void 0) payload.prompt.version = promptData.version;
|
|
216
|
+
if (promptData?.id !== void 0) payload.prompt.id = promptData.id;
|
|
217
|
+
metadataToOmit.push("langfuse");
|
|
218
|
+
}
|
|
219
|
+
if (modelAttr.completionStartTime !== void 0) {
|
|
220
|
+
payload.completionStartTime = modelAttr.completionStartTime;
|
|
221
|
+
attributesToOmit.push("completionStartTime");
|
|
222
|
+
}
|
|
270
223
|
}
|
|
271
224
|
payload.metadata = {
|
|
272
225
|
spanType: span.type,
|
|
273
|
-
...
|
|
274
|
-
...
|
|
226
|
+
...utils.omitKeys(attributes, attributesToOmit),
|
|
227
|
+
...utils.omitKeys(metadata, metadataToOmit)
|
|
275
228
|
};
|
|
276
229
|
if (span.errorInfo) {
|
|
277
230
|
payload.level = "ERROR";
|
|
@@ -287,9 +240,9 @@ var LangfuseExporter = class {
|
|
|
287
240
|
scorerName,
|
|
288
241
|
metadata
|
|
289
242
|
}) {
|
|
290
|
-
if (!this
|
|
243
|
+
if (!this.#client) return;
|
|
291
244
|
try {
|
|
292
|
-
await this
|
|
245
|
+
await this.#client.score({
|
|
293
246
|
id: `${traceId}-${scorerName}`,
|
|
294
247
|
traceId,
|
|
295
248
|
observationId: spanId,
|
|
@@ -308,14 +261,40 @@ var LangfuseExporter = class {
|
|
|
308
261
|
});
|
|
309
262
|
}
|
|
310
263
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
264
|
+
/**
|
|
265
|
+
* Force flush any buffered data to Langfuse without shutting down.
|
|
266
|
+
*/
|
|
267
|
+
async _flush() {
|
|
268
|
+
if (this.#client) {
|
|
269
|
+
await this.#client.flushAsync();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
async _postShutdown() {
|
|
273
|
+
if (this.#client) {
|
|
274
|
+
await this.#client.shutdownAsync();
|
|
314
275
|
}
|
|
315
|
-
this.traceMap.clear();
|
|
316
276
|
}
|
|
317
277
|
};
|
|
318
278
|
|
|
279
|
+
// src/helpers.ts
|
|
280
|
+
function withLangfusePrompt(prompt) {
|
|
281
|
+
return (opts) => ({
|
|
282
|
+
...opts,
|
|
283
|
+
metadata: {
|
|
284
|
+
...opts.metadata,
|
|
285
|
+
langfuse: {
|
|
286
|
+
...opts.metadata?.langfuse,
|
|
287
|
+
prompt: {
|
|
288
|
+
...prompt.name !== void 0 && { name: prompt.name },
|
|
289
|
+
...prompt.version !== void 0 && { version: prompt.version },
|
|
290
|
+
...prompt.id !== void 0 && { id: prompt.id }
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
319
297
|
exports.LangfuseExporter = LangfuseExporter;
|
|
298
|
+
exports.withLangfusePrompt = withLangfusePrompt;
|
|
320
299
|
//# sourceMappingURL=index.cjs.map
|
|
321
300
|
//# sourceMappingURL=index.cjs.map
|