langsmith 0.3.37 → 0.3.39

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
@@ -10,4 +10,4 @@ Object.defineProperty(exports, "overrideFetchImplementation", { enumerable: true
10
10
  var project_js_1 = require("./utils/project.cjs");
11
11
  Object.defineProperty(exports, "getDefaultProjectName", { enumerable: true, get: function () { return project_js_1.getDefaultProjectName; } });
12
12
  // Update using yarn bump-version
13
- exports.__version__ = "0.3.37";
13
+ exports.__version__ = "0.3.39";
package/dist/index.d.ts CHANGED
@@ -3,4 +3,4 @@ export type { Dataset, Example, TracerSession, Run, Feedback, RetrieverOutput, }
3
3
  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
- export declare const __version__ = "0.3.37";
6
+ export declare const __version__ = "0.3.39";
package/dist/index.js CHANGED
@@ -3,4 +3,4 @@ export { RunTree } from "./run_trees.js";
3
3
  export { overrideFetchImplementation } from "./singletons/fetch.js";
4
4
  export { getDefaultProjectName } from "./utils/project.js";
5
5
  // Update using yarn bump-version
6
- export const __version__ = "0.3.37";
6
+ export const __version__ = "0.3.39";
@@ -4,7 +4,6 @@ exports.ROOT = exports.AsyncLocalStorageProviderSingleton = void 0;
4
4
  exports.getCurrentRunTree = getCurrentRunTree;
5
5
  exports.withRunTree = withRunTree;
6
6
  exports.isTraceableFunction = isTraceableFunction;
7
- const run_trees_js_1 = require("../run_trees.cjs");
8
7
  class MockAsyncLocalStorage {
9
8
  getStore() {
10
9
  return undefined;
@@ -31,7 +30,7 @@ class AsyncLocalStorageProvider {
31
30
  exports.AsyncLocalStorageProviderSingleton = new AsyncLocalStorageProvider();
32
31
  function getCurrentRunTree(permitAbsentRunTree = false) {
33
32
  const runTree = exports.AsyncLocalStorageProviderSingleton.getInstance().getStore();
34
- if (!permitAbsentRunTree && !(0, run_trees_js_1.isRunTree)(runTree)) {
33
+ if (!permitAbsentRunTree && runTree === undefined) {
35
34
  throw new Error("Could not get the current run tree.\n\nPlease make sure you are calling this method within a traceable function and that tracing is enabled.");
36
35
  }
37
36
  return runTree;
@@ -1,8 +1,8 @@
1
- import { RunTree } from "../run_trees.js";
2
- import { TraceableFunction } from "./types.js";
1
+ import type { RunTree } from "../run_trees.js";
2
+ import type { ContextPlaceholder, TraceableFunction } from "./types.js";
3
3
  interface AsyncLocalStorageInterface {
4
- getStore: () => RunTree | undefined;
5
- run: (context: RunTree | undefined, fn: () => void) => void;
4
+ getStore: () => RunTree | ContextPlaceholder | undefined;
5
+ run: (context: RunTree | ContextPlaceholder | undefined, fn: () => void) => void;
6
6
  }
7
7
  declare class AsyncLocalStorageProvider {
8
8
  getInstance(): AsyncLocalStorageInterface;
@@ -1,4 +1,3 @@
1
- import { isRunTree } from "../run_trees.js";
2
1
  class MockAsyncLocalStorage {
3
2
  getStore() {
4
3
  return undefined;
@@ -25,7 +24,7 @@ class AsyncLocalStorageProvider {
25
24
  export const AsyncLocalStorageProviderSingleton = new AsyncLocalStorageProvider();
26
25
  export function getCurrentRunTree(permitAbsentRunTree = false) {
27
26
  const runTree = AsyncLocalStorageProviderSingleton.getInstance().getStore();
28
- if (!permitAbsentRunTree && !isRunTree(runTree)) {
27
+ if (!permitAbsentRunTree && runTree === undefined) {
29
28
  throw new Error("Could not get the current run tree.\n\nPlease make sure you are calling this method within a traceable function and that tracing is enabled.");
30
29
  }
31
30
  return runTree;
@@ -1,2 +1,3 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const constants_js_1 = require("./constants.cjs");
@@ -1,5 +1,6 @@
1
1
  import { RunTree, RunnableConfigLike } from "../run_trees.js";
2
2
  import { ROOT } from "./traceable.js";
3
+ import { _LC_CONTEXT_VARIABLES_KEY } from "./constants.js";
3
4
  type SmartPromise<T> = T extends AsyncGenerator ? T : T extends Promise<unknown> ? T : Promise<T>;
4
5
  type WrapArgReturnPair<Pair> = Pair extends [
5
6
  infer Args extends any[],
@@ -37,4 +38,7 @@ export type TraceableFunction<Func extends (...args: any[]) => any> = (Func exte
37
38
  [K in keyof Func]: Func[K];
38
39
  };
39
40
  export type RunTreeLike = RunTree;
41
+ export type ContextPlaceholder = {
42
+ [_LC_CONTEXT_VARIABLES_KEY]?: Record<string, unknown>;
43
+ };
40
44
  export {};
@@ -1 +1 @@
1
- export {};
1
+ import { _LC_CONTEXT_VARIABLES_KEY } from "./constants.js";
@@ -143,7 +143,7 @@ const handleRunAttachments = (rawInputs, extractAttachments) => {
143
143
  };
144
144
  const getTracingRunTree = (runTree, inputs, getInvocationParams, processInputs, extractAttachments) => {
145
145
  if (!(0, env_js_1.isTracingEnabled)(runTree.tracingEnabled)) {
146
- return undefined;
146
+ return {};
147
147
  }
148
148
  const [attached, args] = handleRunAttachments(inputs, extractAttachments);
149
149
  runTree.attachments = attached;
@@ -364,7 +364,7 @@ function traceable(wrappedFunc, config) {
364
364
  for (let i = 0; i < processedArgs.length; i++) {
365
365
  processedArgs[i] = convertSerializableArg(processedArgs[i]);
366
366
  }
367
- const [currentRunTree, rawInputs] = (() => {
367
+ const [currentContext, rawInputs] = (() => {
368
368
  const [firstArg, ...restArgs] = processedArgs;
369
369
  // used for handoff between LangChain.JS and traceable functions
370
370
  if ((0, run_trees_js_1.isRunnableConfigLike)(firstArg)) {
@@ -391,24 +391,32 @@ function traceable(wrappedFunc, config) {
391
391
  // Node.JS uses AsyncLocalStorage (ALS) and AsyncResource
392
392
  // to allow storing context
393
393
  const prevRunFromStore = asyncLocalStorage.getStore();
394
- if ((0, run_trees_js_1.isRunTree)(prevRunFromStore)) {
395
- return [
396
- getTracingRunTree(prevRunFromStore.createChild(ensuredConfig), processedArgs, config?.getInvocationParams, processInputsFn, extractAttachmentsFn),
397
- processedArgs,
398
- ];
399
- }
400
- const currentRunTree = getTracingRunTree(new run_trees_js_1.RunTree(ensuredConfig), processedArgs, config?.getInvocationParams, processInputsFn, extractAttachmentsFn);
394
+ let lc_contextVars;
401
395
  // If a context var is set by LangChain outside of a traceable,
402
396
  // it will be an object with a single property and we should copy
403
397
  // context vars over into the new run tree.
404
398
  if (prevRunFromStore !== undefined &&
405
399
  constants_js_1._LC_CONTEXT_VARIABLES_KEY in prevRunFromStore) {
406
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
407
- currentRunTree[constants_js_1._LC_CONTEXT_VARIABLES_KEY] =
408
- prevRunFromStore[constants_js_1._LC_CONTEXT_VARIABLES_KEY];
400
+ lc_contextVars = prevRunFromStore[constants_js_1._LC_CONTEXT_VARIABLES_KEY];
401
+ }
402
+ if ((0, run_trees_js_1.isRunTree)(prevRunFromStore)) {
403
+ const currentRunTree = getTracingRunTree(prevRunFromStore.createChild(ensuredConfig), processedArgs, config?.getInvocationParams, processInputsFn, extractAttachmentsFn);
404
+ if (lc_contextVars) {
405
+ (currentRunTree ?? {})[constants_js_1._LC_CONTEXT_VARIABLES_KEY] =
406
+ lc_contextVars;
407
+ }
408
+ return [currentRunTree, processedArgs];
409
+ }
410
+ const currentRunTree = getTracingRunTree(new run_trees_js_1.RunTree(ensuredConfig), processedArgs, config?.getInvocationParams, processInputsFn, extractAttachmentsFn);
411
+ if (lc_contextVars) {
412
+ (currentRunTree ?? {})[constants_js_1._LC_CONTEXT_VARIABLES_KEY] =
413
+ lc_contextVars;
409
414
  }
410
415
  return [currentRunTree, processedArgs];
411
416
  })();
417
+ const currentRunTree = (0, run_trees_js_1.isRunTree)(currentContext)
418
+ ? currentContext
419
+ : undefined;
412
420
  const otelContextManager = maybeCreateOtelContext(currentRunTree, config?.tracer);
413
421
  const otel_context = (0, otel_js_1.getOTELContext)();
414
422
  const runWithContext = () => {
@@ -648,10 +656,10 @@ function traceable(wrappedFunc, config) {
648
656
  };
649
657
  // Wrap with OTEL context if available, similar to Python's implementation
650
658
  if (otelContextManager) {
651
- return asyncLocalStorage.run(currentRunTree, () => otelContextManager(runWithContext));
659
+ return asyncLocalStorage.run(currentContext, () => otelContextManager(runWithContext));
652
660
  }
653
661
  else {
654
- return asyncLocalStorage.run(currentRunTree, runWithContext);
662
+ return asyncLocalStorage.run(currentContext, runWithContext);
655
663
  }
656
664
  };
657
665
  Object.defineProperty(traceableFunc, "langsmith:traceable", {
@@ -1,22 +1,8 @@
1
1
  import { RunTreeConfig } from "./run_trees.js";
2
2
  import { Attachments, InvocationParamsSchema, KVMap } from "./schemas.js";
3
- import { TraceableFunction } from "./singletons/types.js";
3
+ import type { TraceableFunction } from "./singletons/types.js";
4
4
  import { OTELTracer } from "./experimental/otel/types.js";
5
- /**
6
- * Higher-order function that takes function as input and returns a
7
- * "TraceableFunction" - a wrapped version of the input that
8
- * automatically handles tracing. If the returned traceable function calls any
9
- * traceable functions, those are automatically traced as well.
10
- *
11
- * The returned TraceableFunction can accept a run tree or run tree config as
12
- * its first argument. If omitted, it will default to the caller's run tree,
13
- * or will be treated as a root run.
14
- *
15
- * @param wrappedFunc Targeted function to be traced
16
- * @param config Additional metadata such as name, tags or providing
17
- * a custom LangSmith client instance
18
- */
19
- export declare function traceable<Func extends (...args: any[]) => any>(wrappedFunc: Func, config?: Partial<Omit<RunTreeConfig, "inputs" | "outputs">> & {
5
+ export type TraceableConfig<Func extends (...args: any[]) => any> = Partial<Omit<RunTreeConfig, "inputs" | "outputs">> & {
20
6
  aggregator?: (args: any[]) => any;
21
7
  argsConfigPath?: [number] | [number, string];
22
8
  tracer?: OTELTracer;
@@ -54,6 +40,21 @@ export declare function traceable<Func extends (...args: any[]) => any>(wrappedF
54
40
  * @returns Transformed key-value map
55
41
  */
56
42
  processOutputs?: (outputs: Readonly<KVMap>) => KVMap;
57
- }): TraceableFunction<Func>;
43
+ };
44
+ /**
45
+ * Higher-order function that takes function as input and returns a
46
+ * "TraceableFunction" - a wrapped version of the input that
47
+ * automatically handles tracing. If the returned traceable function calls any
48
+ * traceable functions, those are automatically traced as well.
49
+ *
50
+ * The returned TraceableFunction can accept a run tree or run tree config as
51
+ * its first argument. If omitted, it will default to the caller's run tree,
52
+ * or will be treated as a root run.
53
+ *
54
+ * @param wrappedFunc Targeted function to be traced
55
+ * @param config Additional metadata such as name, tags or providing
56
+ * a custom LangSmith client instance
57
+ */
58
+ export declare function traceable<Func extends (...args: any[]) => any>(wrappedFunc: Func, config?: TraceableConfig<Func>): TraceableFunction<Func>;
58
59
  export { getCurrentRunTree, isTraceableFunction, withRunTree, ROOT, } from "./singletons/traceable.js";
59
60
  export type { RunTreeLike, TraceableFunction } from "./singletons/types.js";
package/dist/traceable.js CHANGED
@@ -139,7 +139,7 @@ const handleRunAttachments = (rawInputs, extractAttachments) => {
139
139
  };
140
140
  const getTracingRunTree = (runTree, inputs, getInvocationParams, processInputs, extractAttachments) => {
141
141
  if (!isTracingEnabled(runTree.tracingEnabled)) {
142
- return undefined;
142
+ return {};
143
143
  }
144
144
  const [attached, args] = handleRunAttachments(inputs, extractAttachments);
145
145
  runTree.attachments = attached;
@@ -360,7 +360,7 @@ export function traceable(wrappedFunc, config) {
360
360
  for (let i = 0; i < processedArgs.length; i++) {
361
361
  processedArgs[i] = convertSerializableArg(processedArgs[i]);
362
362
  }
363
- const [currentRunTree, rawInputs] = (() => {
363
+ const [currentContext, rawInputs] = (() => {
364
364
  const [firstArg, ...restArgs] = processedArgs;
365
365
  // used for handoff between LangChain.JS and traceable functions
366
366
  if (isRunnableConfigLike(firstArg)) {
@@ -387,24 +387,32 @@ export function traceable(wrappedFunc, config) {
387
387
  // Node.JS uses AsyncLocalStorage (ALS) and AsyncResource
388
388
  // to allow storing context
389
389
  const prevRunFromStore = asyncLocalStorage.getStore();
390
- if (isRunTree(prevRunFromStore)) {
391
- return [
392
- getTracingRunTree(prevRunFromStore.createChild(ensuredConfig), processedArgs, config?.getInvocationParams, processInputsFn, extractAttachmentsFn),
393
- processedArgs,
394
- ];
395
- }
396
- const currentRunTree = getTracingRunTree(new RunTree(ensuredConfig), processedArgs, config?.getInvocationParams, processInputsFn, extractAttachmentsFn);
390
+ let lc_contextVars;
397
391
  // If a context var is set by LangChain outside of a traceable,
398
392
  // it will be an object with a single property and we should copy
399
393
  // context vars over into the new run tree.
400
394
  if (prevRunFromStore !== undefined &&
401
395
  _LC_CONTEXT_VARIABLES_KEY in prevRunFromStore) {
402
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
403
- currentRunTree[_LC_CONTEXT_VARIABLES_KEY] =
404
- prevRunFromStore[_LC_CONTEXT_VARIABLES_KEY];
396
+ lc_contextVars = prevRunFromStore[_LC_CONTEXT_VARIABLES_KEY];
397
+ }
398
+ if (isRunTree(prevRunFromStore)) {
399
+ const currentRunTree = getTracingRunTree(prevRunFromStore.createChild(ensuredConfig), processedArgs, config?.getInvocationParams, processInputsFn, extractAttachmentsFn);
400
+ if (lc_contextVars) {
401
+ (currentRunTree ?? {})[_LC_CONTEXT_VARIABLES_KEY] =
402
+ lc_contextVars;
403
+ }
404
+ return [currentRunTree, processedArgs];
405
+ }
406
+ const currentRunTree = getTracingRunTree(new RunTree(ensuredConfig), processedArgs, config?.getInvocationParams, processInputsFn, extractAttachmentsFn);
407
+ if (lc_contextVars) {
408
+ (currentRunTree ?? {})[_LC_CONTEXT_VARIABLES_KEY] =
409
+ lc_contextVars;
405
410
  }
406
411
  return [currentRunTree, processedArgs];
407
412
  })();
413
+ const currentRunTree = isRunTree(currentContext)
414
+ ? currentContext
415
+ : undefined;
408
416
  const otelContextManager = maybeCreateOtelContext(currentRunTree, config?.tracer);
409
417
  const otel_context = getOTELContext();
410
418
  const runWithContext = () => {
@@ -644,10 +652,10 @@ export function traceable(wrappedFunc, config) {
644
652
  };
645
653
  // Wrap with OTEL context if available, similar to Python's implementation
646
654
  if (otelContextManager) {
647
- return asyncLocalStorage.run(currentRunTree, () => otelContextManager(runWithContext));
655
+ return asyncLocalStorage.run(currentContext, () => otelContextManager(runWithContext));
648
656
  }
649
657
  else {
650
- return asyncLocalStorage.run(currentRunTree, runWithContext);
658
+ return asyncLocalStorage.run(currentContext, runWithContext);
651
659
  }
652
660
  };
653
661
  Object.defineProperty(traceableFunc, "langsmith:traceable", {
package/dist/vercel.cjs CHANGED
@@ -513,10 +513,15 @@ class AISDKExporter {
513
513
  time: convertToTimestamp(firstChunkEvent.time),
514
514
  });
515
515
  }
516
+ const runType = span.name === "ai.generateText" || span.name === "ai.streamText"
517
+ ? "chain"
518
+ : "llm";
519
+ const error = span.status?.code === 2 ? span.status.message : undefined;
516
520
  // TODO: add first_token_time
517
521
  return asRunCreate({
518
- run_type: "llm",
522
+ run_type: runType,
519
523
  name: span.attributes["ai.model.provider"],
524
+ error,
520
525
  inputs,
521
526
  outputs,
522
527
  events,
@@ -524,6 +529,15 @@ class AISDKExporter {
524
529
  invocation_params: invocationParams,
525
530
  batch_size: 1,
526
531
  metadata: {
532
+ ...(error
533
+ ? {
534
+ usage_metadata: {
535
+ input_tokens: 0,
536
+ output_tokens: 0,
537
+ total_tokens: 0,
538
+ },
539
+ }
540
+ : undefined),
527
541
  ls_provider: span.attributes["ai.model.provider"]
528
542
  .split(".")
529
543
  .at(0),
@@ -546,9 +560,22 @@ class AISDKExporter {
546
560
  if (typeof output === "object" && output != null) {
547
561
  outputs = output;
548
562
  }
563
+ const error = span.status?.code === 2 ? span.status.message : undefined;
549
564
  return asRunCreate({
550
565
  run_type: "tool",
551
566
  name: span.attributes["ai.toolCall.name"],
567
+ error,
568
+ extra: error
569
+ ? {
570
+ metadata: {
571
+ usage_metadata: {
572
+ input_tokens: 0,
573
+ output_tokens: 0,
574
+ total_tokens: 0,
575
+ },
576
+ },
577
+ }
578
+ : undefined,
552
579
  inputs,
553
580
  outputs,
554
581
  });
@@ -599,15 +626,29 @@ class AISDKExporter {
599
626
  time: convertToTimestamp(firstChunkEvent.time),
600
627
  });
601
628
  }
629
+ const runType = span.name === "ai.generateObject" || span.name === "ai.streamObject"
630
+ ? "chain"
631
+ : "llm";
632
+ const error = span.status?.code === 2 ? span.status.message : undefined;
602
633
  return asRunCreate({
603
- run_type: "llm",
634
+ run_type: runType,
604
635
  name: span.attributes["ai.model.provider"],
636
+ error,
605
637
  inputs,
606
638
  outputs,
607
639
  events,
608
640
  extra: {
609
641
  batch_size: 1,
610
642
  metadata: {
643
+ ...(error
644
+ ? {
645
+ usage_metadata: {
646
+ input_tokens: 0,
647
+ output_tokens: 0,
648
+ total_tokens: 0,
649
+ },
650
+ }
651
+ : undefined),
611
652
  ls_provider: span.attributes["ai.model.provider"]
612
653
  .split(".")
613
654
  .at(0),
package/dist/vercel.js CHANGED
@@ -510,10 +510,15 @@ export class AISDKExporter {
510
510
  time: convertToTimestamp(firstChunkEvent.time),
511
511
  });
512
512
  }
513
+ const runType = span.name === "ai.generateText" || span.name === "ai.streamText"
514
+ ? "chain"
515
+ : "llm";
516
+ const error = span.status?.code === 2 ? span.status.message : undefined;
513
517
  // TODO: add first_token_time
514
518
  return asRunCreate({
515
- run_type: "llm",
519
+ run_type: runType,
516
520
  name: span.attributes["ai.model.provider"],
521
+ error,
517
522
  inputs,
518
523
  outputs,
519
524
  events,
@@ -521,6 +526,15 @@ export class AISDKExporter {
521
526
  invocation_params: invocationParams,
522
527
  batch_size: 1,
523
528
  metadata: {
529
+ ...(error
530
+ ? {
531
+ usage_metadata: {
532
+ input_tokens: 0,
533
+ output_tokens: 0,
534
+ total_tokens: 0,
535
+ },
536
+ }
537
+ : undefined),
524
538
  ls_provider: span.attributes["ai.model.provider"]
525
539
  .split(".")
526
540
  .at(0),
@@ -543,9 +557,22 @@ export class AISDKExporter {
543
557
  if (typeof output === "object" && output != null) {
544
558
  outputs = output;
545
559
  }
560
+ const error = span.status?.code === 2 ? span.status.message : undefined;
546
561
  return asRunCreate({
547
562
  run_type: "tool",
548
563
  name: span.attributes["ai.toolCall.name"],
564
+ error,
565
+ extra: error
566
+ ? {
567
+ metadata: {
568
+ usage_metadata: {
569
+ input_tokens: 0,
570
+ output_tokens: 0,
571
+ total_tokens: 0,
572
+ },
573
+ },
574
+ }
575
+ : undefined,
549
576
  inputs,
550
577
  outputs,
551
578
  });
@@ -596,15 +623,29 @@ export class AISDKExporter {
596
623
  time: convertToTimestamp(firstChunkEvent.time),
597
624
  });
598
625
  }
626
+ const runType = span.name === "ai.generateObject" || span.name === "ai.streamObject"
627
+ ? "chain"
628
+ : "llm";
629
+ const error = span.status?.code === 2 ? span.status.message : undefined;
599
630
  return asRunCreate({
600
- run_type: "llm",
631
+ run_type: runType,
601
632
  name: span.attributes["ai.model.provider"],
633
+ error,
602
634
  inputs,
603
635
  outputs,
604
636
  events,
605
637
  extra: {
606
638
  batch_size: 1,
607
639
  metadata: {
640
+ ...(error
641
+ ? {
642
+ usage_metadata: {
643
+ input_tokens: 0,
644
+ output_tokens: 0,
645
+ total_tokens: 0,
646
+ },
647
+ }
648
+ : undefined),
608
649
  ls_provider: span.attributes["ai.model.provider"]
609
650
  .split(".")
610
651
  .at(0),
@@ -93,6 +93,19 @@ const chatAggregator = (chunks) => {
93
93
  aggregatedOutput.choices = Object.values(choicesByIndex).map((choices) => _combineChatCompletionChoices(choices));
94
94
  return aggregatedOutput;
95
95
  };
96
+ const responsesAggregator = (events) => {
97
+ if (!events || events.length === 0) {
98
+ return {};
99
+ }
100
+ // Find the response.completed event which contains the final response
101
+ for (const event of events) {
102
+ if (event.type === "response.completed" && event.response) {
103
+ return event.response;
104
+ }
105
+ }
106
+ // If no completed event found, return the last event
107
+ return events[events.length - 1] || {};
108
+ };
96
109
  const textAggregator = (allChunks
97
110
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
98
111
  ) => {
@@ -187,74 +200,50 @@ const wrapOpenAI = (openai, options) => {
187
200
  // Some internal OpenAI methods call each other, so we need to preserve original
188
201
  // OpenAI methods.
189
202
  const tracedOpenAIClient = { ...openai };
190
- if (openai.beta &&
191
- openai.beta.chat &&
192
- openai.beta.chat.completions &&
193
- typeof openai.beta.chat.completions.parse === "function") {
194
- tracedOpenAIClient.beta = {
195
- ...openai.beta,
196
- chat: {
197
- ...openai.beta.chat,
198
- completions: {
199
- ...openai.beta.chat.completions,
200
- parse: (0, traceable_js_1.traceable)(openai.beta.chat.completions.parse.bind(openai.beta.chat.completions), {
201
- name: "ChatOpenAI",
202
- run_type: "llm",
203
- aggregator: chatAggregator,
204
- argsConfigPath: [1, "langsmithExtra"],
205
- getInvocationParams: (payload) => {
206
- if (typeof payload !== "object" || payload == null)
207
- return undefined;
208
- // we can safely do so, as the types are not exported in TSC
209
- const params = payload;
210
- const ls_stop = (typeof params.stop === "string"
211
- ? [params.stop]
212
- : params.stop) ?? undefined;
213
- return {
214
- ls_provider: "openai",
215
- ls_model_type: "chat",
216
- ls_model_name: params.model,
217
- ls_max_tokens: params.max_tokens ?? undefined,
218
- ls_temperature: params.temperature ?? undefined,
219
- ls_stop,
220
- };
221
- },
222
- ...options,
223
- }),
224
- },
225
- },
226
- };
203
+ const chatCompletionParseMetadata = {
204
+ name: "ChatOpenAI",
205
+ run_type: "llm",
206
+ aggregator: chatAggregator,
207
+ argsConfigPath: [1, "langsmithExtra"],
208
+ getInvocationParams: (payload) => {
209
+ if (typeof payload !== "object" || payload == null)
210
+ return undefined;
211
+ // we can safely do so, as the types are not exported in TSC
212
+ const params = payload;
213
+ const ls_stop = (typeof params.stop === "string" ? [params.stop] : params.stop) ??
214
+ undefined;
215
+ return {
216
+ ls_provider: "openai",
217
+ ls_model_type: "chat",
218
+ ls_model_name: params.model,
219
+ ls_max_tokens: params.max_completion_tokens ?? params.max_tokens ?? undefined,
220
+ ls_temperature: params.temperature ?? undefined,
221
+ ls_stop,
222
+ };
223
+ },
224
+ processOutputs: processChatCompletion,
225
+ ...options,
226
+ };
227
+ if (openai.beta) {
228
+ tracedOpenAIClient.beta = openai.beta;
229
+ if (openai.beta.chat &&
230
+ openai.beta.chat.completions &&
231
+ typeof openai.beta.chat.completions.parse === "function") {
232
+ tracedOpenAIClient.beta.chat.completions.parse = (0, traceable_js_1.traceable)(openai.beta.chat.completions.parse.bind(openai.beta.chat.completions), chatCompletionParseMetadata);
233
+ }
227
234
  }
228
235
  tracedOpenAIClient.chat = {
229
236
  ...openai.chat,
230
- completions: {
231
- ...openai.chat.completions,
232
- create: (0, traceable_js_1.traceable)(openai.chat.completions.create.bind(openai.chat.completions), {
233
- name: "ChatOpenAI",
234
- run_type: "llm",
235
- aggregator: chatAggregator,
236
- argsConfigPath: [1, "langsmithExtra"],
237
- getInvocationParams: (payload) => {
238
- if (typeof payload !== "object" || payload == null)
239
- return undefined;
240
- // we can safely do so, as the types are not exported in TSC
241
- const params = payload;
242
- const ls_stop = (typeof params.stop === "string" ? [params.stop] : params.stop) ??
243
- undefined;
244
- return {
245
- ls_provider: "openai",
246
- ls_model_type: "chat",
247
- ls_model_name: params.model,
248
- ls_max_tokens: params.max_tokens ?? undefined,
249
- ls_temperature: params.temperature ?? undefined,
250
- ls_stop,
251
- };
252
- },
253
- processOutputs: processChatCompletion,
254
- ...options,
255
- }),
256
- },
237
+ completions: Object.create(Object.getPrototypeOf(openai.chat.completions)),
257
238
  };
239
+ // Copy all own properties and then wrap specific methods
240
+ Object.assign(tracedOpenAIClient.chat.completions, openai.chat.completions);
241
+ // Wrap chat.completions.create
242
+ tracedOpenAIClient.chat.completions.create = (0, traceable_js_1.traceable)(openai.chat.completions.create.bind(openai.chat.completions), chatCompletionParseMetadata);
243
+ // Wrap chat.completions.parse if it exists
244
+ if (typeof openai.chat.completions.parse === "function") {
245
+ tracedOpenAIClient.chat.completions.parse = (0, traceable_js_1.traceable)(openai.chat.completions.parse.bind(openai.chat.completions), chatCompletionParseMetadata);
246
+ }
258
247
  tracedOpenAIClient.completions = {
259
248
  ...openai.completions,
260
249
  create: (0, traceable_js_1.traceable)(openai.completions.create.bind(openai.completions), {
@@ -281,6 +270,38 @@ const wrapOpenAI = (openai, options) => {
281
270
  ...options,
282
271
  }),
283
272
  };
273
+ // Add responses API support if it exists
274
+ if (openai.responses) {
275
+ // Create a new object with the same prototype to preserve all methods
276
+ tracedOpenAIClient.responses = Object.create(Object.getPrototypeOf(openai.responses));
277
+ // Copy all own properties
278
+ if (tracedOpenAIClient.responses) {
279
+ Object.assign(tracedOpenAIClient.responses, openai.responses);
280
+ }
281
+ // Wrap responses.create method
282
+ if (tracedOpenAIClient.responses &&
283
+ typeof tracedOpenAIClient.responses.create === "function") {
284
+ tracedOpenAIClient.responses.create = (0, traceable_js_1.traceable)(openai.responses.create.bind(openai.responses), {
285
+ name: "ChatOpenAI",
286
+ run_type: "llm",
287
+ aggregator: responsesAggregator,
288
+ argsConfigPath: [1, "langsmithExtra"],
289
+ getInvocationParams: (payload) => {
290
+ if (typeof payload !== "object" || payload == null)
291
+ return undefined;
292
+ // Handle responses API parameters
293
+ const params = payload;
294
+ return {
295
+ ls_provider: "openai",
296
+ ls_model_type: "llm",
297
+ ls_model_name: params.model || "unknown",
298
+ };
299
+ },
300
+ processOutputs: processChatCompletion,
301
+ ...options,
302
+ });
303
+ }
304
+ }
284
305
  return tracedOpenAIClient;
285
306
  };
286
307
  exports.wrapOpenAI = wrapOpenAI;
@@ -1,22 +1,21 @@
1
1
  import { OpenAI } from "openai";
2
- import type { APIPromise } from "openai/core";
2
+ import type { APIPromise } from "openai";
3
3
  import type { RunTreeConfig } from "../index.js";
4
4
  type OpenAIType = {
5
- beta?: {
6
- chat?: {
7
- completions?: {
8
- parse?: (...args: any[]) => any;
9
- };
10
- };
11
- };
5
+ beta?: any;
12
6
  chat: {
13
7
  completions: {
14
8
  create: (...args: any[]) => any;
9
+ parse: (...args: any[]) => any;
15
10
  };
16
11
  };
17
12
  completions: {
18
13
  create: (...args: any[]) => any;
19
14
  };
15
+ responses?: {
16
+ create: (...args: any[]) => any;
17
+ retrieve: (...args: any[]) => any;
18
+ };
20
19
  };
21
20
  type ExtraRunTreeConfig = Pick<Partial<RunTreeConfig>, "name" | "metadata" | "tags">;
22
21
  type PatchedOpenAIClient<T extends OpenAIType> = T & {
@@ -1,4 +1,4 @@
1
- import { isTraceableFunction, traceable } from "../traceable.js";
1
+ import { isTraceableFunction, traceable, } from "../traceable.js";
2
2
  function _combineChatCompletionChoices(choices
3
3
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
4
4
  ) {
@@ -90,6 +90,19 @@ const chatAggregator = (chunks) => {
90
90
  aggregatedOutput.choices = Object.values(choicesByIndex).map((choices) => _combineChatCompletionChoices(choices));
91
91
  return aggregatedOutput;
92
92
  };
93
+ const responsesAggregator = (events) => {
94
+ if (!events || events.length === 0) {
95
+ return {};
96
+ }
97
+ // Find the response.completed event which contains the final response
98
+ for (const event of events) {
99
+ if (event.type === "response.completed" && event.response) {
100
+ return event.response;
101
+ }
102
+ }
103
+ // If no completed event found, return the last event
104
+ return events[events.length - 1] || {};
105
+ };
93
106
  const textAggregator = (allChunks
94
107
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
95
108
  ) => {
@@ -184,74 +197,50 @@ export const wrapOpenAI = (openai, options) => {
184
197
  // Some internal OpenAI methods call each other, so we need to preserve original
185
198
  // OpenAI methods.
186
199
  const tracedOpenAIClient = { ...openai };
187
- if (openai.beta &&
188
- openai.beta.chat &&
189
- openai.beta.chat.completions &&
190
- typeof openai.beta.chat.completions.parse === "function") {
191
- tracedOpenAIClient.beta = {
192
- ...openai.beta,
193
- chat: {
194
- ...openai.beta.chat,
195
- completions: {
196
- ...openai.beta.chat.completions,
197
- parse: traceable(openai.beta.chat.completions.parse.bind(openai.beta.chat.completions), {
198
- name: "ChatOpenAI",
199
- run_type: "llm",
200
- aggregator: chatAggregator,
201
- argsConfigPath: [1, "langsmithExtra"],
202
- getInvocationParams: (payload) => {
203
- if (typeof payload !== "object" || payload == null)
204
- return undefined;
205
- // we can safely do so, as the types are not exported in TSC
206
- const params = payload;
207
- const ls_stop = (typeof params.stop === "string"
208
- ? [params.stop]
209
- : params.stop) ?? undefined;
210
- return {
211
- ls_provider: "openai",
212
- ls_model_type: "chat",
213
- ls_model_name: params.model,
214
- ls_max_tokens: params.max_tokens ?? undefined,
215
- ls_temperature: params.temperature ?? undefined,
216
- ls_stop,
217
- };
218
- },
219
- ...options,
220
- }),
221
- },
222
- },
223
- };
200
+ const chatCompletionParseMetadata = {
201
+ name: "ChatOpenAI",
202
+ run_type: "llm",
203
+ aggregator: chatAggregator,
204
+ argsConfigPath: [1, "langsmithExtra"],
205
+ getInvocationParams: (payload) => {
206
+ if (typeof payload !== "object" || payload == null)
207
+ return undefined;
208
+ // we can safely do so, as the types are not exported in TSC
209
+ const params = payload;
210
+ const ls_stop = (typeof params.stop === "string" ? [params.stop] : params.stop) ??
211
+ undefined;
212
+ return {
213
+ ls_provider: "openai",
214
+ ls_model_type: "chat",
215
+ ls_model_name: params.model,
216
+ ls_max_tokens: params.max_completion_tokens ?? params.max_tokens ?? undefined,
217
+ ls_temperature: params.temperature ?? undefined,
218
+ ls_stop,
219
+ };
220
+ },
221
+ processOutputs: processChatCompletion,
222
+ ...options,
223
+ };
224
+ if (openai.beta) {
225
+ tracedOpenAIClient.beta = openai.beta;
226
+ if (openai.beta.chat &&
227
+ openai.beta.chat.completions &&
228
+ typeof openai.beta.chat.completions.parse === "function") {
229
+ tracedOpenAIClient.beta.chat.completions.parse = traceable(openai.beta.chat.completions.parse.bind(openai.beta.chat.completions), chatCompletionParseMetadata);
230
+ }
224
231
  }
225
232
  tracedOpenAIClient.chat = {
226
233
  ...openai.chat,
227
- completions: {
228
- ...openai.chat.completions,
229
- create: traceable(openai.chat.completions.create.bind(openai.chat.completions), {
230
- name: "ChatOpenAI",
231
- run_type: "llm",
232
- aggregator: chatAggregator,
233
- argsConfigPath: [1, "langsmithExtra"],
234
- getInvocationParams: (payload) => {
235
- if (typeof payload !== "object" || payload == null)
236
- return undefined;
237
- // we can safely do so, as the types are not exported in TSC
238
- const params = payload;
239
- const ls_stop = (typeof params.stop === "string" ? [params.stop] : params.stop) ??
240
- undefined;
241
- return {
242
- ls_provider: "openai",
243
- ls_model_type: "chat",
244
- ls_model_name: params.model,
245
- ls_max_tokens: params.max_tokens ?? undefined,
246
- ls_temperature: params.temperature ?? undefined,
247
- ls_stop,
248
- };
249
- },
250
- processOutputs: processChatCompletion,
251
- ...options,
252
- }),
253
- },
234
+ completions: Object.create(Object.getPrototypeOf(openai.chat.completions)),
254
235
  };
236
+ // Copy all own properties and then wrap specific methods
237
+ Object.assign(tracedOpenAIClient.chat.completions, openai.chat.completions);
238
+ // Wrap chat.completions.create
239
+ tracedOpenAIClient.chat.completions.create = traceable(openai.chat.completions.create.bind(openai.chat.completions), chatCompletionParseMetadata);
240
+ // Wrap chat.completions.parse if it exists
241
+ if (typeof openai.chat.completions.parse === "function") {
242
+ tracedOpenAIClient.chat.completions.parse = traceable(openai.chat.completions.parse.bind(openai.chat.completions), chatCompletionParseMetadata);
243
+ }
255
244
  tracedOpenAIClient.completions = {
256
245
  ...openai.completions,
257
246
  create: traceable(openai.completions.create.bind(openai.completions), {
@@ -278,5 +267,37 @@ export const wrapOpenAI = (openai, options) => {
278
267
  ...options,
279
268
  }),
280
269
  };
270
+ // Add responses API support if it exists
271
+ if (openai.responses) {
272
+ // Create a new object with the same prototype to preserve all methods
273
+ tracedOpenAIClient.responses = Object.create(Object.getPrototypeOf(openai.responses));
274
+ // Copy all own properties
275
+ if (tracedOpenAIClient.responses) {
276
+ Object.assign(tracedOpenAIClient.responses, openai.responses);
277
+ }
278
+ // Wrap responses.create method
279
+ if (tracedOpenAIClient.responses &&
280
+ typeof tracedOpenAIClient.responses.create === "function") {
281
+ tracedOpenAIClient.responses.create = traceable(openai.responses.create.bind(openai.responses), {
282
+ name: "ChatOpenAI",
283
+ run_type: "llm",
284
+ aggregator: responsesAggregator,
285
+ argsConfigPath: [1, "langsmithExtra"],
286
+ getInvocationParams: (payload) => {
287
+ if (typeof payload !== "object" || payload == null)
288
+ return undefined;
289
+ // Handle responses API parameters
290
+ const params = payload;
291
+ return {
292
+ ls_provider: "openai",
293
+ ls_model_type: "llm",
294
+ ls_model_name: params.model || "unknown",
295
+ };
296
+ },
297
+ processOutputs: processChatCompletion,
298
+ ...options,
299
+ });
300
+ }
301
+ }
281
302
  return tracedOpenAIClient;
282
303
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langsmith",
3
- "version": "0.3.37",
3
+ "version": "0.3.39",
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": [
@@ -145,9 +145,9 @@
145
145
  "@faker-js/faker": "^8.4.1",
146
146
  "@jest/globals": "^29.5.0",
147
147
  "@jest/reporters": "^29.7.0",
148
- "@langchain/core": "^0.3.14",
149
- "@langchain/langgraph": "^0.2.20",
150
- "@langchain/openai": "^0.3.11",
148
+ "@langchain/core": "^0.3.61",
149
+ "@langchain/langgraph": "^0.3.6",
150
+ "@langchain/openai": "^0.5.16",
151
151
  "@opentelemetry/api": "^1.9.0",
152
152
  "@opentelemetry/auto-instrumentations-node": "^0.58.0",
153
153
  "@opentelemetry/sdk-node": "^0.200.0",
@@ -155,6 +155,7 @@
155
155
  "@opentelemetry/sdk-trace-node": "^2.0.0",
156
156
  "@tsconfig/recommended": "^1.0.2",
157
157
  "@types/jest": "^29.5.1",
158
+ "@types/node-fetch": "^2.6.12",
158
159
  "@typescript-eslint/eslint-plugin": "^5.59.8",
159
160
  "@typescript-eslint/parser": "^5.59.8",
160
161
  "ai": "^4.3.10",
@@ -167,9 +168,9 @@
167
168
  "eslint-plugin-no-instanceof": "^1.0.1",
168
169
  "eslint-plugin-prettier": "^4.2.1",
169
170
  "jest": "^29.5.0",
170
- "langchain": "^0.3.3",
171
+ "langchain": "^0.3.29",
171
172
  "node-fetch": "^2.7.0",
172
- "openai": "^4.67.3",
173
+ "openai": "^5.8.2",
173
174
  "prettier": "^2.8.8",
174
175
  "ts-jest": "^29.1.0",
175
176
  "ts-node": "^10.9.1",
@@ -180,10 +181,10 @@
180
181
  "zod": "^3.23.8"
181
182
  },
182
183
  "peerDependencies": {
183
- "openai": "*",
184
184
  "@opentelemetry/api": "*",
185
185
  "@opentelemetry/exporter-trace-otlp-proto": "*",
186
- "@opentelemetry/sdk-trace-base": "*"
186
+ "@opentelemetry/sdk-trace-base": "*",
187
+ "openai": "*"
187
188
  },
188
189
  "peerDependenciesMeta": {
189
190
  "openai": {