langsmith 0.3.37 → 0.3.38

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.38";
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.38";
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.38";
@@ -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", {
@@ -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.38",
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": {