langsmith 0.1.24 → 0.1.26

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
@@ -907,8 +907,23 @@ class Client {
907
907
  select: select ? select : default_select,
908
908
  is_root: isRoot,
909
909
  };
910
+ let runsYielded = 0;
910
911
  for await (const runs of this._getCursorPaginatedList("/runs/query", body)) {
911
- yield* runs;
912
+ if (limit) {
913
+ if (runsYielded >= limit) {
914
+ break;
915
+ }
916
+ if (runs.length + runsYielded > limit) {
917
+ const newRuns = runs.slice(0, limit - runsYielded);
918
+ yield* newRuns;
919
+ break;
920
+ }
921
+ runsYielded += runs.length;
922
+ yield* runs;
923
+ }
924
+ else {
925
+ yield* runs;
926
+ }
912
927
  }
913
928
  }
914
929
  async shareRun(runId, { shareId } = {}) {
@@ -1170,6 +1185,14 @@ class Client {
1170
1185
  const tenantId = await this._getTenantId();
1171
1186
  return `${this.getHostUrl()}/o/${tenantId}/projects/p/${project.id}`;
1172
1187
  }
1188
+ async getDatasetUrl({ datasetId, datasetName, }) {
1189
+ if (datasetId === undefined && datasetName === undefined) {
1190
+ throw new Error("Must provide either datasetName or datasetId");
1191
+ }
1192
+ const dataset = await this.readDataset({ datasetId, datasetName });
1193
+ const tenantId = await this._getTenantId();
1194
+ return `${this.getHostUrl()}/o/${tenantId}/datasets/${dataset.id}`;
1195
+ }
1173
1196
  async _getTenantId() {
1174
1197
  if (this._tenantId !== null) {
1175
1198
  return this._tenantId;
@@ -1428,7 +1451,7 @@ class Client {
1428
1451
  }
1429
1452
  await response.json();
1430
1453
  }
1431
- async createExample(inputs, outputs, { datasetId, datasetName, createdAt, exampleId, metadata, }) {
1454
+ async createExample(inputs, outputs, { datasetId, datasetName, createdAt, exampleId, metadata, split, }) {
1432
1455
  let datasetId_ = datasetId;
1433
1456
  if (datasetId_ === undefined && datasetName === undefined) {
1434
1457
  throw new Error("Must provide either datasetName or datasetId");
@@ -1448,6 +1471,7 @@ class Client {
1448
1471
  created_at: createdAt_?.toISOString(),
1449
1472
  id: exampleId,
1450
1473
  metadata,
1474
+ split,
1451
1475
  };
1452
1476
  const response = await this.caller.call(fetch, `${this.apiUrl}/examples`, {
1453
1477
  method: "POST",
@@ -1481,6 +1505,7 @@ class Client {
1481
1505
  inputs: input,
1482
1506
  outputs: outputs ? outputs[idx] : undefined,
1483
1507
  metadata: metadata ? metadata[idx] : undefined,
1508
+ split: props.splits ? props.splits[idx] : undefined,
1484
1509
  id: exampleIds ? exampleIds[idx] : undefined,
1485
1510
  source_run_id: sourceRunIds ? sourceRunIds[idx] : undefined,
1486
1511
  };
@@ -1518,7 +1543,7 @@ class Client {
1518
1543
  const path = `/examples/${exampleId}`;
1519
1544
  return await this._get(path);
1520
1545
  }
1521
- async *listExamples({ datasetId, datasetName, exampleIds, asOf, inlineS3Urls, metadata, } = {}) {
1546
+ async *listExamples({ datasetId, datasetName, exampleIds, asOf, splits, inlineS3Urls, metadata, } = {}) {
1522
1547
  let datasetId_;
1523
1548
  if (datasetId !== undefined && datasetName !== undefined) {
1524
1549
  throw new Error("Must provide either datasetName or datasetId, not both");
@@ -1549,6 +1574,11 @@ class Client {
1549
1574
  params.append("id", id_);
1550
1575
  }
1551
1576
  }
1577
+ if (splits !== undefined) {
1578
+ for (const split of splits) {
1579
+ params.append("splits", split);
1580
+ }
1581
+ }
1552
1582
  if (metadata !== undefined) {
1553
1583
  const serializedMetadata = JSON.stringify(metadata);
1554
1584
  params.append("metadata", serializedMetadata);
package/dist/client.d.ts CHANGED
@@ -145,6 +145,7 @@ export type CreateExampleOptions = {
145
145
  createdAt?: Date;
146
146
  exampleId?: string;
147
147
  metadata?: KVMap;
148
+ split?: string | string[];
148
149
  };
149
150
  export declare class Queue<T> {
150
151
  items: [T, () => void][];
@@ -338,6 +339,10 @@ export declare class Client {
338
339
  projectId?: string;
339
340
  projectName?: string;
340
341
  }): Promise<string>;
342
+ getDatasetUrl({ datasetId, datasetName, }: {
343
+ datasetId?: string;
344
+ datasetName?: string;
345
+ }): Promise<string>;
341
346
  private _getTenantId;
342
347
  listProjects({ projectIds, name, nameContains, referenceDatasetId, referenceDatasetName, referenceFree, }?: {
343
348
  projectIds?: string[];
@@ -385,11 +390,12 @@ export declare class Client {
385
390
  datasetId?: string;
386
391
  datasetName?: string;
387
392
  }): Promise<void>;
388
- createExample(inputs: KVMap, outputs: KVMap, { datasetId, datasetName, createdAt, exampleId, metadata, }: CreateExampleOptions): Promise<Example>;
393
+ createExample(inputs: KVMap, outputs: KVMap, { datasetId, datasetName, createdAt, exampleId, metadata, split, }: CreateExampleOptions): Promise<Example>;
389
394
  createExamples(props: {
390
395
  inputs: Array<KVMap>;
391
396
  outputs?: Array<KVMap>;
392
397
  metadata?: Array<KVMap>;
398
+ splits?: Array<string | Array<string>>;
393
399
  sourceRunIds?: Array<string>;
394
400
  exampleIds?: Array<string>;
395
401
  datasetId?: string;
@@ -398,11 +404,12 @@ export declare class Client {
398
404
  createLLMExample(input: string, generation: string | undefined, options: CreateExampleOptions): Promise<Example>;
399
405
  createChatExample(input: KVMap[] | LangChainBaseMessage[], generations: KVMap | LangChainBaseMessage | undefined, options: CreateExampleOptions): Promise<Example>;
400
406
  readExample(exampleId: string): Promise<Example>;
401
- listExamples({ datasetId, datasetName, exampleIds, asOf, inlineS3Urls, metadata, }?: {
407
+ listExamples({ datasetId, datasetName, exampleIds, asOf, splits, inlineS3Urls, metadata, }?: {
402
408
  datasetId?: string;
403
409
  datasetName?: string;
404
410
  exampleIds?: string[];
405
411
  asOf?: string | Date;
412
+ splits?: string[];
406
413
  inlineS3Urls?: boolean;
407
414
  metadata?: KVMap;
408
415
  }): AsyncIterable<Example>;
package/dist/client.js CHANGED
@@ -880,8 +880,23 @@ export class Client {
880
880
  select: select ? select : default_select,
881
881
  is_root: isRoot,
882
882
  };
883
+ let runsYielded = 0;
883
884
  for await (const runs of this._getCursorPaginatedList("/runs/query", body)) {
884
- yield* runs;
885
+ if (limit) {
886
+ if (runsYielded >= limit) {
887
+ break;
888
+ }
889
+ if (runs.length + runsYielded > limit) {
890
+ const newRuns = runs.slice(0, limit - runsYielded);
891
+ yield* newRuns;
892
+ break;
893
+ }
894
+ runsYielded += runs.length;
895
+ yield* runs;
896
+ }
897
+ else {
898
+ yield* runs;
899
+ }
885
900
  }
886
901
  }
887
902
  async shareRun(runId, { shareId } = {}) {
@@ -1143,6 +1158,14 @@ export class Client {
1143
1158
  const tenantId = await this._getTenantId();
1144
1159
  return `${this.getHostUrl()}/o/${tenantId}/projects/p/${project.id}`;
1145
1160
  }
1161
+ async getDatasetUrl({ datasetId, datasetName, }) {
1162
+ if (datasetId === undefined && datasetName === undefined) {
1163
+ throw new Error("Must provide either datasetName or datasetId");
1164
+ }
1165
+ const dataset = await this.readDataset({ datasetId, datasetName });
1166
+ const tenantId = await this._getTenantId();
1167
+ return `${this.getHostUrl()}/o/${tenantId}/datasets/${dataset.id}`;
1168
+ }
1146
1169
  async _getTenantId() {
1147
1170
  if (this._tenantId !== null) {
1148
1171
  return this._tenantId;
@@ -1401,7 +1424,7 @@ export class Client {
1401
1424
  }
1402
1425
  await response.json();
1403
1426
  }
1404
- async createExample(inputs, outputs, { datasetId, datasetName, createdAt, exampleId, metadata, }) {
1427
+ async createExample(inputs, outputs, { datasetId, datasetName, createdAt, exampleId, metadata, split, }) {
1405
1428
  let datasetId_ = datasetId;
1406
1429
  if (datasetId_ === undefined && datasetName === undefined) {
1407
1430
  throw new Error("Must provide either datasetName or datasetId");
@@ -1421,6 +1444,7 @@ export class Client {
1421
1444
  created_at: createdAt_?.toISOString(),
1422
1445
  id: exampleId,
1423
1446
  metadata,
1447
+ split,
1424
1448
  };
1425
1449
  const response = await this.caller.call(fetch, `${this.apiUrl}/examples`, {
1426
1450
  method: "POST",
@@ -1454,6 +1478,7 @@ export class Client {
1454
1478
  inputs: input,
1455
1479
  outputs: outputs ? outputs[idx] : undefined,
1456
1480
  metadata: metadata ? metadata[idx] : undefined,
1481
+ split: props.splits ? props.splits[idx] : undefined,
1457
1482
  id: exampleIds ? exampleIds[idx] : undefined,
1458
1483
  source_run_id: sourceRunIds ? sourceRunIds[idx] : undefined,
1459
1484
  };
@@ -1491,7 +1516,7 @@ export class Client {
1491
1516
  const path = `/examples/${exampleId}`;
1492
1517
  return await this._get(path);
1493
1518
  }
1494
- async *listExamples({ datasetId, datasetName, exampleIds, asOf, inlineS3Urls, metadata, } = {}) {
1519
+ async *listExamples({ datasetId, datasetName, exampleIds, asOf, splits, inlineS3Urls, metadata, } = {}) {
1495
1520
  let datasetId_;
1496
1521
  if (datasetId !== undefined && datasetName !== undefined) {
1497
1522
  throw new Error("Must provide either datasetName or datasetId, not both");
@@ -1522,6 +1547,11 @@ export class Client {
1522
1547
  params.append("id", id_);
1523
1548
  }
1524
1549
  }
1550
+ if (splits !== undefined) {
1551
+ for (const split of splits) {
1552
+ params.append("splits", split);
1553
+ }
1554
+ }
1525
1555
  if (metadata !== undefined) {
1526
1556
  const serializedMetadata = JSON.stringify(metadata);
1527
1557
  params.append("metadata", serializedMetadata);
@@ -8,6 +8,7 @@ const _uuid_js_1 = require("../utils/_uuid.cjs");
8
8
  const async_caller_js_1 = require("../utils/async_caller.cjs");
9
9
  const atee_js_1 = require("../utils/atee.cjs");
10
10
  const env_js_1 = require("../utils/env.cjs");
11
+ const error_js_1 = require("../utils/error.cjs");
11
12
  const _random_name_js_1 = require("./_random_name.cjs");
12
13
  const evaluator_js_1 = require("./evaluator.cjs");
13
14
  const uuid_1 = require("uuid");
@@ -238,15 +239,21 @@ class _ExperimentManager {
238
239
  }
239
240
  return project;
240
241
  }
241
- _printExperimentStart() {
242
- // @TODO log with experiment URL
242
+ async _printExperimentStart() {
243
243
  console.log(`Starting evaluation of experiment: ${this.experimentName}`);
244
+ const firstExample = this._examples?.[0];
245
+ const datasetId = firstExample?.dataset_id;
246
+ if (!datasetId || !this._experiment)
247
+ return;
248
+ const datasetUrl = await this.client.getDatasetUrl({ datasetId });
249
+ const compareUrl = `${datasetUrl}/compare?selectedSessions=${this._experiment.id}`;
250
+ console.log(`View results at ${compareUrl}`);
244
251
  }
245
252
  async start() {
246
253
  const examples = await this.getExamples();
247
254
  const firstExample = examples[0];
248
255
  const project = await this._getProject(firstExample);
249
- this._printExperimentStart();
256
+ await this._printExperimentStart();
250
257
  return new _ExperimentManager({
251
258
  examples,
252
259
  experiment: project,
@@ -377,7 +384,7 @@ class _ExperimentManager {
377
384
  try {
378
385
  const options = {
379
386
  reference_example_id: example.id,
380
- project_name: fields.experimentName,
387
+ project_name: "evaluators",
381
388
  metadata: {
382
389
  example_version: example.modified_at
383
390
  ? new Date(example.modified_at).toISOString()
@@ -390,6 +397,7 @@ class _ExperimentManager {
390
397
  }
391
398
  catch (e) {
392
399
  console.error(`Error running evaluator ${evaluator.evaluateRun.name} on run ${run.id}: ${e}`);
400
+ (0, error_js_1.printErrorStackTrace)(e);
393
401
  }
394
402
  }
395
403
  return {
@@ -410,7 +418,6 @@ class _ExperimentManager {
410
418
  if (maxConcurrency === 0) {
411
419
  for await (const currentResults of this.getResults()) {
412
420
  yield this._runEvaluators(evaluators, currentResults, {
413
- experimentName: this.experimentName,
414
421
  client: this.client,
415
422
  });
416
423
  }
@@ -422,7 +429,6 @@ class _ExperimentManager {
422
429
  const futures = [];
423
430
  for await (const currentResults of this.getResults()) {
424
431
  futures.push(caller.call(this._runEvaluators, evaluators, currentResults, {
425
- experimentName: this.experimentName,
426
432
  client: this.client,
427
433
  }));
428
434
  }
@@ -460,6 +466,7 @@ class _ExperimentManager {
460
466
  }
461
467
  catch (e) {
462
468
  console.error(`Error running summary evaluator ${evaluator.name}: ${JSON.stringify(e, null, 2)}`);
469
+ (0, error_js_1.printErrorStackTrace)(e);
463
470
  }
464
471
  }
465
472
  yield {
@@ -495,6 +502,21 @@ class _ExperimentManager {
495
502
  return undefined;
496
503
  return modifiedAtTime.reduce((max, current) => (current.time > max.time ? current : max), modifiedAtTime[0]).date;
497
504
  }
505
+ async _getDatasetSplits() {
506
+ const examples = await this.getExamples();
507
+ const allSplits = examples.reduce((acc, ex) => {
508
+ if (ex.metadata && ex.metadata.dataset_split) {
509
+ if (Array.isArray(ex.metadata.dataset_split)) {
510
+ ex.metadata.dataset_split.forEach((split) => acc.add(split));
511
+ }
512
+ else if (typeof ex.metadata.dataset_split === "string") {
513
+ acc.add(ex.metadata.dataset_split);
514
+ }
515
+ }
516
+ return acc;
517
+ }, new Set());
518
+ return allSplits.size ? Array.from(allSplits) : undefined;
519
+ }
498
520
  async _end() {
499
521
  const experiment = this._experiment;
500
522
  if (!experiment) {
@@ -502,6 +524,7 @@ class _ExperimentManager {
502
524
  }
503
525
  const projectMetadata = await this._getExperimentMetadata();
504
526
  projectMetadata["dataset_version"] = await this._getDatasetVersion();
527
+ projectMetadata["dataset_splits"] = await this._getDatasetSplits();
505
528
  // Update revision_id if not already set
506
529
  if (!projectMetadata["revision_id"]) {
507
530
  projectMetadata["revision_id"] = await (0, _git_js_1.getDefaultRevisionId)();
@@ -633,6 +656,7 @@ async function _forward(fn, example, experimentName, metadata, client) {
633
656
  }
634
657
  catch (e) {
635
658
  console.error(`Error running target function: ${e}`);
659
+ (0, error_js_1.printErrorStackTrace)(e);
636
660
  }
637
661
  if (!run) {
638
662
  throw new Error(`Run not created by target function.
@@ -104,7 +104,7 @@ declare class _ExperimentManager {
104
104
  _getExperiment(): TracerSession;
105
105
  _getExperimentMetadata(): Promise<KVMap>;
106
106
  _getProject(firstExample: Example): Promise<TracerSession>;
107
- _printExperimentStart(): void;
107
+ protected _printExperimentStart(): Promise<void>;
108
108
  start(): Promise<_ExperimentManager>;
109
109
  withPredictions(target: TargetNoInvoke, options?: {
110
110
  maxConcurrency?: number;
@@ -125,7 +125,6 @@ declare class _ExperimentManager {
125
125
  maxConcurrency?: number;
126
126
  }): AsyncGenerator<_ForwardResults>;
127
127
  _runEvaluators(evaluators: Array<RunEvaluator>, currentResults: ExperimentResultRow, fields: {
128
- experimentName: string;
129
128
  client: Client;
130
129
  }): Promise<ExperimentResultRow>;
131
130
  /**
@@ -140,6 +139,7 @@ declare class _ExperimentManager {
140
139
  }): AsyncGenerator<ExperimentResultRow>;
141
140
  _applySummaryEvaluators(summaryEvaluators: Array<SummaryEvaluatorT>): AsyncGenerator<(runsArray: Run[]) => AsyncGenerator<EvaluationResults>>;
142
141
  _getDatasetVersion(): Promise<string | undefined>;
142
+ _getDatasetSplits(): Promise<string[] | undefined>;
143
143
  _end(): Promise<void>;
144
144
  }
145
145
  /**
@@ -5,6 +5,7 @@ import { assertUuid } from "../utils/_uuid.js";
5
5
  import { AsyncCaller } from "../utils/async_caller.js";
6
6
  import { atee } from "../utils/atee.js";
7
7
  import { getLangChainEnvVarsMetadata } from "../utils/env.js";
8
+ import { printErrorStackTrace } from "../utils/error.js";
8
9
  import { randomName } from "./_random_name.js";
9
10
  import { runEvaluator, } from "./evaluator.js";
10
11
  import { v4 as uuidv4 } from "uuid";
@@ -234,15 +235,21 @@ class _ExperimentManager {
234
235
  }
235
236
  return project;
236
237
  }
237
- _printExperimentStart() {
238
- // @TODO log with experiment URL
238
+ async _printExperimentStart() {
239
239
  console.log(`Starting evaluation of experiment: ${this.experimentName}`);
240
+ const firstExample = this._examples?.[0];
241
+ const datasetId = firstExample?.dataset_id;
242
+ if (!datasetId || !this._experiment)
243
+ return;
244
+ const datasetUrl = await this.client.getDatasetUrl({ datasetId });
245
+ const compareUrl = `${datasetUrl}/compare?selectedSessions=${this._experiment.id}`;
246
+ console.log(`View results at ${compareUrl}`);
240
247
  }
241
248
  async start() {
242
249
  const examples = await this.getExamples();
243
250
  const firstExample = examples[0];
244
251
  const project = await this._getProject(firstExample);
245
- this._printExperimentStart();
252
+ await this._printExperimentStart();
246
253
  return new _ExperimentManager({
247
254
  examples,
248
255
  experiment: project,
@@ -373,7 +380,7 @@ class _ExperimentManager {
373
380
  try {
374
381
  const options = {
375
382
  reference_example_id: example.id,
376
- project_name: fields.experimentName,
383
+ project_name: "evaluators",
377
384
  metadata: {
378
385
  example_version: example.modified_at
379
386
  ? new Date(example.modified_at).toISOString()
@@ -386,6 +393,7 @@ class _ExperimentManager {
386
393
  }
387
394
  catch (e) {
388
395
  console.error(`Error running evaluator ${evaluator.evaluateRun.name} on run ${run.id}: ${e}`);
396
+ printErrorStackTrace(e);
389
397
  }
390
398
  }
391
399
  return {
@@ -406,7 +414,6 @@ class _ExperimentManager {
406
414
  if (maxConcurrency === 0) {
407
415
  for await (const currentResults of this.getResults()) {
408
416
  yield this._runEvaluators(evaluators, currentResults, {
409
- experimentName: this.experimentName,
410
417
  client: this.client,
411
418
  });
412
419
  }
@@ -418,7 +425,6 @@ class _ExperimentManager {
418
425
  const futures = [];
419
426
  for await (const currentResults of this.getResults()) {
420
427
  futures.push(caller.call(this._runEvaluators, evaluators, currentResults, {
421
- experimentName: this.experimentName,
422
428
  client: this.client,
423
429
  }));
424
430
  }
@@ -456,6 +462,7 @@ class _ExperimentManager {
456
462
  }
457
463
  catch (e) {
458
464
  console.error(`Error running summary evaluator ${evaluator.name}: ${JSON.stringify(e, null, 2)}`);
465
+ printErrorStackTrace(e);
459
466
  }
460
467
  }
461
468
  yield {
@@ -491,6 +498,21 @@ class _ExperimentManager {
491
498
  return undefined;
492
499
  return modifiedAtTime.reduce((max, current) => (current.time > max.time ? current : max), modifiedAtTime[0]).date;
493
500
  }
501
+ async _getDatasetSplits() {
502
+ const examples = await this.getExamples();
503
+ const allSplits = examples.reduce((acc, ex) => {
504
+ if (ex.metadata && ex.metadata.dataset_split) {
505
+ if (Array.isArray(ex.metadata.dataset_split)) {
506
+ ex.metadata.dataset_split.forEach((split) => acc.add(split));
507
+ }
508
+ else if (typeof ex.metadata.dataset_split === "string") {
509
+ acc.add(ex.metadata.dataset_split);
510
+ }
511
+ }
512
+ return acc;
513
+ }, new Set());
514
+ return allSplits.size ? Array.from(allSplits) : undefined;
515
+ }
494
516
  async _end() {
495
517
  const experiment = this._experiment;
496
518
  if (!experiment) {
@@ -498,6 +520,7 @@ class _ExperimentManager {
498
520
  }
499
521
  const projectMetadata = await this._getExperimentMetadata();
500
522
  projectMetadata["dataset_version"] = await this._getDatasetVersion();
523
+ projectMetadata["dataset_splits"] = await this._getDatasetSplits();
501
524
  // Update revision_id if not already set
502
525
  if (!projectMetadata["revision_id"]) {
503
526
  projectMetadata["revision_id"] = await getDefaultRevisionId();
@@ -629,6 +652,7 @@ async function _forward(fn, example, experimentName, metadata, client) {
629
652
  }
630
653
  catch (e) {
631
654
  console.error(`Error running target function: ${e}`);
655
+ printErrorStackTrace(e);
632
656
  }
633
657
  if (!run) {
634
658
  throw new Error(`Run not created by target function.
@@ -59,7 +59,12 @@ class DynamicRunEvaluator {
59
59
  if (typeof this.func !== "function") {
60
60
  throw new Error("Target must be runnable function");
61
61
  }
62
- const wrappedTraceableFunc = (0, traceable_js_1.traceable)(this.func, { project_name: "evaluators", name: "evaluator", ...options });
62
+ const wrappedTraceableFunc = (0, traceable_js_1.traceable)(this.func, {
63
+ project_name: "evaluators",
64
+ name: "evaluator",
65
+ id: sourceRunId,
66
+ ...options,
67
+ });
63
68
  const result = (await wrappedTraceableFunc(
64
69
  // Pass data via `langSmithRunAndExample` key to avoid conflicts with other
65
70
  // inputs. This key is extracted in the wrapped function, with `run` and
@@ -56,7 +56,12 @@ export class DynamicRunEvaluator {
56
56
  if (typeof this.func !== "function") {
57
57
  throw new Error("Target must be runnable function");
58
58
  }
59
- const wrappedTraceableFunc = traceable(this.func, { project_name: "evaluators", name: "evaluator", ...options });
59
+ const wrappedTraceableFunc = traceable(this.func, {
60
+ project_name: "evaluators",
61
+ name: "evaluator",
62
+ id: sourceRunId,
63
+ ...options,
64
+ });
60
65
  const result = (await wrappedTraceableFunc(
61
66
  // Pass data via `langSmithRunAndExample` key to avoid conflicts with other
62
67
  // inputs. This key is extracted in the wrapped function, with `run` and
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.24";
9
+ exports.__version__ = "0.1.26";
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { Client } from "./client.js";
2
- export type { Dataset, Example, TracerSession, Run, Feedback, } from "./schemas.js";
2
+ export type { Dataset, Example, TracerSession, Run, Feedback, RetrieverOutput, } from "./schemas.js";
3
3
  export { RunTree, type RunTreeConfig } from "./run_trees.js";
4
- export declare const __version__ = "0.1.24";
4
+ export declare const __version__ = "0.1.26";
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.24";
4
+ export const __version__ = "0.1.26";
package/dist/schemas.d.ts CHANGED
@@ -162,6 +162,7 @@ export interface RunUpdate {
162
162
  export interface ExampleCreate extends BaseExample {
163
163
  id?: string;
164
164
  created_at?: string;
165
+ split?: string | string[];
165
166
  }
166
167
  export interface Example extends BaseExample {
167
168
  id: string;
@@ -175,6 +176,7 @@ export interface ExampleUpdate {
175
176
  inputs?: KVMap;
176
177
  outputs?: KVMap;
177
178
  metadata?: KVMap;
179
+ split?: string | string[];
178
180
  }
179
181
  export interface BaseDataset {
180
182
  name: string;
@@ -293,4 +295,22 @@ export interface ComparativeExperiment {
293
295
  experiments_info?: Array<Record<string, unknown>>;
294
296
  feedback_stats?: Record<string, unknown>;
295
297
  }
298
+ /**
299
+ * Represents the expected output schema returned by traceable
300
+ * or by run tree output for LangSmith to correctly display
301
+ * documents in the UI
302
+ */
303
+ export type RetrieverOutput = Array<{
304
+ page_content: string;
305
+ type: "Document";
306
+ metadata?: KVMap;
307
+ }>;
308
+ export interface InvocationParamsSchema {
309
+ ls_provider?: string;
310
+ ls_model_name?: string;
311
+ ls_model_type: "chat";
312
+ ls_temperature?: number;
313
+ ls_max_tokens?: number;
314
+ ls_stop?: string[];
315
+ }
296
316
  export {};
@@ -63,12 +63,20 @@ const handleRunOutputs = (rawOutputs) => {
63
63
  }
64
64
  return { outputs: rawOutputs };
65
65
  };
66
- const getTracingRunTree = (runTree, inputs) => {
66
+ const getTracingRunTree = (runTree, inputs, getInvocationParams) => {
67
67
  const tracingEnabled_ = tracingIsEnabled(runTree.tracingEnabled);
68
68
  if (!tracingEnabled_) {
69
69
  return undefined;
70
70
  }
71
71
  runTree.inputs = handleRunInputs(inputs);
72
+ const invocationParams = getInvocationParams?.(...inputs);
73
+ if (invocationParams != null) {
74
+ runTree.extra ??= {};
75
+ runTree.extra.metadata = {
76
+ ...runTree.extra.metadata,
77
+ ...invocationParams,
78
+ };
79
+ }
72
80
  return runTree;
73
81
  };
74
82
  // idea: store the state of the promise outside
@@ -279,7 +287,7 @@ function traceable(wrappedFunc, config) {
279
287
  // used for handoff between LangChain.JS and traceable functions
280
288
  if ((0, run_trees_js_1.isRunnableConfigLike)(firstArg)) {
281
289
  return [
282
- getTracingRunTree(run_trees_js_1.RunTree.fromRunnableConfig(firstArg, ensuredConfig), restArgs),
290
+ getTracingRunTree(run_trees_js_1.RunTree.fromRunnableConfig(firstArg, ensuredConfig), restArgs, config?.getInvocationParams),
283
291
  restArgs,
284
292
  ];
285
293
  }
@@ -295,7 +303,7 @@ function traceable(wrappedFunc, config) {
295
303
  if (firstArg === exports.ROOT || (0, run_trees_js_1.isRunTree)(firstArg)) {
296
304
  const currentRunTree = getTracingRunTree(firstArg === exports.ROOT
297
305
  ? new run_trees_js_1.RunTree(ensuredConfig)
298
- : firstArg.createChild(ensuredConfig), restArgs);
306
+ : firstArg.createChild(ensuredConfig), restArgs, config?.getInvocationParams);
299
307
  return [currentRunTree, [currentRunTree, ...restArgs]];
300
308
  }
301
309
  // Node.JS uses AsyncLocalStorage (ALS) and AsyncResource
@@ -303,11 +311,11 @@ function traceable(wrappedFunc, config) {
303
311
  const prevRunFromStore = asyncLocalStorage.getStore();
304
312
  if (prevRunFromStore) {
305
313
  return [
306
- getTracingRunTree(prevRunFromStore.createChild(ensuredConfig), processedArgs),
314
+ getTracingRunTree(prevRunFromStore.createChild(ensuredConfig), processedArgs, config?.getInvocationParams),
307
315
  processedArgs,
308
316
  ];
309
317
  }
310
- const currentRunTree = getTracingRunTree(new run_trees_js_1.RunTree(ensuredConfig), processedArgs);
318
+ const currentRunTree = getTracingRunTree(new run_trees_js_1.RunTree(ensuredConfig), processedArgs, config?.getInvocationParams);
311
319
  return [currentRunTree, processedArgs];
312
320
  })();
313
321
  return asyncLocalStorage.run(currentRunTree, () => {
@@ -1,4 +1,5 @@
1
1
  import { RunTree, RunTreeConfig, RunnableConfigLike } from "./run_trees.js";
2
+ import { InvocationParamsSchema } from "./schemas.js";
2
3
  export declare const ROOT: unique symbol;
3
4
  export type RunTreeLike = RunTree;
4
5
  type SmartPromise<T> = T extends AsyncGenerator ? T : T extends Promise<unknown> ? T : Promise<T>;
@@ -52,6 +53,15 @@ export type TraceableFunction<Func extends (...args: any[]) => any> = Func exten
52
53
  export declare function traceable<Func extends (...args: any[]) => any>(wrappedFunc: Func, config?: Partial<RunTreeConfig> & {
53
54
  aggregator?: (args: any[]) => any;
54
55
  argsConfigPath?: [number] | [number, string];
56
+ /**
57
+ * Extract invocation parameters from the arguments of the traced function.
58
+ * This is useful for LangSmith to properly track common metadata like
59
+ * provider, model name and temperature.
60
+ *
61
+ * @param args Arguments of the traced function
62
+ * @returns Key-value map of the invocation parameters, which will be merged with the existing metadata
63
+ */
64
+ getInvocationParams?: (...args: Parameters<Func>) => InvocationParamsSchema | undefined;
55
65
  }): TraceableFunction<Func>;
56
66
  /**
57
67
  * Return the current run tree from within a traceable-wrapped function.
package/dist/traceable.js CHANGED
@@ -60,12 +60,20 @@ const handleRunOutputs = (rawOutputs) => {
60
60
  }
61
61
  return { outputs: rawOutputs };
62
62
  };
63
- const getTracingRunTree = (runTree, inputs) => {
63
+ const getTracingRunTree = (runTree, inputs, getInvocationParams) => {
64
64
  const tracingEnabled_ = tracingIsEnabled(runTree.tracingEnabled);
65
65
  if (!tracingEnabled_) {
66
66
  return undefined;
67
67
  }
68
68
  runTree.inputs = handleRunInputs(inputs);
69
+ const invocationParams = getInvocationParams?.(...inputs);
70
+ if (invocationParams != null) {
71
+ runTree.extra ??= {};
72
+ runTree.extra.metadata = {
73
+ ...runTree.extra.metadata,
74
+ ...invocationParams,
75
+ };
76
+ }
69
77
  return runTree;
70
78
  };
71
79
  // idea: store the state of the promise outside
@@ -276,7 +284,7 @@ export function traceable(wrappedFunc, config) {
276
284
  // used for handoff between LangChain.JS and traceable functions
277
285
  if (isRunnableConfigLike(firstArg)) {
278
286
  return [
279
- getTracingRunTree(RunTree.fromRunnableConfig(firstArg, ensuredConfig), restArgs),
287
+ getTracingRunTree(RunTree.fromRunnableConfig(firstArg, ensuredConfig), restArgs, config?.getInvocationParams),
280
288
  restArgs,
281
289
  ];
282
290
  }
@@ -292,7 +300,7 @@ export function traceable(wrappedFunc, config) {
292
300
  if (firstArg === ROOT || isRunTree(firstArg)) {
293
301
  const currentRunTree = getTracingRunTree(firstArg === ROOT
294
302
  ? new RunTree(ensuredConfig)
295
- : firstArg.createChild(ensuredConfig), restArgs);
303
+ : firstArg.createChild(ensuredConfig), restArgs, config?.getInvocationParams);
296
304
  return [currentRunTree, [currentRunTree, ...restArgs]];
297
305
  }
298
306
  // Node.JS uses AsyncLocalStorage (ALS) and AsyncResource
@@ -300,11 +308,11 @@ export function traceable(wrappedFunc, config) {
300
308
  const prevRunFromStore = asyncLocalStorage.getStore();
301
309
  if (prevRunFromStore) {
302
310
  return [
303
- getTracingRunTree(prevRunFromStore.createChild(ensuredConfig), processedArgs),
311
+ getTracingRunTree(prevRunFromStore.createChild(ensuredConfig), processedArgs, config?.getInvocationParams),
304
312
  processedArgs,
305
313
  ];
306
314
  }
307
- const currentRunTree = getTracingRunTree(new RunTree(ensuredConfig), processedArgs);
315
+ const currentRunTree = getTracingRunTree(new RunTree(ensuredConfig), processedArgs, config?.getInvocationParams);
308
316
  return [currentRunTree, processedArgs];
309
317
  })();
310
318
  return asyncLocalStorage.run(currentRunTree, () => {
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.printErrorStackTrace = void 0;
4
+ function getErrorStackTrace(e) {
5
+ if (typeof e !== "object" || e == null)
6
+ return undefined;
7
+ if (!("stack" in e) || typeof e.stack !== "string")
8
+ return undefined;
9
+ let stack = e.stack;
10
+ const prevLine = `${e}`;
11
+ if (stack.startsWith(prevLine)) {
12
+ stack = stack.slice(prevLine.length);
13
+ }
14
+ if (stack.startsWith("\n")) {
15
+ stack = stack.slice(1);
16
+ }
17
+ return stack;
18
+ }
19
+ function printErrorStackTrace(e) {
20
+ const stack = getErrorStackTrace(e);
21
+ if (stack == null)
22
+ return;
23
+ console.error(stack);
24
+ }
25
+ exports.printErrorStackTrace = printErrorStackTrace;
@@ -0,0 +1 @@
1
+ export declare function printErrorStackTrace(e: unknown): void;
@@ -0,0 +1,21 @@
1
+ function getErrorStackTrace(e) {
2
+ if (typeof e !== "object" || e == null)
3
+ return undefined;
4
+ if (!("stack" in e) || typeof e.stack !== "string")
5
+ return undefined;
6
+ let stack = e.stack;
7
+ const prevLine = `${e}`;
8
+ if (stack.startsWith(prevLine)) {
9
+ stack = stack.slice(prevLine.length);
10
+ }
11
+ if (stack.startsWith("\n")) {
12
+ stack = stack.slice(1);
13
+ }
14
+ return stack;
15
+ }
16
+ export function printErrorStackTrace(e) {
17
+ const stack = getErrorStackTrace(e);
18
+ if (stack == null)
19
+ return;
20
+ console.error(stack);
21
+ }
@@ -147,6 +147,22 @@ const wrapOpenAI = (openai, options) => {
147
147
  run_type: "llm",
148
148
  aggregator: chatAggregator,
149
149
  argsConfigPath: [1, "langsmithExtra"],
150
+ getInvocationParams: (payload) => {
151
+ if (typeof payload !== "object" || payload == null)
152
+ return undefined;
153
+ // we can safely do so, as the types are not exported in TSC
154
+ const params = payload;
155
+ const ls_stop = (typeof params.stop === "string" ? [params.stop] : params.stop) ??
156
+ undefined;
157
+ return {
158
+ ls_provider: "openai",
159
+ ls_model_type: "chat",
160
+ ls_model_name: params.model,
161
+ ls_max_tokens: params.max_tokens ?? undefined,
162
+ ls_temperature: params.temperature ?? undefined,
163
+ ls_stop,
164
+ };
165
+ },
150
166
  ...options,
151
167
  });
152
168
  openai.completions.create = (0, traceable_js_1.traceable)(openai.completions.create.bind(openai.completions), {
@@ -154,6 +170,22 @@ const wrapOpenAI = (openai, options) => {
154
170
  run_type: "llm",
155
171
  aggregator: textAggregator,
156
172
  argsConfigPath: [1, "langsmithExtra"],
173
+ getInvocationParams: (payload) => {
174
+ if (typeof payload !== "object" || payload == null)
175
+ return undefined;
176
+ // we can safely do so, as the types are not exported in TSC
177
+ const params = payload;
178
+ const ls_stop = (typeof params.stop === "string" ? [params.stop] : params.stop) ??
179
+ undefined;
180
+ return {
181
+ ls_provider: "openai",
182
+ ls_model_type: "chat",
183
+ ls_model_name: params.model,
184
+ ls_max_tokens: params.max_tokens ?? undefined,
185
+ ls_temperature: params.temperature ?? undefined,
186
+ ls_stop,
187
+ };
188
+ },
157
189
  ...options,
158
190
  });
159
191
  return openai;
@@ -144,6 +144,22 @@ export const wrapOpenAI = (openai, options) => {
144
144
  run_type: "llm",
145
145
  aggregator: chatAggregator,
146
146
  argsConfigPath: [1, "langsmithExtra"],
147
+ getInvocationParams: (payload) => {
148
+ if (typeof payload !== "object" || payload == null)
149
+ return undefined;
150
+ // we can safely do so, as the types are not exported in TSC
151
+ const params = payload;
152
+ const ls_stop = (typeof params.stop === "string" ? [params.stop] : params.stop) ??
153
+ undefined;
154
+ return {
155
+ ls_provider: "openai",
156
+ ls_model_type: "chat",
157
+ ls_model_name: params.model,
158
+ ls_max_tokens: params.max_tokens ?? undefined,
159
+ ls_temperature: params.temperature ?? undefined,
160
+ ls_stop,
161
+ };
162
+ },
147
163
  ...options,
148
164
  });
149
165
  openai.completions.create = traceable(openai.completions.create.bind(openai.completions), {
@@ -151,6 +167,22 @@ export const wrapOpenAI = (openai, options) => {
151
167
  run_type: "llm",
152
168
  aggregator: textAggregator,
153
169
  argsConfigPath: [1, "langsmithExtra"],
170
+ getInvocationParams: (payload) => {
171
+ if (typeof payload !== "object" || payload == null)
172
+ return undefined;
173
+ // we can safely do so, as the types are not exported in TSC
174
+ const params = payload;
175
+ const ls_stop = (typeof params.stop === "string" ? [params.stop] : params.stop) ??
176
+ undefined;
177
+ return {
178
+ ls_provider: "openai",
179
+ ls_model_type: "chat",
180
+ ls_model_name: params.model,
181
+ ls_max_tokens: params.max_tokens ?? undefined,
182
+ ls_temperature: params.temperature ?? undefined,
183
+ ls_stop,
184
+ };
185
+ },
154
186
  ...options,
155
187
  });
156
188
  return openai;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langsmith",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
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": [
@@ -85,6 +85,7 @@
85
85
  },
86
86
  "devDependencies": {
87
87
  "@babel/preset-env": "^7.22.4",
88
+ "@faker-js/faker": "^8.4.1",
88
89
  "@jest/globals": "^29.5.0",
89
90
  "@langchain/core": "^0.1.32",
90
91
  "@langchain/langgraph": "^0.0.8",
@@ -196,4 +197,4 @@
196
197
  },
197
198
  "./package.json": "./package.json"
198
199
  }
199
- }
200
+ }