langsmith 0.1.65 → 0.1.66

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/index.cjs CHANGED
@@ -8,4 +8,4 @@ Object.defineProperty(exports, "RunTree", { enumerable: true, get: function () {
8
8
  var fetch_js_1 = require("./singletons/fetch.cjs");
9
9
  Object.defineProperty(exports, "overrideFetchImplementation", { enumerable: true, get: function () { return fetch_js_1.overrideFetchImplementation; } });
10
10
  // Update using yarn bump-version
11
- exports.__version__ = "0.1.65";
11
+ exports.__version__ = "0.1.66";
package/dist/index.d.ts CHANGED
@@ -2,4 +2,4 @@ export { Client, type ClientConfig } from "./client.js";
2
2
  export type { Dataset, Example, TracerSession, Run, Feedback, RetrieverOutput, } from "./schemas.js";
3
3
  export { RunTree, type RunTreeConfig } from "./run_trees.js";
4
4
  export { overrideFetchImplementation } from "./singletons/fetch.js";
5
- export declare const __version__ = "0.1.65";
5
+ export declare const __version__ = "0.1.66";
package/dist/index.js CHANGED
@@ -2,4 +2,4 @@ export { Client } from "./client.js";
2
2
  export { RunTree } from "./run_trees.js";
3
3
  export { overrideFetchImplementation } from "./singletons/fetch.js";
4
4
  // Update using yarn bump-version
5
- export const __version__ = "0.1.65";
5
+ export const __version__ = "0.1.66";
package/dist/schemas.d.ts CHANGED
@@ -391,4 +391,75 @@ export interface RunWithAnnotationQueueInfo extends BaseRun {
391
391
  /** The time this run was added to the queue. */
392
392
  added_at?: string;
393
393
  }
394
+ /**
395
+ * Breakdown of input token counts.
396
+ *
397
+ * Does not *need* to sum to full input token count. Does *not* need to have all keys.
398
+ */
399
+ export type InputTokenDetails = {
400
+ /**
401
+ * Audio input tokens.
402
+ */
403
+ audio?: number;
404
+ /**
405
+ * Input tokens that were cached and there was a cache hit.
406
+ *
407
+ * Since there was a cache hit, the tokens were read from the cache.
408
+ * More precisely, the model state given these tokens was read from the cache.
409
+ */
410
+ cache_read?: number;
411
+ /**
412
+ * Input tokens that were cached and there was a cache miss.
413
+ *
414
+ * Since there was a cache miss, the cache was created from these tokens.
415
+ */
416
+ cache_creation?: number;
417
+ };
418
+ /**
419
+ * Breakdown of output token counts.
420
+ *
421
+ * Does *not* need to sum to full output token count. Does *not* need to have all keys.
422
+ */
423
+ export type OutputTokenDetails = {
424
+ /**
425
+ * Audio output tokens
426
+ */
427
+ audio?: number;
428
+ /**
429
+ * Reasoning output tokens.
430
+ *
431
+ * Tokens generated by the model in a chain of thought process (i.e. by
432
+ * OpenAI's o1 models) that are not returned as part of model output.
433
+ */
434
+ reasoning?: number;
435
+ };
436
+ /**
437
+ * Usage metadata for a message, such as token counts.
438
+ */
439
+ export type UsageMetadata = {
440
+ /**
441
+ * Count of input (or prompt) tokens. Sum of all input token types.
442
+ */
443
+ input_tokens: number;
444
+ /**
445
+ * Count of output (or completion) tokens. Sum of all output token types.
446
+ */
447
+ output_tokens: number;
448
+ /**
449
+ * Total token count. Sum of input_tokens + output_tokens.
450
+ */
451
+ total_tokens: number;
452
+ /**
453
+ * Breakdown of input token counts.
454
+ *
455
+ * Does *not* need to sum to full input token count. Does *not* need to have all keys.
456
+ */
457
+ input_token_details?: InputTokenDetails;
458
+ /**
459
+ * Breakdown of output token counts.
460
+ *
461
+ * Does *not* need to sum to full output token count. Does *not* need to have all keys.
462
+ */
463
+ output_token_details?: OutputTokenDetails;
464
+ };
394
465
  export {};
@@ -8,30 +8,50 @@ const traceable_js_1 = require("./singletons/traceable.cjs");
8
8
  const constants_js_1 = require("./singletons/constants.cjs");
9
9
  const asserts_js_1 = require("./utils/asserts.cjs");
10
10
  traceable_js_1.AsyncLocalStorageProviderSingleton.initializeGlobalInstance(new node_async_hooks_1.AsyncLocalStorage());
11
- const handleRunInputs = (rawInputs) => {
11
+ const handleRunInputs = (rawInputs, processInputs) => {
12
12
  const firstInput = rawInputs[0];
13
+ let inputs;
13
14
  if (firstInput == null) {
14
- return {};
15
+ inputs = {};
15
16
  }
16
- if (rawInputs.length > 1) {
17
- return { args: rawInputs };
17
+ else if (rawInputs.length > 1) {
18
+ inputs = { args: rawInputs };
18
19
  }
19
- if ((0, asserts_js_1.isKVMap)(firstInput)) {
20
- return firstInput;
20
+ else if ((0, asserts_js_1.isKVMap)(firstInput)) {
21
+ inputs = firstInput;
22
+ }
23
+ else {
24
+ inputs = { input: firstInput };
25
+ }
26
+ try {
27
+ return processInputs(inputs);
28
+ }
29
+ catch (e) {
30
+ console.error("Error occurred during processInputs. Sending raw inputs:", e);
31
+ return inputs;
21
32
  }
22
- return { input: firstInput };
23
33
  };
24
- const handleRunOutputs = (rawOutputs) => {
34
+ const handleRunOutputs = (rawOutputs, processOutputs) => {
35
+ let outputs;
25
36
  if ((0, asserts_js_1.isKVMap)(rawOutputs)) {
26
- return rawOutputs;
37
+ outputs = rawOutputs;
38
+ }
39
+ else {
40
+ outputs = { outputs: rawOutputs };
41
+ }
42
+ try {
43
+ return processOutputs(outputs);
44
+ }
45
+ catch (e) {
46
+ console.error("Error occurred during processOutputs. Sending raw outputs:", e);
47
+ return outputs;
27
48
  }
28
- return { outputs: rawOutputs };
29
49
  };
30
- const getTracingRunTree = (runTree, inputs, getInvocationParams) => {
50
+ const getTracingRunTree = (runTree, inputs, getInvocationParams, processInputs) => {
31
51
  if (!(0, env_js_1.isTracingEnabled)(runTree.tracingEnabled)) {
32
52
  return undefined;
33
53
  }
34
- runTree.inputs = handleRunInputs(inputs);
54
+ runTree.inputs = handleRunInputs(inputs, processInputs);
35
55
  const invocationParams = getInvocationParams?.(...inputs);
36
56
  if (invocationParams != null) {
37
57
  runTree.extra ??= {};
@@ -193,7 +213,9 @@ const convertSerializableArg = (arg) => {
193
213
  */
194
214
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
195
215
  function traceable(wrappedFunc, config) {
196
- const { aggregator, argsConfigPath, __finalTracedIteratorKey, ...runTreeConfig } = config ?? {};
216
+ const { aggregator, argsConfigPath, __finalTracedIteratorKey, processInputs, processOutputs, ...runTreeConfig } = config ?? {};
217
+ const processInputsFn = processInputs ?? ((x) => x);
218
+ const processOutputsFn = processOutputs ?? ((x) => x);
197
219
  const traceableFunc = (...args) => {
198
220
  let ensuredConfig;
199
221
  try {
@@ -251,7 +273,7 @@ function traceable(wrappedFunc, config) {
251
273
  // used for handoff between LangChain.JS and traceable functions
252
274
  if ((0, run_trees_js_1.isRunnableConfigLike)(firstArg)) {
253
275
  return [
254
- getTracingRunTree(run_trees_js_1.RunTree.fromRunnableConfig(firstArg, ensuredConfig), restArgs, config?.getInvocationParams),
276
+ getTracingRunTree(run_trees_js_1.RunTree.fromRunnableConfig(firstArg, ensuredConfig), restArgs, config?.getInvocationParams, processInputsFn),
255
277
  restArgs,
256
278
  ];
257
279
  }
@@ -267,7 +289,7 @@ function traceable(wrappedFunc, config) {
267
289
  if (firstArg === traceable_js_1.ROOT || (0, run_trees_js_1.isRunTree)(firstArg)) {
268
290
  const currentRunTree = getTracingRunTree(firstArg === traceable_js_1.ROOT
269
291
  ? new run_trees_js_1.RunTree(ensuredConfig)
270
- : firstArg.createChild(ensuredConfig), restArgs, config?.getInvocationParams);
292
+ : firstArg.createChild(ensuredConfig), restArgs, config?.getInvocationParams, processInputsFn);
271
293
  return [currentRunTree, [currentRunTree, ...restArgs]];
272
294
  }
273
295
  // Node.JS uses AsyncLocalStorage (ALS) and AsyncResource
@@ -275,11 +297,11 @@ function traceable(wrappedFunc, config) {
275
297
  const prevRunFromStore = asyncLocalStorage.getStore();
276
298
  if ((0, run_trees_js_1.isRunTree)(prevRunFromStore)) {
277
299
  return [
278
- getTracingRunTree(prevRunFromStore.createChild(ensuredConfig), processedArgs, config?.getInvocationParams),
300
+ getTracingRunTree(prevRunFromStore.createChild(ensuredConfig), processedArgs, config?.getInvocationParams, processInputsFn),
279
301
  processedArgs,
280
302
  ];
281
303
  }
282
- const currentRunTree = getTracingRunTree(new run_trees_js_1.RunTree(ensuredConfig), processedArgs, config?.getInvocationParams);
304
+ const currentRunTree = getTracingRunTree(new run_trees_js_1.RunTree(ensuredConfig), processedArgs, config?.getInvocationParams, processInputsFn);
283
305
  // If a context var is set by LangChain outside of a traceable,
284
306
  // it will be an object with a single property and we should copy
285
307
  // context vars over into the new run tree.
@@ -317,7 +339,7 @@ function traceable(wrappedFunc, config) {
317
339
  : reader.read());
318
340
  if (result.done) {
319
341
  finished = true;
320
- await currentRunTree?.end(handleRunOutputs(await handleChunks(chunks)));
342
+ await currentRunTree?.end(handleRunOutputs(await handleChunks(chunks), processOutputsFn));
321
343
  await handleEnd();
322
344
  controller.close();
323
345
  break;
@@ -329,7 +351,7 @@ function traceable(wrappedFunc, config) {
329
351
  async cancel(reason) {
330
352
  if (!finished)
331
353
  await currentRunTree?.end(undefined, "Cancelled");
332
- await currentRunTree?.end(handleRunOutputs(await handleChunks(chunks)));
354
+ await currentRunTree?.end(handleRunOutputs(await handleChunks(chunks), processOutputsFn));
333
355
  await handleEnd();
334
356
  return reader.cancel(reason);
335
357
  },
@@ -359,7 +381,7 @@ function traceable(wrappedFunc, config) {
359
381
  finally {
360
382
  if (!finished)
361
383
  await currentRunTree?.end(undefined, "Cancelled");
362
- await currentRunTree?.end(handleRunOutputs(await handleChunks(chunks)));
384
+ await currentRunTree?.end(handleRunOutputs(await handleChunks(chunks), processOutputsFn));
363
385
  await handleEnd();
364
386
  }
365
387
  }
@@ -444,7 +466,7 @@ function traceable(wrappedFunc, config) {
444
466
  memo.push(value);
445
467
  }
446
468
  return memo;
447
- }, []))));
469
+ }, [])), processOutputsFn));
448
470
  await handleEnd();
449
471
  }
450
472
  catch (e) {
@@ -459,7 +481,7 @@ function traceable(wrappedFunc, config) {
459
481
  })();
460
482
  }
461
483
  try {
462
- await currentRunTree?.end(handleRunOutputs(rawOutput));
484
+ await currentRunTree?.end(handleRunOutputs(rawOutput, processOutputsFn));
463
485
  await handleEnd();
464
486
  }
465
487
  finally {
@@ -1,5 +1,5 @@
1
1
  import { RunTreeConfig } from "./run_trees.js";
2
- import { InvocationParamsSchema } from "./schemas.js";
2
+ import { InvocationParamsSchema, KVMap } from "./schemas.js";
3
3
  import { TraceableFunction } from "./singletons/types.js";
4
4
  /**
5
5
  * Higher-order function that takes function as input and returns a
@@ -28,6 +28,24 @@ export declare function traceable<Func extends (...args: any[]) => any>(wrappedF
28
28
  * @returns Key-value map of the invocation parameters, which will be merged with the existing metadata
29
29
  */
30
30
  getInvocationParams?: (...args: Parameters<Func>) => InvocationParamsSchema | undefined;
31
+ /**
32
+ * Apply transformations to the inputs before logging.
33
+ * This function should NOT mutate the inputs.
34
+ * `processInputs` is not inherited by nested traceable functions.
35
+ *
36
+ * @param inputs Key-value map of the function inputs.
37
+ * @returns Transformed key-value map
38
+ */
39
+ processInputs?: (inputs: Readonly<KVMap>) => KVMap;
40
+ /**
41
+ * Apply transformations to the outputs before logging.
42
+ * This function should NOT mutate the outputs.
43
+ * `processOutputs` is not inherited by nested traceable functions.
44
+ *
45
+ * @param outputs Key-value map of the function outputs
46
+ * @returns Transformed key-value map
47
+ */
48
+ processOutputs?: (outputs: Readonly<KVMap>) => KVMap;
31
49
  }): TraceableFunction<Func>;
32
50
  export { getCurrentRunTree, isTraceableFunction, withRunTree, ROOT, } from "./singletons/traceable.js";
33
51
  export type { RunTreeLike, TraceableFunction } from "./singletons/types.js";
package/dist/traceable.js CHANGED
@@ -5,30 +5,50 @@ import { ROOT, AsyncLocalStorageProviderSingleton, } from "./singletons/traceabl
5
5
  import { _LC_CONTEXT_VARIABLES_KEY } from "./singletons/constants.js";
6
6
  import { isKVMap, isReadableStream, isAsyncIterable, isIteratorLike, isThenable, isGenerator, isPromiseMethod, } from "./utils/asserts.js";
7
7
  AsyncLocalStorageProviderSingleton.initializeGlobalInstance(new AsyncLocalStorage());
8
- const handleRunInputs = (rawInputs) => {
8
+ const handleRunInputs = (rawInputs, processInputs) => {
9
9
  const firstInput = rawInputs[0];
10
+ let inputs;
10
11
  if (firstInput == null) {
11
- return {};
12
+ inputs = {};
12
13
  }
13
- if (rawInputs.length > 1) {
14
- return { args: rawInputs };
14
+ else if (rawInputs.length > 1) {
15
+ inputs = { args: rawInputs };
15
16
  }
16
- if (isKVMap(firstInput)) {
17
- return firstInput;
17
+ else if (isKVMap(firstInput)) {
18
+ inputs = firstInput;
19
+ }
20
+ else {
21
+ inputs = { input: firstInput };
22
+ }
23
+ try {
24
+ return processInputs(inputs);
25
+ }
26
+ catch (e) {
27
+ console.error("Error occurred during processInputs. Sending raw inputs:", e);
28
+ return inputs;
18
29
  }
19
- return { input: firstInput };
20
30
  };
21
- const handleRunOutputs = (rawOutputs) => {
31
+ const handleRunOutputs = (rawOutputs, processOutputs) => {
32
+ let outputs;
22
33
  if (isKVMap(rawOutputs)) {
23
- return rawOutputs;
34
+ outputs = rawOutputs;
35
+ }
36
+ else {
37
+ outputs = { outputs: rawOutputs };
38
+ }
39
+ try {
40
+ return processOutputs(outputs);
41
+ }
42
+ catch (e) {
43
+ console.error("Error occurred during processOutputs. Sending raw outputs:", e);
44
+ return outputs;
24
45
  }
25
- return { outputs: rawOutputs };
26
46
  };
27
- const getTracingRunTree = (runTree, inputs, getInvocationParams) => {
47
+ const getTracingRunTree = (runTree, inputs, getInvocationParams, processInputs) => {
28
48
  if (!isTracingEnabled(runTree.tracingEnabled)) {
29
49
  return undefined;
30
50
  }
31
- runTree.inputs = handleRunInputs(inputs);
51
+ runTree.inputs = handleRunInputs(inputs, processInputs);
32
52
  const invocationParams = getInvocationParams?.(...inputs);
33
53
  if (invocationParams != null) {
34
54
  runTree.extra ??= {};
@@ -190,7 +210,9 @@ const convertSerializableArg = (arg) => {
190
210
  */
191
211
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
192
212
  export function traceable(wrappedFunc, config) {
193
- const { aggregator, argsConfigPath, __finalTracedIteratorKey, ...runTreeConfig } = config ?? {};
213
+ const { aggregator, argsConfigPath, __finalTracedIteratorKey, processInputs, processOutputs, ...runTreeConfig } = config ?? {};
214
+ const processInputsFn = processInputs ?? ((x) => x);
215
+ const processOutputsFn = processOutputs ?? ((x) => x);
194
216
  const traceableFunc = (...args) => {
195
217
  let ensuredConfig;
196
218
  try {
@@ -248,7 +270,7 @@ export function traceable(wrappedFunc, config) {
248
270
  // used for handoff between LangChain.JS and traceable functions
249
271
  if (isRunnableConfigLike(firstArg)) {
250
272
  return [
251
- getTracingRunTree(RunTree.fromRunnableConfig(firstArg, ensuredConfig), restArgs, config?.getInvocationParams),
273
+ getTracingRunTree(RunTree.fromRunnableConfig(firstArg, ensuredConfig), restArgs, config?.getInvocationParams, processInputsFn),
252
274
  restArgs,
253
275
  ];
254
276
  }
@@ -264,7 +286,7 @@ export function traceable(wrappedFunc, config) {
264
286
  if (firstArg === ROOT || isRunTree(firstArg)) {
265
287
  const currentRunTree = getTracingRunTree(firstArg === ROOT
266
288
  ? new RunTree(ensuredConfig)
267
- : firstArg.createChild(ensuredConfig), restArgs, config?.getInvocationParams);
289
+ : firstArg.createChild(ensuredConfig), restArgs, config?.getInvocationParams, processInputsFn);
268
290
  return [currentRunTree, [currentRunTree, ...restArgs]];
269
291
  }
270
292
  // Node.JS uses AsyncLocalStorage (ALS) and AsyncResource
@@ -272,11 +294,11 @@ export function traceable(wrappedFunc, config) {
272
294
  const prevRunFromStore = asyncLocalStorage.getStore();
273
295
  if (isRunTree(prevRunFromStore)) {
274
296
  return [
275
- getTracingRunTree(prevRunFromStore.createChild(ensuredConfig), processedArgs, config?.getInvocationParams),
297
+ getTracingRunTree(prevRunFromStore.createChild(ensuredConfig), processedArgs, config?.getInvocationParams, processInputsFn),
276
298
  processedArgs,
277
299
  ];
278
300
  }
279
- const currentRunTree = getTracingRunTree(new RunTree(ensuredConfig), processedArgs, config?.getInvocationParams);
301
+ const currentRunTree = getTracingRunTree(new RunTree(ensuredConfig), processedArgs, config?.getInvocationParams, processInputsFn);
280
302
  // If a context var is set by LangChain outside of a traceable,
281
303
  // it will be an object with a single property and we should copy
282
304
  // context vars over into the new run tree.
@@ -314,7 +336,7 @@ export function traceable(wrappedFunc, config) {
314
336
  : reader.read());
315
337
  if (result.done) {
316
338
  finished = true;
317
- await currentRunTree?.end(handleRunOutputs(await handleChunks(chunks)));
339
+ await currentRunTree?.end(handleRunOutputs(await handleChunks(chunks), processOutputsFn));
318
340
  await handleEnd();
319
341
  controller.close();
320
342
  break;
@@ -326,7 +348,7 @@ export function traceable(wrappedFunc, config) {
326
348
  async cancel(reason) {
327
349
  if (!finished)
328
350
  await currentRunTree?.end(undefined, "Cancelled");
329
- await currentRunTree?.end(handleRunOutputs(await handleChunks(chunks)));
351
+ await currentRunTree?.end(handleRunOutputs(await handleChunks(chunks), processOutputsFn));
330
352
  await handleEnd();
331
353
  return reader.cancel(reason);
332
354
  },
@@ -356,7 +378,7 @@ export function traceable(wrappedFunc, config) {
356
378
  finally {
357
379
  if (!finished)
358
380
  await currentRunTree?.end(undefined, "Cancelled");
359
- await currentRunTree?.end(handleRunOutputs(await handleChunks(chunks)));
381
+ await currentRunTree?.end(handleRunOutputs(await handleChunks(chunks), processOutputsFn));
360
382
  await handleEnd();
361
383
  }
362
384
  }
@@ -441,7 +463,7 @@ export function traceable(wrappedFunc, config) {
441
463
  memo.push(value);
442
464
  }
443
465
  return memo;
444
- }, []))));
466
+ }, [])), processOutputsFn));
445
467
  await handleEnd();
446
468
  }
447
469
  catch (e) {
@@ -456,7 +478,7 @@ export function traceable(wrappedFunc, config) {
456
478
  })();
457
479
  }
458
480
  try {
459
- await currentRunTree?.end(handleRunOutputs(rawOutput));
481
+ await currentRunTree?.end(handleRunOutputs(rawOutput, processOutputsFn));
460
482
  await handleEnd();
461
483
  }
462
484
  finally {
@@ -113,6 +113,43 @@ const textAggregator = (allChunks
113
113
  ];
114
114
  return aggregatedOutput;
115
115
  };
116
+ function processChatCompletion(outputs) {
117
+ const chatCompletion = outputs;
118
+ // copy the original object, minus usage
119
+ const result = { ...chatCompletion };
120
+ const usage = chatCompletion.usage;
121
+ if (usage) {
122
+ const inputTokenDetails = {
123
+ ...(usage.prompt_tokens_details?.audio_tokens !== null && {
124
+ audio: usage.prompt_tokens_details?.audio_tokens,
125
+ }),
126
+ ...(usage.prompt_tokens_details?.cached_tokens !== null && {
127
+ cache_read: usage.prompt_tokens_details?.cached_tokens,
128
+ }),
129
+ };
130
+ const outputTokenDetails = {
131
+ ...(usage.completion_tokens_details?.audio_tokens !== null && {
132
+ audio: usage.completion_tokens_details?.audio_tokens,
133
+ }),
134
+ ...(usage.completion_tokens_details?.reasoning_tokens !== null && {
135
+ reasoning: usage.completion_tokens_details?.reasoning_tokens,
136
+ }),
137
+ };
138
+ result.usage_metadata = {
139
+ input_tokens: usage.prompt_tokens ?? 0,
140
+ output_tokens: usage.completion_tokens ?? 0,
141
+ total_tokens: usage.total_tokens ?? 0,
142
+ ...(Object.keys(inputTokenDetails).length > 0 && {
143
+ input_token_details: inputTokenDetails,
144
+ }),
145
+ ...(Object.keys(outputTokenDetails).length > 0 && {
146
+ output_token_details: outputTokenDetails,
147
+ }),
148
+ };
149
+ }
150
+ delete result.usage;
151
+ return result;
152
+ }
116
153
  /**
117
154
  * Wraps an OpenAI client's completion methods, enabling automatic LangSmith
118
155
  * tracing. Method signatures are unchanged, with the exception that you can pass
@@ -208,6 +245,7 @@ const wrapOpenAI = (openai, options) => {
208
245
  ls_stop,
209
246
  };
210
247
  },
248
+ processOutputs: processChatCompletion,
211
249
  ...options,
212
250
  }),
213
251
  },
@@ -110,6 +110,43 @@ const textAggregator = (allChunks
110
110
  ];
111
111
  return aggregatedOutput;
112
112
  };
113
+ function processChatCompletion(outputs) {
114
+ const chatCompletion = outputs;
115
+ // copy the original object, minus usage
116
+ const result = { ...chatCompletion };
117
+ const usage = chatCompletion.usage;
118
+ if (usage) {
119
+ const inputTokenDetails = {
120
+ ...(usage.prompt_tokens_details?.audio_tokens !== null && {
121
+ audio: usage.prompt_tokens_details?.audio_tokens,
122
+ }),
123
+ ...(usage.prompt_tokens_details?.cached_tokens !== null && {
124
+ cache_read: usage.prompt_tokens_details?.cached_tokens,
125
+ }),
126
+ };
127
+ const outputTokenDetails = {
128
+ ...(usage.completion_tokens_details?.audio_tokens !== null && {
129
+ audio: usage.completion_tokens_details?.audio_tokens,
130
+ }),
131
+ ...(usage.completion_tokens_details?.reasoning_tokens !== null && {
132
+ reasoning: usage.completion_tokens_details?.reasoning_tokens,
133
+ }),
134
+ };
135
+ result.usage_metadata = {
136
+ input_tokens: usage.prompt_tokens ?? 0,
137
+ output_tokens: usage.completion_tokens ?? 0,
138
+ total_tokens: usage.total_tokens ?? 0,
139
+ ...(Object.keys(inputTokenDetails).length > 0 && {
140
+ input_token_details: inputTokenDetails,
141
+ }),
142
+ ...(Object.keys(outputTokenDetails).length > 0 && {
143
+ output_token_details: outputTokenDetails,
144
+ }),
145
+ };
146
+ }
147
+ delete result.usage;
148
+ return result;
149
+ }
113
150
  /**
114
151
  * Wraps an OpenAI client's completion methods, enabling automatic LangSmith
115
152
  * tracing. Method signatures are unchanged, with the exception that you can pass
@@ -205,6 +242,7 @@ export const wrapOpenAI = (openai, options) => {
205
242
  ls_stop,
206
243
  };
207
244
  },
245
+ processOutputs: processChatCompletion,
208
246
  ...options,
209
247
  }),
210
248
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langsmith",
3
- "version": "0.1.65",
3
+ "version": "0.1.66",
4
4
  "description": "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform.",
5
5
  "packageManager": "yarn@1.22.19",
6
6
  "files": [