langsmith 0.4.2 → 0.4.3-rc.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/dist/experimental/vercel/index.cjs +67 -28
- package/dist/experimental/vercel/index.d.ts +1 -1
- package/dist/experimental/vercel/index.js +67 -28
- package/dist/experimental/vercel/middleware.cjs +48 -11
- package/dist/experimental/vercel/middleware.d.ts +1 -1
- package/dist/experimental/vercel/middleware.js +48 -11
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/run_trees.cjs +9 -0
- package/dist/run_trees.d.ts +1 -0
- package/dist/run_trees.js +9 -0
- package/dist/traceable.cjs +7 -6
- package/dist/traceable.d.ts +1 -1
- package/dist/traceable.js +7 -6
- package/dist/utils/vercel.cjs +89 -0
- package/dist/utils/vercel.d.ts +3 -3
- package/dist/utils/vercel.js +89 -0
- package/package.json +5 -4
|
@@ -47,23 +47,40 @@ const _getModelId = (model) => {
|
|
|
47
47
|
}
|
|
48
48
|
return typeof model.modelId === "string" ? model.modelId : undefined;
|
|
49
49
|
};
|
|
50
|
-
const _formatTracedInputs = (params) => {
|
|
51
|
-
const { prompt, messages, model, tools, ...rest } = params;
|
|
50
|
+
const _formatTracedInputs = async (params) => {
|
|
51
|
+
const { prompt, messages, model, tools, output, ...rest } = params;
|
|
52
|
+
let processedInputs = {};
|
|
52
53
|
if (Array.isArray(prompt)) {
|
|
53
|
-
|
|
54
|
+
processedInputs = {
|
|
54
55
|
...rest,
|
|
55
56
|
messages: prompt.map((message) => (0, utils_js_1.convertMessageToTracedFormat)(message)),
|
|
56
57
|
};
|
|
57
58
|
}
|
|
58
59
|
else if (Array.isArray(messages)) {
|
|
59
|
-
|
|
60
|
+
processedInputs = {
|
|
60
61
|
...rest,
|
|
61
62
|
messages: messages.map((message) => (0, utils_js_1.convertMessageToTracedFormat)(message)),
|
|
62
63
|
};
|
|
63
64
|
}
|
|
64
65
|
else {
|
|
65
|
-
|
|
66
|
+
processedInputs = { ...rest, prompt, messages };
|
|
66
67
|
}
|
|
68
|
+
try {
|
|
69
|
+
if (output != null &&
|
|
70
|
+
typeof output === "object" &&
|
|
71
|
+
"responseFormat" in output) {
|
|
72
|
+
const responseFormat = await output.responseFormat;
|
|
73
|
+
processedInputs.output = responseFormat;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
processedInputs.output = output;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Could not extract response format from output for tracing
|
|
81
|
+
processedInputs.output = output;
|
|
82
|
+
}
|
|
83
|
+
return processedInputs;
|
|
67
84
|
};
|
|
68
85
|
const _mergeConfig = (baseConfig, runtimeConfig) => {
|
|
69
86
|
return {
|
|
@@ -132,7 +149,7 @@ const createLangSmithProviderOptions = (lsConfig) => {
|
|
|
132
149
|
};
|
|
133
150
|
exports.createLangSmithProviderOptions = createLangSmithProviderOptions;
|
|
134
151
|
/**
|
|
135
|
-
* Wraps Vercel AI SDK 5 functions with LangSmith tracing capabilities.
|
|
152
|
+
* Wraps Vercel AI SDK 6 or AI SDK 5 functions with LangSmith tracing capabilities.
|
|
136
153
|
*
|
|
137
154
|
* @param methods - Object containing AI SDK methods to wrap
|
|
138
155
|
* @param methods.wrapLanguageModel - AI SDK's wrapLanguageModel function
|
|
@@ -149,7 +166,7 @@ exports.createLangSmithProviderOptions = createLangSmithProviderOptions;
|
|
|
149
166
|
*/
|
|
150
167
|
const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject, generateObject, }, baseLsConfig) => {
|
|
151
168
|
/**
|
|
152
|
-
* Wrapped version of AI SDK
|
|
169
|
+
* Wrapped version of AI SDK's generateText with LangSmith tracing.
|
|
153
170
|
*
|
|
154
171
|
* This function has the same signature and behavior as the original generateText,
|
|
155
172
|
* but adds automatic tracing to LangSmith for observability.
|
|
@@ -170,6 +187,8 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
170
187
|
const params = args[0];
|
|
171
188
|
const { langsmith: runtimeLsConfig, ...providerOptions } = params.providerOptions ?? {};
|
|
172
189
|
const { resolvedLsConfig, resolvedChildLLMRunConfig, resolvedToolConfig } = _resolveConfigs(baseLsConfig, runtimeLsConfig);
|
|
190
|
+
const hasExplicitOutput = "output" in params;
|
|
191
|
+
const hasExplicitExperimentalOutput = "experimental_output" in params;
|
|
173
192
|
const traceableFunc = (0, traceable_js_1.traceable)(async (...args) => {
|
|
174
193
|
const [params, ...rest] = args;
|
|
175
194
|
const wrappedModel = wrapLanguageModel({
|
|
@@ -194,7 +213,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
194
213
|
ai_sdk_method: "ai.generateText",
|
|
195
214
|
...resolvedLsConfig?.metadata,
|
|
196
215
|
},
|
|
197
|
-
processInputs: (inputs) => {
|
|
216
|
+
processInputs: async (inputs) => {
|
|
198
217
|
const inputFormatter = resolvedLsConfig?.processInputs ?? _formatTracedInputs;
|
|
199
218
|
return inputFormatter(inputs);
|
|
200
219
|
},
|
|
@@ -209,18 +228,37 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
209
228
|
if (outputs.outputs == null || typeof outputs.outputs !== "object") {
|
|
210
229
|
return outputs;
|
|
211
230
|
}
|
|
212
|
-
// If experimental_output
|
|
213
|
-
// Note:
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if (
|
|
218
|
-
|
|
231
|
+
// If output or experimental_output (legacy) was explicitly provided, return it directly at top level (like generateObject)
|
|
232
|
+
// Note: In AI SDK 6, experimental_output/output is always available as a getter, so we need to check if it was explicitly provided
|
|
233
|
+
if (hasExplicitOutput) {
|
|
234
|
+
try {
|
|
235
|
+
// Try new 'output' property first, then fall back to 'experimental_output' for backwards compatibility
|
|
236
|
+
if ("output" in outputs.outputs) {
|
|
237
|
+
const output = outputs.outputs.output;
|
|
238
|
+
if (output != null && typeof output === "object") {
|
|
239
|
+
if (Array.isArray(output)) {
|
|
240
|
+
return { outputs: output };
|
|
241
|
+
}
|
|
242
|
+
return output;
|
|
243
|
+
}
|
|
219
244
|
}
|
|
220
245
|
}
|
|
246
|
+
catch {
|
|
247
|
+
// output not accessible, continue with normal processing
|
|
248
|
+
}
|
|
221
249
|
}
|
|
222
|
-
|
|
223
|
-
|
|
250
|
+
else if (hasExplicitExperimentalOutput) {
|
|
251
|
+
try {
|
|
252
|
+
if ("experimental_output" in outputs.outputs) {
|
|
253
|
+
const experimentalOutput = outputs.outputs.experimental_output;
|
|
254
|
+
if (experimentalOutput != null) {
|
|
255
|
+
return experimentalOutput;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
// experimental_output not accessible, continue with normal processing
|
|
261
|
+
}
|
|
224
262
|
}
|
|
225
263
|
const { steps } = outputs.outputs;
|
|
226
264
|
if (Array.isArray(steps)) {
|
|
@@ -242,7 +280,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
242
280
|
return traceableFunc(...args);
|
|
243
281
|
};
|
|
244
282
|
/**
|
|
245
|
-
* Wrapped version of AI SDK
|
|
283
|
+
* Wrapped version of AI SDK's generateObject with LangSmith tracing.
|
|
246
284
|
*
|
|
247
285
|
* This function has the same signature and behavior as the original generateObject,
|
|
248
286
|
* but adds automatic tracing to LangSmith for observability.
|
|
@@ -286,7 +324,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
286
324
|
ai_sdk_method: "ai.generateObject",
|
|
287
325
|
...resolvedLsConfig?.metadata,
|
|
288
326
|
},
|
|
289
|
-
processInputs: (inputs) => {
|
|
327
|
+
processInputs: async (inputs) => {
|
|
290
328
|
const inputFormatter = resolvedLsConfig?.processInputs ?? _formatTracedInputs;
|
|
291
329
|
return inputFormatter(inputs);
|
|
292
330
|
},
|
|
@@ -307,7 +345,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
307
345
|
return traceableFunc(...args);
|
|
308
346
|
};
|
|
309
347
|
/**
|
|
310
|
-
* Wrapped version of AI SDK
|
|
348
|
+
* Wrapped version of AI SDK's streamText with LangSmith tracing.
|
|
311
349
|
*
|
|
312
350
|
* Must be called with `await`, but otherwise behaves the same as the
|
|
313
351
|
* original streamText and adds adds automatic tracing to LangSmith
|
|
@@ -329,6 +367,8 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
329
367
|
const params = args[0];
|
|
330
368
|
const { langsmith: runtimeLsConfig, ...providerOptions } = params.providerOptions ?? {};
|
|
331
369
|
const { resolvedLsConfig, resolvedChildLLMRunConfig, resolvedToolConfig } = _resolveConfigs(baseLsConfig, runtimeLsConfig);
|
|
370
|
+
const hasExplicitOutput = "output" in params;
|
|
371
|
+
const hasExplicitExperimentalOutput = "experimental_output" in params;
|
|
332
372
|
const traceableFunc = (0, traceable_js_1.traceable)((...args) => {
|
|
333
373
|
const [params, ...rest] = args;
|
|
334
374
|
const wrappedModel = wrapLanguageModel({
|
|
@@ -353,7 +393,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
353
393
|
ai_sdk_method: "ai.streamText",
|
|
354
394
|
...resolvedLsConfig?.metadata,
|
|
355
395
|
},
|
|
356
|
-
processInputs: (inputs) => {
|
|
396
|
+
processInputs: async (inputs) => {
|
|
357
397
|
const inputFormatter = resolvedLsConfig?.processInputs ?? _formatTracedInputs;
|
|
358
398
|
return inputFormatter(inputs);
|
|
359
399
|
},
|
|
@@ -382,8 +422,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
382
422
|
return outputs;
|
|
383
423
|
}
|
|
384
424
|
try {
|
|
385
|
-
if (
|
|
386
|
-
outputs.outputs.experimental_partialOutputStream != null) {
|
|
425
|
+
if (hasExplicitOutput || hasExplicitExperimentalOutput) {
|
|
387
426
|
const textContent = await outputs.outputs.text;
|
|
388
427
|
return JSON.parse(textContent);
|
|
389
428
|
}
|
|
@@ -397,7 +436,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
397
436
|
const steps = await outputs.outputs.steps;
|
|
398
437
|
responseMetadata = { steps };
|
|
399
438
|
}
|
|
400
|
-
catch
|
|
439
|
+
catch {
|
|
401
440
|
// Do nothing if step parsing fails
|
|
402
441
|
}
|
|
403
442
|
}
|
|
@@ -406,7 +445,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
406
445
|
role: "assistant",
|
|
407
446
|
}, responseMetadata);
|
|
408
447
|
}
|
|
409
|
-
catch
|
|
448
|
+
catch {
|
|
410
449
|
// Handle parsing failures without a log
|
|
411
450
|
return outputs;
|
|
412
451
|
}
|
|
@@ -415,7 +454,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
415
454
|
return traceableFunc(...args);
|
|
416
455
|
};
|
|
417
456
|
/**
|
|
418
|
-
* Wrapped version of AI SDK
|
|
457
|
+
* Wrapped version of AI SDK's streamObject with LangSmith tracing.
|
|
419
458
|
*
|
|
420
459
|
* Must be called with `await`, but otherwise behaves the same as the
|
|
421
460
|
* original streamObject and adds adds automatic tracing to LangSmith
|
|
@@ -460,7 +499,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
460
499
|
ai_sdk_method: "ai.streamObject",
|
|
461
500
|
...resolvedLsConfig?.metadata,
|
|
462
501
|
},
|
|
463
|
-
processInputs: (inputs) => {
|
|
502
|
+
processInputs: async (inputs) => {
|
|
464
503
|
const inputFormatter = resolvedLsConfig?.processInputs ?? _formatTracedInputs;
|
|
465
504
|
return inputFormatter(inputs);
|
|
466
505
|
},
|
|
@@ -483,7 +522,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
483
522
|
}
|
|
484
523
|
return object;
|
|
485
524
|
}
|
|
486
|
-
catch
|
|
525
|
+
catch {
|
|
487
526
|
// Handle parsing failures without a log
|
|
488
527
|
return outputs;
|
|
489
528
|
}
|
|
@@ -230,7 +230,7 @@ export type WrapAISDKConfig<T extends (...args: any[]) => any = (...args: any[])
|
|
|
230
230
|
*/
|
|
231
231
|
export declare const createLangSmithProviderOptions: <T extends (...args: any[]) => any>(lsConfig?: WrapAISDKConfig<T>) => Record<string, JSONValue>;
|
|
232
232
|
/**
|
|
233
|
-
* Wraps Vercel AI SDK 5 functions with LangSmith tracing capabilities.
|
|
233
|
+
* Wraps Vercel AI SDK 6 or AI SDK 5 functions with LangSmith tracing capabilities.
|
|
234
234
|
*
|
|
235
235
|
* @param methods - Object containing AI SDK methods to wrap
|
|
236
236
|
* @param methods.wrapLanguageModel - AI SDK's wrapLanguageModel function
|
|
@@ -43,23 +43,40 @@ const _getModelId = (model) => {
|
|
|
43
43
|
}
|
|
44
44
|
return typeof model.modelId === "string" ? model.modelId : undefined;
|
|
45
45
|
};
|
|
46
|
-
const _formatTracedInputs = (params) => {
|
|
47
|
-
const { prompt, messages, model, tools, ...rest } = params;
|
|
46
|
+
const _formatTracedInputs = async (params) => {
|
|
47
|
+
const { prompt, messages, model, tools, output, ...rest } = params;
|
|
48
|
+
let processedInputs = {};
|
|
48
49
|
if (Array.isArray(prompt)) {
|
|
49
|
-
|
|
50
|
+
processedInputs = {
|
|
50
51
|
...rest,
|
|
51
52
|
messages: prompt.map((message) => convertMessageToTracedFormat(message)),
|
|
52
53
|
};
|
|
53
54
|
}
|
|
54
55
|
else if (Array.isArray(messages)) {
|
|
55
|
-
|
|
56
|
+
processedInputs = {
|
|
56
57
|
...rest,
|
|
57
58
|
messages: messages.map((message) => convertMessageToTracedFormat(message)),
|
|
58
59
|
};
|
|
59
60
|
}
|
|
60
61
|
else {
|
|
61
|
-
|
|
62
|
+
processedInputs = { ...rest, prompt, messages };
|
|
62
63
|
}
|
|
64
|
+
try {
|
|
65
|
+
if (output != null &&
|
|
66
|
+
typeof output === "object" &&
|
|
67
|
+
"responseFormat" in output) {
|
|
68
|
+
const responseFormat = await output.responseFormat;
|
|
69
|
+
processedInputs.output = responseFormat;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
processedInputs.output = output;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// Could not extract response format from output for tracing
|
|
77
|
+
processedInputs.output = output;
|
|
78
|
+
}
|
|
79
|
+
return processedInputs;
|
|
63
80
|
};
|
|
64
81
|
const _mergeConfig = (baseConfig, runtimeConfig) => {
|
|
65
82
|
return {
|
|
@@ -127,7 +144,7 @@ export const createLangSmithProviderOptions = (lsConfig) => {
|
|
|
127
144
|
return (lsConfig ?? {});
|
|
128
145
|
};
|
|
129
146
|
/**
|
|
130
|
-
* Wraps Vercel AI SDK 5 functions with LangSmith tracing capabilities.
|
|
147
|
+
* Wraps Vercel AI SDK 6 or AI SDK 5 functions with LangSmith tracing capabilities.
|
|
131
148
|
*
|
|
132
149
|
* @param methods - Object containing AI SDK methods to wrap
|
|
133
150
|
* @param methods.wrapLanguageModel - AI SDK's wrapLanguageModel function
|
|
@@ -144,7 +161,7 @@ export const createLangSmithProviderOptions = (lsConfig) => {
|
|
|
144
161
|
*/
|
|
145
162
|
const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject, generateObject, }, baseLsConfig) => {
|
|
146
163
|
/**
|
|
147
|
-
* Wrapped version of AI SDK
|
|
164
|
+
* Wrapped version of AI SDK's generateText with LangSmith tracing.
|
|
148
165
|
*
|
|
149
166
|
* This function has the same signature and behavior as the original generateText,
|
|
150
167
|
* but adds automatic tracing to LangSmith for observability.
|
|
@@ -165,6 +182,8 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
165
182
|
const params = args[0];
|
|
166
183
|
const { langsmith: runtimeLsConfig, ...providerOptions } = params.providerOptions ?? {};
|
|
167
184
|
const { resolvedLsConfig, resolvedChildLLMRunConfig, resolvedToolConfig } = _resolveConfigs(baseLsConfig, runtimeLsConfig);
|
|
185
|
+
const hasExplicitOutput = "output" in params;
|
|
186
|
+
const hasExplicitExperimentalOutput = "experimental_output" in params;
|
|
168
187
|
const traceableFunc = traceable(async (...args) => {
|
|
169
188
|
const [params, ...rest] = args;
|
|
170
189
|
const wrappedModel = wrapLanguageModel({
|
|
@@ -189,7 +208,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
189
208
|
ai_sdk_method: "ai.generateText",
|
|
190
209
|
...resolvedLsConfig?.metadata,
|
|
191
210
|
},
|
|
192
|
-
processInputs: (inputs) => {
|
|
211
|
+
processInputs: async (inputs) => {
|
|
193
212
|
const inputFormatter = resolvedLsConfig?.processInputs ?? _formatTracedInputs;
|
|
194
213
|
return inputFormatter(inputs);
|
|
195
214
|
},
|
|
@@ -204,18 +223,37 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
204
223
|
if (outputs.outputs == null || typeof outputs.outputs !== "object") {
|
|
205
224
|
return outputs;
|
|
206
225
|
}
|
|
207
|
-
// If experimental_output
|
|
208
|
-
// Note:
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
if (
|
|
213
|
-
|
|
226
|
+
// If output or experimental_output (legacy) was explicitly provided, return it directly at top level (like generateObject)
|
|
227
|
+
// Note: In AI SDK 6, experimental_output/output is always available as a getter, so we need to check if it was explicitly provided
|
|
228
|
+
if (hasExplicitOutput) {
|
|
229
|
+
try {
|
|
230
|
+
// Try new 'output' property first, then fall back to 'experimental_output' for backwards compatibility
|
|
231
|
+
if ("output" in outputs.outputs) {
|
|
232
|
+
const output = outputs.outputs.output;
|
|
233
|
+
if (output != null && typeof output === "object") {
|
|
234
|
+
if (Array.isArray(output)) {
|
|
235
|
+
return { outputs: output };
|
|
236
|
+
}
|
|
237
|
+
return output;
|
|
238
|
+
}
|
|
214
239
|
}
|
|
215
240
|
}
|
|
241
|
+
catch {
|
|
242
|
+
// output not accessible, continue with normal processing
|
|
243
|
+
}
|
|
216
244
|
}
|
|
217
|
-
|
|
218
|
-
|
|
245
|
+
else if (hasExplicitExperimentalOutput) {
|
|
246
|
+
try {
|
|
247
|
+
if ("experimental_output" in outputs.outputs) {
|
|
248
|
+
const experimentalOutput = outputs.outputs.experimental_output;
|
|
249
|
+
if (experimentalOutput != null) {
|
|
250
|
+
return experimentalOutput;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
// experimental_output not accessible, continue with normal processing
|
|
256
|
+
}
|
|
219
257
|
}
|
|
220
258
|
const { steps } = outputs.outputs;
|
|
221
259
|
if (Array.isArray(steps)) {
|
|
@@ -237,7 +275,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
237
275
|
return traceableFunc(...args);
|
|
238
276
|
};
|
|
239
277
|
/**
|
|
240
|
-
* Wrapped version of AI SDK
|
|
278
|
+
* Wrapped version of AI SDK's generateObject with LangSmith tracing.
|
|
241
279
|
*
|
|
242
280
|
* This function has the same signature and behavior as the original generateObject,
|
|
243
281
|
* but adds automatic tracing to LangSmith for observability.
|
|
@@ -281,7 +319,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
281
319
|
ai_sdk_method: "ai.generateObject",
|
|
282
320
|
...resolvedLsConfig?.metadata,
|
|
283
321
|
},
|
|
284
|
-
processInputs: (inputs) => {
|
|
322
|
+
processInputs: async (inputs) => {
|
|
285
323
|
const inputFormatter = resolvedLsConfig?.processInputs ?? _formatTracedInputs;
|
|
286
324
|
return inputFormatter(inputs);
|
|
287
325
|
},
|
|
@@ -302,7 +340,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
302
340
|
return traceableFunc(...args);
|
|
303
341
|
};
|
|
304
342
|
/**
|
|
305
|
-
* Wrapped version of AI SDK
|
|
343
|
+
* Wrapped version of AI SDK's streamText with LangSmith tracing.
|
|
306
344
|
*
|
|
307
345
|
* Must be called with `await`, but otherwise behaves the same as the
|
|
308
346
|
* original streamText and adds adds automatic tracing to LangSmith
|
|
@@ -324,6 +362,8 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
324
362
|
const params = args[0];
|
|
325
363
|
const { langsmith: runtimeLsConfig, ...providerOptions } = params.providerOptions ?? {};
|
|
326
364
|
const { resolvedLsConfig, resolvedChildLLMRunConfig, resolvedToolConfig } = _resolveConfigs(baseLsConfig, runtimeLsConfig);
|
|
365
|
+
const hasExplicitOutput = "output" in params;
|
|
366
|
+
const hasExplicitExperimentalOutput = "experimental_output" in params;
|
|
327
367
|
const traceableFunc = traceable((...args) => {
|
|
328
368
|
const [params, ...rest] = args;
|
|
329
369
|
const wrappedModel = wrapLanguageModel({
|
|
@@ -348,7 +388,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
348
388
|
ai_sdk_method: "ai.streamText",
|
|
349
389
|
...resolvedLsConfig?.metadata,
|
|
350
390
|
},
|
|
351
|
-
processInputs: (inputs) => {
|
|
391
|
+
processInputs: async (inputs) => {
|
|
352
392
|
const inputFormatter = resolvedLsConfig?.processInputs ?? _formatTracedInputs;
|
|
353
393
|
return inputFormatter(inputs);
|
|
354
394
|
},
|
|
@@ -377,8 +417,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
377
417
|
return outputs;
|
|
378
418
|
}
|
|
379
419
|
try {
|
|
380
|
-
if (
|
|
381
|
-
outputs.outputs.experimental_partialOutputStream != null) {
|
|
420
|
+
if (hasExplicitOutput || hasExplicitExperimentalOutput) {
|
|
382
421
|
const textContent = await outputs.outputs.text;
|
|
383
422
|
return JSON.parse(textContent);
|
|
384
423
|
}
|
|
@@ -392,7 +431,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
392
431
|
const steps = await outputs.outputs.steps;
|
|
393
432
|
responseMetadata = { steps };
|
|
394
433
|
}
|
|
395
|
-
catch
|
|
434
|
+
catch {
|
|
396
435
|
// Do nothing if step parsing fails
|
|
397
436
|
}
|
|
398
437
|
}
|
|
@@ -401,7 +440,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
401
440
|
role: "assistant",
|
|
402
441
|
}, responseMetadata);
|
|
403
442
|
}
|
|
404
|
-
catch
|
|
443
|
+
catch {
|
|
405
444
|
// Handle parsing failures without a log
|
|
406
445
|
return outputs;
|
|
407
446
|
}
|
|
@@ -410,7 +449,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
410
449
|
return traceableFunc(...args);
|
|
411
450
|
};
|
|
412
451
|
/**
|
|
413
|
-
* Wrapped version of AI SDK
|
|
452
|
+
* Wrapped version of AI SDK's streamObject with LangSmith tracing.
|
|
414
453
|
*
|
|
415
454
|
* Must be called with `await`, but otherwise behaves the same as the
|
|
416
455
|
* original streamObject and adds adds automatic tracing to LangSmith
|
|
@@ -455,7 +494,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
455
494
|
ai_sdk_method: "ai.streamObject",
|
|
456
495
|
...resolvedLsConfig?.metadata,
|
|
457
496
|
},
|
|
458
|
-
processInputs: (inputs) => {
|
|
497
|
+
processInputs: async (inputs) => {
|
|
459
498
|
const inputFormatter = resolvedLsConfig?.processInputs ?? _formatTracedInputs;
|
|
460
499
|
return inputFormatter(inputs);
|
|
461
500
|
},
|
|
@@ -478,7 +517,7 @@ const wrapAISDK = ({ wrapLanguageModel, generateText, streamText, streamObject,
|
|
|
478
517
|
}
|
|
479
518
|
return object;
|
|
480
519
|
}
|
|
481
|
-
catch
|
|
520
|
+
catch {
|
|
482
521
|
// Handle parsing failures without a log
|
|
483
522
|
return outputs;
|
|
484
523
|
}
|
|
@@ -38,16 +38,53 @@ const setUsageMetadataOnRunTree = (result, runTree) => {
|
|
|
38
38
|
if (result.usage == null || typeof result.usage !== "object") {
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
const usage = result.usage;
|
|
42
|
+
let inputTokens;
|
|
43
|
+
let outputTokens;
|
|
44
|
+
let totalTokens;
|
|
45
|
+
// AI SDK 6: Check for object-based token structures first
|
|
46
|
+
if (typeof usage.inputTokens === "object" &&
|
|
47
|
+
usage.inputTokens?.total != null) {
|
|
48
|
+
// AI SDK 6 detected
|
|
49
|
+
inputTokens = usage.inputTokens.total;
|
|
50
|
+
if (typeof usage.outputTokens === "object" &&
|
|
51
|
+
usage.outputTokens?.total != null) {
|
|
52
|
+
outputTokens = usage.outputTokens.total;
|
|
53
|
+
}
|
|
54
|
+
totalTokens = result.usage?.totalTokens;
|
|
55
|
+
if (typeof totalTokens !== "number" &&
|
|
56
|
+
typeof inputTokens === "number" &&
|
|
57
|
+
typeof outputTokens === "number") {
|
|
58
|
+
totalTokens = inputTokens + outputTokens;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else if (typeof usage.inputTokens === "number") {
|
|
62
|
+
// AI SDK 5 detected
|
|
63
|
+
inputTokens = usage.inputTokens;
|
|
64
|
+
if (typeof usage.outputTokens === "number") {
|
|
65
|
+
outputTokens = usage.outputTokens;
|
|
66
|
+
}
|
|
67
|
+
totalTokens = result.usage?.totalTokens;
|
|
68
|
+
if (typeof totalTokens !== "number" &&
|
|
69
|
+
typeof inputTokens === "number" &&
|
|
70
|
+
typeof outputTokens === "number") {
|
|
71
|
+
totalTokens = inputTokens + outputTokens;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// AI SDK 4 fallback
|
|
76
|
+
if (typeof usage.promptTokens === "number") {
|
|
77
|
+
inputTokens = usage.promptTokens;
|
|
78
|
+
}
|
|
79
|
+
if (typeof usage.completionTokens === "number") {
|
|
80
|
+
outputTokens = usage.completionTokens;
|
|
81
|
+
}
|
|
82
|
+
totalTokens = result.usage?.totalTokens;
|
|
83
|
+
if (typeof totalTokens !== "number" &&
|
|
84
|
+
typeof inputTokens === "number" &&
|
|
85
|
+
typeof outputTokens === "number") {
|
|
86
|
+
totalTokens = inputTokens + outputTokens;
|
|
87
|
+
}
|
|
51
88
|
}
|
|
52
89
|
const langsmithUsage = {
|
|
53
90
|
input_tokens: inputTokens,
|
|
@@ -73,7 +110,7 @@ const setUsageMetadataOnRunTree = (result, runTree) => {
|
|
|
73
110
|
};
|
|
74
111
|
};
|
|
75
112
|
/**
|
|
76
|
-
* AI SDK middleware that wraps an AI SDK 5 model and adds LangSmith tracing.
|
|
113
|
+
* AI SDK middleware that wraps an AI SDK 6 or 5 model and adds LangSmith tracing.
|
|
77
114
|
*/
|
|
78
115
|
function LangSmithMiddleware(config) {
|
|
79
116
|
const { name, modelId, lsConfig } = config ?? {};
|
|
@@ -15,7 +15,7 @@ export type AggregatedDoStreamOutput = {
|
|
|
15
15
|
finishReason?: LanguageModelV2FinishReason;
|
|
16
16
|
};
|
|
17
17
|
/**
|
|
18
|
-
* AI SDK middleware that wraps an AI SDK 5 model and adds LangSmith tracing.
|
|
18
|
+
* AI SDK middleware that wraps an AI SDK 6 or 5 model and adds LangSmith tracing.
|
|
19
19
|
*/
|
|
20
20
|
export declare function LangSmithMiddleware(config?: {
|
|
21
21
|
name: string;
|
|
@@ -35,16 +35,53 @@ const setUsageMetadataOnRunTree = (result, runTree) => {
|
|
|
35
35
|
if (result.usage == null || typeof result.usage !== "object") {
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
const usage = result.usage;
|
|
39
|
+
let inputTokens;
|
|
40
|
+
let outputTokens;
|
|
41
|
+
let totalTokens;
|
|
42
|
+
// AI SDK 6: Check for object-based token structures first
|
|
43
|
+
if (typeof usage.inputTokens === "object" &&
|
|
44
|
+
usage.inputTokens?.total != null) {
|
|
45
|
+
// AI SDK 6 detected
|
|
46
|
+
inputTokens = usage.inputTokens.total;
|
|
47
|
+
if (typeof usage.outputTokens === "object" &&
|
|
48
|
+
usage.outputTokens?.total != null) {
|
|
49
|
+
outputTokens = usage.outputTokens.total;
|
|
50
|
+
}
|
|
51
|
+
totalTokens = result.usage?.totalTokens;
|
|
52
|
+
if (typeof totalTokens !== "number" &&
|
|
53
|
+
typeof inputTokens === "number" &&
|
|
54
|
+
typeof outputTokens === "number") {
|
|
55
|
+
totalTokens = inputTokens + outputTokens;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else if (typeof usage.inputTokens === "number") {
|
|
59
|
+
// AI SDK 5 detected
|
|
60
|
+
inputTokens = usage.inputTokens;
|
|
61
|
+
if (typeof usage.outputTokens === "number") {
|
|
62
|
+
outputTokens = usage.outputTokens;
|
|
63
|
+
}
|
|
64
|
+
totalTokens = result.usage?.totalTokens;
|
|
65
|
+
if (typeof totalTokens !== "number" &&
|
|
66
|
+
typeof inputTokens === "number" &&
|
|
67
|
+
typeof outputTokens === "number") {
|
|
68
|
+
totalTokens = inputTokens + outputTokens;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
// AI SDK 4 fallback
|
|
73
|
+
if (typeof usage.promptTokens === "number") {
|
|
74
|
+
inputTokens = usage.promptTokens;
|
|
75
|
+
}
|
|
76
|
+
if (typeof usage.completionTokens === "number") {
|
|
77
|
+
outputTokens = usage.completionTokens;
|
|
78
|
+
}
|
|
79
|
+
totalTokens = result.usage?.totalTokens;
|
|
80
|
+
if (typeof totalTokens !== "number" &&
|
|
81
|
+
typeof inputTokens === "number" &&
|
|
82
|
+
typeof outputTokens === "number") {
|
|
83
|
+
totalTokens = inputTokens + outputTokens;
|
|
84
|
+
}
|
|
48
85
|
}
|
|
49
86
|
const langsmithUsage = {
|
|
50
87
|
input_tokens: inputTokens,
|
|
@@ -70,7 +107,7 @@ const setUsageMetadataOnRunTree = (result, runTree) => {
|
|
|
70
107
|
};
|
|
71
108
|
};
|
|
72
109
|
/**
|
|
73
|
-
* AI SDK middleware that wraps an AI SDK 5 model and adds LangSmith tracing.
|
|
110
|
+
* AI SDK middleware that wraps an AI SDK 6 or 5 model and adds LangSmith tracing.
|
|
74
111
|
*/
|
|
75
112
|
export function LangSmithMiddleware(config) {
|
|
76
113
|
const { name, modelId, lsConfig } = config ?? {};
|
package/dist/index.cjs
CHANGED
|
@@ -13,4 +13,4 @@ var uuid_js_1 = require("./uuid.cjs");
|
|
|
13
13
|
Object.defineProperty(exports, "uuid7", { enumerable: true, get: function () { return uuid_js_1.uuid7; } });
|
|
14
14
|
Object.defineProperty(exports, "uuid7FromTime", { enumerable: true, get: function () { return uuid_js_1.uuid7FromTime; } });
|
|
15
15
|
// Update using yarn bump-version
|
|
16
|
-
exports.__version__ = "0.4.
|
|
16
|
+
exports.__version__ = "0.4.3-rc.0";
|
package/dist/index.d.ts
CHANGED
|
@@ -4,4 +4,4 @@ export { RunTree, type RunTreeConfig } from "./run_trees.js";
|
|
|
4
4
|
export { overrideFetchImplementation } from "./singletons/fetch.js";
|
|
5
5
|
export { getDefaultProjectName } from "./utils/project.js";
|
|
6
6
|
export { uuid7, uuid7FromTime } from "./uuid.js";
|
|
7
|
-
export declare const __version__ = "0.4.
|
|
7
|
+
export declare const __version__ = "0.4.3-rc.0";
|
package/dist/index.js
CHANGED
|
@@ -4,4 +4,4 @@ export { overrideFetchImplementation } from "./singletons/fetch.js";
|
|
|
4
4
|
export { getDefaultProjectName } from "./utils/project.js";
|
|
5
5
|
export { uuid7, uuid7FromTime } from "./uuid.js";
|
|
6
6
|
// Update using yarn bump-version
|
|
7
|
-
export const __version__ = "0.4.
|
|
7
|
+
export const __version__ = "0.4.3-rc.0";
|
package/dist/run_trees.cjs
CHANGED
|
@@ -287,6 +287,12 @@ class RunTree {
|
|
|
287
287
|
writable: true,
|
|
288
288
|
value: void 0
|
|
289
289
|
});
|
|
290
|
+
Object.defineProperty(this, "_awaitInputsOnPost", {
|
|
291
|
+
enumerable: true,
|
|
292
|
+
configurable: true,
|
|
293
|
+
writable: true,
|
|
294
|
+
value: void 0
|
|
295
|
+
});
|
|
290
296
|
// If you pass in a run tree directly, return a shallow clone
|
|
291
297
|
if (isRunTree(originalConfig)) {
|
|
292
298
|
Object.assign(this, { ...originalConfig });
|
|
@@ -644,6 +650,9 @@ class RunTree {
|
|
|
644
650
|
};
|
|
645
651
|
}
|
|
646
652
|
async postRun(excludeChildRuns = true) {
|
|
653
|
+
if (this._awaitInputsOnPost) {
|
|
654
|
+
this.inputs = await this.inputs;
|
|
655
|
+
}
|
|
647
656
|
try {
|
|
648
657
|
const runtimeEnv = (0, env_js_2.getRuntimeEnvironment)();
|
|
649
658
|
if (this.replicas && this.replicas.length > 0) {
|
package/dist/run_trees.d.ts
CHANGED
|
@@ -101,6 +101,7 @@ export declare class RunTree implements BaseRun {
|
|
|
101
101
|
replicas?: WriteReplica[];
|
|
102
102
|
distributedParentId?: string;
|
|
103
103
|
private _serialized_start_time;
|
|
104
|
+
_awaitInputsOnPost?: boolean;
|
|
104
105
|
constructor(originalConfig: RunTreeConfig | RunTree);
|
|
105
106
|
set metadata(metadata: KVMap);
|
|
106
107
|
get metadata(): KVMap;
|
package/dist/run_trees.js
CHANGED
|
@@ -281,6 +281,12 @@ export class RunTree {
|
|
|
281
281
|
writable: true,
|
|
282
282
|
value: void 0
|
|
283
283
|
});
|
|
284
|
+
Object.defineProperty(this, "_awaitInputsOnPost", {
|
|
285
|
+
enumerable: true,
|
|
286
|
+
configurable: true,
|
|
287
|
+
writable: true,
|
|
288
|
+
value: void 0
|
|
289
|
+
});
|
|
284
290
|
// If you pass in a run tree directly, return a shallow clone
|
|
285
291
|
if (isRunTree(originalConfig)) {
|
|
286
292
|
Object.assign(this, { ...originalConfig });
|
|
@@ -638,6 +644,9 @@ export class RunTree {
|
|
|
638
644
|
};
|
|
639
645
|
}
|
|
640
646
|
async postRun(excludeChildRuns = true) {
|
|
647
|
+
if (this._awaitInputsOnPost) {
|
|
648
|
+
this.inputs = await this.inputs;
|
|
649
|
+
}
|
|
641
650
|
try {
|
|
642
651
|
const runtimeEnv = getRuntimeEnvironment();
|
|
643
652
|
if (this.replicas && this.replicas.length > 0) {
|
package/dist/traceable.cjs
CHANGED
|
@@ -108,14 +108,12 @@ const _extractUsage = (runData) => {
|
|
|
108
108
|
return runData.outputs?.usage_metadata ?? usageMetadataFromMetadata;
|
|
109
109
|
};
|
|
110
110
|
async function handleEnd(params) {
|
|
111
|
-
const { runTree, on_end, postRunPromise, deferredInputs
|
|
111
|
+
const { runTree, on_end, postRunPromise, deferredInputs } = params;
|
|
112
112
|
const onEnd = on_end;
|
|
113
113
|
if (onEnd) {
|
|
114
114
|
onEnd(runTree);
|
|
115
115
|
}
|
|
116
|
-
|
|
117
|
-
await postRunPromise;
|
|
118
|
-
}
|
|
116
|
+
await postRunPromise;
|
|
119
117
|
if (deferredInputs) {
|
|
120
118
|
await runTree?.postRun();
|
|
121
119
|
}
|
|
@@ -240,7 +238,11 @@ const getTracingRunTree = (runTree, inputs, getInvocationParams, processInputs,
|
|
|
240
238
|
}
|
|
241
239
|
const [attached, args] = handleRunAttachments(inputs, extractAttachments);
|
|
242
240
|
runTree.attachments = attached;
|
|
243
|
-
|
|
241
|
+
const processedInputs = handleRunInputs(args, processInputs);
|
|
242
|
+
if (isAsyncFn(processInputs)) {
|
|
243
|
+
runTree._awaitInputsOnPost = true;
|
|
244
|
+
}
|
|
245
|
+
runTree.inputs = processedInputs;
|
|
244
246
|
const invocationParams = getInvocationParams?.(...inputs);
|
|
245
247
|
if (invocationParams != null) {
|
|
246
248
|
runTree.extra ??= {};
|
|
@@ -777,7 +779,6 @@ function traceable(wrappedFunc, config) {
|
|
|
777
779
|
postRunPromise,
|
|
778
780
|
on_end,
|
|
779
781
|
deferredInputs,
|
|
780
|
-
skipChildPromiseDelay: true,
|
|
781
782
|
});
|
|
782
783
|
throw error;
|
|
783
784
|
})
|
package/dist/traceable.d.ts
CHANGED
|
@@ -49,7 +49,7 @@ export type TraceableConfig<Func extends (...args: any[]) => any> = Partial<Omit
|
|
|
49
49
|
* @param inputs Key-value map of the function inputs.
|
|
50
50
|
* @returns Transformed key-value map
|
|
51
51
|
*/
|
|
52
|
-
processInputs?: (inputs: Readonly<ProcessInputs<Parameters<Func>>>) => KVMap
|
|
52
|
+
processInputs?: (inputs: Readonly<ProcessInputs<Parameters<Func>>>) => KVMap | Promise<KVMap>;
|
|
53
53
|
/**
|
|
54
54
|
* Apply transformations to the outputs before logging.
|
|
55
55
|
* This function should NOT mutate the outputs.
|
package/dist/traceable.js
CHANGED
|
@@ -104,14 +104,12 @@ const _extractUsage = (runData) => {
|
|
|
104
104
|
return runData.outputs?.usage_metadata ?? usageMetadataFromMetadata;
|
|
105
105
|
};
|
|
106
106
|
async function handleEnd(params) {
|
|
107
|
-
const { runTree, on_end, postRunPromise, deferredInputs
|
|
107
|
+
const { runTree, on_end, postRunPromise, deferredInputs } = params;
|
|
108
108
|
const onEnd = on_end;
|
|
109
109
|
if (onEnd) {
|
|
110
110
|
onEnd(runTree);
|
|
111
111
|
}
|
|
112
|
-
|
|
113
|
-
await postRunPromise;
|
|
114
|
-
}
|
|
112
|
+
await postRunPromise;
|
|
115
113
|
if (deferredInputs) {
|
|
116
114
|
await runTree?.postRun();
|
|
117
115
|
}
|
|
@@ -236,7 +234,11 @@ const getTracingRunTree = (runTree, inputs, getInvocationParams, processInputs,
|
|
|
236
234
|
}
|
|
237
235
|
const [attached, args] = handleRunAttachments(inputs, extractAttachments);
|
|
238
236
|
runTree.attachments = attached;
|
|
239
|
-
|
|
237
|
+
const processedInputs = handleRunInputs(args, processInputs);
|
|
238
|
+
if (isAsyncFn(processInputs)) {
|
|
239
|
+
runTree._awaitInputsOnPost = true;
|
|
240
|
+
}
|
|
241
|
+
runTree.inputs = processedInputs;
|
|
240
242
|
const invocationParams = getInvocationParams?.(...inputs);
|
|
241
243
|
if (invocationParams != null) {
|
|
242
244
|
runTree.extra ??= {};
|
|
@@ -773,7 +775,6 @@ export function traceable(wrappedFunc, config) {
|
|
|
773
775
|
postRunPromise,
|
|
774
776
|
on_end,
|
|
775
777
|
deferredInputs,
|
|
776
|
-
skipChildPromiseDelay: true,
|
|
777
778
|
});
|
|
778
779
|
throw error;
|
|
779
780
|
})
|
package/dist/utils/vercel.cjs
CHANGED
|
@@ -16,7 +16,41 @@ function extractTraceableServiceTier(providerMetadata) {
|
|
|
16
16
|
}
|
|
17
17
|
return undefined;
|
|
18
18
|
}
|
|
19
|
+
function isLanguageModelV3Usage(usage) {
|
|
20
|
+
return usage.inputTokens != null && typeof usage.inputTokens === "object";
|
|
21
|
+
}
|
|
22
|
+
function extractAISDK6OutputTokenDetails(usage, providerMetadata) {
|
|
23
|
+
const openAIServiceTier = extractTraceableServiceTier(providerMetadata ?? {});
|
|
24
|
+
const outputTokenDetailsKeyPrefix = openAIServiceTier
|
|
25
|
+
? `${openAIServiceTier}_`
|
|
26
|
+
: "";
|
|
27
|
+
const outputTokens = usage.outputTokens;
|
|
28
|
+
const outputTokenDetails = {};
|
|
29
|
+
// Extract reasoning tokens from AI SDK 6
|
|
30
|
+
if (typeof outputTokens?.reasoning === "number" &&
|
|
31
|
+
outputTokens?.reasoning > 0) {
|
|
32
|
+
outputTokenDetails[`${outputTokenDetailsKeyPrefix}reasoning`] =
|
|
33
|
+
outputTokens.reasoning;
|
|
34
|
+
}
|
|
35
|
+
// Apply service tier logic for AI SDK 6
|
|
36
|
+
if (openAIServiceTier && typeof outputTokens?.total === "number") {
|
|
37
|
+
// Avoid counting reasoning tokens towards the output token count
|
|
38
|
+
// since service tier tokens are already priced differently
|
|
39
|
+
outputTokenDetails[openAIServiceTier] =
|
|
40
|
+
outputTokens.total -
|
|
41
|
+
(outputTokenDetails[`${outputTokenDetailsKeyPrefix}reasoning`] ?? 0);
|
|
42
|
+
}
|
|
43
|
+
return outputTokenDetails;
|
|
44
|
+
}
|
|
19
45
|
function extractOutputTokenDetails(usage, providerMetadata) {
|
|
46
|
+
if (usage == null) {
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
49
|
+
// AI SDK 6: Check for built-in outputTokens breakdown first
|
|
50
|
+
if (isLanguageModelV3Usage(usage)) {
|
|
51
|
+
// Return AI SDK 6 results (even if empty, to prevent falling through to SDK 5 logic)
|
|
52
|
+
return extractAISDK6OutputTokenDetails(usage, providerMetadata);
|
|
53
|
+
}
|
|
20
54
|
const openAIServiceTier = extractTraceableServiceTier(providerMetadata ?? {});
|
|
21
55
|
const outputTokenDetailsKeyPrefix = openAIServiceTier
|
|
22
56
|
? `${openAIServiceTier}_`
|
|
@@ -35,7 +69,62 @@ function extractOutputTokenDetails(usage, providerMetadata) {
|
|
|
35
69
|
}
|
|
36
70
|
return outputTokenDetails;
|
|
37
71
|
}
|
|
72
|
+
function extractAISDK6InputTokenDetails(usage, providerMetadata) {
|
|
73
|
+
let inputTokenDetails = {};
|
|
74
|
+
const inputTokens = usage.inputTokens;
|
|
75
|
+
// Extract standard AI SDK 6 input token breakdowns
|
|
76
|
+
// Map AI SDK 6 fields to LangSmith token detail fields:
|
|
77
|
+
// - cacheRead -> cache_read
|
|
78
|
+
// - cacheWrite -> cache_creation
|
|
79
|
+
if (providerMetadata?.anthropic != null &&
|
|
80
|
+
typeof providerMetadata?.anthropic === "object") {
|
|
81
|
+
const anthropic = providerMetadata.anthropic;
|
|
82
|
+
if (anthropic.usage != null && typeof anthropic.usage === "object") {
|
|
83
|
+
// Raw usage from Anthropic returned in AI SDK 5
|
|
84
|
+
const usage = anthropic.usage;
|
|
85
|
+
inputTokenDetails = (0, usage_js_1.convertAnthropicUsageToInputTokenDetails)(usage);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
if (typeof inputTokens?.cacheRead === "number" &&
|
|
90
|
+
inputTokens.cacheRead > 0) {
|
|
91
|
+
inputTokenDetails.cache_read = inputTokens.cacheRead;
|
|
92
|
+
}
|
|
93
|
+
if (typeof inputTokens?.cacheWrite === "number" &&
|
|
94
|
+
inputTokens?.cacheWrite > 0) {
|
|
95
|
+
inputTokenDetails.cache_creation = inputTokens?.cacheWrite;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Handle OpenAI service tier for AI SDK 6
|
|
99
|
+
const openAIServiceTier = extractTraceableServiceTier(providerMetadata ?? {});
|
|
100
|
+
if (openAIServiceTier) {
|
|
101
|
+
const serviceTierPrefix = `${openAIServiceTier}_`;
|
|
102
|
+
// Add cache_read with service tier prefix if we have cached tokens
|
|
103
|
+
if (typeof inputTokens?.cacheRead === "number" &&
|
|
104
|
+
inputTokens?.cacheRead > 0) {
|
|
105
|
+
inputTokenDetails[`${serviceTierPrefix}cache_read`] =
|
|
106
|
+
inputTokens.cacheRead;
|
|
107
|
+
// Remove the non-prefixed version since we're using service tier
|
|
108
|
+
delete inputTokenDetails.cache_read;
|
|
109
|
+
}
|
|
110
|
+
// Calculate service tier tokens (total minus cached)
|
|
111
|
+
if (typeof inputTokens?.total === "number") {
|
|
112
|
+
inputTokenDetails[openAIServiceTier] =
|
|
113
|
+
inputTokens.total -
|
|
114
|
+
(inputTokenDetails[`${serviceTierPrefix}cache_read`] ?? 0);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return inputTokenDetails;
|
|
118
|
+
}
|
|
38
119
|
function extractInputTokenDetails(usage, providerMetadata) {
|
|
120
|
+
if (usage == null) {
|
|
121
|
+
return {};
|
|
122
|
+
}
|
|
123
|
+
// AI SDK 6: Check for built-in inputTokens breakdown first
|
|
124
|
+
if (isLanguageModelV3Usage(usage)) {
|
|
125
|
+
// Return AI SDK 6 results (even if empty, to prevent falling through to SDK 5 logic)
|
|
126
|
+
return extractAISDK6InputTokenDetails(usage, providerMetadata);
|
|
127
|
+
}
|
|
39
128
|
let inputTokenDetails = {};
|
|
40
129
|
if (providerMetadata?.anthropic != null &&
|
|
41
130
|
typeof providerMetadata?.anthropic === "object") {
|
package/dist/utils/vercel.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { LanguageModelV2Usage } from "@ai-sdk/provider";
|
|
1
|
+
import type { LanguageModelV2Usage, LanguageModelV3Usage } from "@ai-sdk/provider";
|
|
2
2
|
import { KVMap } from "../schemas.js";
|
|
3
|
-
export declare function extractOutputTokenDetails(usage?: Partial<LanguageModelV2Usage>, providerMetadata?: Record<string, unknown>): Record<string, number>;
|
|
4
|
-
export declare function extractInputTokenDetails(usage?: Partial<LanguageModelV2Usage>, providerMetadata?: Record<string, unknown>): Record<string, number>;
|
|
3
|
+
export declare function extractOutputTokenDetails(usage?: Partial<LanguageModelV2Usage> | Partial<LanguageModelV3Usage>, providerMetadata?: Record<string, unknown>): Record<string, number>;
|
|
4
|
+
export declare function extractInputTokenDetails(usage?: Partial<LanguageModelV2Usage> | Partial<LanguageModelV3Usage>, providerMetadata?: Record<string, unknown>): Record<string, number>;
|
|
5
5
|
export declare function extractUsageMetadata(span?: {
|
|
6
6
|
status?: {
|
|
7
7
|
code: number;
|
package/dist/utils/vercel.js
CHANGED
|
@@ -11,7 +11,41 @@ function extractTraceableServiceTier(providerMetadata) {
|
|
|
11
11
|
}
|
|
12
12
|
return undefined;
|
|
13
13
|
}
|
|
14
|
+
function isLanguageModelV3Usage(usage) {
|
|
15
|
+
return usage.inputTokens != null && typeof usage.inputTokens === "object";
|
|
16
|
+
}
|
|
17
|
+
function extractAISDK6OutputTokenDetails(usage, providerMetadata) {
|
|
18
|
+
const openAIServiceTier = extractTraceableServiceTier(providerMetadata ?? {});
|
|
19
|
+
const outputTokenDetailsKeyPrefix = openAIServiceTier
|
|
20
|
+
? `${openAIServiceTier}_`
|
|
21
|
+
: "";
|
|
22
|
+
const outputTokens = usage.outputTokens;
|
|
23
|
+
const outputTokenDetails = {};
|
|
24
|
+
// Extract reasoning tokens from AI SDK 6
|
|
25
|
+
if (typeof outputTokens?.reasoning === "number" &&
|
|
26
|
+
outputTokens?.reasoning > 0) {
|
|
27
|
+
outputTokenDetails[`${outputTokenDetailsKeyPrefix}reasoning`] =
|
|
28
|
+
outputTokens.reasoning;
|
|
29
|
+
}
|
|
30
|
+
// Apply service tier logic for AI SDK 6
|
|
31
|
+
if (openAIServiceTier && typeof outputTokens?.total === "number") {
|
|
32
|
+
// Avoid counting reasoning tokens towards the output token count
|
|
33
|
+
// since service tier tokens are already priced differently
|
|
34
|
+
outputTokenDetails[openAIServiceTier] =
|
|
35
|
+
outputTokens.total -
|
|
36
|
+
(outputTokenDetails[`${outputTokenDetailsKeyPrefix}reasoning`] ?? 0);
|
|
37
|
+
}
|
|
38
|
+
return outputTokenDetails;
|
|
39
|
+
}
|
|
14
40
|
export function extractOutputTokenDetails(usage, providerMetadata) {
|
|
41
|
+
if (usage == null) {
|
|
42
|
+
return {};
|
|
43
|
+
}
|
|
44
|
+
// AI SDK 6: Check for built-in outputTokens breakdown first
|
|
45
|
+
if (isLanguageModelV3Usage(usage)) {
|
|
46
|
+
// Return AI SDK 6 results (even if empty, to prevent falling through to SDK 5 logic)
|
|
47
|
+
return extractAISDK6OutputTokenDetails(usage, providerMetadata);
|
|
48
|
+
}
|
|
15
49
|
const openAIServiceTier = extractTraceableServiceTier(providerMetadata ?? {});
|
|
16
50
|
const outputTokenDetailsKeyPrefix = openAIServiceTier
|
|
17
51
|
? `${openAIServiceTier}_`
|
|
@@ -30,7 +64,62 @@ export function extractOutputTokenDetails(usage, providerMetadata) {
|
|
|
30
64
|
}
|
|
31
65
|
return outputTokenDetails;
|
|
32
66
|
}
|
|
67
|
+
function extractAISDK6InputTokenDetails(usage, providerMetadata) {
|
|
68
|
+
let inputTokenDetails = {};
|
|
69
|
+
const inputTokens = usage.inputTokens;
|
|
70
|
+
// Extract standard AI SDK 6 input token breakdowns
|
|
71
|
+
// Map AI SDK 6 fields to LangSmith token detail fields:
|
|
72
|
+
// - cacheRead -> cache_read
|
|
73
|
+
// - cacheWrite -> cache_creation
|
|
74
|
+
if (providerMetadata?.anthropic != null &&
|
|
75
|
+
typeof providerMetadata?.anthropic === "object") {
|
|
76
|
+
const anthropic = providerMetadata.anthropic;
|
|
77
|
+
if (anthropic.usage != null && typeof anthropic.usage === "object") {
|
|
78
|
+
// Raw usage from Anthropic returned in AI SDK 5
|
|
79
|
+
const usage = anthropic.usage;
|
|
80
|
+
inputTokenDetails = convertAnthropicUsageToInputTokenDetails(usage);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
if (typeof inputTokens?.cacheRead === "number" &&
|
|
85
|
+
inputTokens.cacheRead > 0) {
|
|
86
|
+
inputTokenDetails.cache_read = inputTokens.cacheRead;
|
|
87
|
+
}
|
|
88
|
+
if (typeof inputTokens?.cacheWrite === "number" &&
|
|
89
|
+
inputTokens?.cacheWrite > 0) {
|
|
90
|
+
inputTokenDetails.cache_creation = inputTokens?.cacheWrite;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Handle OpenAI service tier for AI SDK 6
|
|
94
|
+
const openAIServiceTier = extractTraceableServiceTier(providerMetadata ?? {});
|
|
95
|
+
if (openAIServiceTier) {
|
|
96
|
+
const serviceTierPrefix = `${openAIServiceTier}_`;
|
|
97
|
+
// Add cache_read with service tier prefix if we have cached tokens
|
|
98
|
+
if (typeof inputTokens?.cacheRead === "number" &&
|
|
99
|
+
inputTokens?.cacheRead > 0) {
|
|
100
|
+
inputTokenDetails[`${serviceTierPrefix}cache_read`] =
|
|
101
|
+
inputTokens.cacheRead;
|
|
102
|
+
// Remove the non-prefixed version since we're using service tier
|
|
103
|
+
delete inputTokenDetails.cache_read;
|
|
104
|
+
}
|
|
105
|
+
// Calculate service tier tokens (total minus cached)
|
|
106
|
+
if (typeof inputTokens?.total === "number") {
|
|
107
|
+
inputTokenDetails[openAIServiceTier] =
|
|
108
|
+
inputTokens.total -
|
|
109
|
+
(inputTokenDetails[`${serviceTierPrefix}cache_read`] ?? 0);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return inputTokenDetails;
|
|
113
|
+
}
|
|
33
114
|
export function extractInputTokenDetails(usage, providerMetadata) {
|
|
115
|
+
if (usage == null) {
|
|
116
|
+
return {};
|
|
117
|
+
}
|
|
118
|
+
// AI SDK 6: Check for built-in inputTokens breakdown first
|
|
119
|
+
if (isLanguageModelV3Usage(usage)) {
|
|
120
|
+
// Return AI SDK 6 results (even if empty, to prevent falling through to SDK 5 logic)
|
|
121
|
+
return extractAISDK6InputTokenDetails(usage, providerMetadata);
|
|
122
|
+
}
|
|
34
123
|
let inputTokenDetails = {};
|
|
35
124
|
if (providerMetadata?.anthropic != null &&
|
|
36
125
|
typeof providerMetadata?.anthropic === "object") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "langsmith",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3-rc.0",
|
|
4
4
|
"description": "Client library to connect to the LangSmith Observability and Evaluation Platform.",
|
|
5
5
|
"packageManager": "yarn@1.22.19",
|
|
6
6
|
"files": [
|
|
@@ -140,8 +140,9 @@
|
|
|
140
140
|
"uuid": "^10.0.0"
|
|
141
141
|
},
|
|
142
142
|
"devDependencies": {
|
|
143
|
-
"@ai-sdk/anthropic": "^
|
|
144
|
-
"@ai-sdk/openai": "^
|
|
143
|
+
"@ai-sdk/anthropic": "^3.0.0",
|
|
144
|
+
"@ai-sdk/openai": "^3.0.0",
|
|
145
|
+
"@ai-sdk/provider": "^3.0.0",
|
|
145
146
|
"@anthropic-ai/sdk": "^0.71.2",
|
|
146
147
|
"@babel/preset-env": "^7.22.4",
|
|
147
148
|
"@faker-js/faker": "^8.4.1",
|
|
@@ -160,7 +161,7 @@
|
|
|
160
161
|
"@types/node-fetch": "^2.6.12",
|
|
161
162
|
"@typescript-eslint/eslint-plugin": "^5.59.8",
|
|
162
163
|
"@typescript-eslint/parser": "^5.59.8",
|
|
163
|
-
"ai": "^
|
|
164
|
+
"ai": "^6.0.1",
|
|
164
165
|
"babel-jest": "^29.5.0",
|
|
165
166
|
"cross-env": "^7.0.3",
|
|
166
167
|
"dotenv": "^16.1.3",
|