langsmith 0.3.87 → 0.4.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/README.md +0 -50
- package/dist/client.cjs +52 -31
- package/dist/client.d.ts +2 -9
- package/dist/client.js +53 -32
- package/dist/evaluation/_runner.cjs +34 -32
- package/dist/evaluation/_runner.js +35 -33
- package/dist/experimental/otel/setup.cjs +2 -0
- package/dist/experimental/otel/setup.d.ts +2 -0
- package/dist/experimental/otel/setup.js +2 -0
- package/dist/experimental/vercel/index.d.ts +11 -0
- package/dist/experimental/vercel/middleware.cjs +15 -4
- package/dist/experimental/vercel/middleware.d.ts +1 -0
- package/dist/experimental/vercel/middleware.js +15 -4
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/singletons/constants.cjs +2 -1
- package/dist/singletons/constants.d.ts +1 -0
- package/dist/singletons/constants.js +1 -0
- package/dist/traceable.cjs +107 -43
- package/dist/traceable.js +108 -44
- package/dist/utils/error.cjs +33 -1
- package/dist/utils/error.d.ts +13 -0
- package/dist/utils/error.js +30 -0
- package/dist/utils/jestlike/index.cjs +1 -1
- package/dist/utils/jestlike/index.js +1 -1
- package/dist/utils/jestlike/types.d.ts +0 -4
- package/package.json +1 -40
- package/dist/evaluation/langchain.cjs +0 -54
- package/dist/evaluation/langchain.d.ts +0 -21
- package/dist/evaluation/langchain.js +0 -51
- package/dist/utils/vercel.types.cjs +0 -2
- package/dist/utils/vercel.types.d.ts +0 -1
- package/dist/utils/vercel.types.js +0 -1
- package/dist/vercel.cjs +0 -866
- package/dist/vercel.d.ts +0 -87
- package/dist/vercel.js +0 -861
- package/dist/wrappers/vercel.cjs +0 -101
- package/dist/wrappers/vercel.d.ts +0 -31
- package/dist/wrappers/vercel.js +0 -97
- package/evaluation/langchain.cjs +0 -1
- package/evaluation/langchain.d.cts +0 -1
- package/evaluation/langchain.d.ts +0 -1
- package/evaluation/langchain.js +0 -1
- package/vercel.cjs +0 -1
- package/vercel.d.cts +0 -1
- package/vercel.d.ts +0 -1
- package/vercel.js +0 -1
- package/wrappers/vercel.cjs +0 -1
- package/wrappers/vercel.d.cts +0 -1
- package/wrappers/vercel.d.ts +0 -1
- package/wrappers/vercel.js +0 -1
package/dist/vercel.cjs
DELETED
|
@@ -1,866 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AISDKExporter = exports.parseStrippedIsoTime = void 0;
|
|
4
|
-
const vercel_js_1 = require("./utils/vercel.cjs");
|
|
5
|
-
const index_js_1 = require("./index.cjs");
|
|
6
|
-
const uuid_1 = require("uuid");
|
|
7
|
-
const traceable_js_1 = require("./singletons/traceable.cjs");
|
|
8
|
-
const env_js_1 = require("./utils/env.cjs");
|
|
9
|
-
const env_js_2 = require("./env.cjs");
|
|
10
|
-
const constants_js_1 = require("./experimental/otel/constants.cjs");
|
|
11
|
-
// Attempt to convert CoreMessage to a LangChain-compatible format
|
|
12
|
-
// which allows us to render messages more nicely in LangSmith
|
|
13
|
-
function convertCoreToSmith(message) {
|
|
14
|
-
if (message.role === "assistant") {
|
|
15
|
-
const data = { content: message.content };
|
|
16
|
-
if (Array.isArray(message.content)) {
|
|
17
|
-
data.content = message.content.map((part) => {
|
|
18
|
-
if (part.type === "text") {
|
|
19
|
-
return {
|
|
20
|
-
type: "text",
|
|
21
|
-
text: part.text,
|
|
22
|
-
// Backcompat for AI SDK 4
|
|
23
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
|
-
...part.experimental_providerMetadata,
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
if (part.type === "tool-call") {
|
|
28
|
-
// Backcompat for AI SDK 4
|
|
29
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
-
const legacyToolCallInput = part.args;
|
|
31
|
-
return {
|
|
32
|
-
type: "tool_use",
|
|
33
|
-
name: part.toolName,
|
|
34
|
-
id: part.toolCallId,
|
|
35
|
-
input: legacyToolCallInput ?? part.input,
|
|
36
|
-
// Backcompat for AI SDK 4
|
|
37
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
38
|
-
...part.experimental_providerMetadata,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
return part;
|
|
42
|
-
});
|
|
43
|
-
const toolCalls = message.content.filter((part) => part.type === "tool-call");
|
|
44
|
-
if (toolCalls.length > 0) {
|
|
45
|
-
data.additional_kwargs ??= {};
|
|
46
|
-
data.additional_kwargs.tool_calls = toolCalls.map((part) => {
|
|
47
|
-
// Backcompat for AI SDK 4
|
|
48
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
49
|
-
const legacyToolCallInput = part.args;
|
|
50
|
-
return {
|
|
51
|
-
id: part.toolCallId,
|
|
52
|
-
type: "function",
|
|
53
|
-
function: {
|
|
54
|
-
name: part.toolName,
|
|
55
|
-
id: part.toolCallId,
|
|
56
|
-
arguments: JSON.stringify(legacyToolCallInput ?? part.input),
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return { type: "ai", data };
|
|
63
|
-
}
|
|
64
|
-
if (message.role === "user") {
|
|
65
|
-
const data = { content: message.content };
|
|
66
|
-
if (Array.isArray(message.content)) {
|
|
67
|
-
data.content = message.content.map((part) => {
|
|
68
|
-
if (part.type === "text") {
|
|
69
|
-
return {
|
|
70
|
-
type: "text",
|
|
71
|
-
text: part.text,
|
|
72
|
-
// Backcompat for AI SDK 4
|
|
73
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
74
|
-
...part.experimental_providerMetadata,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
if (part.type === "image") {
|
|
78
|
-
let imageUrl = part.image;
|
|
79
|
-
if (typeof imageUrl !== "string") {
|
|
80
|
-
let uint8Array;
|
|
81
|
-
if (imageUrl != null &&
|
|
82
|
-
typeof imageUrl === "object" &&
|
|
83
|
-
"type" in imageUrl &&
|
|
84
|
-
"data" in imageUrl) {
|
|
85
|
-
// Typing is wrong here if a buffer is passed in
|
|
86
|
-
uint8Array = new Uint8Array(imageUrl.data);
|
|
87
|
-
}
|
|
88
|
-
else if (imageUrl != null &&
|
|
89
|
-
typeof imageUrl === "object" &&
|
|
90
|
-
Object.keys(imageUrl).every((key) => !isNaN(Number(key)))) {
|
|
91
|
-
// ArrayBuffers get turned into objects with numeric keys for some reason
|
|
92
|
-
uint8Array = new Uint8Array(Array.from({
|
|
93
|
-
...imageUrl,
|
|
94
|
-
length: Object.keys(imageUrl).length,
|
|
95
|
-
}));
|
|
96
|
-
}
|
|
97
|
-
if (uint8Array) {
|
|
98
|
-
let binary = "";
|
|
99
|
-
for (let i = 0; i < uint8Array.length; i++) {
|
|
100
|
-
binary += String.fromCharCode(uint8Array[i]);
|
|
101
|
-
}
|
|
102
|
-
imageUrl = btoa(binary);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return {
|
|
106
|
-
type: "image_url",
|
|
107
|
-
image_url: imageUrl,
|
|
108
|
-
// Backcompat for AI SDK 4
|
|
109
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
110
|
-
...part.experimental_providerMetadata,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
return part;
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
return { type: "human", data };
|
|
117
|
-
}
|
|
118
|
-
if (message.role === "system") {
|
|
119
|
-
return { type: "system", data: { content: message.content } };
|
|
120
|
-
}
|
|
121
|
-
if (message.role === "tool") {
|
|
122
|
-
const res = message.content.map((toolCall) => {
|
|
123
|
-
// Backcompat for AI SDK 4
|
|
124
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
125
|
-
const legacyToolCallResult = toolCall.result;
|
|
126
|
-
return {
|
|
127
|
-
type: "tool",
|
|
128
|
-
data: {
|
|
129
|
-
content: JSON.stringify(legacyToolCallResult ?? toolCall.output),
|
|
130
|
-
name: toolCall.toolName,
|
|
131
|
-
tool_call_id: toolCall.toolCallId,
|
|
132
|
-
},
|
|
133
|
-
};
|
|
134
|
-
});
|
|
135
|
-
if (res.length === 1)
|
|
136
|
-
return res[0];
|
|
137
|
-
return res;
|
|
138
|
-
}
|
|
139
|
-
return message;
|
|
140
|
-
}
|
|
141
|
-
const tryJson = (str) => {
|
|
142
|
-
try {
|
|
143
|
-
if (!str)
|
|
144
|
-
return str;
|
|
145
|
-
if (typeof str !== "string")
|
|
146
|
-
return str;
|
|
147
|
-
return JSON.parse(str);
|
|
148
|
-
}
|
|
149
|
-
catch {
|
|
150
|
-
return str;
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
function stripNonAlphanumeric(input) {
|
|
154
|
-
return input.replace(/[-:.]/g, "");
|
|
155
|
-
}
|
|
156
|
-
function getDotOrder(item) {
|
|
157
|
-
const { startTime: [seconds, nanoseconds], id: runId, executionOrder, } = item;
|
|
158
|
-
// Date only has millisecond precision, so we use the microseconds to break
|
|
159
|
-
// possible ties, avoiding incorrect run order
|
|
160
|
-
const nanosecondString = String(nanoseconds).padStart(9, "0");
|
|
161
|
-
const msFull = Number(nanosecondString.slice(0, 6)) + executionOrder;
|
|
162
|
-
const msString = String(msFull).padStart(6, "0");
|
|
163
|
-
const ms = Number(msString.slice(0, -3));
|
|
164
|
-
const ns = msString.slice(-3);
|
|
165
|
-
return (stripNonAlphanumeric(`${new Date(seconds * 1000 + ms).toISOString().slice(0, -1)}${ns}Z`) + runId);
|
|
166
|
-
}
|
|
167
|
-
function joinDotOrder(...segments) {
|
|
168
|
-
return segments.filter(Boolean).join(".");
|
|
169
|
-
}
|
|
170
|
-
function removeDotOrder(dotOrder, ...ids) {
|
|
171
|
-
return dotOrder
|
|
172
|
-
.split(".")
|
|
173
|
-
.filter((i) => !ids.some((id) => i.includes(id)))
|
|
174
|
-
.join(".");
|
|
175
|
-
}
|
|
176
|
-
function reparentDotOrder(dotOrder, sourceRunId, parentDotOrder) {
|
|
177
|
-
const segments = dotOrder.split(".");
|
|
178
|
-
const sourceIndex = segments.findIndex((i) => i.includes(sourceRunId));
|
|
179
|
-
if (sourceIndex === -1)
|
|
180
|
-
return dotOrder;
|
|
181
|
-
return joinDotOrder(...parentDotOrder.split("."), ...segments.slice(sourceIndex));
|
|
182
|
-
}
|
|
183
|
-
// Helper function to convert dotted order version of start time to ISO string
|
|
184
|
-
const parseStrippedIsoTime = (stripped) => {
|
|
185
|
-
const year = stripped.slice(0, 4);
|
|
186
|
-
const month = stripped.slice(4, 6);
|
|
187
|
-
const day = stripped.slice(6, 8);
|
|
188
|
-
const hour = stripped.slice(9, 11); // Skip 'T'
|
|
189
|
-
const minute = stripped.slice(11, 13);
|
|
190
|
-
const second = stripped.slice(13, 15);
|
|
191
|
-
const ms = stripped.slice(15, 18); // milliseconds
|
|
192
|
-
const us = stripped.length >= 21 ? stripped.slice(18, 21) : "000"; // microseconds
|
|
193
|
-
// Create ISO string with microsecond precision only if microseconds are present
|
|
194
|
-
return us !== "000"
|
|
195
|
-
? `${year}-${month}-${day}T${hour}:${minute}:${second}.${ms}${us}Z`
|
|
196
|
-
: `${year}-${month}-${day}T${hour}:${minute}:${second}.${ms}Z`;
|
|
197
|
-
};
|
|
198
|
-
exports.parseStrippedIsoTime = parseStrippedIsoTime;
|
|
199
|
-
function getMutableRunCreate(dotOrder) {
|
|
200
|
-
const segments = dotOrder.split(".").map((i) => {
|
|
201
|
-
const [startTime, runId] = i.split("Z");
|
|
202
|
-
return { startTime, runId };
|
|
203
|
-
});
|
|
204
|
-
const traceId = segments[0].runId;
|
|
205
|
-
const parentRunId = segments.at(-2)?.runId;
|
|
206
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
207
|
-
const lastSegment = segments.at(-1);
|
|
208
|
-
const startTime = (0, exports.parseStrippedIsoTime)(lastSegment.startTime);
|
|
209
|
-
return {
|
|
210
|
-
id: lastSegment.runId,
|
|
211
|
-
trace_id: traceId,
|
|
212
|
-
dotted_order: dotOrder,
|
|
213
|
-
parent_run_id: parentRunId,
|
|
214
|
-
start_time: startTime,
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
function convertToTimestamp([seconds, nanoseconds]) {
|
|
218
|
-
const ms = String(nanoseconds).slice(0, 3);
|
|
219
|
-
return Number(String(seconds) + ms);
|
|
220
|
-
}
|
|
221
|
-
function sortByHr(a, b) {
|
|
222
|
-
if (a.startTime[0] !== b.startTime[0]) {
|
|
223
|
-
return Math.sign(a.startTime[0] - b.startTime[0]);
|
|
224
|
-
}
|
|
225
|
-
else if (a.startTime[1] !== b.startTime[1]) {
|
|
226
|
-
return Math.sign(a.startTime[1] - b.startTime[1]);
|
|
227
|
-
}
|
|
228
|
-
else if (getParentSpanId(a) === b.spanContext().spanId) {
|
|
229
|
-
return -1;
|
|
230
|
-
}
|
|
231
|
-
else if (getParentSpanId(b) === a.spanContext().spanId) {
|
|
232
|
-
return 1;
|
|
233
|
-
}
|
|
234
|
-
else {
|
|
235
|
-
return 0;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
const ROOT = "$";
|
|
239
|
-
const RUN_ID_NAMESPACE = "5c718b20-9078-11ef-9a3d-325096b39f47";
|
|
240
|
-
const RUN_ID_METADATA_KEY = {
|
|
241
|
-
input: "langsmith:runId",
|
|
242
|
-
output: "ai.telemetry.metadata.langsmith:runId",
|
|
243
|
-
};
|
|
244
|
-
const RUN_NAME_METADATA_KEY = {
|
|
245
|
-
input: "langsmith:runName",
|
|
246
|
-
output: "ai.telemetry.metadata.langsmith:runName",
|
|
247
|
-
};
|
|
248
|
-
const TRACE_METADATA_KEY = {
|
|
249
|
-
input: "langsmith:trace",
|
|
250
|
-
output: "ai.telemetry.metadata.langsmith:trace",
|
|
251
|
-
};
|
|
252
|
-
const BAGGAGE_METADATA_KEY = {
|
|
253
|
-
input: "langsmith:baggage",
|
|
254
|
-
output: "ai.telemetry.metadata.langsmith:baggage",
|
|
255
|
-
};
|
|
256
|
-
const RESERVED_METADATA_KEYS = [
|
|
257
|
-
RUN_ID_METADATA_KEY.output,
|
|
258
|
-
RUN_NAME_METADATA_KEY.output,
|
|
259
|
-
TRACE_METADATA_KEY.output,
|
|
260
|
-
BAGGAGE_METADATA_KEY.output,
|
|
261
|
-
];
|
|
262
|
-
function getParentSpanId(span) {
|
|
263
|
-
// Backcompat shim to support OTEL 1.x and 2.x
|
|
264
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
265
|
-
return (span.parentSpanId ?? span.parentSpanContext?.spanId ?? undefined);
|
|
266
|
-
}
|
|
267
|
-
/**
|
|
268
|
-
* @deprecated Use `wrapAISDK` from `langsmith/experimental/vercel` instead.
|
|
269
|
-
* OpenTelemetry trace exporter for Vercel AI SDK.
|
|
270
|
-
*
|
|
271
|
-
* @example
|
|
272
|
-
* ```ts
|
|
273
|
-
* import { AISDKExporter } from "langsmith/vercel";
|
|
274
|
-
* import { Client } from "langsmith";
|
|
275
|
-
*
|
|
276
|
-
* import { generateText } from "ai";
|
|
277
|
-
* import { openai } from "@ai-sdk/openai";
|
|
278
|
-
*
|
|
279
|
-
* import { NodeSDK } from "@opentelemetry/sdk-node";
|
|
280
|
-
* import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
|
|
281
|
-
*
|
|
282
|
-
* const client = new Client();
|
|
283
|
-
*
|
|
284
|
-
* const sdk = new NodeSDK({
|
|
285
|
-
* traceExporter: new AISDKExporter({ client }),
|
|
286
|
-
* instrumentations: [getNodeAutoInstrumentations()],
|
|
287
|
-
* });
|
|
288
|
-
*
|
|
289
|
-
* sdk.start();
|
|
290
|
-
*
|
|
291
|
-
* const res = await generateText({
|
|
292
|
-
* model: openai("gpt-4o-mini"),
|
|
293
|
-
* messages: [
|
|
294
|
-
* {
|
|
295
|
-
* role: "user",
|
|
296
|
-
* content: "What color is the sky?",
|
|
297
|
-
* },
|
|
298
|
-
* ],
|
|
299
|
-
* experimental_telemetry: AISDKExporter.getSettings({
|
|
300
|
-
* runName: "langsmith_traced_call",
|
|
301
|
-
* metadata: { userId: "123", language: "english" },
|
|
302
|
-
* }),
|
|
303
|
-
* });
|
|
304
|
-
*
|
|
305
|
-
* await sdk.shutdown();
|
|
306
|
-
* ```
|
|
307
|
-
*/
|
|
308
|
-
class AISDKExporter {
|
|
309
|
-
constructor(args) {
|
|
310
|
-
Object.defineProperty(this, "client", {
|
|
311
|
-
enumerable: true,
|
|
312
|
-
configurable: true,
|
|
313
|
-
writable: true,
|
|
314
|
-
value: void 0
|
|
315
|
-
});
|
|
316
|
-
Object.defineProperty(this, "traceByMap", {
|
|
317
|
-
enumerable: true,
|
|
318
|
-
configurable: true,
|
|
319
|
-
writable: true,
|
|
320
|
-
value: {}
|
|
321
|
-
});
|
|
322
|
-
Object.defineProperty(this, "seenSpanInfo", {
|
|
323
|
-
enumerable: true,
|
|
324
|
-
configurable: true,
|
|
325
|
-
writable: true,
|
|
326
|
-
value: {}
|
|
327
|
-
});
|
|
328
|
-
Object.defineProperty(this, "pendingSpans", {
|
|
329
|
-
enumerable: true,
|
|
330
|
-
configurable: true,
|
|
331
|
-
writable: true,
|
|
332
|
-
value: {}
|
|
333
|
-
});
|
|
334
|
-
Object.defineProperty(this, "debug", {
|
|
335
|
-
enumerable: true,
|
|
336
|
-
configurable: true,
|
|
337
|
-
writable: true,
|
|
338
|
-
value: void 0
|
|
339
|
-
});
|
|
340
|
-
Object.defineProperty(this, "projectName", {
|
|
341
|
-
enumerable: true,
|
|
342
|
-
configurable: true,
|
|
343
|
-
writable: true,
|
|
344
|
-
value: void 0
|
|
345
|
-
});
|
|
346
|
-
/** @internal */
|
|
347
|
-
Object.defineProperty(this, "getSpanAttributeKey", {
|
|
348
|
-
enumerable: true,
|
|
349
|
-
configurable: true,
|
|
350
|
-
writable: true,
|
|
351
|
-
value: (span, key) => {
|
|
352
|
-
const attributes = span.attributes;
|
|
353
|
-
return key in attributes && typeof attributes[key] === "string"
|
|
354
|
-
? attributes[key]
|
|
355
|
-
: undefined;
|
|
356
|
-
}
|
|
357
|
-
});
|
|
358
|
-
this.client = args?.client ?? new index_js_1.Client();
|
|
359
|
-
this.debug =
|
|
360
|
-
args?.debug ?? (0, env_js_1.getEnvironmentVariable)("OTEL_LOG_LEVEL") === "DEBUG";
|
|
361
|
-
this.projectName = args?.projectName;
|
|
362
|
-
this.logDebug("creating exporter", { tracingEnabled: (0, env_js_2.isTracingEnabled)() });
|
|
363
|
-
}
|
|
364
|
-
static getSettings(settings) {
|
|
365
|
-
const { runId, runName, ...rest } = settings ?? {};
|
|
366
|
-
const metadata = { ...rest?.metadata };
|
|
367
|
-
if (runId != null)
|
|
368
|
-
metadata[RUN_ID_METADATA_KEY.input] = runId;
|
|
369
|
-
if (runName != null)
|
|
370
|
-
metadata[RUN_NAME_METADATA_KEY.input] = runName;
|
|
371
|
-
// attempt to obtain the run tree if used within a traceable function
|
|
372
|
-
let defaultEnabled = settings?.isEnabled ?? (0, env_js_2.isTracingEnabled)();
|
|
373
|
-
try {
|
|
374
|
-
const runTree = (0, traceable_js_1.getCurrentRunTree)();
|
|
375
|
-
const headers = runTree.toHeaders();
|
|
376
|
-
metadata[TRACE_METADATA_KEY.input] = headers["langsmith-trace"];
|
|
377
|
-
metadata[BAGGAGE_METADATA_KEY.input] = headers["baggage"];
|
|
378
|
-
// honor the tracingEnabled flag if coming from traceable
|
|
379
|
-
if (runTree.tracingEnabled != null) {
|
|
380
|
-
defaultEnabled = runTree.tracingEnabled;
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
catch {
|
|
384
|
-
// pass
|
|
385
|
-
}
|
|
386
|
-
if (metadata[RUN_ID_METADATA_KEY.input] &&
|
|
387
|
-
metadata[TRACE_METADATA_KEY.input]) {
|
|
388
|
-
throw new Error("Cannot provide `runId` when used within traceable function.");
|
|
389
|
-
}
|
|
390
|
-
return { ...rest, isEnabled: rest.isEnabled ?? defaultEnabled, metadata };
|
|
391
|
-
}
|
|
392
|
-
/** @internal */
|
|
393
|
-
parseInteropFromMetadata(span, parentSpan) {
|
|
394
|
-
if (!this.isRootRun(span))
|
|
395
|
-
return undefined;
|
|
396
|
-
if (parentSpan?.name === "ai.toolCall") {
|
|
397
|
-
return undefined;
|
|
398
|
-
}
|
|
399
|
-
const userTraceId = this.getSpanAttributeKey(span, RUN_ID_METADATA_KEY.output);
|
|
400
|
-
const parentTrace = this.getSpanAttributeKey(span, TRACE_METADATA_KEY.output);
|
|
401
|
-
if (parentTrace && userTraceId) {
|
|
402
|
-
throw new Error(`Cannot provide both "${RUN_ID_METADATA_KEY.input}" and "${TRACE_METADATA_KEY.input}" metadata keys.`);
|
|
403
|
-
}
|
|
404
|
-
if (parentTrace) {
|
|
405
|
-
const parentRunTree = index_js_1.RunTree.fromHeaders({
|
|
406
|
-
"langsmith-trace": parentTrace,
|
|
407
|
-
baggage: this.getSpanAttributeKey(span, BAGGAGE_METADATA_KEY.output) || "",
|
|
408
|
-
});
|
|
409
|
-
if (!parentRunTree)
|
|
410
|
-
throw new Error("Unreachable code: empty parent run tree");
|
|
411
|
-
return { type: "traceable", parentRunTree };
|
|
412
|
-
}
|
|
413
|
-
if (userTraceId)
|
|
414
|
-
return { type: "user", userRunId: userTraceId };
|
|
415
|
-
return undefined;
|
|
416
|
-
}
|
|
417
|
-
/** @internal */
|
|
418
|
-
getRunCreate(span, projectName) {
|
|
419
|
-
const asRunCreate = (rawConfig) => {
|
|
420
|
-
const aiMetadata = Object.keys(span.attributes)
|
|
421
|
-
.filter((key) => key.startsWith("ai.telemetry.metadata.") &&
|
|
422
|
-
!RESERVED_METADATA_KEYS.includes(key))
|
|
423
|
-
.reduce((acc, key) => {
|
|
424
|
-
acc[key.slice("ai.telemetry.metadata.".length)] =
|
|
425
|
-
span.attributes[key];
|
|
426
|
-
return acc;
|
|
427
|
-
}, {});
|
|
428
|
-
if (("ai.telemetry.functionId" in span.attributes &&
|
|
429
|
-
span.attributes["ai.telemetry.functionId"]) ||
|
|
430
|
-
("resource.name" in span.attributes && span.attributes["resource.name"])) {
|
|
431
|
-
aiMetadata["functionId"] =
|
|
432
|
-
span.attributes["ai.telemetry.functionId"] ||
|
|
433
|
-
span.attributes["resource.name"];
|
|
434
|
-
}
|
|
435
|
-
const parsedStart = convertToTimestamp(span.startTime);
|
|
436
|
-
const parsedEnd = convertToTimestamp(span.endTime);
|
|
437
|
-
let name = rawConfig.name;
|
|
438
|
-
// if user provided a custom name, only use it if it's the root
|
|
439
|
-
if (this.isRootRun(span)) {
|
|
440
|
-
name =
|
|
441
|
-
this.getSpanAttributeKey(span, RUN_NAME_METADATA_KEY.output) || name;
|
|
442
|
-
}
|
|
443
|
-
const config = {
|
|
444
|
-
...rawConfig,
|
|
445
|
-
name,
|
|
446
|
-
extra: {
|
|
447
|
-
...rawConfig.extra,
|
|
448
|
-
metadata: {
|
|
449
|
-
...rawConfig.extra?.metadata,
|
|
450
|
-
...aiMetadata,
|
|
451
|
-
"ai.operationId": span.attributes["ai.operationId"],
|
|
452
|
-
},
|
|
453
|
-
},
|
|
454
|
-
session_name: projectName ??
|
|
455
|
-
this.projectName ??
|
|
456
|
-
(0, env_js_1.getLangSmithEnvironmentVariable)("PROJECT") ??
|
|
457
|
-
(0, env_js_1.getLangSmithEnvironmentVariable)("SESSION"),
|
|
458
|
-
start_time: Math.min(parsedStart, parsedEnd),
|
|
459
|
-
end_time: Math.max(parsedStart, parsedEnd),
|
|
460
|
-
};
|
|
461
|
-
return config;
|
|
462
|
-
};
|
|
463
|
-
switch (span.name) {
|
|
464
|
-
case "ai.generateText.doGenerate":
|
|
465
|
-
case "ai.generateText":
|
|
466
|
-
case "ai.streamText.doStream":
|
|
467
|
-
case "ai.streamText": {
|
|
468
|
-
const inputs = (() => {
|
|
469
|
-
if ("ai.prompt.messages" in span.attributes) {
|
|
470
|
-
return {
|
|
471
|
-
messages: tryJson(span.attributes["ai.prompt.messages"]).flatMap((i) => convertCoreToSmith(i)),
|
|
472
|
-
};
|
|
473
|
-
}
|
|
474
|
-
if ("ai.prompt" in span.attributes) {
|
|
475
|
-
const input = tryJson(span.attributes["ai.prompt"]);
|
|
476
|
-
if (typeof input === "object" &&
|
|
477
|
-
input != null &&
|
|
478
|
-
"messages" in input &&
|
|
479
|
-
Array.isArray(input.messages)) {
|
|
480
|
-
return {
|
|
481
|
-
messages: input.messages.flatMap((i) => convertCoreToSmith(i)),
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
|
-
return { input };
|
|
485
|
-
}
|
|
486
|
-
return {};
|
|
487
|
-
})();
|
|
488
|
-
const outputs = (() => {
|
|
489
|
-
let result = undefined;
|
|
490
|
-
if (span.attributes["ai.response.toolCalls"]) {
|
|
491
|
-
let content = tryJson(span.attributes["ai.response.toolCalls"]);
|
|
492
|
-
if (Array.isArray(content)) {
|
|
493
|
-
content = content.map((i) => ({
|
|
494
|
-
type: "tool-call",
|
|
495
|
-
...i,
|
|
496
|
-
args: tryJson(i.args),
|
|
497
|
-
}));
|
|
498
|
-
}
|
|
499
|
-
result = {
|
|
500
|
-
llm_output: convertCoreToSmith({
|
|
501
|
-
role: "assistant",
|
|
502
|
-
content,
|
|
503
|
-
}),
|
|
504
|
-
};
|
|
505
|
-
}
|
|
506
|
-
else if (span.attributes["ai.response.text"]) {
|
|
507
|
-
result = {
|
|
508
|
-
llm_output: convertCoreToSmith({
|
|
509
|
-
role: "assistant",
|
|
510
|
-
content: span.attributes["ai.response.text"],
|
|
511
|
-
}),
|
|
512
|
-
};
|
|
513
|
-
}
|
|
514
|
-
return result;
|
|
515
|
-
})();
|
|
516
|
-
const invocationParams = (() => {
|
|
517
|
-
if ("ai.prompt.tools" in span.attributes) {
|
|
518
|
-
return {
|
|
519
|
-
tools: span.attributes["ai.prompt.tools"].flatMap((tool) => {
|
|
520
|
-
try {
|
|
521
|
-
return JSON.parse(tool);
|
|
522
|
-
}
|
|
523
|
-
catch {
|
|
524
|
-
// pass
|
|
525
|
-
}
|
|
526
|
-
return [];
|
|
527
|
-
}),
|
|
528
|
-
};
|
|
529
|
-
}
|
|
530
|
-
return {};
|
|
531
|
-
})();
|
|
532
|
-
const events = [];
|
|
533
|
-
const firstChunkEvent = span.events.find((i) => i.name === "ai.stream.firstChunk");
|
|
534
|
-
if (firstChunkEvent) {
|
|
535
|
-
events.push({
|
|
536
|
-
name: "new_token",
|
|
537
|
-
time: convertToTimestamp(firstChunkEvent.time),
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
|
-
const runType = span.name === "ai.generateText" || span.name === "ai.streamText"
|
|
541
|
-
? "chain"
|
|
542
|
-
: "llm";
|
|
543
|
-
const error = span.status?.code === 2 ? span.status.message : undefined;
|
|
544
|
-
const usageMetadata = (0, vercel_js_1.extractUsageMetadata)(span);
|
|
545
|
-
// TODO: add first_token_time
|
|
546
|
-
return asRunCreate({
|
|
547
|
-
run_type: runType,
|
|
548
|
-
name: span.attributes["ai.model.provider"],
|
|
549
|
-
error,
|
|
550
|
-
inputs,
|
|
551
|
-
outputs,
|
|
552
|
-
events,
|
|
553
|
-
extra: {
|
|
554
|
-
invocation_params: invocationParams,
|
|
555
|
-
batch_size: 1,
|
|
556
|
-
metadata: {
|
|
557
|
-
usage_metadata: usageMetadata,
|
|
558
|
-
ls_provider: span.attributes["ai.model.provider"]
|
|
559
|
-
.split(".")
|
|
560
|
-
.at(0),
|
|
561
|
-
ls_model_type: span.attributes["ai.model.provider"]
|
|
562
|
-
.split(".")
|
|
563
|
-
.at(1),
|
|
564
|
-
ls_model_name: span.attributes["ai.model.id"],
|
|
565
|
-
},
|
|
566
|
-
},
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
case "ai.toolCall": {
|
|
570
|
-
const args = tryJson(span.attributes["ai.toolCall.input"] ??
|
|
571
|
-
span.attributes["ai.toolCall.args"]);
|
|
572
|
-
let inputs = { args };
|
|
573
|
-
if (typeof args === "object" && args != null) {
|
|
574
|
-
inputs = args;
|
|
575
|
-
}
|
|
576
|
-
const output = tryJson(span.attributes["ai.toolCall.output"] ??
|
|
577
|
-
span.attributes["ai.toolCall.result"]);
|
|
578
|
-
let outputs = { output };
|
|
579
|
-
if (typeof output === "object" && output != null) {
|
|
580
|
-
outputs = output;
|
|
581
|
-
}
|
|
582
|
-
const error = span.status?.code === 2 ? span.status.message : undefined;
|
|
583
|
-
return asRunCreate({
|
|
584
|
-
run_type: "tool",
|
|
585
|
-
name: span.attributes["ai.toolCall.name"],
|
|
586
|
-
error,
|
|
587
|
-
extra: error
|
|
588
|
-
? {
|
|
589
|
-
metadata: {
|
|
590
|
-
usage_metadata: {
|
|
591
|
-
input_tokens: 0,
|
|
592
|
-
output_tokens: 0,
|
|
593
|
-
total_tokens: 0,
|
|
594
|
-
},
|
|
595
|
-
},
|
|
596
|
-
}
|
|
597
|
-
: undefined,
|
|
598
|
-
inputs,
|
|
599
|
-
outputs,
|
|
600
|
-
});
|
|
601
|
-
}
|
|
602
|
-
case "ai.streamObject":
|
|
603
|
-
case "ai.streamObject.doStream":
|
|
604
|
-
case "ai.generateObject":
|
|
605
|
-
case "ai.generateObject.doGenerate": {
|
|
606
|
-
const inputs = (() => {
|
|
607
|
-
if ("ai.prompt.messages" in span.attributes) {
|
|
608
|
-
return {
|
|
609
|
-
messages: tryJson(span.attributes["ai.prompt.messages"]).flatMap((i) => convertCoreToSmith(i)),
|
|
610
|
-
};
|
|
611
|
-
}
|
|
612
|
-
if ("ai.prompt" in span.attributes) {
|
|
613
|
-
return { input: tryJson(span.attributes["ai.prompt"]) };
|
|
614
|
-
}
|
|
615
|
-
return {};
|
|
616
|
-
})();
|
|
617
|
-
const outputs = (() => {
|
|
618
|
-
let result = undefined;
|
|
619
|
-
if (span.attributes["ai.response.object"]) {
|
|
620
|
-
result = {
|
|
621
|
-
output: tryJson(span.attributes["ai.response.object"]),
|
|
622
|
-
};
|
|
623
|
-
}
|
|
624
|
-
return result;
|
|
625
|
-
})();
|
|
626
|
-
const events = [];
|
|
627
|
-
const firstChunkEvent = span.events.find((i) => i.name === "ai.stream.firstChunk");
|
|
628
|
-
if (firstChunkEvent) {
|
|
629
|
-
events.push({
|
|
630
|
-
name: "new_token",
|
|
631
|
-
time: convertToTimestamp(firstChunkEvent.time),
|
|
632
|
-
});
|
|
633
|
-
}
|
|
634
|
-
const runType = span.name === "ai.generateObject" || span.name === "ai.streamObject"
|
|
635
|
-
? "chain"
|
|
636
|
-
: "llm";
|
|
637
|
-
const error = span.status?.code === 2 ? span.status.message : undefined;
|
|
638
|
-
const usageMetadata = (0, vercel_js_1.extractUsageMetadata)(span);
|
|
639
|
-
return asRunCreate({
|
|
640
|
-
run_type: runType,
|
|
641
|
-
name: span.attributes["ai.model.provider"],
|
|
642
|
-
error,
|
|
643
|
-
inputs,
|
|
644
|
-
outputs,
|
|
645
|
-
events,
|
|
646
|
-
extra: {
|
|
647
|
-
batch_size: 1,
|
|
648
|
-
metadata: {
|
|
649
|
-
usage_metadata: usageMetadata,
|
|
650
|
-
ls_provider: span.attributes["ai.model.provider"]
|
|
651
|
-
.split(".")
|
|
652
|
-
.at(0),
|
|
653
|
-
ls_model_type: span.attributes["ai.model.provider"]
|
|
654
|
-
.split(".")
|
|
655
|
-
.at(1),
|
|
656
|
-
ls_model_name: span.attributes["ai.model.id"],
|
|
657
|
-
},
|
|
658
|
-
},
|
|
659
|
-
});
|
|
660
|
-
}
|
|
661
|
-
case "ai.embed":
|
|
662
|
-
case "ai.embed.doEmbed":
|
|
663
|
-
case "ai.embedMany":
|
|
664
|
-
case "ai.embedMany.doEmbed":
|
|
665
|
-
default:
|
|
666
|
-
return undefined;
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
/** @internal */
|
|
670
|
-
isRootRun(span) {
|
|
671
|
-
switch (span.name) {
|
|
672
|
-
case "ai.generateText":
|
|
673
|
-
case "ai.streamText":
|
|
674
|
-
case "ai.generateObject":
|
|
675
|
-
case "ai.streamObject":
|
|
676
|
-
case "ai.embed":
|
|
677
|
-
case "ai.embedMany":
|
|
678
|
-
return true;
|
|
679
|
-
default:
|
|
680
|
-
return false;
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
_export(spans, resultCallback) {
|
|
684
|
-
this.logDebug("exporting spans", spans);
|
|
685
|
-
const typedSpans = spans
|
|
686
|
-
.concat(Object.values(this.pendingSpans))
|
|
687
|
-
.slice()
|
|
688
|
-
// Parent spans should go before child spans in the final order,
|
|
689
|
-
// but may have the same exact start time as their children.
|
|
690
|
-
// They will end earlier, so break ties by end time.
|
|
691
|
-
// TODO: Figure out why this happens.
|
|
692
|
-
.sort((a, b) => sortByHr(a, b));
|
|
693
|
-
for (const span of typedSpans) {
|
|
694
|
-
const { traceId, spanId } = span.spanContext();
|
|
695
|
-
const runId = (0, uuid_1.v5)(spanId, RUN_ID_NAMESPACE);
|
|
696
|
-
let parentId = getParentSpanId(span);
|
|
697
|
-
if (constants_js_1.LANGSMITH_IS_ROOT in span.attributes) {
|
|
698
|
-
parentId = undefined;
|
|
699
|
-
}
|
|
700
|
-
else if (constants_js_1.LANGSMITH_TRACEABLE_PARENT_OTEL_SPAN_ID in span.attributes &&
|
|
701
|
-
typeof span.attributes[constants_js_1.LANGSMITH_TRACEABLE_PARENT_OTEL_SPAN_ID] ===
|
|
702
|
-
"string") {
|
|
703
|
-
parentId = span.attributes[constants_js_1.LANGSMITH_TRACEABLE_PARENT_OTEL_SPAN_ID];
|
|
704
|
-
}
|
|
705
|
-
let parentRunId = parentId
|
|
706
|
-
? (0, uuid_1.v5)(parentId, RUN_ID_NAMESPACE)
|
|
707
|
-
: undefined;
|
|
708
|
-
let parentSpanInfo = parentRunId
|
|
709
|
-
? this.seenSpanInfo[parentRunId]
|
|
710
|
-
: undefined;
|
|
711
|
-
// Unrelated, untraced spans should behave as passthroughs from LangSmith's perspective.
|
|
712
|
-
while (parentSpanInfo != null &&
|
|
713
|
-
this.getRunCreate(parentSpanInfo.span) == null) {
|
|
714
|
-
parentId = getParentSpanId(parentSpanInfo.span);
|
|
715
|
-
if (parentId == null) {
|
|
716
|
-
break;
|
|
717
|
-
}
|
|
718
|
-
parentRunId = parentId ? (0, uuid_1.v5)(parentId, RUN_ID_NAMESPACE) : undefined;
|
|
719
|
-
parentSpanInfo = parentRunId
|
|
720
|
-
? this.seenSpanInfo[parentRunId]
|
|
721
|
-
: undefined;
|
|
722
|
-
}
|
|
723
|
-
// Export may be called in any order, so we need to queue any spans with missing parents
|
|
724
|
-
// for retry later in order to determine whether their parents are tool calls
|
|
725
|
-
// and should not be reparented below.
|
|
726
|
-
if (parentRunId !== undefined && parentSpanInfo === undefined) {
|
|
727
|
-
this.pendingSpans[spanId] = span;
|
|
728
|
-
continue;
|
|
729
|
-
}
|
|
730
|
-
else {
|
|
731
|
-
delete this.pendingSpans[spanId];
|
|
732
|
-
}
|
|
733
|
-
this.traceByMap[traceId] ??= {
|
|
734
|
-
childMap: {},
|
|
735
|
-
nodeMap: {},
|
|
736
|
-
relativeExecutionOrder: {},
|
|
737
|
-
};
|
|
738
|
-
const traceMap = this.traceByMap[traceId];
|
|
739
|
-
traceMap.relativeExecutionOrder[parentRunId ?? ROOT] ??= -1;
|
|
740
|
-
traceMap.relativeExecutionOrder[parentRunId ?? ROOT] += 1;
|
|
741
|
-
const interop = this.parseInteropFromMetadata(span, parentSpanInfo?.span);
|
|
742
|
-
const projectName = (interop?.type === "traceable"
|
|
743
|
-
? interop.parentRunTree.project_name
|
|
744
|
-
: undefined) ?? parentSpanInfo?.projectName;
|
|
745
|
-
const run = this.getRunCreate(span, projectName);
|
|
746
|
-
traceMap.nodeMap[runId] ??= {
|
|
747
|
-
id: runId,
|
|
748
|
-
startTime: span.startTime,
|
|
749
|
-
run,
|
|
750
|
-
sent: false,
|
|
751
|
-
interop,
|
|
752
|
-
executionOrder: traceMap.relativeExecutionOrder[parentRunId ?? ROOT],
|
|
753
|
-
};
|
|
754
|
-
if (this.seenSpanInfo[runId] == null) {
|
|
755
|
-
this.seenSpanInfo[runId] = {
|
|
756
|
-
span,
|
|
757
|
-
dotOrder: joinDotOrder(parentSpanInfo?.dotOrder, getDotOrder(traceMap.nodeMap[runId])),
|
|
758
|
-
projectName,
|
|
759
|
-
sent: false,
|
|
760
|
-
};
|
|
761
|
-
}
|
|
762
|
-
if (this.debug)
|
|
763
|
-
console.log(`[${span.name}] ${runId}`, run);
|
|
764
|
-
traceMap.childMap[parentRunId ?? ROOT] ??= [];
|
|
765
|
-
traceMap.childMap[parentRunId ?? ROOT].push(traceMap.nodeMap[runId]);
|
|
766
|
-
}
|
|
767
|
-
const sampled = [];
|
|
768
|
-
const actions = [];
|
|
769
|
-
for (const traceId of Object.keys(this.traceByMap)) {
|
|
770
|
-
const traceMap = this.traceByMap[traceId];
|
|
771
|
-
const queue = Object.keys(traceMap.childMap)
|
|
772
|
-
.map((runId) => {
|
|
773
|
-
if (runId === ROOT) {
|
|
774
|
-
return traceMap.childMap[runId];
|
|
775
|
-
}
|
|
776
|
-
return [];
|
|
777
|
-
})
|
|
778
|
-
.flat();
|
|
779
|
-
const seen = new Set();
|
|
780
|
-
while (queue.length) {
|
|
781
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
782
|
-
const task = queue.shift();
|
|
783
|
-
if (seen.has(task.id))
|
|
784
|
-
continue;
|
|
785
|
-
let taskDotOrder = this.seenSpanInfo[task.id].dotOrder;
|
|
786
|
-
if (!task.sent) {
|
|
787
|
-
if (task.run != null) {
|
|
788
|
-
if (task.interop?.type === "user") {
|
|
789
|
-
actions.push({
|
|
790
|
-
type: "rename",
|
|
791
|
-
sourceRunId: task.id,
|
|
792
|
-
targetRunId: task.interop.userRunId,
|
|
793
|
-
});
|
|
794
|
-
}
|
|
795
|
-
if (task.interop?.type === "traceable") {
|
|
796
|
-
actions.push({
|
|
797
|
-
type: "reparent",
|
|
798
|
-
runId: task.id,
|
|
799
|
-
parentDotOrder: task.interop.parentRunTree.dotted_order,
|
|
800
|
-
});
|
|
801
|
-
}
|
|
802
|
-
for (const action of actions) {
|
|
803
|
-
if (action.type === "delete") {
|
|
804
|
-
taskDotOrder = removeDotOrder(taskDotOrder, action.runId);
|
|
805
|
-
}
|
|
806
|
-
if (action.type === "reparent") {
|
|
807
|
-
taskDotOrder = reparentDotOrder(taskDotOrder, action.runId, action.parentDotOrder);
|
|
808
|
-
}
|
|
809
|
-
if (action.type === "rename") {
|
|
810
|
-
taskDotOrder = taskDotOrder.replace(action.sourceRunId, action.targetRunId);
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
this.seenSpanInfo[task.id].dotOrder = taskDotOrder;
|
|
814
|
-
if (!this.seenSpanInfo[task.id].sent) {
|
|
815
|
-
sampled.push({
|
|
816
|
-
...task.run,
|
|
817
|
-
...getMutableRunCreate(taskDotOrder),
|
|
818
|
-
});
|
|
819
|
-
}
|
|
820
|
-
this.seenSpanInfo[task.id].sent = true;
|
|
821
|
-
}
|
|
822
|
-
else {
|
|
823
|
-
actions.push({ type: "delete", runId: task.id });
|
|
824
|
-
}
|
|
825
|
-
task.sent = true;
|
|
826
|
-
}
|
|
827
|
-
const children = traceMap.childMap[task.id] ?? [];
|
|
828
|
-
queue.push(...children);
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
this.logDebug(`sampled runs to be sent to LangSmith`, sampled);
|
|
832
|
-
Promise.all(sampled.map((run) => this.client.createRun(run))).then(() => resultCallback({ code: 0 }), (error) => resultCallback({ code: 1, error }));
|
|
833
|
-
}
|
|
834
|
-
export(spans, resultCallback) {
|
|
835
|
-
this._export(spans, (result) => {
|
|
836
|
-
if (result.code === 0) {
|
|
837
|
-
// Empty export to try flushing pending spans to rule out any trace order shenanigans
|
|
838
|
-
this._export([], resultCallback);
|
|
839
|
-
}
|
|
840
|
-
else {
|
|
841
|
-
resultCallback(result);
|
|
842
|
-
}
|
|
843
|
-
});
|
|
844
|
-
}
|
|
845
|
-
async shutdown() {
|
|
846
|
-
// find nodes which are incomplete
|
|
847
|
-
const incompleteNodes = Object.values(this.traceByMap).flatMap((trace) => Object.values(trace.nodeMap).filter((i) => !i.sent && i.run != null));
|
|
848
|
-
this.logDebug("shutting down", { incompleteNodes });
|
|
849
|
-
if (incompleteNodes.length > 0) {
|
|
850
|
-
console.warn("Some incomplete nodes were found before shutdown and not sent to LangSmith.");
|
|
851
|
-
}
|
|
852
|
-
await this.forceFlush();
|
|
853
|
-
}
|
|
854
|
-
async forceFlush() {
|
|
855
|
-
await new Promise((resolve) => {
|
|
856
|
-
this.export([], resolve);
|
|
857
|
-
});
|
|
858
|
-
await this.client.awaitPendingTraceBatches();
|
|
859
|
-
}
|
|
860
|
-
logDebug(...args) {
|
|
861
|
-
if (!this.debug)
|
|
862
|
-
return;
|
|
863
|
-
console.debug(`[${new Date().toISOString()}] [LangSmith]`, ...args);
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
exports.AISDKExporter = AISDKExporter;
|