langsmith 0.3.19 → 0.3.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
@@ -435,7 +435,7 @@ class Client {
435
435
  }
436
436
  return headers;
437
437
  }
438
- processInputs(inputs) {
438
+ async processInputs(inputs) {
439
439
  if (this.hideInputs === false) {
440
440
  return inputs;
441
441
  }
@@ -447,7 +447,7 @@ class Client {
447
447
  }
448
448
  return inputs;
449
449
  }
450
- processOutputs(outputs) {
450
+ async processOutputs(outputs) {
451
451
  if (this.hideOutputs === false) {
452
452
  return outputs;
453
453
  }
@@ -459,13 +459,13 @@ class Client {
459
459
  }
460
460
  return outputs;
461
461
  }
462
- prepareRunCreateOrUpdateInputs(run) {
462
+ async prepareRunCreateOrUpdateInputs(run) {
463
463
  const runParams = { ...run };
464
464
  if (runParams.inputs !== undefined) {
465
- runParams.inputs = this.processInputs(runParams.inputs);
465
+ runParams.inputs = await this.processInputs(runParams.inputs);
466
466
  }
467
467
  if (runParams.outputs !== undefined) {
468
- runParams.outputs = this.processOutputs(runParams.outputs);
468
+ runParams.outputs = await this.processOutputs(runParams.outputs);
469
469
  }
470
470
  return runParams;
471
471
  }
@@ -718,7 +718,7 @@ class Client {
718
718
  const headers = { ...this.headers, "Content-Type": "application/json" };
719
719
  const session_name = run.project_name;
720
720
  delete run.project_name;
721
- const runCreate = this.prepareRunCreateOrUpdateInputs({
721
+ const runCreate = await this.prepareRunCreateOrUpdateInputs({
722
722
  session_name,
723
723
  ...run,
724
724
  start_time: run.start_time ?? Date.now(),
@@ -750,8 +750,8 @@ class Client {
750
750
  if (runCreates === undefined && runUpdates === undefined) {
751
751
  return;
752
752
  }
753
- let preparedCreateParams = runCreates?.map((create) => this.prepareRunCreateOrUpdateInputs(create)) ?? [];
754
- let preparedUpdateParams = runUpdates?.map((update) => this.prepareRunCreateOrUpdateInputs(update)) ?? [];
753
+ let preparedCreateParams = await Promise.all(runCreates?.map((create) => this.prepareRunCreateOrUpdateInputs(create)) ?? []);
754
+ let preparedUpdateParams = await Promise.all(runUpdates?.map((update) => this.prepareRunCreateOrUpdateInputs(update)) ?? []);
755
755
  if (preparedCreateParams.length > 0 && preparedUpdateParams.length > 0) {
756
756
  const createById = preparedCreateParams.reduce((params, run) => {
757
757
  if (!run.id) {
@@ -827,7 +827,7 @@ class Client {
827
827
  const allAttachments = {};
828
828
  let preparedCreateParams = [];
829
829
  for (const create of runCreates ?? []) {
830
- const preparedCreate = this.prepareRunCreateOrUpdateInputs(create);
830
+ const preparedCreate = await this.prepareRunCreateOrUpdateInputs(create);
831
831
  if (preparedCreate.id !== undefined &&
832
832
  preparedCreate.attachments !== undefined) {
833
833
  allAttachments[preparedCreate.id] = preparedCreate.attachments;
@@ -837,7 +837,7 @@ class Client {
837
837
  }
838
838
  let preparedUpdateParams = [];
839
839
  for (const update of runUpdates ?? []) {
840
- preparedUpdateParams.push(this.prepareRunCreateOrUpdateInputs(update));
840
+ preparedUpdateParams.push(await this.prepareRunCreateOrUpdateInputs(update));
841
841
  }
842
842
  // require trace_id and dotted_order
843
843
  const invalidRunCreate = preparedCreateParams.find((runCreate) => {
@@ -989,10 +989,10 @@ class Client {
989
989
  async updateRun(runId, run) {
990
990
  (0, _uuid_js_1.assertUuid)(runId);
991
991
  if (run.inputs) {
992
- run.inputs = this.processInputs(run.inputs);
992
+ run.inputs = await this.processInputs(run.inputs);
993
993
  }
994
994
  if (run.outputs) {
995
- run.outputs = this.processOutputs(run.outputs);
995
+ run.outputs = await this.processOutputs(run.outputs);
996
996
  }
997
997
  // TODO: Untangle types
998
998
  const data = { ...run, id: runId };
package/dist/client.d.ts CHANGED
@@ -7,9 +7,9 @@ export interface ClientConfig {
7
7
  callerOptions?: AsyncCallerParams;
8
8
  timeout_ms?: number;
9
9
  webUrl?: string;
10
- anonymizer?: (values: KVMap) => KVMap;
11
- hideInputs?: boolean | ((inputs: KVMap) => KVMap);
12
- hideOutputs?: boolean | ((outputs: KVMap) => KVMap);
10
+ anonymizer?: (values: KVMap) => KVMap | Promise<KVMap>;
11
+ hideInputs?: boolean | ((inputs: KVMap) => KVMap | Promise<KVMap>);
12
+ hideOutputs?: boolean | ((outputs: KVMap) => KVMap | Promise<KVMap>);
13
13
  autoBatchTracing?: boolean;
14
14
  batchSizeBytesLimit?: number;
15
15
  blockOnRootRunFinalization?: boolean;
package/dist/client.js CHANGED
@@ -397,7 +397,7 @@ export class Client {
397
397
  }
398
398
  return headers;
399
399
  }
400
- processInputs(inputs) {
400
+ async processInputs(inputs) {
401
401
  if (this.hideInputs === false) {
402
402
  return inputs;
403
403
  }
@@ -409,7 +409,7 @@ export class Client {
409
409
  }
410
410
  return inputs;
411
411
  }
412
- processOutputs(outputs) {
412
+ async processOutputs(outputs) {
413
413
  if (this.hideOutputs === false) {
414
414
  return outputs;
415
415
  }
@@ -421,13 +421,13 @@ export class Client {
421
421
  }
422
422
  return outputs;
423
423
  }
424
- prepareRunCreateOrUpdateInputs(run) {
424
+ async prepareRunCreateOrUpdateInputs(run) {
425
425
  const runParams = { ...run };
426
426
  if (runParams.inputs !== undefined) {
427
- runParams.inputs = this.processInputs(runParams.inputs);
427
+ runParams.inputs = await this.processInputs(runParams.inputs);
428
428
  }
429
429
  if (runParams.outputs !== undefined) {
430
- runParams.outputs = this.processOutputs(runParams.outputs);
430
+ runParams.outputs = await this.processOutputs(runParams.outputs);
431
431
  }
432
432
  return runParams;
433
433
  }
@@ -680,7 +680,7 @@ export class Client {
680
680
  const headers = { ...this.headers, "Content-Type": "application/json" };
681
681
  const session_name = run.project_name;
682
682
  delete run.project_name;
683
- const runCreate = this.prepareRunCreateOrUpdateInputs({
683
+ const runCreate = await this.prepareRunCreateOrUpdateInputs({
684
684
  session_name,
685
685
  ...run,
686
686
  start_time: run.start_time ?? Date.now(),
@@ -712,8 +712,8 @@ export class Client {
712
712
  if (runCreates === undefined && runUpdates === undefined) {
713
713
  return;
714
714
  }
715
- let preparedCreateParams = runCreates?.map((create) => this.prepareRunCreateOrUpdateInputs(create)) ?? [];
716
- let preparedUpdateParams = runUpdates?.map((update) => this.prepareRunCreateOrUpdateInputs(update)) ?? [];
715
+ let preparedCreateParams = await Promise.all(runCreates?.map((create) => this.prepareRunCreateOrUpdateInputs(create)) ?? []);
716
+ let preparedUpdateParams = await Promise.all(runUpdates?.map((update) => this.prepareRunCreateOrUpdateInputs(update)) ?? []);
717
717
  if (preparedCreateParams.length > 0 && preparedUpdateParams.length > 0) {
718
718
  const createById = preparedCreateParams.reduce((params, run) => {
719
719
  if (!run.id) {
@@ -789,7 +789,7 @@ export class Client {
789
789
  const allAttachments = {};
790
790
  let preparedCreateParams = [];
791
791
  for (const create of runCreates ?? []) {
792
- const preparedCreate = this.prepareRunCreateOrUpdateInputs(create);
792
+ const preparedCreate = await this.prepareRunCreateOrUpdateInputs(create);
793
793
  if (preparedCreate.id !== undefined &&
794
794
  preparedCreate.attachments !== undefined) {
795
795
  allAttachments[preparedCreate.id] = preparedCreate.attachments;
@@ -799,7 +799,7 @@ export class Client {
799
799
  }
800
800
  let preparedUpdateParams = [];
801
801
  for (const update of runUpdates ?? []) {
802
- preparedUpdateParams.push(this.prepareRunCreateOrUpdateInputs(update));
802
+ preparedUpdateParams.push(await this.prepareRunCreateOrUpdateInputs(update));
803
803
  }
804
804
  // require trace_id and dotted_order
805
805
  const invalidRunCreate = preparedCreateParams.find((runCreate) => {
@@ -951,10 +951,10 @@ export class Client {
951
951
  async updateRun(runId, run) {
952
952
  assertUuid(runId);
953
953
  if (run.inputs) {
954
- run.inputs = this.processInputs(run.inputs);
954
+ run.inputs = await this.processInputs(run.inputs);
955
955
  }
956
956
  if (run.outputs) {
957
- run.outputs = this.processOutputs(run.outputs);
957
+ run.outputs = await this.processOutputs(run.outputs);
958
958
  }
959
959
  // TODO: Untangle types
960
960
  const data = { ...run, id: runId };
package/dist/index.cjs CHANGED
@@ -8,4 +8,4 @@ Object.defineProperty(exports, "RunTree", { enumerable: true, get: function () {
8
8
  var fetch_js_1 = require("./singletons/fetch.cjs");
9
9
  Object.defineProperty(exports, "overrideFetchImplementation", { enumerable: true, get: function () { return fetch_js_1.overrideFetchImplementation; } });
10
10
  // Update using yarn bump-version
11
- exports.__version__ = "0.3.19";
11
+ exports.__version__ = "0.3.21";
package/dist/index.d.ts CHANGED
@@ -2,4 +2,4 @@ export { Client, type ClientConfig, type LangSmithTracingClientInterface, } from
2
2
  export type { Dataset, Example, TracerSession, Run, Feedback, RetrieverOutput, } from "./schemas.js";
3
3
  export { RunTree, type RunTreeConfig } from "./run_trees.js";
4
4
  export { overrideFetchImplementation } from "./singletons/fetch.js";
5
- export declare const __version__ = "0.3.19";
5
+ export declare const __version__ = "0.3.21";
package/dist/index.js CHANGED
@@ -2,4 +2,4 @@ export { Client, } from "./client.js";
2
2
  export { RunTree } from "./run_trees.js";
3
3
  export { overrideFetchImplementation } from "./singletons/fetch.js";
4
4
  // Update using yarn bump-version
5
- export const __version__ = "0.3.19";
5
+ export const __version__ = "0.3.21";
@@ -85,6 +85,42 @@ declare const test: (<I extends Record<string, any> = Record<string, any>, O ext
85
85
  referenceOutputs?: O;
86
86
  } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
87
87
  };
88
+ concurrent: (<I extends Record<string, any> = Record<string, any>, O extends Record<string, any> = Record<string, any>>(name: string, lsParams: LangSmithJestlikeWrapperParams<I, O>, testFn: (data: {
89
+ inputs: I;
90
+ referenceOutputs?: O;
91
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void) & {
92
+ each: <I extends import("../schemas.js").KVMap, O extends import("../schemas.js").KVMap>(table: ({
93
+ inputs: I;
94
+ referenceOutputs?: O;
95
+ } & Record<string, any>)[], config?: import("../utils/jestlike/types.js").LangSmithJestlikeWrapperConfig) => (name: string, fn: (params: {
96
+ inputs: I;
97
+ referenceOutputs?: O;
98
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
99
+ only: (<I extends Record<string, any> = Record<string, any>, O extends Record<string, any> = Record<string, any>>(name: string, lsParams: LangSmithJestlikeWrapperParams<I, O>, testFn: (data: {
100
+ inputs: I;
101
+ referenceOutputs?: O;
102
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void) & {
103
+ each: <I extends import("../schemas.js").KVMap, O extends import("../schemas.js").KVMap>(table: ({
104
+ inputs: I;
105
+ referenceOutputs?: O;
106
+ } & Record<string, any>)[], config?: import("../utils/jestlike/types.js").LangSmithJestlikeWrapperConfig) => (name: string, fn: (params: {
107
+ inputs: I;
108
+ referenceOutputs?: O;
109
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
110
+ };
111
+ skip: (<I extends Record<string, any> = Record<string, any>, O extends Record<string, any> = Record<string, any>>(name: string, lsParams: LangSmithJestlikeWrapperParams<I, O>, testFn: (data: {
112
+ inputs: I;
113
+ referenceOutputs?: O;
114
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void) & {
115
+ each: <I extends import("../schemas.js").KVMap, O extends import("../schemas.js").KVMap>(table: ({
116
+ inputs: I;
117
+ referenceOutputs?: O;
118
+ } & Record<string, any>)[], config?: import("../utils/jestlike/types.js").LangSmithJestlikeWrapperConfig) => (name: string, fn: (params: {
119
+ inputs: I;
120
+ referenceOutputs?: O;
121
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
122
+ };
123
+ };
88
124
  each: <I extends import("../schemas.js").KVMap, O extends import("../schemas.js").KVMap>(table: ({
89
125
  inputs: I;
90
126
  referenceOutputs?: O;
@@ -120,6 +156,42 @@ declare const test: (<I extends Record<string, any> = Record<string, any>, O ext
120
156
  referenceOutputs?: O;
121
157
  } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
122
158
  };
159
+ concurrent: (<I extends Record<string, any> = Record<string, any>, O extends Record<string, any> = Record<string, any>>(name: string, lsParams: LangSmithJestlikeWrapperParams<I, O>, testFn: (data: {
160
+ inputs: I;
161
+ referenceOutputs?: O;
162
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void) & {
163
+ each: <I extends import("../schemas.js").KVMap, O extends import("../schemas.js").KVMap>(table: ({
164
+ inputs: I;
165
+ referenceOutputs?: O;
166
+ } & Record<string, any>)[], config?: import("../utils/jestlike/types.js").LangSmithJestlikeWrapperConfig) => (name: string, fn: (params: {
167
+ inputs: I;
168
+ referenceOutputs?: O;
169
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
170
+ only: (<I extends Record<string, any> = Record<string, any>, O extends Record<string, any> = Record<string, any>>(name: string, lsParams: LangSmithJestlikeWrapperParams<I, O>, testFn: (data: {
171
+ inputs: I;
172
+ referenceOutputs?: O;
173
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void) & {
174
+ each: <I extends import("../schemas.js").KVMap, O extends import("../schemas.js").KVMap>(table: ({
175
+ inputs: I;
176
+ referenceOutputs?: O;
177
+ } & Record<string, any>)[], config?: import("../utils/jestlike/types.js").LangSmithJestlikeWrapperConfig) => (name: string, fn: (params: {
178
+ inputs: I;
179
+ referenceOutputs?: O;
180
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
181
+ };
182
+ skip: (<I extends Record<string, any> = Record<string, any>, O extends Record<string, any> = Record<string, any>>(name: string, lsParams: LangSmithJestlikeWrapperParams<I, O>, testFn: (data: {
183
+ inputs: I;
184
+ referenceOutputs?: O;
185
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void) & {
186
+ each: <I extends import("../schemas.js").KVMap, O extends import("../schemas.js").KVMap>(table: ({
187
+ inputs: I;
188
+ referenceOutputs?: O;
189
+ } & Record<string, any>)[], config?: import("../utils/jestlike/types.js").LangSmithJestlikeWrapperConfig) => (name: string, fn: (params: {
190
+ inputs: I;
191
+ referenceOutputs?: O;
192
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
193
+ };
194
+ };
123
195
  each: <I extends import("../schemas.js").KVMap, O extends import("../schemas.js").KVMap>(table: ({
124
196
  inputs: I;
125
197
  referenceOutputs?: O;
@@ -130,6 +202,7 @@ declare const test: (<I extends Record<string, any> = Record<string, any>, O ext
130
202
  }, describe: import("../utils/jestlike/types.js").LangSmithJestlikeDescribeWrapper & {
131
203
  only: import("../utils/jestlike/types.js").LangSmithJestlikeDescribeWrapper;
132
204
  skip: import("../utils/jestlike/types.js").LangSmithJestlikeDescribeWrapper;
205
+ concurrent: import("../utils/jestlike/types.js").LangSmithJestlikeDescribeWrapper;
133
206
  }, expect: jest.Expect;
134
207
  export {
135
208
  /**
@@ -19,6 +19,7 @@ export type TestWrapperAsyncLocalStorageData = {
19
19
  suiteUuid: string;
20
20
  suiteName: string;
21
21
  testRootRunTree?: RunTree;
22
+ setupPromise?: Promise<void>;
22
23
  };
23
24
  export declare const testWrapperAsyncLocalStorageInstance: AsyncLocalStorage<TestWrapperAsyncLocalStorageData>;
24
25
  export declare function trackingEnabled(context: TestWrapperAsyncLocalStorageData): boolean;
@@ -229,13 +229,27 @@ function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
229
229
  }
230
230
  return storageValue;
231
231
  }
232
- function wrapDescribeMethod(method) {
232
+ function wrapDescribeMethod(method, methodName) {
233
233
  if ((0, env_js_1.isJsDom)()) {
234
234
  console.error(`[LANGSMITH]: You seem to be using a jsdom environment. This is not supported and you may experience unexpected behavior. Please set the "environment" or "testEnvironment" field in your test config file to "node".`);
235
235
  }
236
236
  return function (testSuiteName, fn, experimentConfig) {
237
+ if (typeof method !== "function") {
238
+ throw new Error(`"${methodName}" is not supported by your test runner.`);
239
+ }
240
+ if (globals_js_1.testWrapperAsyncLocalStorageInstance.getStore() !== undefined) {
241
+ throw new Error([
242
+ `You seem to be nesting an ls.describe block named "${testSuiteName}" inside another ls.describe block.`,
243
+ "This is not supported because each ls.describe block corresponds to a LangSmith dataset.",
244
+ "To logically group tests, nest the native Jest or Vitest describe methods instead.",
245
+ ].join("\n"));
246
+ }
237
247
  const client = experimentConfig?.client ?? globals_js_1.DEFAULT_TEST_CLIENT;
238
248
  const suiteName = experimentConfig?.testSuiteName ?? testSuiteName;
249
+ let setupPromiseResolver;
250
+ const setupPromise = new Promise((resolve) => {
251
+ setupPromiseResolver = resolve;
252
+ });
239
253
  return method(suiteName, () => {
240
254
  const startTime = new Date();
241
255
  const suiteUuid = (0, uuid_1.v4)();
@@ -269,10 +283,12 @@ function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
269
283
  metadata: suiteMetadata,
270
284
  },
271
285
  enableTestTracking: experimentConfig?.enableTestTracking,
286
+ setupPromise,
272
287
  };
273
288
  beforeAll(async () => {
274
289
  const storageValue = await runDatasetSetup(context);
275
290
  datasetSetupInfo.set(suiteUuid, storageValue);
291
+ setupPromiseResolver();
276
292
  });
277
293
  afterAll(async () => {
278
294
  await Promise.all([
@@ -353,9 +369,10 @@ function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
353
369
  });
354
370
  };
355
371
  }
356
- const lsDescribe = Object.assign(wrapDescribeMethod(describe), {
357
- only: wrapDescribeMethod(describe.only),
358
- skip: wrapDescribeMethod(describe.skip),
372
+ const lsDescribe = Object.assign(wrapDescribeMethod(describe, "describe"), {
373
+ only: wrapDescribeMethod(describe.only, "describe.only"),
374
+ skip: wrapDescribeMethod(describe.skip, "describe.skip"),
375
+ concurrent: wrapDescribeMethod(describe.concurrent, "describe.concurrent"),
359
376
  });
360
377
  function wrapTestMethod(method) {
361
378
  return function (name, lsParams, testFn, timeout) {
@@ -374,7 +391,12 @@ function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
374
391
  // Jest will not group tests under the same "describe" group if you await the test and
375
392
  // total runs is greater than 1.
376
393
  const resultsPath = path.join(os.tmpdir(), "langsmith_test_results", `${testUuid}.json`);
377
- void method(`${name}${totalRuns > 1 ? `, run ${i}` : ""}${constants_js_1.TEST_ID_DELIMITER}${testUuid}`, async () => {
394
+ void method(`${name}${totalRuns > 1 ? `, run ${i}` : ""}${constants_js_1.TEST_ID_DELIMITER}${testUuid}`, async (...args) => {
395
+ // Jest will magically introspect args and pass a "done" callback if
396
+ // we use a non-spread parameter. To obtain and pass Vitest test context
397
+ // through into the test function, we must therefore refer to Vitest
398
+ // args using this signature
399
+ const jestlikeArgs = args[0];
378
400
  if (context === undefined) {
379
401
  throw new Error([
380
402
  `Could not retrieve test context.`,
@@ -382,6 +404,11 @@ function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
382
404
  `See this page for more information: https://docs.smith.langchain.com/evaluation/how_to_guides/vitest_jest`,
383
405
  ].join("\n"));
384
406
  }
407
+ // Jest .concurrent is super buggy and doesn't wait for beforeAll to complete
408
+ // before running test functions, so we need to wait for the setup promise
409
+ // to resolve before we can continue.
410
+ // Seee https://github.com/jestjs/jest/issues/4281
411
+ await context.setupPromise;
385
412
  if (!datasetSetupInfo.get(context.suiteUuid)) {
386
413
  throw new Error("Dataset failed to initialize. Please check your LangSmith environment variables.");
387
414
  }
@@ -414,11 +441,13 @@ function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
414
441
  throw new Error("Could not identify test context after setting test root run tree. Please contact us for help.");
415
442
  }
416
443
  try {
417
- const res = await testFn({
444
+ const res = await testFn(Object.assign(typeof jestlikeArgs === "object" && jestlikeArgs != null
445
+ ? jestlikeArgs
446
+ : {}, {
418
447
  ...rest,
419
448
  inputs: testInput,
420
449
  referenceOutputs: testOutput,
421
- });
450
+ }));
422
451
  (0, globals_js_1._logTestFeedback)({
423
452
  exampleId,
424
453
  feedback: { key: "pass", score: true },
@@ -582,6 +611,16 @@ function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
582
611
  }
583
612
  return eachMethod;
584
613
  }
614
+ // Roughly mirrors: https://jestjs.io/docs/api#methods
615
+ const concurrentMethod = Object.assign(wrapTestMethod(test.concurrent), {
616
+ each: createEachMethod(test.concurrent),
617
+ only: Object.assign(wrapTestMethod(test.concurrent.only), {
618
+ each: createEachMethod(test.concurrent.only),
619
+ }),
620
+ skip: Object.assign(wrapTestMethod(test.concurrent.skip), {
621
+ each: createEachMethod(test.concurrent.skip),
622
+ }),
623
+ });
585
624
  const lsTest = Object.assign(wrapTestMethod(test), {
586
625
  only: Object.assign(wrapTestMethod(test.only), {
587
626
  each: createEachMethod(test.only),
@@ -589,6 +628,7 @@ function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
589
628
  skip: Object.assign(wrapTestMethod(test.skip), {
590
629
  each: createEachMethod(test.skip),
591
630
  }),
631
+ concurrent: concurrentMethod,
592
632
  each: createEachMethod(test),
593
633
  });
594
634
  const wrappedExpect = (0, chain_js_1.wrapExpect)(expect);
@@ -36,6 +36,42 @@ export declare function generateWrapperFromJestlikeMethods(methods: Record<strin
36
36
  referenceOutputs?: O;
37
37
  } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
38
38
  };
39
+ concurrent: (<I extends Record<string, any> = Record<string, any>, O extends Record<string, any> = Record<string, any>>(name: string, lsParams: LangSmithJestlikeWrapperParams<I, O>, testFn: (data: {
40
+ inputs: I;
41
+ referenceOutputs?: O;
42
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void) & {
43
+ each: <I extends KVMap, O extends KVMap>(table: ({
44
+ inputs: I;
45
+ referenceOutputs?: O;
46
+ } & Record<string, any>)[], config?: LangSmithJestlikeWrapperConfig) => (name: string, fn: (params: {
47
+ inputs: I;
48
+ referenceOutputs?: O;
49
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
50
+ only: (<I extends Record<string, any> = Record<string, any>, O extends Record<string, any> = Record<string, any>>(name: string, lsParams: LangSmithJestlikeWrapperParams<I, O>, testFn: (data: {
51
+ inputs: I;
52
+ referenceOutputs?: O;
53
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void) & {
54
+ each: <I extends KVMap, O extends KVMap>(table: ({
55
+ inputs: I;
56
+ referenceOutputs?: O;
57
+ } & Record<string, any>)[], config?: LangSmithJestlikeWrapperConfig) => (name: string, fn: (params: {
58
+ inputs: I;
59
+ referenceOutputs?: O;
60
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
61
+ };
62
+ skip: (<I extends Record<string, any> = Record<string, any>, O extends Record<string, any> = Record<string, any>>(name: string, lsParams: LangSmithJestlikeWrapperParams<I, O>, testFn: (data: {
63
+ inputs: I;
64
+ referenceOutputs?: O;
65
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void) & {
66
+ each: <I extends KVMap, O extends KVMap>(table: ({
67
+ inputs: I;
68
+ referenceOutputs?: O;
69
+ } & Record<string, any>)[], config?: LangSmithJestlikeWrapperConfig) => (name: string, fn: (params: {
70
+ inputs: I;
71
+ referenceOutputs?: O;
72
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
73
+ };
74
+ };
39
75
  each: <I extends KVMap, O extends KVMap>(table: ({
40
76
  inputs: I;
41
77
  referenceOutputs?: O;
@@ -72,6 +108,42 @@ export declare function generateWrapperFromJestlikeMethods(methods: Record<strin
72
108
  referenceOutputs?: O;
73
109
  } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
74
110
  };
111
+ concurrent: (<I extends Record<string, any> = Record<string, any>, O extends Record<string, any> = Record<string, any>>(name: string, lsParams: LangSmithJestlikeWrapperParams<I, O>, testFn: (data: {
112
+ inputs: I;
113
+ referenceOutputs?: O;
114
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void) & {
115
+ each: <I extends KVMap, O extends KVMap>(table: ({
116
+ inputs: I;
117
+ referenceOutputs?: O;
118
+ } & Record<string, any>)[], config?: LangSmithJestlikeWrapperConfig) => (name: string, fn: (params: {
119
+ inputs: I;
120
+ referenceOutputs?: O;
121
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
122
+ only: (<I extends Record<string, any> = Record<string, any>, O extends Record<string, any> = Record<string, any>>(name: string, lsParams: LangSmithJestlikeWrapperParams<I, O>, testFn: (data: {
123
+ inputs: I;
124
+ referenceOutputs?: O;
125
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void) & {
126
+ each: <I extends KVMap, O extends KVMap>(table: ({
127
+ inputs: I;
128
+ referenceOutputs?: O;
129
+ } & Record<string, any>)[], config?: LangSmithJestlikeWrapperConfig) => (name: string, fn: (params: {
130
+ inputs: I;
131
+ referenceOutputs?: O;
132
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
133
+ };
134
+ skip: (<I extends Record<string, any> = Record<string, any>, O extends Record<string, any> = Record<string, any>>(name: string, lsParams: LangSmithJestlikeWrapperParams<I, O>, testFn: (data: {
135
+ inputs: I;
136
+ referenceOutputs?: O;
137
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void) & {
138
+ each: <I extends KVMap, O extends KVMap>(table: ({
139
+ inputs: I;
140
+ referenceOutputs?: O;
141
+ } & Record<string, any>)[], config?: LangSmithJestlikeWrapperConfig) => (name: string, fn: (params: {
142
+ inputs: I;
143
+ referenceOutputs?: O;
144
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
145
+ };
146
+ };
75
147
  each: <I extends KVMap, O extends KVMap>(table: ({
76
148
  inputs: I;
77
149
  referenceOutputs?: O;
@@ -83,6 +155,7 @@ export declare function generateWrapperFromJestlikeMethods(methods: Record<strin
83
155
  describe: LangSmithJestlikeDescribeWrapper & {
84
156
  only: LangSmithJestlikeDescribeWrapper;
85
157
  skip: LangSmithJestlikeDescribeWrapper;
158
+ concurrent: LangSmithJestlikeDescribeWrapper;
86
159
  };
87
160
  expect: jest.Expect;
88
161
  toBeRelativeCloseTo: typeof toBeRelativeCloseTo;
@@ -182,13 +182,27 @@ export function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
182
182
  }
183
183
  return storageValue;
184
184
  }
185
- function wrapDescribeMethod(method) {
185
+ function wrapDescribeMethod(method, methodName) {
186
186
  if (isJsDom()) {
187
187
  console.error(`[LANGSMITH]: You seem to be using a jsdom environment. This is not supported and you may experience unexpected behavior. Please set the "environment" or "testEnvironment" field in your test config file to "node".`);
188
188
  }
189
189
  return function (testSuiteName, fn, experimentConfig) {
190
+ if (typeof method !== "function") {
191
+ throw new Error(`"${methodName}" is not supported by your test runner.`);
192
+ }
193
+ if (testWrapperAsyncLocalStorageInstance.getStore() !== undefined) {
194
+ throw new Error([
195
+ `You seem to be nesting an ls.describe block named "${testSuiteName}" inside another ls.describe block.`,
196
+ "This is not supported because each ls.describe block corresponds to a LangSmith dataset.",
197
+ "To logically group tests, nest the native Jest or Vitest describe methods instead.",
198
+ ].join("\n"));
199
+ }
190
200
  const client = experimentConfig?.client ?? DEFAULT_TEST_CLIENT;
191
201
  const suiteName = experimentConfig?.testSuiteName ?? testSuiteName;
202
+ let setupPromiseResolver;
203
+ const setupPromise = new Promise((resolve) => {
204
+ setupPromiseResolver = resolve;
205
+ });
192
206
  return method(suiteName, () => {
193
207
  const startTime = new Date();
194
208
  const suiteUuid = v4();
@@ -222,10 +236,12 @@ export function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
222
236
  metadata: suiteMetadata,
223
237
  },
224
238
  enableTestTracking: experimentConfig?.enableTestTracking,
239
+ setupPromise,
225
240
  };
226
241
  beforeAll(async () => {
227
242
  const storageValue = await runDatasetSetup(context);
228
243
  datasetSetupInfo.set(suiteUuid, storageValue);
244
+ setupPromiseResolver();
229
245
  });
230
246
  afterAll(async () => {
231
247
  await Promise.all([
@@ -306,9 +322,10 @@ export function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
306
322
  });
307
323
  };
308
324
  }
309
- const lsDescribe = Object.assign(wrapDescribeMethod(describe), {
310
- only: wrapDescribeMethod(describe.only),
311
- skip: wrapDescribeMethod(describe.skip),
325
+ const lsDescribe = Object.assign(wrapDescribeMethod(describe, "describe"), {
326
+ only: wrapDescribeMethod(describe.only, "describe.only"),
327
+ skip: wrapDescribeMethod(describe.skip, "describe.skip"),
328
+ concurrent: wrapDescribeMethod(describe.concurrent, "describe.concurrent"),
312
329
  });
313
330
  function wrapTestMethod(method) {
314
331
  return function (name, lsParams, testFn, timeout) {
@@ -327,7 +344,12 @@ export function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
327
344
  // Jest will not group tests under the same "describe" group if you await the test and
328
345
  // total runs is greater than 1.
329
346
  const resultsPath = path.join(os.tmpdir(), "langsmith_test_results", `${testUuid}.json`);
330
- void method(`${name}${totalRuns > 1 ? `, run ${i}` : ""}${TEST_ID_DELIMITER}${testUuid}`, async () => {
347
+ void method(`${name}${totalRuns > 1 ? `, run ${i}` : ""}${TEST_ID_DELIMITER}${testUuid}`, async (...args) => {
348
+ // Jest will magically introspect args and pass a "done" callback if
349
+ // we use a non-spread parameter. To obtain and pass Vitest test context
350
+ // through into the test function, we must therefore refer to Vitest
351
+ // args using this signature
352
+ const jestlikeArgs = args[0];
331
353
  if (context === undefined) {
332
354
  throw new Error([
333
355
  `Could not retrieve test context.`,
@@ -335,6 +357,11 @@ export function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
335
357
  `See this page for more information: https://docs.smith.langchain.com/evaluation/how_to_guides/vitest_jest`,
336
358
  ].join("\n"));
337
359
  }
360
+ // Jest .concurrent is super buggy and doesn't wait for beforeAll to complete
361
+ // before running test functions, so we need to wait for the setup promise
362
+ // to resolve before we can continue.
363
+ // Seee https://github.com/jestjs/jest/issues/4281
364
+ await context.setupPromise;
338
365
  if (!datasetSetupInfo.get(context.suiteUuid)) {
339
366
  throw new Error("Dataset failed to initialize. Please check your LangSmith environment variables.");
340
367
  }
@@ -367,11 +394,13 @@ export function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
367
394
  throw new Error("Could not identify test context after setting test root run tree. Please contact us for help.");
368
395
  }
369
396
  try {
370
- const res = await testFn({
397
+ const res = await testFn(Object.assign(typeof jestlikeArgs === "object" && jestlikeArgs != null
398
+ ? jestlikeArgs
399
+ : {}, {
371
400
  ...rest,
372
401
  inputs: testInput,
373
402
  referenceOutputs: testOutput,
374
- });
403
+ }));
375
404
  _logTestFeedback({
376
405
  exampleId,
377
406
  feedback: { key: "pass", score: true },
@@ -535,6 +564,16 @@ export function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
535
564
  }
536
565
  return eachMethod;
537
566
  }
567
+ // Roughly mirrors: https://jestjs.io/docs/api#methods
568
+ const concurrentMethod = Object.assign(wrapTestMethod(test.concurrent), {
569
+ each: createEachMethod(test.concurrent),
570
+ only: Object.assign(wrapTestMethod(test.concurrent.only), {
571
+ each: createEachMethod(test.concurrent.only),
572
+ }),
573
+ skip: Object.assign(wrapTestMethod(test.concurrent.skip), {
574
+ each: createEachMethod(test.concurrent.skip),
575
+ }),
576
+ });
538
577
  const lsTest = Object.assign(wrapTestMethod(test), {
539
578
  only: Object.assign(wrapTestMethod(test.only), {
540
579
  each: createEachMethod(test.only),
@@ -542,6 +581,7 @@ export function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
542
581
  skip: Object.assign(wrapTestMethod(test.skip), {
543
582
  each: createEachMethod(test.skip),
544
583
  }),
584
+ concurrent: concurrentMethod,
545
585
  each: createEachMethod(test),
546
586
  });
547
587
  const wrappedExpect = wrapExpect(expect);
@@ -83,6 +83,42 @@ declare const test: (<I extends Record<string, any> = Record<string, any>, O ext
83
83
  referenceOutputs?: O;
84
84
  } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
85
85
  };
86
+ concurrent: (<I extends Record<string, any> = Record<string, any>, O extends Record<string, any> = Record<string, any>>(name: string, lsParams: LangSmithJestlikeWrapperParams<I, O>, testFn: (data: {
87
+ inputs: I;
88
+ referenceOutputs?: O;
89
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void) & {
90
+ each: <I extends import("../schemas.js").KVMap, O extends import("../schemas.js").KVMap>(table: ({
91
+ inputs: I;
92
+ referenceOutputs?: O;
93
+ } & Record<string, any>)[], config?: import("../utils/jestlike/types.js").LangSmithJestlikeWrapperConfig) => (name: string, fn: (params: {
94
+ inputs: I;
95
+ referenceOutputs?: O;
96
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
97
+ only: (<I extends Record<string, any> = Record<string, any>, O extends Record<string, any> = Record<string, any>>(name: string, lsParams: LangSmithJestlikeWrapperParams<I, O>, testFn: (data: {
98
+ inputs: I;
99
+ referenceOutputs?: O;
100
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void) & {
101
+ each: <I extends import("../schemas.js").KVMap, O extends import("../schemas.js").KVMap>(table: ({
102
+ inputs: I;
103
+ referenceOutputs?: O;
104
+ } & Record<string, any>)[], config?: import("../utils/jestlike/types.js").LangSmithJestlikeWrapperConfig) => (name: string, fn: (params: {
105
+ inputs: I;
106
+ referenceOutputs?: O;
107
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
108
+ };
109
+ skip: (<I extends Record<string, any> = Record<string, any>, O extends Record<string, any> = Record<string, any>>(name: string, lsParams: LangSmithJestlikeWrapperParams<I, O>, testFn: (data: {
110
+ inputs: I;
111
+ referenceOutputs?: O;
112
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void) & {
113
+ each: <I extends import("../schemas.js").KVMap, O extends import("../schemas.js").KVMap>(table: ({
114
+ inputs: I;
115
+ referenceOutputs?: O;
116
+ } & Record<string, any>)[], config?: import("../utils/jestlike/types.js").LangSmithJestlikeWrapperConfig) => (name: string, fn: (params: {
117
+ inputs: I;
118
+ referenceOutputs?: O;
119
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
120
+ };
121
+ };
86
122
  each: <I extends import("../schemas.js").KVMap, O extends import("../schemas.js").KVMap>(table: ({
87
123
  inputs: I;
88
124
  referenceOutputs?: O;
@@ -118,6 +154,42 @@ declare const test: (<I extends Record<string, any> = Record<string, any>, O ext
118
154
  referenceOutputs?: O;
119
155
  } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
120
156
  };
157
+ concurrent: (<I extends Record<string, any> = Record<string, any>, O extends Record<string, any> = Record<string, any>>(name: string, lsParams: LangSmithJestlikeWrapperParams<I, O>, testFn: (data: {
158
+ inputs: I;
159
+ referenceOutputs?: O;
160
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void) & {
161
+ each: <I extends import("../schemas.js").KVMap, O extends import("../schemas.js").KVMap>(table: ({
162
+ inputs: I;
163
+ referenceOutputs?: O;
164
+ } & Record<string, any>)[], config?: import("../utils/jestlike/types.js").LangSmithJestlikeWrapperConfig) => (name: string, fn: (params: {
165
+ inputs: I;
166
+ referenceOutputs?: O;
167
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
168
+ only: (<I extends Record<string, any> = Record<string, any>, O extends Record<string, any> = Record<string, any>>(name: string, lsParams: LangSmithJestlikeWrapperParams<I, O>, testFn: (data: {
169
+ inputs: I;
170
+ referenceOutputs?: O;
171
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void) & {
172
+ each: <I extends import("../schemas.js").KVMap, O extends import("../schemas.js").KVMap>(table: ({
173
+ inputs: I;
174
+ referenceOutputs?: O;
175
+ } & Record<string, any>)[], config?: import("../utils/jestlike/types.js").LangSmithJestlikeWrapperConfig) => (name: string, fn: (params: {
176
+ inputs: I;
177
+ referenceOutputs?: O;
178
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
179
+ };
180
+ skip: (<I extends Record<string, any> = Record<string, any>, O extends Record<string, any> = Record<string, any>>(name: string, lsParams: LangSmithJestlikeWrapperParams<I, O>, testFn: (data: {
181
+ inputs: I;
182
+ referenceOutputs?: O;
183
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void) & {
184
+ each: <I extends import("../schemas.js").KVMap, O extends import("../schemas.js").KVMap>(table: ({
185
+ inputs: I;
186
+ referenceOutputs?: O;
187
+ } & Record<string, any>)[], config?: import("../utils/jestlike/types.js").LangSmithJestlikeWrapperConfig) => (name: string, fn: (params: {
188
+ inputs: I;
189
+ referenceOutputs?: O;
190
+ } & Record<string, any>) => unknown | Promise<unknown>, timeout?: number) => void;
191
+ };
192
+ };
121
193
  each: <I extends import("../schemas.js").KVMap, O extends import("../schemas.js").KVMap>(table: ({
122
194
  inputs: I;
123
195
  referenceOutputs?: O;
@@ -128,6 +200,7 @@ declare const test: (<I extends Record<string, any> = Record<string, any>, O ext
128
200
  }, describe: import("../utils/jestlike/types.js").LangSmithJestlikeDescribeWrapper & {
129
201
  only: import("../utils/jestlike/types.js").LangSmithJestlikeDescribeWrapper;
130
202
  skip: import("../utils/jestlike/types.js").LangSmithJestlikeDescribeWrapper;
203
+ concurrent: import("../utils/jestlike/types.js").LangSmithJestlikeDescribeWrapper;
131
204
  }, expect: jest.Expect;
132
205
  export {
133
206
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langsmith",
3
- "version": "0.3.19",
3
+ "version": "0.3.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": [