@mastra/langsmith 1.0.0-beta.9 → 1.0.0
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 +283 -0
- package/README.md +35 -6
- package/dist/index.cjs +65 -178
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +66 -179
- package/dist/index.js.map +1 -1
- package/dist/tracing.d.ts +40 -17
- package/dist/tracing.d.ts.map +1 -1
- package/package.json +8 -7
package/dist/index.cjs
CHANGED
|
@@ -67,117 +67,88 @@ function mapSpanType(spanType) {
|
|
|
67
67
|
function isKVMap(value) {
|
|
68
68
|
return value != null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date);
|
|
69
69
|
}
|
|
70
|
-
var LangSmithExporter = class extends observability.
|
|
70
|
+
var LangSmithExporter = class extends observability.TrackingExporter {
|
|
71
71
|
name = "langsmith";
|
|
72
|
-
|
|
73
|
-
config
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
this.
|
|
81
|
-
this.client = null;
|
|
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})`);
|
|
82
81
|
return;
|
|
83
82
|
}
|
|
84
|
-
this
|
|
85
|
-
this.config = config;
|
|
83
|
+
this.#client = config.client ?? new langsmith.Client(this.config);
|
|
86
84
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
switch (event.type) {
|
|
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
|
-
}
|
|
85
|
+
skipBuildRootTask = true;
|
|
86
|
+
async _buildRoot(_args) {
|
|
87
|
+
throw new Error("Method not implemented.");
|
|
103
88
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
spanId: span.id,
|
|
109
|
-
spanName: span.name
|
|
110
|
-
});
|
|
89
|
+
async _buildSpan(args) {
|
|
90
|
+
const { span, traceData } = args;
|
|
91
|
+
const parent = span.isRootSpan ? void 0 : traceData.getParent(args);
|
|
92
|
+
if (!span.isRootSpan && !parent) {
|
|
111
93
|
return;
|
|
112
94
|
}
|
|
113
|
-
this.traceMap.set(span.traceId, { spans: /* @__PURE__ */ new Map(), activeIds: /* @__PURE__ */ new Set() });
|
|
114
|
-
}
|
|
115
|
-
async handleSpanStarted(span) {
|
|
116
|
-
this.logger.debug("LangSmith exporter: handleSpanStarted", span.id, span.name);
|
|
117
|
-
if (span.isRootSpan) {
|
|
118
|
-
this.initializeRootSpan(span);
|
|
119
|
-
}
|
|
120
|
-
const method = "handleSpanStarted";
|
|
121
|
-
const spanData = this.getSpanData({ span, method });
|
|
122
|
-
if (!spanData) {
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
if (!span.isEvent) {
|
|
126
|
-
spanData.activeIds.add(span.id);
|
|
127
|
-
}
|
|
128
95
|
const payload = {
|
|
129
96
|
name: span.name,
|
|
130
|
-
|
|
131
|
-
...this.buildRunTreePayload(span)
|
|
97
|
+
...this.buildRunTreePayload(span, true)
|
|
132
98
|
};
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
langsmithRunTree = new langsmith.RunTree(payload);
|
|
137
|
-
} else {
|
|
138
|
-
langsmithRunTree = langsmithParent.createChild(payload);
|
|
139
|
-
}
|
|
140
|
-
spanData.spans.set(span.id, langsmithRunTree);
|
|
141
|
-
await langsmithRunTree.postRun();
|
|
99
|
+
const langSmithSpan = span.isRootSpan ? new langsmith.RunTree(payload) : parent.createChild(payload);
|
|
100
|
+
await langSmithSpan.postRun();
|
|
101
|
+
return langSmithSpan;
|
|
142
102
|
}
|
|
143
|
-
async
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const spanData = this.getSpanData({ span, method });
|
|
147
|
-
if (!spanData) {
|
|
103
|
+
async _buildEvent(args) {
|
|
104
|
+
const langSmithSpan = await this._buildSpan(args);
|
|
105
|
+
if (!langSmithSpan) {
|
|
148
106
|
return;
|
|
149
107
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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) {
|
|
161
132
|
return;
|
|
162
133
|
}
|
|
163
134
|
const updatePayload = this.buildRunTreePayload(span);
|
|
164
|
-
|
|
165
|
-
...
|
|
135
|
+
langSmithSpan.metadata = {
|
|
136
|
+
...langSmithSpan.metadata,
|
|
166
137
|
...updatePayload.metadata
|
|
167
138
|
};
|
|
168
139
|
if (updatePayload.inputs != null) {
|
|
169
|
-
|
|
140
|
+
langSmithSpan.inputs = updatePayload.inputs;
|
|
170
141
|
}
|
|
171
142
|
if (updatePayload.outputs != null) {
|
|
172
|
-
|
|
143
|
+
langSmithSpan.outputs = updatePayload.outputs;
|
|
173
144
|
}
|
|
174
145
|
if (updatePayload.error != null) {
|
|
175
|
-
|
|
146
|
+
langSmithSpan.error = updatePayload.error;
|
|
176
147
|
}
|
|
177
148
|
if (span.type === observability$1.SpanType.MODEL_GENERATION) {
|
|
178
149
|
const modelAttr = span.attributes ?? {};
|
|
179
150
|
if (modelAttr.completionStartTime !== void 0) {
|
|
180
|
-
|
|
151
|
+
langSmithSpan.addEvent({
|
|
181
152
|
name: "new_token",
|
|
182
153
|
time: modelAttr.completionStartTime.toISOString()
|
|
183
154
|
});
|
|
@@ -185,96 +156,25 @@ var LangSmithExporter = class extends observability.BaseExporter {
|
|
|
185
156
|
}
|
|
186
157
|
if (isEnd) {
|
|
187
158
|
if (span.endTime) {
|
|
188
|
-
await
|
|
159
|
+
await langSmithSpan.end({ endTime: span.endTime.getTime() });
|
|
189
160
|
} else {
|
|
190
|
-
await
|
|
191
|
-
}
|
|
192
|
-
await langsmithRunTree.patchRun();
|
|
193
|
-
if (!span.isEvent) {
|
|
194
|
-
spanData.activeIds.delete(span.id);
|
|
195
|
-
}
|
|
196
|
-
if (spanData.activeIds.size === 0) {
|
|
197
|
-
this.traceMap.delete(span.traceId);
|
|
161
|
+
await langSmithSpan.end();
|
|
198
162
|
}
|
|
199
163
|
}
|
|
164
|
+
await langSmithSpan.patchRun();
|
|
200
165
|
}
|
|
201
|
-
|
|
202
|
-
if (span.isRootSpan) {
|
|
203
|
-
this.logger.debug("LangSmith exporter: Creating logger for event", {
|
|
204
|
-
traceId: span.traceId,
|
|
205
|
-
spanId: span.id,
|
|
206
|
-
spanName: span.name,
|
|
207
|
-
method: "handleEventSpan"
|
|
208
|
-
});
|
|
209
|
-
this.initializeRootSpan(span);
|
|
210
|
-
}
|
|
211
|
-
const method = "handleEventSpan";
|
|
212
|
-
const spanData = this.getSpanData({ span, method });
|
|
213
|
-
if (!spanData) {
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
const langsmithParent = this.getLangSmithParent({ spanData, span, method });
|
|
166
|
+
buildRunTreePayload(span, isNew = false) {
|
|
217
167
|
const payload = {
|
|
218
|
-
|
|
219
|
-
name: span.name,
|
|
220
|
-
type: mapSpanType(span.type),
|
|
221
|
-
startTime: span.startTime.getTime() / 1e3
|
|
222
|
-
};
|
|
223
|
-
let langsmithRunTree;
|
|
224
|
-
if (!langsmithParent) {
|
|
225
|
-
langsmithRunTree = new langsmith.RunTree(payload);
|
|
226
|
-
} else {
|
|
227
|
-
langsmithRunTree = langsmithParent.createChild(payload);
|
|
228
|
-
}
|
|
229
|
-
await langsmithRunTree.postRun();
|
|
230
|
-
await langsmithRunTree.end({ endTime: span.startTime.getTime() / 1e3 });
|
|
231
|
-
await langsmithRunTree.patchRun();
|
|
232
|
-
}
|
|
233
|
-
getSpanData(options) {
|
|
234
|
-
const { span, method } = options;
|
|
235
|
-
if (this.traceMap.has(span.traceId)) {
|
|
236
|
-
return this.traceMap.get(span.traceId);
|
|
237
|
-
}
|
|
238
|
-
this.logger.warn("LangSmith exporter: No span data found for span", {
|
|
239
|
-
traceId: span.traceId,
|
|
240
|
-
spanId: span.id,
|
|
241
|
-
spanName: span.name,
|
|
242
|
-
spanType: span.type,
|
|
243
|
-
isRootSpan: span.isRootSpan,
|
|
244
|
-
parentSpanId: span.parentSpanId,
|
|
245
|
-
method
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
getLangSmithParent(options) {
|
|
249
|
-
const { spanData, span, method } = options;
|
|
250
|
-
const parentId = span.parentSpanId;
|
|
251
|
-
if (!parentId) {
|
|
252
|
-
return void 0;
|
|
253
|
-
}
|
|
254
|
-
if (spanData.spans.has(parentId)) {
|
|
255
|
-
return spanData.spans.get(parentId);
|
|
256
|
-
}
|
|
257
|
-
if (parentId && !spanData.spans.has(parentId)) {
|
|
258
|
-
return void 0;
|
|
259
|
-
}
|
|
260
|
-
this.logger.warn("LangSmith exporter: No parent data found for span", {
|
|
261
|
-
traceId: span.traceId,
|
|
262
|
-
spanId: span.id,
|
|
263
|
-
spanName: span.name,
|
|
264
|
-
spanType: span.type,
|
|
265
|
-
isRootSpan: span.isRootSpan,
|
|
266
|
-
parentSpanId: span.parentSpanId,
|
|
267
|
-
method
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
buildRunTreePayload(span) {
|
|
271
|
-
const payload = {
|
|
272
|
-
client: this.client,
|
|
168
|
+
client: this.#client,
|
|
273
169
|
metadata: {
|
|
274
170
|
mastra_span_type: span.type,
|
|
275
171
|
...span.metadata
|
|
276
172
|
}
|
|
277
173
|
};
|
|
174
|
+
if (isNew) {
|
|
175
|
+
payload.run_type = mapSpanType(span.type);
|
|
176
|
+
payload.start_time = span.startTime.getTime();
|
|
177
|
+
}
|
|
278
178
|
if (this.config.projectName) {
|
|
279
179
|
payload.project_name = this.config.projectName;
|
|
280
180
|
}
|
|
@@ -300,7 +200,7 @@ var LangSmithExporter = class extends observability.BaseExporter {
|
|
|
300
200
|
if (modelAttr.parameters !== void 0) {
|
|
301
201
|
payload.metadata.modelParameters = modelAttr.parameters;
|
|
302
202
|
}
|
|
303
|
-
const otherAttributes = utils.omitKeys(attributes, ["model", "usage", "parameters", "completionStartTime"]);
|
|
203
|
+
const otherAttributes = utils.omitKeys(attributes, ["model", "provider", "usage", "parameters", "completionStartTime"]);
|
|
304
204
|
payload.metadata = {
|
|
305
205
|
...payload.metadata,
|
|
306
206
|
...otherAttributes
|
|
@@ -317,19 +217,6 @@ var LangSmithExporter = class extends observability.BaseExporter {
|
|
|
317
217
|
}
|
|
318
218
|
return payload;
|
|
319
219
|
}
|
|
320
|
-
async shutdown() {
|
|
321
|
-
if (!this.config) {
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
for (const [_traceId, spanData] of this.traceMap) {
|
|
325
|
-
for (const [_spanId, runTree] of spanData.spans) {
|
|
326
|
-
await runTree.end();
|
|
327
|
-
await runTree.patchRun();
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
this.traceMap.clear();
|
|
331
|
-
await super.shutdown();
|
|
332
|
-
}
|
|
333
220
|
};
|
|
334
221
|
|
|
335
222
|
exports.LangSmithExporter = LangSmithExporter;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/metrics.ts","../src/tracing.ts"],"names":["SpanType","BaseExporter","Client","RunTree","omitKeys"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { UsageStats } from '@mastra/core/observability';\n\n/**\n * LangSmithUsageMetrics\n *\n * Canonical metric keys expected by LangSmith for LLM usage accounting.\n * See: https://docs.langchain.com/langsmith/log-llm-trace#provide-token-and-cost-information\n */\nexport interface LangSmithUsageMetrics {\n input_tokens?: number;\n output_tokens?: number;\n total_tokens?: number;\n input_token_details?: {\n [key: string]: number;\n };\n output_token_details?: {\n [key: string]: number;\n };\n}\n\n/**\n * Formats UsageStats to LangSmith's expected metric format.\n */\nexport function formatUsageMetrics(usage?: UsageStats): LangSmithUsageMetrics {\n const metrics: LangSmithUsageMetrics = {};\n\n if (usage?.inputTokens !== undefined) {\n metrics.input_tokens = usage.inputTokens;\n }\n\n if (usage?.outputTokens !== undefined) {\n metrics.output_tokens = usage.outputTokens;\n }\n\n // Compute total if we have both\n if (metrics.input_tokens !== undefined && metrics.output_tokens !== undefined) {\n metrics.total_tokens = metrics.input_tokens + metrics.output_tokens;\n }\n\n if (usage?.outputDetails?.reasoning !== undefined) {\n metrics.output_token_details = {\n ...(metrics.output_token_details ?? {}),\n reasoning_tokens: usage.outputDetails.reasoning,\n };\n }\n\n if (usage?.inputDetails?.cacheRead !== undefined) {\n metrics.input_token_details = {\n ...(metrics.input_token_details ?? {}),\n cache_read: usage.inputDetails.cacheRead,\n };\n }\n\n if (usage?.inputDetails?.cacheWrite !== undefined) {\n metrics.input_token_details = {\n ...(metrics.input_token_details ?? {}),\n cache_write: usage.inputDetails.cacheWrite,\n };\n }\n\n if (usage?.inputDetails?.audio !== undefined) {\n metrics.input_token_details = {\n ...(metrics.input_token_details ?? {}),\n audio: usage.inputDetails.audio,\n };\n }\n\n if (usage?.outputDetails?.audio !== undefined) {\n metrics.output_token_details = {\n ...(metrics.output_token_details ?? {}),\n audio: usage.outputDetails.audio,\n };\n }\n\n return metrics;\n}\n","/**\n * LangSmith Exporter for Mastra Tracing\n *\n * This exporter sends observability data to LangSmith\n * Root spans become top-level LangSmith RunTrees (no trace wrapper).\n * Events are handled as zero-duration RunTrees with matching start/end times.\n */\n\nimport type { TracingEvent, AnyExportedSpan, ModelGenerationAttributes } from '@mastra/core/observability';\nimport { SpanType } from '@mastra/core/observability';\nimport { omitKeys } from '@mastra/core/utils';\nimport { BaseExporter } from '@mastra/observability';\nimport type { BaseExporterConfig } from '@mastra/observability';\nimport type { ClientConfig, RunTreeConfig } from 'langsmith';\nimport { Client, RunTree } from 'langsmith';\nimport type { KVMap } from 'langsmith/schemas';\nimport { formatUsageMetrics } from './metrics';\n\nexport interface LangSmithExporterConfig extends ClientConfig, BaseExporterConfig {\n /** LangSmith client instance */\n client?: Client;\n /**\n * The name of the LangSmith project to send traces to.\n * Overrides the LANGCHAIN_PROJECT environment variable.\n * If neither is set, traces are sent to the \"default\" project.\n */\n projectName?: string;\n}\n\ntype SpanData = {\n spans: Map<string, RunTree>; // Maps span.id to LangSmith RunTrees\n activeIds: Set<string>; // Tracks started (non-event) spans not yet ended, including root\n};\n\n// Default span type for all spans\nconst DEFAULT_SPAN_TYPE = 'chain';\n\n// Exceptions to the default mapping\nconst SPAN_TYPE_EXCEPTIONS: Partial<Record<SpanType, 'llm' | 'tool' | 'chain'>> = {\n [SpanType.MODEL_GENERATION]: 'llm',\n [SpanType.TOOL_CALL]: 'tool',\n [SpanType.MCP_TOOL_CALL]: 'tool',\n [SpanType.WORKFLOW_CONDITIONAL_EVAL]: 'chain',\n [SpanType.WORKFLOW_WAIT_EVENT]: 'chain',\n};\n\n// Mapping function - returns valid LangSmith span types\nfunction mapSpanType(spanType: SpanType): 'llm' | 'tool' | 'chain' {\n return SPAN_TYPE_EXCEPTIONS[spanType] ?? DEFAULT_SPAN_TYPE;\n}\n\nfunction isKVMap(value: unknown): value is KVMap {\n return value != null && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date);\n}\n\nexport class LangSmithExporter extends BaseExporter {\n name = 'langsmith';\n private traceMap = new Map<string, SpanData>();\n private config: LangSmithExporterConfig;\n private client: Client;\n\n constructor(config: LangSmithExporterConfig) {\n super(config);\n\n config.apiKey = config.apiKey ?? process.env.LANGSMITH_API_KEY;\n\n if (!config.apiKey) {\n this.setDisabled(`Missing required credentials (apiKey: ${!!config.apiKey})`);\n this.config = null as any;\n this.client = null as any;\n return;\n }\n\n this.client = config.client ?? new Client(config);\n this.config = config;\n }\n\n protected async _exportTracingEvent(event: TracingEvent): Promise<void> {\n if (event.exportedSpan.isEvent) {\n await this.handleEventSpan(event.exportedSpan);\n return;\n }\n\n switch (event.type) {\n case 'span_started':\n await this.handleSpanStarted(event.exportedSpan);\n break;\n case 'span_updated':\n await this.handleSpanUpdateOrEnd(event.exportedSpan, false);\n break;\n case 'span_ended':\n await this.handleSpanUpdateOrEnd(event.exportedSpan, true);\n break;\n }\n }\n\n private initializeRootSpan(span: AnyExportedSpan) {\n // Check if trace already exists - reuse existing trace data\n if (this.traceMap.has(span.traceId)) {\n this.logger.debug('LangSmith exporter: Reusing existing trace from local map', {\n traceId: span.traceId,\n spanId: span.id,\n spanName: span.name,\n });\n return;\n }\n\n this.traceMap.set(span.traceId, { spans: new Map(), activeIds: new Set() });\n }\n\n private async handleSpanStarted(span: AnyExportedSpan): Promise<void> {\n this.logger.debug('LangSmith exporter: handleSpanStarted', span.id, span.name);\n if (span.isRootSpan) {\n this.initializeRootSpan(span);\n }\n\n const method = 'handleSpanStarted';\n const spanData = this.getSpanData({ span, method });\n if (!spanData) {\n return;\n }\n\n // Refcount: track active non-event spans (including root)\n if (!span.isEvent) {\n spanData.activeIds.add(span.id);\n }\n\n const payload = {\n name: span.name,\n run_type: mapSpanType(span.type),\n ...this.buildRunTreePayload(span),\n };\n\n const langsmithParent = this.getLangSmithParent({ spanData, span, method });\n let langsmithRunTree: RunTree;\n if (!langsmithParent) {\n langsmithRunTree = new RunTree(payload);\n } else {\n langsmithRunTree = langsmithParent.createChild(payload);\n }\n\n spanData.spans.set(span.id, langsmithRunTree);\n\n await langsmithRunTree.postRun();\n }\n\n private async handleSpanUpdateOrEnd(span: AnyExportedSpan, isEnd: boolean): Promise<void> {\n this.logger.debug('LangSmith exporter: handleSpanUpdateOrEnd', span.id, span.name, 'isEnd:', isEnd);\n const method = isEnd ? 'handleSpanEnd' : 'handleSpanUpdate';\n\n const spanData = this.getSpanData({ span, method });\n if (!spanData) {\n return;\n }\n\n const langsmithRunTree = spanData.spans.get(span.id);\n if (!langsmithRunTree) {\n this.logger.warn('LangSmith exporter: No LangSmith span found for span update/end', {\n traceId: span.traceId,\n spanId: span.id,\n spanName: span.name,\n spanType: span.type,\n isRootSpan: span.isRootSpan,\n parentSpanId: span.parentSpanId,\n method,\n });\n return;\n }\n\n const updatePayload = this.buildRunTreePayload(span);\n langsmithRunTree.metadata = {\n ...langsmithRunTree.metadata,\n ...updatePayload.metadata,\n };\n if (updatePayload.inputs != null) {\n langsmithRunTree.inputs = updatePayload.inputs;\n }\n if (updatePayload.outputs != null) {\n langsmithRunTree.outputs = updatePayload.outputs;\n }\n if (updatePayload.error != null) {\n langsmithRunTree.error = updatePayload.error;\n }\n\n // Add new_token event for TTFT tracking on MODEL_GENERATION spans\n if (span.type === SpanType.MODEL_GENERATION) {\n const modelAttr = (span.attributes ?? {}) as ModelGenerationAttributes;\n if (modelAttr.completionStartTime !== undefined) {\n langsmithRunTree.addEvent({\n name: 'new_token',\n time: modelAttr.completionStartTime.toISOString(),\n });\n }\n }\n\n if (isEnd) {\n // End the span with the correct endTime (convert milliseconds to seconds)\n if (span.endTime) {\n await langsmithRunTree.end({ endTime: span.endTime.getTime() / 1000 });\n } else {\n await langsmithRunTree.end();\n }\n await langsmithRunTree.patchRun();\n\n // Refcount: mark this span as ended\n if (!span.isEvent) {\n spanData.activeIds.delete(span.id);\n }\n\n // If no more active spans remain for this trace, clean up the trace entry\n if (spanData.activeIds.size === 0) {\n this.traceMap.delete(span.traceId);\n }\n }\n }\n\n private async handleEventSpan(span: AnyExportedSpan): Promise<void> {\n if (span.isRootSpan) {\n this.logger.debug('LangSmith exporter: Creating logger for event', {\n traceId: span.traceId,\n spanId: span.id,\n spanName: span.name,\n method: 'handleEventSpan',\n });\n this.initializeRootSpan(span);\n }\n\n const method = 'handleEventSpan';\n const spanData = this.getSpanData({ span, method });\n if (!spanData) {\n return;\n }\n\n const langsmithParent = this.getLangSmithParent({ spanData, span, method });\n const payload = {\n ...this.buildRunTreePayload(span),\n name: span.name,\n type: mapSpanType(span.type),\n startTime: span.startTime.getTime() / 1000,\n };\n\n let langsmithRunTree: RunTree;\n if (!langsmithParent) {\n langsmithRunTree = new RunTree(payload);\n } else {\n langsmithRunTree = langsmithParent.createChild(payload);\n }\n\n await langsmithRunTree.postRun();\n\n await langsmithRunTree.end({ endTime: span.startTime.getTime() / 1000 });\n await langsmithRunTree.patchRun();\n }\n\n private getSpanData(options: { span: AnyExportedSpan; method: string }): SpanData | undefined {\n const { span, method } = options;\n if (this.traceMap.has(span.traceId)) {\n return this.traceMap.get(span.traceId);\n }\n\n this.logger.warn('LangSmith exporter: No span data found for span', {\n traceId: span.traceId,\n spanId: span.id,\n spanName: span.name,\n spanType: span.type,\n isRootSpan: span.isRootSpan,\n parentSpanId: span.parentSpanId,\n method,\n });\n }\n\n private getLangSmithParent(options: {\n spanData: SpanData;\n span: AnyExportedSpan;\n method: string;\n }): RunTree | undefined {\n const { spanData, span, method } = options;\n\n const parentId = span.parentSpanId;\n if (!parentId) {\n return undefined;\n }\n\n if (spanData.spans.has(parentId)) {\n return spanData.spans.get(parentId);\n }\n\n if (parentId && !spanData.spans.has(parentId)) {\n // This means the parent exists but isn't tracked as a LangSmith span,\n // which happens when the parent is the root span\n return undefined;\n }\n\n this.logger.warn('LangSmith exporter: No parent data found for span', {\n traceId: span.traceId,\n spanId: span.id,\n spanName: span.name,\n spanType: span.type,\n isRootSpan: span.isRootSpan,\n parentSpanId: span.parentSpanId,\n method,\n });\n }\n\n private buildRunTreePayload(span: AnyExportedSpan): Partial<RunTreeConfig> {\n const payload: Partial<RunTreeConfig> & { metadata: KVMap } = {\n client: this.client,\n metadata: {\n mastra_span_type: span.type,\n ...span.metadata,\n },\n };\n\n // Add project name if configured\n if (this.config.projectName) {\n payload.project_name = this.config.projectName;\n }\n\n // Add tags for root spans\n if (span.isRootSpan && span.tags?.length) {\n payload.tags = span.tags;\n }\n\n // Core span data\n if (span.input !== undefined) {\n payload.inputs = isKVMap(span.input) ? span.input : { input: span.input };\n }\n\n if (span.output !== undefined) {\n payload.outputs = isKVMap(span.output) ? span.output : { output: span.output };\n }\n\n const attributes = (span.attributes ?? {}) as Record<string, any>;\n\n if (span.type === SpanType.MODEL_GENERATION) {\n const modelAttr = attributes as ModelGenerationAttributes;\n\n // See: https://docs.langchain.com/langsmith/log-llm-trace\n if (modelAttr.model !== undefined) {\n // Note - this should map to a model name recognized by LangSmith\n // eg “gpt-4o-mini”, “claude-3-opus-20240307”, etc.\n payload.metadata.ls_model_name = modelAttr.model;\n }\n\n // Provider goes to metadata (if provided by attributes)\n if (modelAttr.provider !== undefined) {\n // Note - this should map to a provider name recognized by\n // LangSmith eg “openai”, “anthropic”, etc.\n payload.metadata.ls_provider = modelAttr.provider;\n }\n\n // Usage/token info goes to metrics\n payload.metadata.usage_metadata = formatUsageMetrics(modelAttr.usage);\n\n // Model parameters go to metadata\n if (modelAttr.parameters !== undefined) {\n payload.metadata.modelParameters = modelAttr.parameters;\n }\n\n // Other LLM attributes go to metadata\n const otherAttributes = omitKeys(attributes, ['model', 'usage', 'parameters', 'completionStartTime']);\n payload.metadata = {\n ...payload.metadata,\n ...otherAttributes,\n };\n } else {\n // For non-LLM spans, put all attributes in metadata\n payload.metadata = {\n ...payload.metadata,\n ...attributes,\n };\n }\n\n // Handle errors\n if (span.errorInfo) {\n payload.error = span.errorInfo.message;\n payload.metadata.errorDetails = span.errorInfo;\n }\n\n return payload;\n }\n\n async shutdown(): Promise<void> {\n if (!this.config) {\n return;\n }\n\n // End all active spans\n for (const [_traceId, spanData] of this.traceMap) {\n for (const [_spanId, runTree] of spanData.spans) {\n await runTree.end();\n await runTree.patchRun();\n }\n }\n this.traceMap.clear();\n await super.shutdown();\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/metrics.ts","../src/tracing.ts"],"names":["SpanType","TrackingExporter","Client","RunTree","omitKeys"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { UsageStats } from '@mastra/core/observability';\n\n/**\n * LangSmithUsageMetrics\n *\n * Canonical metric keys expected by LangSmith for LLM usage accounting.\n * See: https://docs.langchain.com/langsmith/log-llm-trace#provide-token-and-cost-information\n */\nexport interface LangSmithUsageMetrics {\n input_tokens?: number;\n output_tokens?: number;\n total_tokens?: number;\n input_token_details?: {\n [key: string]: number;\n };\n output_token_details?: {\n [key: string]: number;\n };\n}\n\n/**\n * Formats UsageStats to LangSmith's expected metric format.\n */\nexport function formatUsageMetrics(usage?: UsageStats): LangSmithUsageMetrics {\n const metrics: LangSmithUsageMetrics = {};\n\n if (usage?.inputTokens !== undefined) {\n metrics.input_tokens = usage.inputTokens;\n }\n\n if (usage?.outputTokens !== undefined) {\n metrics.output_tokens = usage.outputTokens;\n }\n\n // Compute total if we have both\n if (metrics.input_tokens !== undefined && metrics.output_tokens !== undefined) {\n metrics.total_tokens = metrics.input_tokens + metrics.output_tokens;\n }\n\n if (usage?.outputDetails?.reasoning !== undefined) {\n metrics.output_token_details = {\n ...(metrics.output_token_details ?? {}),\n reasoning_tokens: usage.outputDetails.reasoning,\n };\n }\n\n if (usage?.inputDetails?.cacheRead !== undefined) {\n metrics.input_token_details = {\n ...(metrics.input_token_details ?? {}),\n cache_read: usage.inputDetails.cacheRead,\n };\n }\n\n if (usage?.inputDetails?.cacheWrite !== undefined) {\n metrics.input_token_details = {\n ...(metrics.input_token_details ?? {}),\n cache_write: usage.inputDetails.cacheWrite,\n };\n }\n\n if (usage?.inputDetails?.audio !== undefined) {\n metrics.input_token_details = {\n ...(metrics.input_token_details ?? {}),\n audio: usage.inputDetails.audio,\n };\n }\n\n if (usage?.outputDetails?.audio !== undefined) {\n metrics.output_token_details = {\n ...(metrics.output_token_details ?? {}),\n audio: usage.outputDetails.audio,\n };\n }\n\n return metrics;\n}\n","/**\n * LangSmith Exporter for Mastra Tracing\n *\n * This exporter sends observability data to LangSmith\n * Root spans become top-level LangSmith RunTrees (no trace wrapper).\n * Events are handled as zero-duration RunTrees with matching start/end times.\n */\n\nimport type { AnyExportedSpan, ModelGenerationAttributes, SpanErrorInfo } from '@mastra/core/observability';\nimport { SpanType } from '@mastra/core/observability';\nimport { omitKeys } from '@mastra/core/utils';\nimport { TrackingExporter } from '@mastra/observability';\nimport type { TraceData, TrackingExporterConfig } from '@mastra/observability';\nimport type { ClientConfig, RunTreeConfig } from 'langsmith';\nimport { Client, RunTree } from 'langsmith';\nimport type { KVMap } from 'langsmith/schemas';\nimport { formatUsageMetrics } from './metrics';\n\nexport interface LangSmithExporterConfig extends ClientConfig, TrackingExporterConfig {\n /** LangSmith client instance */\n client?: Client;\n /**\n * The name of the LangSmith project to send traces to.\n * Overrides the LANGCHAIN_PROJECT environment variable.\n * If neither is set, traces are sent to the \"default\" project.\n */\n projectName?: string;\n}\n\ntype LangSmithRoot = undefined;\ntype LangSmithSpan = RunTree;\ntype LangSmithEvent = RunTree;\ntype LangSmithMetadata = unknown;\ntype LangSmithTraceData = TraceData<LangSmithRoot, LangSmithSpan, LangSmithEvent, LangSmithMetadata>;\n\n// Default span type for all spans\nconst DEFAULT_SPAN_TYPE = 'chain';\n\n// Exceptions to the default mapping\nconst SPAN_TYPE_EXCEPTIONS: Partial<Record<SpanType, 'llm' | 'tool' | 'chain'>> = {\n [SpanType.MODEL_GENERATION]: 'llm',\n [SpanType.TOOL_CALL]: 'tool',\n [SpanType.MCP_TOOL_CALL]: 'tool',\n [SpanType.WORKFLOW_CONDITIONAL_EVAL]: 'chain',\n [SpanType.WORKFLOW_WAIT_EVENT]: 'chain',\n};\n\n// Mapping function - returns valid LangSmith span types\nfunction mapSpanType(spanType: SpanType): 'llm' | 'tool' | 'chain' {\n return SPAN_TYPE_EXCEPTIONS[spanType] ?? DEFAULT_SPAN_TYPE;\n}\n\nfunction isKVMap(value: unknown): value is KVMap {\n return value != null && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date);\n}\n\nexport class LangSmithExporter extends TrackingExporter<\n LangSmithRoot,\n LangSmithSpan,\n LangSmithEvent,\n LangSmithMetadata,\n LangSmithExporterConfig\n> {\n override name = 'langsmith';\n #client: Client | undefined;\n\n constructor(config: LangSmithExporterConfig = {}) {\n // Resolve env vars BEFORE calling super (config is readonly in base class)\n const apiKey = config.apiKey ?? process.env.LANGSMITH_API_KEY;\n\n super({\n ...config,\n apiKey,\n });\n\n if (!apiKey) {\n this.setDisabled(`Missing required credentials (apiKey: ${!!apiKey})`);\n return;\n }\n\n this.#client = config.client ?? new Client(this.config);\n }\n\n protected override skipBuildRootTask = true;\n protected override async _buildRoot(_args: {\n span: AnyExportedSpan;\n traceData: LangSmithTraceData;\n }): Promise<LangSmithRoot | undefined> {\n throw new Error('Method not implemented.');\n }\n\n protected override async _buildSpan(args: {\n span: AnyExportedSpan;\n traceData: LangSmithTraceData;\n }): Promise<LangSmithSpan | undefined> {\n const { span, traceData } = args;\n\n const parent = span.isRootSpan ? undefined : traceData.getParent(args);\n\n if (!span.isRootSpan && !parent) {\n // parent doesn't exist and not creating rootSpan, return early data\n return;\n }\n\n const payload = {\n name: span.name,\n ...this.buildRunTreePayload(span, true),\n };\n\n const langSmithSpan = span.isRootSpan ? new RunTree(payload) : parent!.createChild(payload);\n\n await langSmithSpan.postRun();\n return langSmithSpan;\n }\n\n protected override async _buildEvent(args: {\n span: AnyExportedSpan;\n traceData: LangSmithTraceData;\n }): Promise<LangSmithEvent | undefined> {\n const langSmithSpan = await this._buildSpan(args);\n\n if (!langSmithSpan) {\n // parent doesn't exist and not creating rootSpan, return early data\n return;\n }\n\n // use start-time as end-time to make an event span.\n await langSmithSpan.end({ endTime: args.span.startTime.getTime() });\n await langSmithSpan.patchRun();\n return langSmithSpan;\n }\n\n protected override async _updateSpan(args: { span: AnyExportedSpan; traceData: LangSmithTraceData }): Promise<void> {\n await this.handleSpanUpdateOrEnd({ ...args, isEnd: false });\n }\n\n protected override async _finishSpan(args: { span: AnyExportedSpan; traceData: LangSmithTraceData }): Promise<void> {\n await this.handleSpanUpdateOrEnd({ ...args, isEnd: true });\n }\n\n protected override async _abortSpan(args: {\n span: LangSmithSpan;\n traceData: LangSmithTraceData;\n reason: SpanErrorInfo;\n }): Promise<void> {\n const { span, reason } = args;\n span.error = reason.message;\n span.metadata = {\n ...span.metadata,\n errorDetails: reason,\n };\n await span.end();\n await span.patchRun();\n }\n\n private async handleSpanUpdateOrEnd(args: {\n span: AnyExportedSpan;\n traceData: LangSmithTraceData;\n isEnd: boolean;\n }): Promise<void> {\n const { span, traceData, isEnd } = args;\n\n const langSmithSpan = traceData.getSpan({ spanId: span.id });\n if (!langSmithSpan) {\n //update occurred before span start, return early data\n return;\n }\n\n const updatePayload = this.buildRunTreePayload(span);\n\n langSmithSpan.metadata = {\n ...langSmithSpan.metadata,\n ...updatePayload.metadata,\n };\n if (updatePayload.inputs != null) {\n langSmithSpan.inputs = updatePayload.inputs;\n }\n if (updatePayload.outputs != null) {\n langSmithSpan.outputs = updatePayload.outputs;\n }\n if (updatePayload.error != null) {\n langSmithSpan.error = updatePayload.error;\n }\n\n // Add new_token event for TTFT tracking on MODEL_GENERATION spans\n if (span.type === SpanType.MODEL_GENERATION) {\n const modelAttr = (span.attributes ?? {}) as ModelGenerationAttributes;\n if (modelAttr.completionStartTime !== undefined) {\n langSmithSpan.addEvent({\n name: 'new_token',\n time: modelAttr.completionStartTime.toISOString(),\n });\n }\n }\n\n if (isEnd) {\n // End the span with the correct endTime\n if (span.endTime) {\n await langSmithSpan.end({ endTime: span.endTime.getTime() });\n } else {\n await langSmithSpan.end();\n }\n }\n await langSmithSpan.patchRun();\n }\n\n private buildRunTreePayload(span: AnyExportedSpan, isNew = false): Partial<RunTreeConfig> {\n const payload: Partial<RunTreeConfig> & { metadata: KVMap } = {\n client: this.#client,\n metadata: {\n mastra_span_type: span.type,\n ...span.metadata,\n },\n };\n\n if (isNew) {\n payload.run_type = mapSpanType(span.type);\n payload.start_time = span.startTime.getTime();\n }\n\n // Add project name if configured\n if (this.config.projectName) {\n payload.project_name = this.config.projectName;\n }\n\n // Add tags for root spans\n if (span.isRootSpan && span.tags?.length) {\n payload.tags = span.tags;\n }\n\n // Core span data\n if (span.input !== undefined) {\n payload.inputs = isKVMap(span.input) ? span.input : { input: span.input };\n }\n\n if (span.output !== undefined) {\n payload.outputs = isKVMap(span.output) ? span.output : { output: span.output };\n }\n\n const attributes = (span.attributes ?? {}) as Record<string, any>;\n\n if (span.type === SpanType.MODEL_GENERATION) {\n const modelAttr = attributes as ModelGenerationAttributes;\n\n // See: https://docs.langchain.com/langsmith/log-llm-trace\n if (modelAttr.model !== undefined) {\n // Note - this should map to a model name recognized by LangSmith\n // eg “gpt-4o-mini”, “claude-3-opus-20240307”, etc.\n payload.metadata.ls_model_name = modelAttr.model;\n }\n\n // Provider goes to metadata (if provided by attributes)\n if (modelAttr.provider !== undefined) {\n // Note - this should map to a provider name recognized by\n // LangSmith eg “openai”, “anthropic”, etc.\n payload.metadata.ls_provider = modelAttr.provider;\n }\n\n // Usage/token info goes to metrics\n payload.metadata.usage_metadata = formatUsageMetrics(modelAttr.usage);\n\n // Model parameters go to metadata\n if (modelAttr.parameters !== undefined) {\n payload.metadata.modelParameters = modelAttr.parameters;\n }\n\n // Other LLM attributes go to metadata\n const otherAttributes = omitKeys(attributes, ['model', 'provider', 'usage', 'parameters', 'completionStartTime']);\n payload.metadata = {\n ...payload.metadata,\n ...otherAttributes,\n };\n } else {\n // For non-LLM spans, put all attributes in metadata\n payload.metadata = {\n ...payload.metadata,\n ...attributes,\n };\n }\n\n // Handle errors\n if (span.errorInfo) {\n payload.error = span.errorInfo.message;\n payload.metadata.errorDetails = span.errorInfo;\n }\n\n return payload;\n }\n}\n"]}
|