langsmith 0.1.19 → 0.1.21

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/client.cjs CHANGED
@@ -148,7 +148,7 @@ class Queue {
148
148
  }
149
149
  exports.Queue = Queue;
150
150
  // 20 MB
151
- exports.DEFAULT_BATCH_SIZE_LIMIT_BYTES = 20971520;
151
+ exports.DEFAULT_BATCH_SIZE_LIMIT_BYTES = 20_971_520;
152
152
  class Client {
153
153
  constructor(config = {}) {
154
154
  Object.defineProperty(this, "apiKey", {
@@ -276,7 +276,7 @@ class Client {
276
276
  this.apiUrl = trimQuotes(config.apiUrl ?? defaultConfig.apiUrl) ?? "";
277
277
  this.apiKey = trimQuotes(config.apiKey ?? defaultConfig.apiKey);
278
278
  this.webUrl = trimQuotes(config.webUrl ?? defaultConfig.webUrl);
279
- this.timeout_ms = config.timeout_ms ?? 12000;
279
+ this.timeout_ms = config.timeout_ms ?? 12_000;
280
280
  this.caller = new async_caller_js_1.AsyncCaller(config.callerOptions ?? {});
281
281
  this.batchIngestCaller = new async_caller_js_1.AsyncCaller({
282
282
  ...(config.callerOptions ?? {}),
package/dist/client.d.ts CHANGED
@@ -138,6 +138,7 @@ interface ProjectOptions {
138
138
  projectName?: string;
139
139
  projectId?: string;
140
140
  }
141
+ type RecordStringAny = Record<string, any>;
141
142
  export type FeedbackSourceType = "model" | "api" | "app";
142
143
  export type CreateExampleOptions = {
143
144
  datasetId?: string;
@@ -313,16 +314,16 @@ export declare class Client {
313
314
  createProject({ projectName, description, metadata, upsert, projectExtra, referenceDatasetId, }: {
314
315
  projectName: string;
315
316
  description?: string | null;
316
- metadata?: Record<string, any> | null;
317
+ metadata?: RecordStringAny | null;
317
318
  upsert?: boolean;
318
- projectExtra?: Record<string, any> | null;
319
+ projectExtra?: RecordStringAny | null;
319
320
  referenceDatasetId?: string | null;
320
321
  }): Promise<TracerSession>;
321
322
  updateProject(projectId: string, { name, description, metadata, projectExtra, endTime, }: {
322
323
  name?: string | null;
323
324
  description?: string | null;
324
- metadata?: Record<string, any> | null;
325
- projectExtra?: Record<string, any> | null;
325
+ metadata?: RecordStringAny | null;
326
+ projectExtra?: RecordStringAny | null;
326
327
  endTime?: string | null;
327
328
  }): Promise<TracerSession>;
328
329
  hasProject({ projectId, projectName, }: {
package/dist/client.js CHANGED
@@ -121,7 +121,7 @@ export class Queue {
121
121
  }
122
122
  }
123
123
  // 20 MB
124
- export const DEFAULT_BATCH_SIZE_LIMIT_BYTES = 20971520;
124
+ export const DEFAULT_BATCH_SIZE_LIMIT_BYTES = 20_971_520;
125
125
  export class Client {
126
126
  constructor(config = {}) {
127
127
  Object.defineProperty(this, "apiKey", {
@@ -249,7 +249,7 @@ export class Client {
249
249
  this.apiUrl = trimQuotes(config.apiUrl ?? defaultConfig.apiUrl) ?? "";
250
250
  this.apiKey = trimQuotes(config.apiKey ?? defaultConfig.apiKey);
251
251
  this.webUrl = trimQuotes(config.webUrl ?? defaultConfig.webUrl);
252
- this.timeout_ms = config.timeout_ms ?? 12000;
252
+ this.timeout_ms = config.timeout_ms ?? 12_000;
253
253
  this.caller = new AsyncCaller(config.callerOptions ?? {});
254
254
  this.batchIngestCaller = new AsyncCaller({
255
255
  ...(config.callerOptions ?? {}),
package/dist/index.cjs CHANGED
@@ -6,4 +6,4 @@ Object.defineProperty(exports, "Client", { enumerable: true, get: function () {
6
6
  var run_trees_js_1 = require("./run_trees.cjs");
7
7
  Object.defineProperty(exports, "RunTree", { enumerable: true, get: function () { return run_trees_js_1.RunTree; } });
8
8
  // Update using yarn bump-version
9
- exports.__version__ = "0.1.19";
9
+ exports.__version__ = "0.1.21";
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { Client } from "./client.js";
2
2
  export type { Dataset, Example, TracerSession, Run, Feedback, } from "./schemas.js";
3
3
  export { RunTree, type RunTreeConfig } from "./run_trees.js";
4
- export declare const __version__ = "0.1.19";
4
+ export declare const __version__ = "0.1.21";
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export { Client } from "./client.js";
2
2
  export { RunTree } from "./run_trees.js";
3
3
  // Update using yarn bump-version
4
- export const __version__ = "0.1.19";
4
+ export const __version__ = "0.1.21";
@@ -232,7 +232,7 @@ class RunTree {
232
232
  extra: {},
233
233
  };
234
234
  }
235
- async createChild(config) {
235
+ createChild(config) {
236
236
  const child = new RunTree({
237
237
  ...config,
238
238
  parent_run: this,
@@ -243,9 +243,9 @@ class RunTree {
243
243
  return child;
244
244
  }
245
245
  async end(outputs, error, endTime = Date.now()) {
246
- this.outputs = outputs;
247
- this.error = error;
248
- this.end_time = endTime;
246
+ this.outputs = this.outputs ?? outputs;
247
+ this.error = this.error ?? error;
248
+ this.end_time = this.end_time ?? endTime;
249
249
  }
250
250
  async _convertToCreate(run, excludeChildRuns = true) {
251
251
  const runExtra = run.extra ?? {};
@@ -65,7 +65,7 @@ export declare class RunTree implements BaseRun {
65
65
  metadata?: KVMap;
66
66
  }): RunTree;
67
67
  private static getDefaultConfig;
68
- createChild(config: RunTreeConfig): Promise<RunTree>;
68
+ createChild(config: RunTreeConfig): RunTree;
69
69
  end(outputs?: KVMap, error?: string, endTime?: number): Promise<void>;
70
70
  private _convertToCreate;
71
71
  postRun(excludeChildRuns?: boolean): Promise<void>;
package/dist/run_trees.js CHANGED
@@ -205,7 +205,7 @@ export class RunTree {
205
205
  extra: {},
206
206
  };
207
207
  }
208
- async createChild(config) {
208
+ createChild(config) {
209
209
  const child = new RunTree({
210
210
  ...config,
211
211
  parent_run: this,
@@ -216,9 +216,9 @@ export class RunTree {
216
216
  return child;
217
217
  }
218
218
  async end(outputs, error, endTime = Date.now()) {
219
- this.outputs = outputs;
220
- this.error = error;
221
- this.end_time = endTime;
219
+ this.outputs = this.outputs ?? outputs;
220
+ this.error = this.error ?? error;
221
+ this.end_time = this.end_time ?? endTime;
222
222
  }
223
223
  async _convertToCreate(run, excludeChildRuns = true) {
224
224
  const runExtra = run.extra ?? {};
@@ -3,11 +3,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isTraceableFunction = exports.getCurrentRunTree = exports.traceable = void 0;
4
4
  const async_hooks_1 = require("async_hooks");
5
5
  const run_trees_js_1 = require("./run_trees.cjs");
6
+ const env_js_1 = require("./utils/env.cjs");
7
+ function isPromiseMethod(x) {
8
+ if (x === "then" || x === "catch" || x === "finally") {
9
+ return true;
10
+ }
11
+ return false;
12
+ }
6
13
  const asyncLocalStorage = new async_hooks_1.AsyncLocalStorage();
7
14
  const isAsyncIterable = (x) => x != null &&
8
15
  typeof x === "object" &&
9
16
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
17
  typeof x[Symbol.asyncIterator] === "function";
18
+ const getTracingRunTree = (runTree) => {
19
+ const tracingEnabled = (0, env_js_1.getEnvironmentVariable)("LANGSMITH_TRACING_V2") === "true" ||
20
+ (0, env_js_1.getEnvironmentVariable)("LANGCHAIN_TRACING_V2") === "true";
21
+ if (!tracingEnabled) {
22
+ return undefined;
23
+ }
24
+ return runTree;
25
+ };
11
26
  /**
12
27
  * Higher-order function that takes function as input and returns a
13
28
  * "TraceableFunction" - a wrapped version of the input that
@@ -24,14 +39,55 @@ const isAsyncIterable = (x) => x != null &&
24
39
  */
25
40
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
41
  function traceable(wrappedFunc, config) {
27
- const { aggregator, ...runTreeConfig } = config ?? {};
28
- const traceableFunc = async (...args) => {
42
+ const { aggregator, argsConfigPath, ...runTreeConfig } = config ?? {};
43
+ const traceableFunc = (...args) => {
29
44
  let currentRunTree;
30
45
  let rawInputs;
31
- const ensuredConfig = {
32
- name: wrappedFunc.name || "<lambda>",
33
- ...runTreeConfig,
34
- };
46
+ let ensuredConfig;
47
+ try {
48
+ let runtimeConfig;
49
+ if (argsConfigPath) {
50
+ const [index, path] = argsConfigPath;
51
+ if (index === args.length - 1 && !path) {
52
+ runtimeConfig = args.pop();
53
+ }
54
+ else if (index <= args.length &&
55
+ typeof args[index] === "object" &&
56
+ args[index] !== null) {
57
+ if (path) {
58
+ const { [path]: extracted, ...rest } = args[index];
59
+ runtimeConfig = extracted;
60
+ args[index] = rest;
61
+ }
62
+ else {
63
+ runtimeConfig = args[index];
64
+ args.splice(index, 1);
65
+ }
66
+ }
67
+ }
68
+ ensuredConfig = {
69
+ name: wrappedFunc.name || "<lambda>",
70
+ ...runTreeConfig,
71
+ ...runtimeConfig,
72
+ tags: [
73
+ ...new Set([
74
+ ...(runTreeConfig?.tags ?? []),
75
+ ...(runtimeConfig?.tags ?? []),
76
+ ]),
77
+ ],
78
+ metadata: {
79
+ ...runTreeConfig?.metadata,
80
+ ...runtimeConfig?.metadata,
81
+ },
82
+ };
83
+ }
84
+ catch (err) {
85
+ console.warn(`Failed to extract runtime config from args for ${runTreeConfig?.name ?? wrappedFunc.name}`, err);
86
+ ensuredConfig = {
87
+ name: wrappedFunc.name || "<lambda>",
88
+ ...runTreeConfig,
89
+ };
90
+ }
35
91
  const previousRunTree = asyncLocalStorage.getStore();
36
92
  if ((0, run_trees_js_1.isRunTree)(args[0])) {
37
93
  currentRunTree = args[0];
@@ -42,13 +98,14 @@ function traceable(wrappedFunc, config) {
42
98
  rawInputs = args.slice(1);
43
99
  }
44
100
  else if (previousRunTree !== undefined) {
45
- currentRunTree = await previousRunTree.createChild(ensuredConfig);
101
+ currentRunTree = previousRunTree.createChild(ensuredConfig);
46
102
  rawInputs = args;
47
103
  }
48
104
  else {
49
105
  currentRunTree = new run_trees_js_1.RunTree(ensuredConfig);
50
106
  rawInputs = args;
51
107
  }
108
+ currentRunTree = getTracingRunTree(currentRunTree);
52
109
  let inputs;
53
110
  const firstInput = rawInputs[0];
54
111
  if (firstInput == null) {
@@ -63,71 +120,148 @@ function traceable(wrappedFunc, config) {
63
120
  else {
64
121
  inputs = { input: firstInput };
65
122
  }
66
- currentRunTree.inputs = inputs;
67
- const initialOutputs = currentRunTree.outputs;
68
- const initialError = currentRunTree.error;
69
- await currentRunTree.postRun();
70
- return new Promise((resolve, reject) => {
71
- void asyncLocalStorage.run(currentRunTree, async () => {
72
- try {
73
- const rawOutput = await wrappedFunc(...rawInputs);
123
+ if (currentRunTree) {
124
+ currentRunTree.inputs = inputs;
125
+ }
126
+ return asyncLocalStorage.run(currentRunTree, () => {
127
+ const postRunPromise = currentRunTree?.postRun();
128
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
129
+ let returnValue;
130
+ try {
131
+ returnValue = wrappedFunc(...rawInputs);
132
+ }
133
+ catch (err) {
134
+ returnValue = Promise.reject(err);
135
+ }
136
+ if (isAsyncIterable(returnValue)) {
137
+ // eslint-disable-next-line no-inner-declarations
138
+ async function* wrapOutputForTracing() {
139
+ let finished = false;
140
+ const chunks = [];
141
+ try {
142
+ for await (const chunk of returnValue) {
143
+ chunks.push(chunk);
144
+ yield chunk;
145
+ }
146
+ finished = true;
147
+ }
148
+ catch (e) {
149
+ await currentRunTree?.end(undefined, String(e));
150
+ throw e;
151
+ }
152
+ finally {
153
+ if (!finished) {
154
+ await currentRunTree?.end(undefined, "Cancelled");
155
+ }
156
+ let finalOutputs;
157
+ if (aggregator !== undefined) {
158
+ try {
159
+ finalOutputs = await aggregator(chunks);
160
+ }
161
+ catch (e) {
162
+ console.error(`[ERROR]: LangSmith aggregation failed: `, e);
163
+ finalOutputs = chunks;
164
+ }
165
+ }
166
+ else {
167
+ finalOutputs = chunks;
168
+ }
169
+ if (typeof finalOutputs === "object" &&
170
+ !Array.isArray(finalOutputs)) {
171
+ await currentRunTree?.end(finalOutputs);
172
+ }
173
+ else {
174
+ await currentRunTree?.end({ outputs: finalOutputs });
175
+ }
176
+ await postRunPromise;
177
+ await currentRunTree?.patchRun();
178
+ }
179
+ }
180
+ return wrapOutputForTracing();
181
+ }
182
+ const tracedPromise = new Promise((resolve, reject) => {
183
+ Promise.resolve(returnValue)
184
+ .then(
185
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
186
+ async (rawOutput) => {
74
187
  if (isAsyncIterable(rawOutput)) {
75
188
  // eslint-disable-next-line no-inner-declarations
76
189
  async function* wrapOutputForTracing() {
190
+ let finished = false;
77
191
  const chunks = [];
78
- // TypeScript thinks this is unsafe
79
- for await (const chunk of rawOutput) {
80
- chunks.push(chunk);
81
- yield chunk;
192
+ try {
193
+ // TypeScript thinks this is unsafe
194
+ for await (const chunk of rawOutput) {
195
+ chunks.push(chunk);
196
+ yield chunk;
197
+ }
198
+ finished = true;
199
+ }
200
+ catch (e) {
201
+ await currentRunTree?.end(undefined, String(e));
202
+ throw e;
82
203
  }
83
- let finalOutputs;
84
- if (aggregator !== undefined) {
85
- try {
86
- finalOutputs = await aggregator(chunks);
204
+ finally {
205
+ if (!finished) {
206
+ await currentRunTree?.end(undefined, "Cancelled");
207
+ }
208
+ let finalOutputs;
209
+ if (aggregator !== undefined) {
210
+ try {
211
+ finalOutputs = await aggregator(chunks);
212
+ }
213
+ catch (e) {
214
+ console.error(`[ERROR]: LangSmith aggregation failed: `, e);
215
+ finalOutputs = chunks;
216
+ }
87
217
  }
88
- catch (e) {
89
- console.error(`[ERROR]: LangSmith aggregation failed: `, e);
218
+ else {
90
219
  finalOutputs = chunks;
91
220
  }
221
+ if (typeof finalOutputs === "object" &&
222
+ !Array.isArray(finalOutputs)) {
223
+ await currentRunTree?.end(finalOutputs);
224
+ }
225
+ else {
226
+ await currentRunTree?.end({ outputs: finalOutputs });
227
+ }
228
+ await postRunPromise;
229
+ await currentRunTree?.patchRun();
92
230
  }
93
- else {
94
- finalOutputs = chunks;
95
- }
96
- if (typeof finalOutputs === "object" &&
97
- !Array.isArray(finalOutputs)) {
98
- await currentRunTree.end(finalOutputs);
99
- }
100
- else {
101
- await currentRunTree.end({ outputs: finalOutputs });
102
- }
103
- await currentRunTree.patchRun();
104
231
  }
105
232
  return resolve(wrapOutputForTracing());
106
233
  }
107
234
  else {
108
- const outputs = isKVMap(rawOutput)
109
- ? rawOutput
110
- : { outputs: rawOutput };
111
- if (initialOutputs === currentRunTree.outputs) {
112
- await currentRunTree.end(outputs);
235
+ try {
236
+ await currentRunTree?.end(isKVMap(rawOutput) ? rawOutput : { outputs: rawOutput });
237
+ await postRunPromise;
238
+ await currentRunTree?.patchRun();
113
239
  }
114
- else {
115
- currentRunTree.end_time = Date.now();
240
+ finally {
241
+ // eslint-disable-next-line no-unsafe-finally
242
+ return rawOutput;
116
243
  }
117
- await currentRunTree.patchRun();
118
- return resolve(rawOutput);
119
244
  }
120
- }
121
- catch (error) {
122
- if (initialError === currentRunTree.error) {
123
- await currentRunTree.end(initialOutputs, String(error));
124
- }
125
- else {
126
- currentRunTree.end_time = Date.now();
245
+ },
246
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
247
+ async (error) => {
248
+ await currentRunTree?.end(undefined, String(error));
249
+ await postRunPromise;
250
+ await currentRunTree?.patchRun();
251
+ throw error;
252
+ })
253
+ .then(resolve, reject);
254
+ });
255
+ if (typeof returnValue !== "object" || returnValue === null) {
256
+ return tracedPromise;
257
+ }
258
+ return new Proxy(returnValue, {
259
+ get(target, prop, receiver) {
260
+ if (isPromiseMethod(prop)) {
261
+ return tracedPromise[prop].bind(tracedPromise);
127
262
  }
128
- await currentRunTree.patchRun();
129
- reject(error);
130
- }
263
+ return Reflect.get(target, prop, receiver);
264
+ },
131
265
  });
132
266
  });
133
267
  };
@@ -1,12 +1,13 @@
1
1
  import { RunTree, RunTreeConfig, RunnableConfigLike } from "./run_trees.js";
2
2
  export type RunTreeLike = RunTree;
3
+ type SmartPromise<T> = T extends AsyncGenerator ? T : T extends Promise<unknown> ? T : Promise<T>;
3
4
  type WrapArgReturnPair<Pair> = Pair extends [
4
5
  infer Args extends any[],
5
6
  infer Return
6
7
  ] ? {
7
- (...args: Args): Promise<Return>;
8
- (...args: [runTree: RunTreeLike, ...rest: Args]): Promise<Return>;
9
- (...args: [config: RunnableConfigLike, ...rest: Args]): Promise<Return>;
8
+ (...args: Args): SmartPromise<Return>;
9
+ (...args: [runTree: RunTreeLike, ...rest: Args]): SmartPromise<Return>;
10
+ (...args: [config: RunnableConfigLike, ...rest: Args]): SmartPromise<Return>;
10
11
  } : never;
11
12
  type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I : never;
12
13
  export type TraceableFunction<Func extends (...args: any[]) => any> = Func extends {
@@ -46,6 +47,7 @@ export type TraceableFunction<Func extends (...args: any[]) => any> = Func exten
46
47
  */
47
48
  export declare function traceable<Func extends (...args: any[]) => any>(wrappedFunc: Func, config?: Partial<RunTreeConfig> & {
48
49
  aggregator?: (args: any[]) => any;
50
+ argsConfigPath?: [number] | [number, string];
49
51
  }): TraceableFunction<Func>;
50
52
  /**
51
53
  * Return the current run tree from within a traceable-wrapped function.
package/dist/traceable.js CHANGED
@@ -1,10 +1,25 @@
1
1
  import { AsyncLocalStorage } from "async_hooks";
2
2
  import { RunTree, isRunTree, isRunnableConfigLike, } from "./run_trees.js";
3
+ import { getEnvironmentVariable } from "./utils/env.js";
4
+ function isPromiseMethod(x) {
5
+ if (x === "then" || x === "catch" || x === "finally") {
6
+ return true;
7
+ }
8
+ return false;
9
+ }
3
10
  const asyncLocalStorage = new AsyncLocalStorage();
4
11
  const isAsyncIterable = (x) => x != null &&
5
12
  typeof x === "object" &&
6
13
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
14
  typeof x[Symbol.asyncIterator] === "function";
15
+ const getTracingRunTree = (runTree) => {
16
+ const tracingEnabled = getEnvironmentVariable("LANGSMITH_TRACING_V2") === "true" ||
17
+ getEnvironmentVariable("LANGCHAIN_TRACING_V2") === "true";
18
+ if (!tracingEnabled) {
19
+ return undefined;
20
+ }
21
+ return runTree;
22
+ };
8
23
  /**
9
24
  * Higher-order function that takes function as input and returns a
10
25
  * "TraceableFunction" - a wrapped version of the input that
@@ -21,14 +36,55 @@ const isAsyncIterable = (x) => x != null &&
21
36
  */
22
37
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
38
  export function traceable(wrappedFunc, config) {
24
- const { aggregator, ...runTreeConfig } = config ?? {};
25
- const traceableFunc = async (...args) => {
39
+ const { aggregator, argsConfigPath, ...runTreeConfig } = config ?? {};
40
+ const traceableFunc = (...args) => {
26
41
  let currentRunTree;
27
42
  let rawInputs;
28
- const ensuredConfig = {
29
- name: wrappedFunc.name || "<lambda>",
30
- ...runTreeConfig,
31
- };
43
+ let ensuredConfig;
44
+ try {
45
+ let runtimeConfig;
46
+ if (argsConfigPath) {
47
+ const [index, path] = argsConfigPath;
48
+ if (index === args.length - 1 && !path) {
49
+ runtimeConfig = args.pop();
50
+ }
51
+ else if (index <= args.length &&
52
+ typeof args[index] === "object" &&
53
+ args[index] !== null) {
54
+ if (path) {
55
+ const { [path]: extracted, ...rest } = args[index];
56
+ runtimeConfig = extracted;
57
+ args[index] = rest;
58
+ }
59
+ else {
60
+ runtimeConfig = args[index];
61
+ args.splice(index, 1);
62
+ }
63
+ }
64
+ }
65
+ ensuredConfig = {
66
+ name: wrappedFunc.name || "<lambda>",
67
+ ...runTreeConfig,
68
+ ...runtimeConfig,
69
+ tags: [
70
+ ...new Set([
71
+ ...(runTreeConfig?.tags ?? []),
72
+ ...(runtimeConfig?.tags ?? []),
73
+ ]),
74
+ ],
75
+ metadata: {
76
+ ...runTreeConfig?.metadata,
77
+ ...runtimeConfig?.metadata,
78
+ },
79
+ };
80
+ }
81
+ catch (err) {
82
+ console.warn(`Failed to extract runtime config from args for ${runTreeConfig?.name ?? wrappedFunc.name}`, err);
83
+ ensuredConfig = {
84
+ name: wrappedFunc.name || "<lambda>",
85
+ ...runTreeConfig,
86
+ };
87
+ }
32
88
  const previousRunTree = asyncLocalStorage.getStore();
33
89
  if (isRunTree(args[0])) {
34
90
  currentRunTree = args[0];
@@ -39,13 +95,14 @@ export function traceable(wrappedFunc, config) {
39
95
  rawInputs = args.slice(1);
40
96
  }
41
97
  else if (previousRunTree !== undefined) {
42
- currentRunTree = await previousRunTree.createChild(ensuredConfig);
98
+ currentRunTree = previousRunTree.createChild(ensuredConfig);
43
99
  rawInputs = args;
44
100
  }
45
101
  else {
46
102
  currentRunTree = new RunTree(ensuredConfig);
47
103
  rawInputs = args;
48
104
  }
105
+ currentRunTree = getTracingRunTree(currentRunTree);
49
106
  let inputs;
50
107
  const firstInput = rawInputs[0];
51
108
  if (firstInput == null) {
@@ -60,71 +117,148 @@ export function traceable(wrappedFunc, config) {
60
117
  else {
61
118
  inputs = { input: firstInput };
62
119
  }
63
- currentRunTree.inputs = inputs;
64
- const initialOutputs = currentRunTree.outputs;
65
- const initialError = currentRunTree.error;
66
- await currentRunTree.postRun();
67
- return new Promise((resolve, reject) => {
68
- void asyncLocalStorage.run(currentRunTree, async () => {
69
- try {
70
- const rawOutput = await wrappedFunc(...rawInputs);
120
+ if (currentRunTree) {
121
+ currentRunTree.inputs = inputs;
122
+ }
123
+ return asyncLocalStorage.run(currentRunTree, () => {
124
+ const postRunPromise = currentRunTree?.postRun();
125
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
126
+ let returnValue;
127
+ try {
128
+ returnValue = wrappedFunc(...rawInputs);
129
+ }
130
+ catch (err) {
131
+ returnValue = Promise.reject(err);
132
+ }
133
+ if (isAsyncIterable(returnValue)) {
134
+ // eslint-disable-next-line no-inner-declarations
135
+ async function* wrapOutputForTracing() {
136
+ let finished = false;
137
+ const chunks = [];
138
+ try {
139
+ for await (const chunk of returnValue) {
140
+ chunks.push(chunk);
141
+ yield chunk;
142
+ }
143
+ finished = true;
144
+ }
145
+ catch (e) {
146
+ await currentRunTree?.end(undefined, String(e));
147
+ throw e;
148
+ }
149
+ finally {
150
+ if (!finished) {
151
+ await currentRunTree?.end(undefined, "Cancelled");
152
+ }
153
+ let finalOutputs;
154
+ if (aggregator !== undefined) {
155
+ try {
156
+ finalOutputs = await aggregator(chunks);
157
+ }
158
+ catch (e) {
159
+ console.error(`[ERROR]: LangSmith aggregation failed: `, e);
160
+ finalOutputs = chunks;
161
+ }
162
+ }
163
+ else {
164
+ finalOutputs = chunks;
165
+ }
166
+ if (typeof finalOutputs === "object" &&
167
+ !Array.isArray(finalOutputs)) {
168
+ await currentRunTree?.end(finalOutputs);
169
+ }
170
+ else {
171
+ await currentRunTree?.end({ outputs: finalOutputs });
172
+ }
173
+ await postRunPromise;
174
+ await currentRunTree?.patchRun();
175
+ }
176
+ }
177
+ return wrapOutputForTracing();
178
+ }
179
+ const tracedPromise = new Promise((resolve, reject) => {
180
+ Promise.resolve(returnValue)
181
+ .then(
182
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
183
+ async (rawOutput) => {
71
184
  if (isAsyncIterable(rawOutput)) {
72
185
  // eslint-disable-next-line no-inner-declarations
73
186
  async function* wrapOutputForTracing() {
187
+ let finished = false;
74
188
  const chunks = [];
75
- // TypeScript thinks this is unsafe
76
- for await (const chunk of rawOutput) {
77
- chunks.push(chunk);
78
- yield chunk;
189
+ try {
190
+ // TypeScript thinks this is unsafe
191
+ for await (const chunk of rawOutput) {
192
+ chunks.push(chunk);
193
+ yield chunk;
194
+ }
195
+ finished = true;
196
+ }
197
+ catch (e) {
198
+ await currentRunTree?.end(undefined, String(e));
199
+ throw e;
79
200
  }
80
- let finalOutputs;
81
- if (aggregator !== undefined) {
82
- try {
83
- finalOutputs = await aggregator(chunks);
201
+ finally {
202
+ if (!finished) {
203
+ await currentRunTree?.end(undefined, "Cancelled");
204
+ }
205
+ let finalOutputs;
206
+ if (aggregator !== undefined) {
207
+ try {
208
+ finalOutputs = await aggregator(chunks);
209
+ }
210
+ catch (e) {
211
+ console.error(`[ERROR]: LangSmith aggregation failed: `, e);
212
+ finalOutputs = chunks;
213
+ }
84
214
  }
85
- catch (e) {
86
- console.error(`[ERROR]: LangSmith aggregation failed: `, e);
215
+ else {
87
216
  finalOutputs = chunks;
88
217
  }
218
+ if (typeof finalOutputs === "object" &&
219
+ !Array.isArray(finalOutputs)) {
220
+ await currentRunTree?.end(finalOutputs);
221
+ }
222
+ else {
223
+ await currentRunTree?.end({ outputs: finalOutputs });
224
+ }
225
+ await postRunPromise;
226
+ await currentRunTree?.patchRun();
89
227
  }
90
- else {
91
- finalOutputs = chunks;
92
- }
93
- if (typeof finalOutputs === "object" &&
94
- !Array.isArray(finalOutputs)) {
95
- await currentRunTree.end(finalOutputs);
96
- }
97
- else {
98
- await currentRunTree.end({ outputs: finalOutputs });
99
- }
100
- await currentRunTree.patchRun();
101
228
  }
102
229
  return resolve(wrapOutputForTracing());
103
230
  }
104
231
  else {
105
- const outputs = isKVMap(rawOutput)
106
- ? rawOutput
107
- : { outputs: rawOutput };
108
- if (initialOutputs === currentRunTree.outputs) {
109
- await currentRunTree.end(outputs);
232
+ try {
233
+ await currentRunTree?.end(isKVMap(rawOutput) ? rawOutput : { outputs: rawOutput });
234
+ await postRunPromise;
235
+ await currentRunTree?.patchRun();
110
236
  }
111
- else {
112
- currentRunTree.end_time = Date.now();
237
+ finally {
238
+ // eslint-disable-next-line no-unsafe-finally
239
+ return rawOutput;
113
240
  }
114
- await currentRunTree.patchRun();
115
- return resolve(rawOutput);
116
241
  }
117
- }
118
- catch (error) {
119
- if (initialError === currentRunTree.error) {
120
- await currentRunTree.end(initialOutputs, String(error));
121
- }
122
- else {
123
- currentRunTree.end_time = Date.now();
242
+ },
243
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
244
+ async (error) => {
245
+ await currentRunTree?.end(undefined, String(error));
246
+ await postRunPromise;
247
+ await currentRunTree?.patchRun();
248
+ throw error;
249
+ })
250
+ .then(resolve, reject);
251
+ });
252
+ if (typeof returnValue !== "object" || returnValue === null) {
253
+ return tracedPromise;
254
+ }
255
+ return new Proxy(returnValue, {
256
+ get(target, prop, receiver) {
257
+ if (isPromiseMethod(prop)) {
258
+ return tracedPromise[prop].bind(tracedPromise);
124
259
  }
125
- await currentRunTree.patchRun();
126
- reject(error);
127
- }
260
+ return Reflect.get(target, prop, receiver);
261
+ },
128
262
  });
129
263
  });
130
264
  };
@@ -7,13 +7,13 @@ exports.AsyncCaller = void 0;
7
7
  const p_retry_1 = __importDefault(require("p-retry"));
8
8
  const p_queue_1 = __importDefault(require("p-queue"));
9
9
  const STATUS_NO_RETRY = [
10
- 400,
11
- 401,
12
- 403,
13
- 404,
14
- 405,
15
- 406,
16
- 407,
10
+ 400, // Bad Request
11
+ 401, // Unauthorized
12
+ 403, // Forbidden
13
+ 404, // Not Found
14
+ 405, // Method Not Allowed
15
+ 406, // Not Acceptable
16
+ 407, // Proxy Authentication Required
17
17
  408, // Request Timeout
18
18
  ];
19
19
  const STATUS_IGNORE = [
@@ -60,8 +60,16 @@ class AsyncCaller {
60
60
  });
61
61
  this.maxConcurrency = params.maxConcurrency ?? Infinity;
62
62
  this.maxRetries = params.maxRetries ?? 6;
63
- const PQueue = "default" in p_queue_1.default ? p_queue_1.default.default : p_queue_1.default;
64
- this.queue = new PQueue({ concurrency: this.maxConcurrency });
63
+ if ("default" in p_queue_1.default) {
64
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
65
+ this.queue = new p_queue_1.default.default({
66
+ concurrency: this.maxConcurrency,
67
+ });
68
+ }
69
+ else {
70
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
71
+ this.queue = new p_queue_1.default({ concurrency: this.maxConcurrency });
72
+ }
65
73
  this.onFailedResponseHook = params?.onFailedResponseHook;
66
74
  }
67
75
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -1,13 +1,13 @@
1
1
  import pRetry from "p-retry";
2
2
  import PQueueMod from "p-queue";
3
3
  const STATUS_NO_RETRY = [
4
- 400,
5
- 401,
6
- 403,
7
- 404,
8
- 405,
9
- 406,
10
- 407,
4
+ 400, // Bad Request
5
+ 401, // Unauthorized
6
+ 403, // Forbidden
7
+ 404, // Not Found
8
+ 405, // Method Not Allowed
9
+ 406, // Not Acceptable
10
+ 407, // Proxy Authentication Required
11
11
  408, // Request Timeout
12
12
  ];
13
13
  const STATUS_IGNORE = [
@@ -54,8 +54,16 @@ export class AsyncCaller {
54
54
  });
55
55
  this.maxConcurrency = params.maxConcurrency ?? Infinity;
56
56
  this.maxRetries = params.maxRetries ?? 6;
57
- const PQueue = "default" in PQueueMod ? PQueueMod.default : PQueueMod;
58
- this.queue = new PQueue({ concurrency: this.maxConcurrency });
57
+ if ("default" in PQueueMod) {
58
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
59
+ this.queue = new PQueueMod.default({
60
+ concurrency: this.maxConcurrency,
61
+ });
62
+ }
63
+ else {
64
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
65
+ this.queue = new PQueueMod({ concurrency: this.maxConcurrency });
66
+ }
59
67
  this.onFailedResponseHook = params?.onFailedResponseHook;
60
68
  }
61
69
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.convertLangChainMessageToExample = exports.isLangChainMessage = void 0;
4
- function isLangChainMessage(message) {
4
+ function isLangChainMessage(
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ message) {
5
7
  return typeof message?._getType === "function";
6
8
  }
7
9
  exports.isLangChainMessage = isLangChainMessage;
@@ -1,4 +1,6 @@
1
- export function isLangChainMessage(message) {
1
+ export function isLangChainMessage(
2
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3
+ message) {
2
4
  return typeof message?._getType === "function";
3
5
  }
4
6
  export function convertLangChainMessageToExample(message) {
@@ -2,8 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.wrapSDK = exports.wrapOpenAI = void 0;
4
4
  const traceable_js_1 = require("../traceable.cjs");
5
- function _combineChatCompletionChoices(choices) {
5
+ function _combineChatCompletionChoices(choices
6
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
+ ) {
6
8
  const reversedChoices = choices.slice().reverse();
9
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
10
  const message = {
8
11
  role: "assistant",
9
12
  content: "",
@@ -73,26 +76,43 @@ function _combineChatCompletionChoices(choices) {
73
76
  message: message,
74
77
  };
75
78
  }
76
- async function extractLangSmithExtraAndCall(openAIMethod, args, defaultRunConfig) {
77
- if (args[1]?.langsmithExtra !== undefined) {
78
- const { langsmithExtra, ...openAIOptions } = args[1];
79
- const wrappedMethod = (0, traceable_js_1.traceable)(openAIMethod, {
80
- ...defaultRunConfig,
81
- ...langsmithExtra,
82
- });
83
- const finalArgs = [args[0]];
84
- if (args.length > 2) {
85
- finalArgs.push(openAIOptions);
86
- finalArgs.push(args.slice(2));
79
+ const chatAggregator = (chunks) => {
80
+ if (!chunks || chunks.length === 0) {
81
+ return { choices: [{ message: { role: "assistant", content: "" } }] };
82
+ }
83
+ const choicesByIndex = {};
84
+ for (const chunk of chunks) {
85
+ for (const choice of chunk.choices) {
86
+ if (choicesByIndex[choice.index] === undefined) {
87
+ choicesByIndex[choice.index] = [];
88
+ }
89
+ choicesByIndex[choice.index].push(choice);
87
90
  }
88
- else if (Object.keys(openAIOptions).length !== 0) {
89
- finalArgs.push(openAIOptions);
91
+ }
92
+ const aggregatedOutput = chunks[chunks.length - 1];
93
+ aggregatedOutput.choices = Object.values(choicesByIndex).map((choices) => _combineChatCompletionChoices(choices));
94
+ return aggregatedOutput;
95
+ };
96
+ const textAggregator = (allChunks
97
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
98
+ ) => {
99
+ if (allChunks.length === 0) {
100
+ return { choices: [{ text: "" }] };
101
+ }
102
+ const allContent = [];
103
+ for (const chunk of allChunks) {
104
+ const content = chunk.choices[0].text;
105
+ if (content != null) {
106
+ allContent.push(content);
90
107
  }
91
- return wrappedMethod(...finalArgs);
92
108
  }
93
- const wrappedMethod = (0, traceable_js_1.traceable)(openAIMethod, defaultRunConfig);
94
- return wrappedMethod(...args);
95
- }
109
+ const content = allContent.join("");
110
+ const aggregatedOutput = allChunks[allChunks.length - 1];
111
+ aggregatedOutput.choices = [
112
+ { ...aggregatedOutput.choices[0], text: content },
113
+ ];
114
+ return aggregatedOutput;
115
+ };
96
116
  /**
97
117
  * Wraps an OpenAI client's completion methods, enabling automatic LangSmith
98
118
  * tracing. Method signatures are unchanged, with the exception that you can pass
@@ -118,61 +138,20 @@ async function extractLangSmithExtraAndCall(openAIMethod, args, defaultRunConfig
118
138
  * ```
119
139
  */
120
140
  const wrapOpenAI = (openai, options) => {
121
- const originalChatCompletionsFn = openai.chat.completions.create.bind(openai.chat.completions);
122
- openai.chat.completions.create = async (...args) => {
123
- const aggregator = (chunks) => {
124
- if (!chunks || chunks.length === 0) {
125
- return { choices: [{ message: { role: "assistant", content: "" } }] };
126
- }
127
- const choicesByIndex = {};
128
- for (const chunk of chunks) {
129
- for (const choice of chunk.choices) {
130
- if (choicesByIndex[choice.index] === undefined) {
131
- choicesByIndex[choice.index] = [];
132
- }
133
- choicesByIndex[choice.index].push(choice);
134
- }
135
- }
136
- const aggregatedOutput = chunks[chunks.length - 1];
137
- aggregatedOutput.choices = Object.values(choicesByIndex).map((choices) => _combineChatCompletionChoices(choices));
138
- return aggregatedOutput;
139
- };
140
- const defaultRunConfig = {
141
- name: "ChatOpenAI",
142
- run_type: "llm",
143
- aggregator,
144
- ...options,
145
- };
146
- return extractLangSmithExtraAndCall(originalChatCompletionsFn, args, defaultRunConfig);
147
- };
148
- const originalCompletionsFn = openai.completions.create.bind(openai.chat.completions);
149
- openai.completions.create = async (...args) => {
150
- const aggregator = (allChunks) => {
151
- if (allChunks.length === 0) {
152
- return { choices: [{ text: "" }] };
153
- }
154
- const allContent = [];
155
- for (const chunk of allChunks) {
156
- const content = chunk.choices[0].text;
157
- if (content != null) {
158
- allContent.push(content);
159
- }
160
- }
161
- const content = allContent.join("");
162
- const aggregatedOutput = allChunks[allChunks.length - 1];
163
- aggregatedOutput.choices = [
164
- { ...aggregatedOutput.choices[0], text: content },
165
- ];
166
- return aggregatedOutput;
167
- };
168
- const defaultRunConfig = {
169
- name: "OpenAI",
170
- run_type: "llm",
171
- aggregator,
172
- ...options,
173
- };
174
- return extractLangSmithExtraAndCall(originalCompletionsFn, args, defaultRunConfig);
175
- };
141
+ openai.chat.completions.create = (0, traceable_js_1.traceable)(openai.chat.completions.create.bind(openai.chat.completions), {
142
+ name: "ChatOpenAI",
143
+ run_type: "llm",
144
+ aggregator: chatAggregator,
145
+ argsConfigPath: [1, "langsmithExtra"],
146
+ ...options,
147
+ });
148
+ openai.completions.create = (0, traceable_js_1.traceable)(openai.completions.create.bind(openai.completions), {
149
+ name: "OpenAI",
150
+ run_type: "llm",
151
+ aggregator: textAggregator,
152
+ argsConfigPath: [1, "langsmithExtra"],
153
+ ...options,
154
+ });
176
155
  return openai;
177
156
  };
178
157
  exports.wrapOpenAI = wrapOpenAI;
@@ -181,7 +160,7 @@ const _wrapClient = (sdk, runName, options) => {
181
160
  get(target, propKey, receiver) {
182
161
  const originalValue = target[propKey];
183
162
  if (typeof originalValue === "function") {
184
- return (0, traceable_js_1.traceable)(originalValue.bind(target), Object.assign({ name: [runName, propKey.toString()].join("."), run_type: "llm" }, options?.client));
163
+ return (0, traceable_js_1.traceable)(originalValue.bind(target), Object.assign({ name: [runName, propKey.toString()].join("."), run_type: "llm" }, options));
185
164
  }
186
165
  else if (originalValue != null &&
187
166
  !Array.isArray(originalValue) &&
@@ -1,4 +1,5 @@
1
- import type { OpenAI } from "openai";
1
+ import { OpenAI } from "openai";
2
+ import type { APIPromise } from "openai/core";
2
3
  import type { Client, RunTreeConfig } from "../index.js";
3
4
  import { type RunnableConfigLike } from "../run_trees.js";
4
5
  import { type RunTreeLike } from "../traceable.js";
@@ -12,31 +13,29 @@ type OpenAIType = {
12
13
  create: (...args: any[]) => any;
13
14
  };
14
15
  };
15
- type PatchedOpenAIClient<T extends OpenAIType> = {
16
- [P in keyof T]: T[P];
17
- } & {
18
- chat: {
19
- completions: {
16
+ type PatchedOpenAIClient<T extends OpenAIType> = T & {
17
+ chat: T["chat"] & {
18
+ completions: T["chat"]["completions"] & {
20
19
  create: {
21
20
  (arg: OpenAI.ChatCompletionCreateParamsStreaming, arg2?: OpenAI.RequestOptions & {
22
21
  langsmithExtra?: RunnableConfigLike | RunTreeLike;
23
- }): Promise<AsyncGenerator<OpenAI.ChatCompletionChunk>>;
22
+ }): APIPromise<AsyncGenerator<OpenAI.ChatCompletionChunk>>;
24
23
  } & {
25
24
  (arg: OpenAI.ChatCompletionCreateParamsNonStreaming, arg2?: OpenAI.RequestOptions & {
26
25
  langsmithExtra?: RunnableConfigLike | RunTreeLike;
27
- }): Promise<OpenAI.ChatCompletionChunk>;
26
+ }): APIPromise<OpenAI.ChatCompletionChunk>;
28
27
  };
29
28
  };
30
29
  };
31
- completions: {
30
+ completions: T["completions"] & {
32
31
  create: {
33
32
  (arg: OpenAI.CompletionCreateParamsStreaming, arg2?: OpenAI.RequestOptions & {
34
33
  langsmithExtra?: RunnableConfigLike | RunTreeLike;
35
- }): Promise<AsyncGenerator<OpenAI.Completion>>;
34
+ }): APIPromise<AsyncGenerator<OpenAI.Completion>>;
36
35
  } & {
37
36
  (arg: OpenAI.CompletionCreateParamsNonStreaming, arg2?: OpenAI.RequestOptions & {
38
37
  langsmithExtra?: RunnableConfigLike | RunTreeLike;
39
- }): Promise<OpenAI.Completion>;
38
+ }): APIPromise<OpenAI.Completion>;
40
39
  };
41
40
  };
42
41
  };
@@ -1,6 +1,9 @@
1
1
  import { traceable } from "../traceable.js";
2
- function _combineChatCompletionChoices(choices) {
2
+ function _combineChatCompletionChoices(choices
3
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4
+ ) {
3
5
  const reversedChoices = choices.slice().reverse();
6
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4
7
  const message = {
5
8
  role: "assistant",
6
9
  content: "",
@@ -70,26 +73,43 @@ function _combineChatCompletionChoices(choices) {
70
73
  message: message,
71
74
  };
72
75
  }
73
- async function extractLangSmithExtraAndCall(openAIMethod, args, defaultRunConfig) {
74
- if (args[1]?.langsmithExtra !== undefined) {
75
- const { langsmithExtra, ...openAIOptions } = args[1];
76
- const wrappedMethod = traceable(openAIMethod, {
77
- ...defaultRunConfig,
78
- ...langsmithExtra,
79
- });
80
- const finalArgs = [args[0]];
81
- if (args.length > 2) {
82
- finalArgs.push(openAIOptions);
83
- finalArgs.push(args.slice(2));
76
+ const chatAggregator = (chunks) => {
77
+ if (!chunks || chunks.length === 0) {
78
+ return { choices: [{ message: { role: "assistant", content: "" } }] };
79
+ }
80
+ const choicesByIndex = {};
81
+ for (const chunk of chunks) {
82
+ for (const choice of chunk.choices) {
83
+ if (choicesByIndex[choice.index] === undefined) {
84
+ choicesByIndex[choice.index] = [];
85
+ }
86
+ choicesByIndex[choice.index].push(choice);
84
87
  }
85
- else if (Object.keys(openAIOptions).length !== 0) {
86
- finalArgs.push(openAIOptions);
88
+ }
89
+ const aggregatedOutput = chunks[chunks.length - 1];
90
+ aggregatedOutput.choices = Object.values(choicesByIndex).map((choices) => _combineChatCompletionChoices(choices));
91
+ return aggregatedOutput;
92
+ };
93
+ const textAggregator = (allChunks
94
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
95
+ ) => {
96
+ if (allChunks.length === 0) {
97
+ return { choices: [{ text: "" }] };
98
+ }
99
+ const allContent = [];
100
+ for (const chunk of allChunks) {
101
+ const content = chunk.choices[0].text;
102
+ if (content != null) {
103
+ allContent.push(content);
87
104
  }
88
- return wrappedMethod(...finalArgs);
89
105
  }
90
- const wrappedMethod = traceable(openAIMethod, defaultRunConfig);
91
- return wrappedMethod(...args);
92
- }
106
+ const content = allContent.join("");
107
+ const aggregatedOutput = allChunks[allChunks.length - 1];
108
+ aggregatedOutput.choices = [
109
+ { ...aggregatedOutput.choices[0], text: content },
110
+ ];
111
+ return aggregatedOutput;
112
+ };
93
113
  /**
94
114
  * Wraps an OpenAI client's completion methods, enabling automatic LangSmith
95
115
  * tracing. Method signatures are unchanged, with the exception that you can pass
@@ -115,61 +135,20 @@ async function extractLangSmithExtraAndCall(openAIMethod, args, defaultRunConfig
115
135
  * ```
116
136
  */
117
137
  export const wrapOpenAI = (openai, options) => {
118
- const originalChatCompletionsFn = openai.chat.completions.create.bind(openai.chat.completions);
119
- openai.chat.completions.create = async (...args) => {
120
- const aggregator = (chunks) => {
121
- if (!chunks || chunks.length === 0) {
122
- return { choices: [{ message: { role: "assistant", content: "" } }] };
123
- }
124
- const choicesByIndex = {};
125
- for (const chunk of chunks) {
126
- for (const choice of chunk.choices) {
127
- if (choicesByIndex[choice.index] === undefined) {
128
- choicesByIndex[choice.index] = [];
129
- }
130
- choicesByIndex[choice.index].push(choice);
131
- }
132
- }
133
- const aggregatedOutput = chunks[chunks.length - 1];
134
- aggregatedOutput.choices = Object.values(choicesByIndex).map((choices) => _combineChatCompletionChoices(choices));
135
- return aggregatedOutput;
136
- };
137
- const defaultRunConfig = {
138
- name: "ChatOpenAI",
139
- run_type: "llm",
140
- aggregator,
141
- ...options,
142
- };
143
- return extractLangSmithExtraAndCall(originalChatCompletionsFn, args, defaultRunConfig);
144
- };
145
- const originalCompletionsFn = openai.completions.create.bind(openai.chat.completions);
146
- openai.completions.create = async (...args) => {
147
- const aggregator = (allChunks) => {
148
- if (allChunks.length === 0) {
149
- return { choices: [{ text: "" }] };
150
- }
151
- const allContent = [];
152
- for (const chunk of allChunks) {
153
- const content = chunk.choices[0].text;
154
- if (content != null) {
155
- allContent.push(content);
156
- }
157
- }
158
- const content = allContent.join("");
159
- const aggregatedOutput = allChunks[allChunks.length - 1];
160
- aggregatedOutput.choices = [
161
- { ...aggregatedOutput.choices[0], text: content },
162
- ];
163
- return aggregatedOutput;
164
- };
165
- const defaultRunConfig = {
166
- name: "OpenAI",
167
- run_type: "llm",
168
- aggregator,
169
- ...options,
170
- };
171
- return extractLangSmithExtraAndCall(originalCompletionsFn, args, defaultRunConfig);
172
- };
138
+ openai.chat.completions.create = traceable(openai.chat.completions.create.bind(openai.chat.completions), {
139
+ name: "ChatOpenAI",
140
+ run_type: "llm",
141
+ aggregator: chatAggregator,
142
+ argsConfigPath: [1, "langsmithExtra"],
143
+ ...options,
144
+ });
145
+ openai.completions.create = traceable(openai.completions.create.bind(openai.completions), {
146
+ name: "OpenAI",
147
+ run_type: "llm",
148
+ aggregator: textAggregator,
149
+ argsConfigPath: [1, "langsmithExtra"],
150
+ ...options,
151
+ });
173
152
  return openai;
174
153
  };
175
154
  const _wrapClient = (sdk, runName, options) => {
@@ -177,7 +156,7 @@ const _wrapClient = (sdk, runName, options) => {
177
156
  get(target, propKey, receiver) {
178
157
  const originalValue = target[propKey];
179
158
  if (typeof originalValue === "function") {
180
- return traceable(originalValue.bind(target), Object.assign({ name: [runName, propKey.toString()].join("."), run_type: "llm" }, options?.client));
159
+ return traceable(originalValue.bind(target), Object.assign({ name: [runName, propKey.toString()].join("."), run_type: "llm" }, options));
181
160
  }
182
161
  else if (originalValue != null &&
183
162
  !Array.isArray(originalValue) &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langsmith",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
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": [
@@ -47,12 +47,13 @@
47
47
  "check-version": "node scripts/check-version.js",
48
48
  "check-npm-version": "node scripts/check-npm-version.js",
49
49
  "clean": "rm -rf dist/ && node scripts/create-entrypoints.js clean",
50
- "build:esm": "tsc --outDir dist/ && rm -rf dist/tests dist/**/tests",
51
- "build:cjs": "tsc --outDir dist-cjs/ -p tsconfig.cjs.json && node scripts/move-cjs-to-dist.js && rm -r dist-cjs",
50
+ "build:esm": "rm -f src/package.json && tsc --outDir dist/ && rm -rf dist/tests dist/**/tests",
51
+ "build:cjs": "echo '{}' > src/package.json && tsc --outDir dist-cjs/ -p tsconfig.cjs.json && node scripts/move-cjs-to-dist.js && rm -r dist-cjs src/package.json",
52
52
  "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --passWithNoTests --testPathIgnorePatterns='\\.int\\.test.[tj]s' --testTimeout 30000",
53
53
  "test:integration": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --testPathPattern=\\.int\\.test.ts --testTimeout 100000",
54
54
  "test:single": "NODE_OPTIONS=--experimental-vm-modules yarn run jest --config jest.config.cjs --testTimeout 100000",
55
- "lint": "eslint 'src/**/*.{ts,tsx}' --quiet --fix",
55
+ "lint": "NODE_OPTIONS=--max-old-space-size=4096 eslint --cache --ext .ts,.js src/",
56
+ "lint:fix": "yarn lint --fix",
56
57
  "format": "prettier --write 'src/**/*.{ts,tsx}'",
57
58
  "format:check": "prettier --check 'src/**/*.{ts,tsx}'",
58
59
  "precommit": "lint-staged",
@@ -103,7 +104,7 @@
103
104
  "prettier": "^2.8.8",
104
105
  "ts-jest": "^29.1.0",
105
106
  "ts-node": "^10.9.1",
106
- "typescript": "^5.0.4"
107
+ "typescript": "^5.4.5"
107
108
  },
108
109
  "peerDependencies": {
109
110
  "openai": "*"