@mastra/langsmith 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 +693 -3
- package/LICENSE.md +15 -0
- package/README.md +44 -5
- package/dist/helpers.d.ts +83 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/index.cjs +175 -205
- 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 +175 -206
- package/dist/index.js.map +1 -1
- package/dist/metrics.d.ts +5 -5
- package/dist/metrics.d.ts.map +1 -1
- package/dist/tracing.d.ts +71 -0
- package/dist/tracing.d.ts.map +1 -0
- package/package.json +19 -13
- package/dist/ai-tracing.d.ts +0 -34
- 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,21 +10,51 @@ npm install @mastra/langsmith
|
|
|
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
|
+
LANGSMITH_API_KEY=lsv2_pt_...
|
|
20
|
+
|
|
21
|
+
# Optional
|
|
22
|
+
LANGCHAIN_PROJECT=my-project # Project name, defaults to "default"
|
|
23
|
+
```
|
|
24
|
+
|
|
13
25
|
```typescript
|
|
14
26
|
import { LangSmithExporter } from '@mastra/langsmith';
|
|
15
27
|
|
|
16
|
-
|
|
28
|
+
const mastra = new Mastra({
|
|
29
|
+
...,
|
|
30
|
+
observability: {
|
|
31
|
+
configs: {
|
|
32
|
+
langsmith: {
|
|
33
|
+
serviceName: 'my-service',
|
|
34
|
+
exporters: [new LangSmithExporter()],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Explicit Configuration
|
|
42
|
+
|
|
43
|
+
You can also pass credentials directly:
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { LangSmithExporter } from '@mastra/langsmith';
|
|
17
47
|
|
|
18
|
-
// Use with Mastra
|
|
19
48
|
const mastra = new Mastra({
|
|
20
49
|
...,
|
|
21
50
|
observability: {
|
|
22
51
|
configs: {
|
|
23
52
|
langsmith: {
|
|
24
|
-
serviceName: 'service',
|
|
53
|
+
serviceName: 'my-service',
|
|
25
54
|
exporters: [
|
|
26
55
|
new LangSmithExporter({
|
|
27
|
-
apiKey:
|
|
56
|
+
apiKey: 'lsv2_pt_...',
|
|
57
|
+
projectName: 'my-custom-project', // Optional
|
|
28
58
|
}),
|
|
29
59
|
],
|
|
30
60
|
},
|
|
@@ -33,9 +63,18 @@ const mastra = new Mastra({
|
|
|
33
63
|
});
|
|
34
64
|
```
|
|
35
65
|
|
|
66
|
+
### Configuration Options
|
|
67
|
+
|
|
68
|
+
| Option | Type | Description |
|
|
69
|
+
| ------------- | -------- | ------------------------------------------------------------------------------------------ |
|
|
70
|
+
| `apiKey` | `string` | LangSmith API key. Defaults to `LANGSMITH_API_KEY` env var |
|
|
71
|
+
| `projectName` | `string` | The name of the LangSmith project to send traces to. Overrides `LANGCHAIN_PROJECT` env var |
|
|
72
|
+
| `apiUrl` | `string` | Custom LangSmith API URL (for self-hosted instances) |
|
|
73
|
+
| `client` | `Client` | Custom LangSmith client instance |
|
|
74
|
+
|
|
36
75
|
## Features
|
|
37
76
|
|
|
38
|
-
###
|
|
77
|
+
### Tracing
|
|
39
78
|
|
|
40
79
|
- **Automatic span mapping**: Root spans become LangSmith traces
|
|
41
80
|
- **Type-specific metadata**: Extracts relevant metadata for each span type (agents, tools, workflows)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LangSmith Tracing Options Helpers
|
|
3
|
+
*
|
|
4
|
+
* These helpers integrate with the `buildTracingOptions` pattern from
|
|
5
|
+
* `@mastra/observability` to add LangSmith-specific tracing features.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { buildTracingOptions } from '@mastra/observability';
|
|
10
|
+
* import { withLangsmithMetadata } from '@mastra/langsmith';
|
|
11
|
+
*
|
|
12
|
+
* const agent = new Agent({
|
|
13
|
+
* defaultGenerateOptions: {
|
|
14
|
+
* tracingOptions: buildTracingOptions(
|
|
15
|
+
* withLangsmithMetadata({ projectName: 'my-project' })
|
|
16
|
+
* ),
|
|
17
|
+
* },
|
|
18
|
+
* });
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
import type { TracingOptionsUpdater } from '@mastra/observability';
|
|
22
|
+
/**
|
|
23
|
+
* LangSmith vendor metadata that can be passed via span metadata.
|
|
24
|
+
* These fields are extracted by the LangSmith exporter and used
|
|
25
|
+
* to override default configuration on a per-span basis.
|
|
26
|
+
*/
|
|
27
|
+
export interface LangSmithMetadataInput {
|
|
28
|
+
/**
|
|
29
|
+
* Override the project name for this span and its children.
|
|
30
|
+
* This allows dynamically routing traces to different LangSmith projects.
|
|
31
|
+
*/
|
|
32
|
+
projectName?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Session ID for grouping related traces in LangSmith.
|
|
35
|
+
*/
|
|
36
|
+
sessionId?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Session name for display in LangSmith.
|
|
39
|
+
*/
|
|
40
|
+
sessionName?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Adds LangSmith metadata to the tracing options.
|
|
44
|
+
*
|
|
45
|
+
* The metadata is added under `metadata.langsmith` and allows you to:
|
|
46
|
+
* - Route traces to different LangSmith projects via `projectName`
|
|
47
|
+
* - Group traces by session via `sessionId` and `sessionName`
|
|
48
|
+
*
|
|
49
|
+
* @param metadata - The LangSmith metadata to add
|
|
50
|
+
* @returns A TracingOptionsUpdater function for use with `buildTracingOptions`
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* import { buildTracingOptions } from '@mastra/observability';
|
|
55
|
+
* import { withLangsmithMetadata } from '@mastra/langsmith';
|
|
56
|
+
*
|
|
57
|
+
* // Route traces to a specific project
|
|
58
|
+
* const tracingOptions = buildTracingOptions(
|
|
59
|
+
* withLangsmithMetadata({ projectName: 'customer-support' }),
|
|
60
|
+
* );
|
|
61
|
+
*
|
|
62
|
+
* // Or set multiple fields
|
|
63
|
+
* const tracingOptions = buildTracingOptions(
|
|
64
|
+
* withLangsmithMetadata({
|
|
65
|
+
* projectName: 'my-project',
|
|
66
|
+
* sessionId: 'session-123',
|
|
67
|
+
* }),
|
|
68
|
+
* );
|
|
69
|
+
*
|
|
70
|
+
* // Use in agent config
|
|
71
|
+
* const agent = new Agent({
|
|
72
|
+
* name: 'support-agent',
|
|
73
|
+
* model: openai('gpt-4o'),
|
|
74
|
+
* defaultGenerateOptions: {
|
|
75
|
+
* tracingOptions: buildTracingOptions(
|
|
76
|
+
* withLangsmithMetadata({ projectName: 'support-traces' })
|
|
77
|
+
* ),
|
|
78
|
+
* },
|
|
79
|
+
* });
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export declare function withLangsmithMetadata(metadata: LangSmithMetadataInput): TracingOptionsUpdater;
|
|
83
|
+
//# 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;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,sBAAsB,GAAG,qBAAqB,CAW7F"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,59 +1,65 @@
|
|
|
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 langsmith = require('langsmith');
|
|
6
7
|
|
|
7
|
-
// src/
|
|
8
|
+
// src/tracing.ts
|
|
8
9
|
|
|
9
10
|
// src/metrics.ts
|
|
10
|
-
function
|
|
11
|
+
function formatUsageMetrics(usage) {
|
|
11
12
|
const metrics = {};
|
|
12
|
-
if (
|
|
13
|
-
metrics.input_tokens =
|
|
14
|
-
} else if (llmAttr.usage?.promptTokens !== void 0) {
|
|
15
|
-
metrics.input_tokens = llmAttr.usage?.promptTokens;
|
|
13
|
+
if (usage?.inputTokens !== void 0) {
|
|
14
|
+
metrics.input_tokens = usage.inputTokens;
|
|
16
15
|
}
|
|
17
|
-
if (
|
|
18
|
-
metrics.output_tokens =
|
|
19
|
-
} else if (llmAttr.usage?.completionTokens !== void 0) {
|
|
20
|
-
metrics.output_tokens = llmAttr.usage?.completionTokens;
|
|
16
|
+
if (usage?.outputTokens !== void 0) {
|
|
17
|
+
metrics.output_tokens = usage.outputTokens;
|
|
21
18
|
}
|
|
22
|
-
if (
|
|
23
|
-
metrics.total_tokens =
|
|
24
|
-
} else if (typeof llmAttr.usage?.inputTokens === "number" && typeof llmAttr.usage?.outputTokens === "number") {
|
|
25
|
-
metrics.total_tokens = llmAttr.usage?.inputTokens + llmAttr.usage?.outputTokens;
|
|
19
|
+
if (metrics.input_tokens !== void 0 && metrics.output_tokens !== void 0) {
|
|
20
|
+
metrics.total_tokens = metrics.input_tokens + metrics.output_tokens;
|
|
26
21
|
}
|
|
27
|
-
if (
|
|
22
|
+
if (usage?.outputDetails?.reasoning !== void 0) {
|
|
28
23
|
metrics.output_token_details = {
|
|
29
24
|
...metrics.output_token_details ?? {},
|
|
30
|
-
reasoning_tokens:
|
|
25
|
+
reasoning_tokens: usage.outputDetails.reasoning
|
|
31
26
|
};
|
|
32
27
|
}
|
|
33
|
-
if (
|
|
28
|
+
if (usage?.inputDetails?.cacheRead !== void 0) {
|
|
34
29
|
metrics.input_token_details = {
|
|
35
30
|
...metrics.input_token_details ?? {},
|
|
36
|
-
cache_read:
|
|
31
|
+
cache_read: usage.inputDetails.cacheRead
|
|
37
32
|
};
|
|
38
33
|
}
|
|
39
|
-
if (
|
|
34
|
+
if (usage?.inputDetails?.cacheWrite !== void 0) {
|
|
40
35
|
metrics.input_token_details = {
|
|
41
36
|
...metrics.input_token_details ?? {},
|
|
42
|
-
cache_write:
|
|
37
|
+
cache_write: usage.inputDetails.cacheWrite
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (usage?.inputDetails?.audio !== void 0) {
|
|
41
|
+
metrics.input_token_details = {
|
|
42
|
+
...metrics.input_token_details ?? {},
|
|
43
|
+
audio: usage.inputDetails.audio
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
if (usage?.outputDetails?.audio !== void 0) {
|
|
47
|
+
metrics.output_token_details = {
|
|
48
|
+
...metrics.output_token_details ?? {},
|
|
49
|
+
audio: usage.outputDetails.audio
|
|
43
50
|
};
|
|
44
51
|
}
|
|
45
52
|
return metrics;
|
|
46
53
|
}
|
|
47
54
|
|
|
48
|
-
// src/
|
|
55
|
+
// src/tracing.ts
|
|
49
56
|
var DEFAULT_SPAN_TYPE = "chain";
|
|
50
57
|
var SPAN_TYPE_EXCEPTIONS = {
|
|
51
|
-
[
|
|
52
|
-
[
|
|
53
|
-
[
|
|
54
|
-
[
|
|
55
|
-
[
|
|
56
|
-
[aiTracing.AISpanType.WORKFLOW_WAIT_EVENT]: "chain"
|
|
58
|
+
[observability$1.SpanType.MODEL_GENERATION]: "llm",
|
|
59
|
+
[observability$1.SpanType.TOOL_CALL]: "tool",
|
|
60
|
+
[observability$1.SpanType.MCP_TOOL_CALL]: "tool",
|
|
61
|
+
[observability$1.SpanType.WORKFLOW_CONDITIONAL_EVAL]: "chain",
|
|
62
|
+
[observability$1.SpanType.WORKFLOW_WAIT_EVENT]: "chain"
|
|
57
63
|
};
|
|
58
64
|
function mapSpanType(spanType) {
|
|
59
65
|
return SPAN_TYPE_EXCEPTIONS[spanType] ?? DEFAULT_SPAN_TYPE;
|
|
@@ -61,203 +67,164 @@ function mapSpanType(spanType) {
|
|
|
61
67
|
function isKVMap(value) {
|
|
62
68
|
return value != null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date);
|
|
63
69
|
}
|
|
64
|
-
var LangSmithExporter = class {
|
|
70
|
+
var LangSmithExporter = class extends observability.TrackingExporter {
|
|
65
71
|
name = "langsmith";
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (!
|
|
74
|
-
this.
|
|
75
|
-
hasApiKey: !!config.apiKey
|
|
76
|
-
});
|
|
77
|
-
this.config = null;
|
|
78
|
-
this.client = null;
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
this.client = config.client ?? new langsmith.Client(config);
|
|
82
|
-
this.config = config;
|
|
83
|
-
}
|
|
84
|
-
async exportEvent(event) {
|
|
85
|
-
if (!this.config) {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
if (event.exportedSpan.isEvent) {
|
|
89
|
-
await this.handleEventSpan(event.exportedSpan);
|
|
72
|
+
#client;
|
|
73
|
+
constructor(config = {}) {
|
|
74
|
+
const apiKey = config.apiKey ?? process.env.LANGSMITH_API_KEY;
|
|
75
|
+
super({
|
|
76
|
+
...config,
|
|
77
|
+
apiKey
|
|
78
|
+
});
|
|
79
|
+
if (!apiKey) {
|
|
80
|
+
this.setDisabled(`Missing required credentials (apiKey: ${!!apiKey})`);
|
|
90
81
|
return;
|
|
91
82
|
}
|
|
92
|
-
|
|
93
|
-
case "span_started":
|
|
94
|
-
await this.handleSpanStarted(event.exportedSpan);
|
|
95
|
-
break;
|
|
96
|
-
case "span_updated":
|
|
97
|
-
await this.handleSpanUpdateOrEnd(event.exportedSpan, false);
|
|
98
|
-
break;
|
|
99
|
-
case "span_ended":
|
|
100
|
-
await this.handleSpanUpdateOrEnd(event.exportedSpan, true);
|
|
101
|
-
break;
|
|
102
|
-
}
|
|
83
|
+
this.#client = config.client ?? new langsmith.Client(this.config);
|
|
103
84
|
}
|
|
104
|
-
|
|
105
|
-
|
|
85
|
+
skipBuildRootTask = true;
|
|
86
|
+
async _buildRoot(_args) {
|
|
87
|
+
throw new Error("Method not implemented.");
|
|
106
88
|
}
|
|
107
|
-
async
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
const method = "handleSpanStarted";
|
|
113
|
-
const spanData = this.getSpanData({ span, method });
|
|
114
|
-
if (!spanData) {
|
|
89
|
+
async _buildSpan(args) {
|
|
90
|
+
const { span, traceData } = args;
|
|
91
|
+
const parent = span.isRootSpan ? void 0 : traceData.getParent(args);
|
|
92
|
+
if (!span.isRootSpan && !parent) {
|
|
115
93
|
return;
|
|
116
94
|
}
|
|
117
|
-
if (!span.isEvent) {
|
|
118
|
-
spanData.activeIds.add(span.id);
|
|
119
|
-
}
|
|
120
95
|
const payload = {
|
|
121
96
|
name: span.name,
|
|
122
|
-
|
|
123
|
-
...this.buildRunTreePayload(span)
|
|
97
|
+
...this.buildRunTreePayload(span, traceData, true)
|
|
124
98
|
};
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
langsmithRunTree = new langsmith.RunTree(payload);
|
|
129
|
-
} else {
|
|
130
|
-
langsmithRunTree = langsmithParent.createChild(payload);
|
|
131
|
-
}
|
|
132
|
-
spanData.spans.set(span.id, langsmithRunTree);
|
|
133
|
-
await langsmithRunTree.postRun();
|
|
99
|
+
const langSmithSpan = span.isRootSpan ? new langsmith.RunTree(payload) : parent.createChild(payload);
|
|
100
|
+
await langSmithSpan.postRun();
|
|
101
|
+
return langSmithSpan;
|
|
134
102
|
}
|
|
135
|
-
async
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const spanData = this.getSpanData({ span, method });
|
|
139
|
-
if (!spanData) {
|
|
103
|
+
async _buildEvent(args) {
|
|
104
|
+
const langSmithSpan = await this._buildSpan(args);
|
|
105
|
+
if (!langSmithSpan) {
|
|
140
106
|
return;
|
|
141
107
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
108
|
+
await langSmithSpan.end({ endTime: args.span.startTime.getTime() });
|
|
109
|
+
await langSmithSpan.patchRun();
|
|
110
|
+
return langSmithSpan;
|
|
111
|
+
}
|
|
112
|
+
async _updateSpan(args) {
|
|
113
|
+
await this.handleSpanUpdateOrEnd({ ...args, isEnd: false });
|
|
114
|
+
}
|
|
115
|
+
async _finishSpan(args) {
|
|
116
|
+
await this.handleSpanUpdateOrEnd({ ...args, isEnd: true });
|
|
117
|
+
}
|
|
118
|
+
async _abortSpan(args) {
|
|
119
|
+
const { span, reason } = args;
|
|
120
|
+
span.error = reason.message;
|
|
121
|
+
span.metadata = {
|
|
122
|
+
...span.metadata,
|
|
123
|
+
errorDetails: reason
|
|
124
|
+
};
|
|
125
|
+
await span.end();
|
|
126
|
+
await span.patchRun();
|
|
127
|
+
}
|
|
128
|
+
async handleSpanUpdateOrEnd(args) {
|
|
129
|
+
const { span, traceData, isEnd } = args;
|
|
130
|
+
const langSmithSpan = traceData.getSpan({ spanId: span.id });
|
|
131
|
+
if (!langSmithSpan) {
|
|
153
132
|
return;
|
|
154
133
|
}
|
|
155
|
-
const updatePayload = this.buildRunTreePayload(span);
|
|
156
|
-
|
|
157
|
-
...
|
|
134
|
+
const updatePayload = this.buildRunTreePayload(span, traceData);
|
|
135
|
+
langSmithSpan.metadata = {
|
|
136
|
+
...langSmithSpan.metadata,
|
|
158
137
|
...updatePayload.metadata
|
|
159
138
|
};
|
|
160
139
|
if (updatePayload.inputs != null) {
|
|
161
|
-
|
|
140
|
+
langSmithSpan.inputs = updatePayload.inputs;
|
|
162
141
|
}
|
|
163
142
|
if (updatePayload.outputs != null) {
|
|
164
|
-
|
|
143
|
+
langSmithSpan.outputs = updatePayload.outputs;
|
|
165
144
|
}
|
|
166
145
|
if (updatePayload.error != null) {
|
|
167
|
-
|
|
146
|
+
langSmithSpan.error = updatePayload.error;
|
|
147
|
+
}
|
|
148
|
+
if (span.type === observability$1.SpanType.MODEL_GENERATION) {
|
|
149
|
+
const modelAttr = span.attributes ?? {};
|
|
150
|
+
if (modelAttr.completionStartTime !== void 0) {
|
|
151
|
+
langSmithSpan.addEvent({
|
|
152
|
+
name: "new_token",
|
|
153
|
+
time: modelAttr.completionStartTime.toISOString()
|
|
154
|
+
});
|
|
155
|
+
}
|
|
168
156
|
}
|
|
169
157
|
if (isEnd) {
|
|
170
158
|
if (span.endTime) {
|
|
171
|
-
await
|
|
159
|
+
await langSmithSpan.end({ endTime: span.endTime.getTime() });
|
|
172
160
|
} else {
|
|
173
|
-
await
|
|
174
|
-
}
|
|
175
|
-
await langsmithRunTree.patchRun();
|
|
176
|
-
if (!span.isEvent) {
|
|
177
|
-
spanData.activeIds.delete(span.id);
|
|
178
|
-
}
|
|
179
|
-
if (spanData.activeIds.size === 0) {
|
|
180
|
-
this.traceMap.delete(span.traceId);
|
|
161
|
+
await langSmithSpan.end();
|
|
181
162
|
}
|
|
182
163
|
}
|
|
164
|
+
await langSmithSpan.patchRun();
|
|
183
165
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const payload = {
|
|
201
|
-
...this.buildRunTreePayload(span),
|
|
202
|
-
name: span.name,
|
|
203
|
-
type: mapSpanType(span.type),
|
|
204
|
-
startTime: span.startTime.getTime() / 1e3
|
|
205
|
-
};
|
|
206
|
-
let langsmithRunTree;
|
|
207
|
-
if (!langsmithParent) {
|
|
208
|
-
langsmithRunTree = new langsmith.RunTree(payload);
|
|
209
|
-
} else {
|
|
210
|
-
langsmithRunTree = langsmithParent.createChild(payload);
|
|
211
|
-
}
|
|
212
|
-
await langsmithRunTree.postRun();
|
|
213
|
-
await langsmithRunTree.end({ endTime: span.startTime.getTime() / 1e3 });
|
|
214
|
-
await langsmithRunTree.patchRun();
|
|
215
|
-
}
|
|
216
|
-
getSpanData(options) {
|
|
217
|
-
const { span, method } = options;
|
|
218
|
-
if (this.traceMap.has(span.traceId)) {
|
|
219
|
-
return this.traceMap.get(span.traceId);
|
|
166
|
+
/**
|
|
167
|
+
* Find LangSmith vendor metadata by walking up the span hierarchy and merging.
|
|
168
|
+
* Metadata is merged from ancestors with child values taking precedence over parent values.
|
|
169
|
+
*
|
|
170
|
+
* TODO(2.0): Extract shared `findVendorMetadata()` to base TrackingExporter class
|
|
171
|
+
* and reuse here and in LangfuseExporter.findLangfusePrompt()
|
|
172
|
+
*/
|
|
173
|
+
findLangsmithMetadata(traceData, span) {
|
|
174
|
+
const metadataChain = [];
|
|
175
|
+
let currentSpanId = span.id;
|
|
176
|
+
while (currentSpanId) {
|
|
177
|
+
const providerMetadata = traceData.getMetadata({ spanId: currentSpanId });
|
|
178
|
+
if (providerMetadata) {
|
|
179
|
+
metadataChain.push(providerMetadata);
|
|
180
|
+
}
|
|
181
|
+
currentSpanId = traceData.getParentId({ spanId: currentSpanId });
|
|
220
182
|
}
|
|
221
|
-
|
|
222
|
-
traceId: span.traceId,
|
|
223
|
-
spanId: span.id,
|
|
224
|
-
spanName: span.name,
|
|
225
|
-
spanType: span.type,
|
|
226
|
-
isRootSpan: span.isRootSpan,
|
|
227
|
-
parentSpanId: span.parentSpanId,
|
|
228
|
-
method
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
getLangSmithParent(options) {
|
|
232
|
-
const { spanData, span, method } = options;
|
|
233
|
-
const parentId = span.parentSpanId;
|
|
234
|
-
if (!parentId) {
|
|
183
|
+
if (metadataChain.length === 0) {
|
|
235
184
|
return void 0;
|
|
236
185
|
}
|
|
237
|
-
|
|
238
|
-
|
|
186
|
+
const merged = {};
|
|
187
|
+
for (let i = metadataChain.length - 1; i >= 0; i--) {
|
|
188
|
+
const meta = metadataChain[i];
|
|
189
|
+
if (meta.projectName !== void 0) merged.projectName = meta.projectName;
|
|
190
|
+
if (meta.sessionId !== void 0) merged.sessionId = meta.sessionId;
|
|
191
|
+
if (meta.sessionName !== void 0) merged.sessionName = meta.sessionName;
|
|
239
192
|
}
|
|
240
|
-
|
|
241
|
-
return void 0;
|
|
242
|
-
}
|
|
243
|
-
this.logger.warn("LangSmith exporter: No parent data found for span", {
|
|
193
|
+
this.logger.debug(`${this.name}: merged vendor metadata from hierarchy`, {
|
|
244
194
|
traceId: span.traceId,
|
|
245
195
|
spanId: span.id,
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
isRootSpan: span.isRootSpan,
|
|
249
|
-
parentSpanId: span.parentSpanId,
|
|
250
|
-
method
|
|
196
|
+
metadataKeys: Object.keys(merged),
|
|
197
|
+
ancestorCount: metadataChain.length
|
|
251
198
|
});
|
|
199
|
+
return merged;
|
|
252
200
|
}
|
|
253
|
-
buildRunTreePayload(span) {
|
|
201
|
+
buildRunTreePayload(span, traceData, isNew = false) {
|
|
202
|
+
const vendorMetadata = this.findLangsmithMetadata(traceData, span);
|
|
203
|
+
const spanMetadata = span.metadata ? utils.omitKeys(span.metadata, ["langsmith"]) : {};
|
|
254
204
|
const payload = {
|
|
255
|
-
client: this
|
|
205
|
+
client: this.#client,
|
|
256
206
|
metadata: {
|
|
257
207
|
mastra_span_type: span.type,
|
|
258
|
-
...
|
|
208
|
+
...spanMetadata
|
|
259
209
|
}
|
|
260
210
|
};
|
|
211
|
+
if (isNew) {
|
|
212
|
+
payload.run_type = mapSpanType(span.type);
|
|
213
|
+
payload.start_time = span.startTime.getTime();
|
|
214
|
+
}
|
|
215
|
+
const projectName = vendorMetadata?.projectName ?? this.config.projectName;
|
|
216
|
+
if (projectName) {
|
|
217
|
+
payload.project_name = projectName;
|
|
218
|
+
}
|
|
219
|
+
if (vendorMetadata?.sessionId) {
|
|
220
|
+
payload.metadata.session_id = vendorMetadata.sessionId;
|
|
221
|
+
}
|
|
222
|
+
if (vendorMetadata?.sessionName) {
|
|
223
|
+
payload.metadata.session_name = vendorMetadata.sessionName;
|
|
224
|
+
}
|
|
225
|
+
if (span.isRootSpan && span.tags?.length) {
|
|
226
|
+
payload.tags = span.tags;
|
|
227
|
+
}
|
|
261
228
|
if (span.input !== void 0) {
|
|
262
229
|
payload.inputs = isKVMap(span.input) ? span.input : { input: span.input };
|
|
263
230
|
}
|
|
@@ -265,19 +232,19 @@ var LangSmithExporter = class {
|
|
|
265
232
|
payload.outputs = isKVMap(span.output) ? span.output : { output: span.output };
|
|
266
233
|
}
|
|
267
234
|
const attributes = span.attributes ?? {};
|
|
268
|
-
if (span.type ===
|
|
269
|
-
const
|
|
270
|
-
if (
|
|
271
|
-
payload.metadata.ls_model_name =
|
|
235
|
+
if (span.type === observability$1.SpanType.MODEL_GENERATION) {
|
|
236
|
+
const modelAttr = attributes;
|
|
237
|
+
if (modelAttr.model !== void 0) {
|
|
238
|
+
payload.metadata.ls_model_name = modelAttr.model;
|
|
272
239
|
}
|
|
273
|
-
if (
|
|
274
|
-
payload.metadata.ls_provider =
|
|
240
|
+
if (modelAttr.provider !== void 0) {
|
|
241
|
+
payload.metadata.ls_provider = modelAttr.provider;
|
|
275
242
|
}
|
|
276
|
-
payload.metadata.usage_metadata =
|
|
277
|
-
if (
|
|
278
|
-
payload.metadata.modelParameters =
|
|
243
|
+
payload.metadata.usage_metadata = formatUsageMetrics(modelAttr.usage);
|
|
244
|
+
if (modelAttr.parameters !== void 0) {
|
|
245
|
+
payload.metadata.modelParameters = modelAttr.parameters;
|
|
279
246
|
}
|
|
280
|
-
const otherAttributes =
|
|
247
|
+
const otherAttributes = utils.omitKeys(attributes, ["model", "provider", "usage", "parameters", "completionStartTime"]);
|
|
281
248
|
payload.metadata = {
|
|
282
249
|
...payload.metadata,
|
|
283
250
|
...otherAttributes
|
|
@@ -294,20 +261,23 @@ var LangSmithExporter = class {
|
|
|
294
261
|
}
|
|
295
262
|
return payload;
|
|
296
263
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
// src/helpers.ts
|
|
267
|
+
function withLangsmithMetadata(metadata) {
|
|
268
|
+
return (opts) => ({
|
|
269
|
+
...opts,
|
|
270
|
+
metadata: {
|
|
271
|
+
...opts.metadata,
|
|
272
|
+
langsmith: {
|
|
273
|
+
...opts.metadata?.langsmith,
|
|
274
|
+
...metadata
|
|
305
275
|
}
|
|
306
276
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
};
|
|
277
|
+
});
|
|
278
|
+
}
|
|
310
279
|
|
|
311
280
|
exports.LangSmithExporter = LangSmithExporter;
|
|
281
|
+
exports.withLangsmithMetadata = withLangsmithMetadata;
|
|
312
282
|
//# sourceMappingURL=index.cjs.map
|
|
313
283
|
//# sourceMappingURL=index.cjs.map
|