langsmith 0.1.31 → 0.1.33

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/anonymizer.cjs ADDED
@@ -0,0 +1 @@
1
+ module.exports = require('./dist/anonymizer/index.cjs');
@@ -0,0 +1 @@
1
+ export * from './dist/anonymizer/index.js'
@@ -0,0 +1 @@
1
+ export * from './dist/anonymizer/index.js'
package/anonymizer.js ADDED
@@ -0,0 +1 @@
1
+ export * from './dist/anonymizer/index.js'
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createAnonymizer = void 0;
7
+ const lodash_set_1 = __importDefault(require("lodash.set"));
8
+ function extractStringNodes(data, options) {
9
+ const parsedOptions = { ...options, maxDepth: options.maxDepth ?? 10 };
10
+ const queue = [
11
+ [data, 0, ""],
12
+ ];
13
+ const result = [];
14
+ while (queue.length > 0) {
15
+ const task = queue.shift();
16
+ if (task == null)
17
+ continue;
18
+ const [value, depth, path] = task;
19
+ if (typeof value === "object" && value != null) {
20
+ if (depth >= parsedOptions.maxDepth)
21
+ continue;
22
+ for (const [key, nestedValue] of Object.entries(value)) {
23
+ queue.push([nestedValue, depth + 1, path ? `${path}.${key}` : key]);
24
+ }
25
+ }
26
+ else if (Array.isArray(value)) {
27
+ if (depth >= parsedOptions.maxDepth)
28
+ continue;
29
+ for (let i = 0; i < value.length; i++) {
30
+ queue.push([value[i], depth + 1, `${path}[${i}]`]);
31
+ }
32
+ }
33
+ else if (typeof value === "string") {
34
+ result.push({ value, path });
35
+ }
36
+ }
37
+ return result;
38
+ }
39
+ function deepClone(data) {
40
+ if ("structuredClone" in globalThis) {
41
+ return globalThis.structuredClone(data);
42
+ }
43
+ return JSON.parse(JSON.stringify(data));
44
+ }
45
+ function createAnonymizer(replacer, options) {
46
+ return (data) => {
47
+ const nodes = extractStringNodes(data, {
48
+ maxDepth: options?.maxDepth,
49
+ });
50
+ // by default we opt-in to mutate the value directly
51
+ // to improve performance
52
+ let mutateValue = options?.deepClone ? deepClone(data) : data;
53
+ const processor = Array.isArray(replacer)
54
+ ? (() => {
55
+ const replacers = replacer.map(({ pattern, type, replace }) => {
56
+ if (type != null && type !== "pattern")
57
+ throw new Error("Invalid anonymizer type");
58
+ return [
59
+ typeof pattern === "string"
60
+ ? new RegExp(pattern, "g")
61
+ : pattern,
62
+ replace ?? "[redacted]",
63
+ ];
64
+ });
65
+ if (replacers.length === 0)
66
+ throw new Error("No replacers provided");
67
+ return {
68
+ maskNodes: (nodes) => {
69
+ return nodes.reduce((memo, item) => {
70
+ const newValue = replacers.reduce((value, [regex, replace]) => {
71
+ const result = value.replace(regex, replace);
72
+ // make sure we reset the state of regex
73
+ regex.lastIndex = 0;
74
+ return result;
75
+ }, item.value);
76
+ if (newValue !== item.value) {
77
+ memo.push({ value: newValue, path: item.path });
78
+ }
79
+ return memo;
80
+ }, []);
81
+ },
82
+ };
83
+ })()
84
+ : typeof replacer === "function"
85
+ ? {
86
+ maskNodes: (nodes) => nodes.reduce((memo, item) => {
87
+ const newValue = replacer(item.value, item.path);
88
+ if (newValue !== item.value) {
89
+ memo.push({ value: newValue, path: item.path });
90
+ }
91
+ return memo;
92
+ }, []),
93
+ }
94
+ : replacer;
95
+ const toUpdate = processor.maskNodes(nodes);
96
+ for (const node of toUpdate) {
97
+ if (node.path === "") {
98
+ mutateValue = node.value;
99
+ }
100
+ else {
101
+ (0, lodash_set_1.default)(mutateValue, node.path, node.value);
102
+ }
103
+ }
104
+ return mutateValue;
105
+ };
106
+ }
107
+ exports.createAnonymizer = createAnonymizer;
@@ -0,0 +1,17 @@
1
+ export interface StringNode {
2
+ value: string;
3
+ path: string;
4
+ }
5
+ export interface StringNodeProcessor {
6
+ maskNodes: (nodes: StringNode[]) => StringNode[];
7
+ }
8
+ export interface StringNodeRule {
9
+ type?: "pattern";
10
+ pattern: RegExp | string;
11
+ replace?: string;
12
+ }
13
+ export type ReplacerType = ((value: string, path?: string) => string) | StringNodeRule[] | StringNodeProcessor;
14
+ export declare function createAnonymizer(replacer: ReplacerType, options?: {
15
+ maxDepth?: number;
16
+ deepClone?: boolean;
17
+ }): <T>(data: T) => T;
@@ -0,0 +1,100 @@
1
+ import set from "lodash.set";
2
+ function extractStringNodes(data, options) {
3
+ const parsedOptions = { ...options, maxDepth: options.maxDepth ?? 10 };
4
+ const queue = [
5
+ [data, 0, ""],
6
+ ];
7
+ const result = [];
8
+ while (queue.length > 0) {
9
+ const task = queue.shift();
10
+ if (task == null)
11
+ continue;
12
+ const [value, depth, path] = task;
13
+ if (typeof value === "object" && value != null) {
14
+ if (depth >= parsedOptions.maxDepth)
15
+ continue;
16
+ for (const [key, nestedValue] of Object.entries(value)) {
17
+ queue.push([nestedValue, depth + 1, path ? `${path}.${key}` : key]);
18
+ }
19
+ }
20
+ else if (Array.isArray(value)) {
21
+ if (depth >= parsedOptions.maxDepth)
22
+ continue;
23
+ for (let i = 0; i < value.length; i++) {
24
+ queue.push([value[i], depth + 1, `${path}[${i}]`]);
25
+ }
26
+ }
27
+ else if (typeof value === "string") {
28
+ result.push({ value, path });
29
+ }
30
+ }
31
+ return result;
32
+ }
33
+ function deepClone(data) {
34
+ if ("structuredClone" in globalThis) {
35
+ return globalThis.structuredClone(data);
36
+ }
37
+ return JSON.parse(JSON.stringify(data));
38
+ }
39
+ export function createAnonymizer(replacer, options) {
40
+ return (data) => {
41
+ const nodes = extractStringNodes(data, {
42
+ maxDepth: options?.maxDepth,
43
+ });
44
+ // by default we opt-in to mutate the value directly
45
+ // to improve performance
46
+ let mutateValue = options?.deepClone ? deepClone(data) : data;
47
+ const processor = Array.isArray(replacer)
48
+ ? (() => {
49
+ const replacers = replacer.map(({ pattern, type, replace }) => {
50
+ if (type != null && type !== "pattern")
51
+ throw new Error("Invalid anonymizer type");
52
+ return [
53
+ typeof pattern === "string"
54
+ ? new RegExp(pattern, "g")
55
+ : pattern,
56
+ replace ?? "[redacted]",
57
+ ];
58
+ });
59
+ if (replacers.length === 0)
60
+ throw new Error("No replacers provided");
61
+ return {
62
+ maskNodes: (nodes) => {
63
+ return nodes.reduce((memo, item) => {
64
+ const newValue = replacers.reduce((value, [regex, replace]) => {
65
+ const result = value.replace(regex, replace);
66
+ // make sure we reset the state of regex
67
+ regex.lastIndex = 0;
68
+ return result;
69
+ }, item.value);
70
+ if (newValue !== item.value) {
71
+ memo.push({ value: newValue, path: item.path });
72
+ }
73
+ return memo;
74
+ }, []);
75
+ },
76
+ };
77
+ })()
78
+ : typeof replacer === "function"
79
+ ? {
80
+ maskNodes: (nodes) => nodes.reduce((memo, item) => {
81
+ const newValue = replacer(item.value, item.path);
82
+ if (newValue !== item.value) {
83
+ memo.push({ value: newValue, path: item.path });
84
+ }
85
+ return memo;
86
+ }, []),
87
+ }
88
+ : replacer;
89
+ const toUpdate = processor.maskNodes(nodes);
90
+ for (const node of toUpdate) {
91
+ if (node.path === "") {
92
+ mutateValue = node.value;
93
+ }
94
+ else {
95
+ set(mutateValue, node.path, node.value);
96
+ }
97
+ }
98
+ return mutateValue;
99
+ };
100
+ }
package/dist/client.cjs CHANGED
@@ -30,6 +30,7 @@ const messages_js_1 = require("./utils/messages.cjs");
30
30
  const env_js_1 = require("./utils/env.cjs");
31
31
  const index_js_1 = require("./index.cjs");
32
32
  const _uuid_js_1 = require("./utils/_uuid.cjs");
33
+ const warn_js_1 = require("./utils/warn.cjs");
33
34
  async function mergeRuntimeEnvIntoRunCreates(runs) {
34
35
  const runtimeEnv = await (0, env_js_1.getRuntimeEnvironment)();
35
36
  const envVars = (0, env_js_1.getLangChainEnvVarsMetadata)();
@@ -278,8 +279,10 @@ class Client {
278
279
  ...(config.callerOptions ?? {}),
279
280
  onFailedResponseHook: handle429,
280
281
  });
281
- this.hideInputs = config.hideInputs ?? defaultConfig.hideInputs;
282
- this.hideOutputs = config.hideOutputs ?? defaultConfig.hideOutputs;
282
+ this.hideInputs =
283
+ config.hideInputs ?? config.anonymizer ?? defaultConfig.hideInputs;
284
+ this.hideOutputs =
285
+ config.hideOutputs ?? config.anonymizer ?? defaultConfig.hideOutputs;
283
286
  this.autoBatchTracing = config.autoBatchTracing ?? this.autoBatchTracing;
284
287
  this.pendingAutoBatchedRunLimit =
285
288
  config.pendingAutoBatchedRunLimit ?? this.pendingAutoBatchedRunLimit;
@@ -1423,6 +1426,30 @@ class Client {
1423
1426
  yield* datasets;
1424
1427
  }
1425
1428
  }
1429
+ /**
1430
+ * Update a dataset
1431
+ * @param props The dataset details to update
1432
+ * @returns The updated dataset
1433
+ */
1434
+ async updateDataset(props) {
1435
+ const { datasetId, datasetName, ...update } = props;
1436
+ if (!datasetId && !datasetName) {
1437
+ throw new Error("Must provide either datasetName or datasetId");
1438
+ }
1439
+ const _datasetId = datasetId ?? (await this.readDataset({ datasetName })).id;
1440
+ (0, _uuid_js_1.assertUuid)(_datasetId);
1441
+ const response = await this.caller.call(fetch, `${this.apiUrl}/datasets/${_datasetId}`, {
1442
+ method: "PATCH",
1443
+ headers: { ...this.headers, "Content-Type": "application/json" },
1444
+ body: JSON.stringify(update),
1445
+ signal: AbortSignal.timeout(this.timeout_ms),
1446
+ ...this.fetchOptions,
1447
+ });
1448
+ if (!response.ok) {
1449
+ throw new Error(`Failed to update dataset ${_datasetId}: ${response.status} ${response.statusText}`);
1450
+ }
1451
+ return (await response.json());
1452
+ }
1426
1453
  async deleteDataset({ datasetId, datasetName, }) {
1427
1454
  let path = "/datasets";
1428
1455
  let datasetId_ = datasetId;
@@ -1616,7 +1643,11 @@ class Client {
1616
1643
  const result = await response.json();
1617
1644
  return result;
1618
1645
  }
1646
+ /**
1647
+ * @deprecated This method is deprecated and will be removed in future LangSmith versions, use `evaluate` from `langsmith/evaluation` instead.
1648
+ */
1619
1649
  async evaluateRun(run, evaluator, { sourceInfo, loadChildRuns, referenceExample, } = { loadChildRuns: false }) {
1650
+ (0, warn_js_1.warnOnce)("This method is deprecated and will be removed in future LangSmith versions, use `evaluate` from `langsmith/evaluation` instead.");
1620
1651
  let run_;
1621
1652
  if (typeof run === "string") {
1622
1653
  run_ = await this.readRun(run, { loadChildRuns });
@@ -1632,20 +1663,8 @@ class Client {
1632
1663
  referenceExample = await this.readExample(run_.reference_example_id);
1633
1664
  }
1634
1665
  const feedbackResult = await evaluator.evaluateRun(run_, referenceExample);
1635
- let sourceInfo_ = sourceInfo ?? {};
1636
- if (feedbackResult.evaluatorInfo) {
1637
- sourceInfo_ = { ...sourceInfo_, ...feedbackResult.evaluatorInfo };
1638
- }
1639
- const runId = feedbackResult.targetRunId ?? run_.id;
1640
- return await this.createFeedback(runId, feedbackResult.key, {
1641
- score: feedbackResult?.score,
1642
- value: feedbackResult?.value,
1643
- comment: feedbackResult?.comment,
1644
- correction: feedbackResult?.correction,
1645
- sourceInfo: sourceInfo_,
1646
- feedbackSourceType: "model",
1647
- sourceRunId: feedbackResult?.sourceRunId,
1648
- });
1666
+ const [_, feedbacks] = await this._logEvaluationFeedback(feedbackResult, run_, sourceInfo);
1667
+ return feedbacks[0];
1649
1668
  }
1650
1669
  async createFeedback(runId, key, { score, value, correction, comment, sourceInfo, feedbackSourceType = "api", sourceRunId, feedbackId, feedbackConfig, projectId, comparativeExperimentId, }) {
1651
1670
  if (!runId && !projectId) {
@@ -1852,9 +1871,10 @@ class Client {
1852
1871
  }
1853
1872
  return results_;
1854
1873
  }
1855
- async logEvaluationFeedback(evaluatorResponse, run, sourceInfo) {
1856
- const results = this._selectEvalResults(evaluatorResponse);
1857
- for (const res of results) {
1874
+ async _logEvaluationFeedback(evaluatorResponse, run, sourceInfo) {
1875
+ const evalResults = this._selectEvalResults(evaluatorResponse);
1876
+ const feedbacks = [];
1877
+ for (const res of evalResults) {
1858
1878
  let sourceInfo_ = sourceInfo || {};
1859
1879
  if (res.evaluatorInfo) {
1860
1880
  sourceInfo_ = { ...res.evaluatorInfo, ...sourceInfo_ };
@@ -1866,7 +1886,7 @@ class Client {
1866
1886
  else if (run) {
1867
1887
  runId_ = run.id;
1868
1888
  }
1869
- await this.createFeedback(runId_, res.key, {
1889
+ feedbacks.push(await this.createFeedback(runId_, res.key, {
1870
1890
  score: res.score,
1871
1891
  value: res.value,
1872
1892
  comment: res.comment,
@@ -1875,8 +1895,12 @@ class Client {
1875
1895
  sourceRunId: res.sourceRunId,
1876
1896
  feedbackConfig: res.feedbackConfig,
1877
1897
  feedbackSourceType: "model",
1878
- });
1898
+ }));
1879
1899
  }
1900
+ return [evalResults, feedbacks];
1901
+ }
1902
+ async logEvaluationFeedback(evaluatorResponse, run, sourceInfo) {
1903
+ const [results] = await this._logEvaluationFeedback(evaluatorResponse, run, sourceInfo);
1880
1904
  return results;
1881
1905
  }
1882
1906
  }
package/dist/client.d.ts CHANGED
@@ -7,8 +7,9 @@ interface ClientConfig {
7
7
  callerOptions?: AsyncCallerParams;
8
8
  timeout_ms?: number;
9
9
  webUrl?: string;
10
- hideInputs?: boolean;
11
- hideOutputs?: boolean;
10
+ anonymizer?: (values: KVMap) => KVMap;
11
+ hideInputs?: boolean | ((inputs: KVMap) => KVMap);
12
+ hideOutputs?: boolean | ((outputs: KVMap) => KVMap);
12
13
  autoBatchTracing?: boolean;
13
14
  pendingAutoBatchedRunLimit?: number;
14
15
  fetchOptions?: RequestInit;
@@ -386,6 +387,17 @@ export declare class Client {
386
387
  datasetName?: string;
387
388
  datasetNameContains?: string;
388
389
  }): AsyncIterable<Dataset>;
390
+ /**
391
+ * Update a dataset
392
+ * @param props The dataset details to update
393
+ * @returns The updated dataset
394
+ */
395
+ updateDataset(props: {
396
+ datasetId?: string;
397
+ datasetName?: string;
398
+ name?: string;
399
+ description?: string;
400
+ }): Promise<Dataset>;
389
401
  deleteDataset({ datasetId, datasetName, }: {
390
402
  datasetId?: string;
391
403
  datasetName?: string;
@@ -415,6 +427,9 @@ export declare class Client {
415
427
  }): AsyncIterable<Example>;
416
428
  deleteExample(exampleId: string): Promise<void>;
417
429
  updateExample(exampleId: string, update: ExampleUpdate): Promise<object>;
430
+ /**
431
+ * @deprecated This method is deprecated and will be removed in future LangSmith versions, use `evaluate` from `langsmith/evaluation` instead.
432
+ */
418
433
  evaluateRun(run: Run | string, evaluator: RunEvaluator, { sourceInfo, loadChildRuns, referenceExample, }?: {
419
434
  sourceInfo?: KVMap;
420
435
  loadChildRuns: boolean;
@@ -482,6 +497,9 @@ export declare class Client {
482
497
  */
483
498
  listPresignedFeedbackTokens(runId: string): AsyncIterable<FeedbackIngestToken>;
484
499
  _selectEvalResults(results: EvaluationResult | EvaluationResults): Array<EvaluationResult>;
500
+ _logEvaluationFeedback(evaluatorResponse: EvaluationResult | EvaluationResults, run?: Run, sourceInfo?: {
501
+ [key: string]: any;
502
+ }): Promise<[results: EvaluationResult[], feedbacks: Feedback[]]>;
485
503
  logEvaluationFeedback(evaluatorResponse: EvaluationResult | EvaluationResults, run?: Run, sourceInfo?: {
486
504
  [key: string]: any;
487
505
  }): Promise<EvaluationResult[]>;
package/dist/client.js CHANGED
@@ -4,6 +4,7 @@ import { convertLangChainMessageToExample, isLangChainMessage, } from "./utils/m
4
4
  import { getEnvironmentVariable, getLangChainEnvVarsMetadata, getRuntimeEnvironment, } from "./utils/env.js";
5
5
  import { __version__ } from "./index.js";
6
6
  import { assertUuid } from "./utils/_uuid.js";
7
+ import { warnOnce } from "./utils/warn.js";
7
8
  async function mergeRuntimeEnvIntoRunCreates(runs) {
8
9
  const runtimeEnv = await getRuntimeEnvironment();
9
10
  const envVars = getLangChainEnvVarsMetadata();
@@ -251,8 +252,10 @@ export class Client {
251
252
  ...(config.callerOptions ?? {}),
252
253
  onFailedResponseHook: handle429,
253
254
  });
254
- this.hideInputs = config.hideInputs ?? defaultConfig.hideInputs;
255
- this.hideOutputs = config.hideOutputs ?? defaultConfig.hideOutputs;
255
+ this.hideInputs =
256
+ config.hideInputs ?? config.anonymizer ?? defaultConfig.hideInputs;
257
+ this.hideOutputs =
258
+ config.hideOutputs ?? config.anonymizer ?? defaultConfig.hideOutputs;
256
259
  this.autoBatchTracing = config.autoBatchTracing ?? this.autoBatchTracing;
257
260
  this.pendingAutoBatchedRunLimit =
258
261
  config.pendingAutoBatchedRunLimit ?? this.pendingAutoBatchedRunLimit;
@@ -1396,6 +1399,30 @@ export class Client {
1396
1399
  yield* datasets;
1397
1400
  }
1398
1401
  }
1402
+ /**
1403
+ * Update a dataset
1404
+ * @param props The dataset details to update
1405
+ * @returns The updated dataset
1406
+ */
1407
+ async updateDataset(props) {
1408
+ const { datasetId, datasetName, ...update } = props;
1409
+ if (!datasetId && !datasetName) {
1410
+ throw new Error("Must provide either datasetName or datasetId");
1411
+ }
1412
+ const _datasetId = datasetId ?? (await this.readDataset({ datasetName })).id;
1413
+ assertUuid(_datasetId);
1414
+ const response = await this.caller.call(fetch, `${this.apiUrl}/datasets/${_datasetId}`, {
1415
+ method: "PATCH",
1416
+ headers: { ...this.headers, "Content-Type": "application/json" },
1417
+ body: JSON.stringify(update),
1418
+ signal: AbortSignal.timeout(this.timeout_ms),
1419
+ ...this.fetchOptions,
1420
+ });
1421
+ if (!response.ok) {
1422
+ throw new Error(`Failed to update dataset ${_datasetId}: ${response.status} ${response.statusText}`);
1423
+ }
1424
+ return (await response.json());
1425
+ }
1399
1426
  async deleteDataset({ datasetId, datasetName, }) {
1400
1427
  let path = "/datasets";
1401
1428
  let datasetId_ = datasetId;
@@ -1589,7 +1616,11 @@ export class Client {
1589
1616
  const result = await response.json();
1590
1617
  return result;
1591
1618
  }
1619
+ /**
1620
+ * @deprecated This method is deprecated and will be removed in future LangSmith versions, use `evaluate` from `langsmith/evaluation` instead.
1621
+ */
1592
1622
  async evaluateRun(run, evaluator, { sourceInfo, loadChildRuns, referenceExample, } = { loadChildRuns: false }) {
1623
+ warnOnce("This method is deprecated and will be removed in future LangSmith versions, use `evaluate` from `langsmith/evaluation` instead.");
1593
1624
  let run_;
1594
1625
  if (typeof run === "string") {
1595
1626
  run_ = await this.readRun(run, { loadChildRuns });
@@ -1605,20 +1636,8 @@ export class Client {
1605
1636
  referenceExample = await this.readExample(run_.reference_example_id);
1606
1637
  }
1607
1638
  const feedbackResult = await evaluator.evaluateRun(run_, referenceExample);
1608
- let sourceInfo_ = sourceInfo ?? {};
1609
- if (feedbackResult.evaluatorInfo) {
1610
- sourceInfo_ = { ...sourceInfo_, ...feedbackResult.evaluatorInfo };
1611
- }
1612
- const runId = feedbackResult.targetRunId ?? run_.id;
1613
- return await this.createFeedback(runId, feedbackResult.key, {
1614
- score: feedbackResult?.score,
1615
- value: feedbackResult?.value,
1616
- comment: feedbackResult?.comment,
1617
- correction: feedbackResult?.correction,
1618
- sourceInfo: sourceInfo_,
1619
- feedbackSourceType: "model",
1620
- sourceRunId: feedbackResult?.sourceRunId,
1621
- });
1639
+ const [_, feedbacks] = await this._logEvaluationFeedback(feedbackResult, run_, sourceInfo);
1640
+ return feedbacks[0];
1622
1641
  }
1623
1642
  async createFeedback(runId, key, { score, value, correction, comment, sourceInfo, feedbackSourceType = "api", sourceRunId, feedbackId, feedbackConfig, projectId, comparativeExperimentId, }) {
1624
1643
  if (!runId && !projectId) {
@@ -1825,9 +1844,10 @@ export class Client {
1825
1844
  }
1826
1845
  return results_;
1827
1846
  }
1828
- async logEvaluationFeedback(evaluatorResponse, run, sourceInfo) {
1829
- const results = this._selectEvalResults(evaluatorResponse);
1830
- for (const res of results) {
1847
+ async _logEvaluationFeedback(evaluatorResponse, run, sourceInfo) {
1848
+ const evalResults = this._selectEvalResults(evaluatorResponse);
1849
+ const feedbacks = [];
1850
+ for (const res of evalResults) {
1831
1851
  let sourceInfo_ = sourceInfo || {};
1832
1852
  if (res.evaluatorInfo) {
1833
1853
  sourceInfo_ = { ...res.evaluatorInfo, ...sourceInfo_ };
@@ -1839,7 +1859,7 @@ export class Client {
1839
1859
  else if (run) {
1840
1860
  runId_ = run.id;
1841
1861
  }
1842
- await this.createFeedback(runId_, res.key, {
1862
+ feedbacks.push(await this.createFeedback(runId_, res.key, {
1843
1863
  score: res.score,
1844
1864
  value: res.value,
1845
1865
  comment: res.comment,
@@ -1848,8 +1868,12 @@ export class Client {
1848
1868
  sourceRunId: res.sourceRunId,
1849
1869
  feedbackConfig: res.feedbackConfig,
1850
1870
  feedbackSourceType: "model",
1851
- });
1871
+ }));
1852
1872
  }
1873
+ return [evalResults, feedbacks];
1874
+ }
1875
+ async logEvaluationFeedback(evaluatorResponse, run, sourceInfo) {
1876
+ const [results] = await this._logEvaluationFeedback(evaluatorResponse, run, sourceInfo);
1853
1877
  return results;
1854
1878
  }
1855
1879
  }
@@ -8,7 +8,7 @@ type TargetT<TInput = any, TOutput = KVMap> = ((input: TInput, config?: KVMap) =
8
8
  };
9
9
  type DataT = string | AsyncIterable<Example> | Example[];
10
10
  type SummaryEvaluatorT = ((runs: Array<Run>, examples: Array<Example>) => Promise<EvaluationResult | EvaluationResults>) | ((runs: Array<Run>, examples: Array<Example>) => EvaluationResult | EvaluationResults);
11
- type EvaluatorT = RunEvaluator | ((run: Run, example?: Example) => EvaluationResult) | ((run: Run, example?: Example) => Promise<EvaluationResult>);
11
+ type EvaluatorT = RunEvaluator | ((run: Run, example?: Example) => EvaluationResult | EvaluationResults) | ((run: Run, example?: Example) => Promise<EvaluationResult | EvaluationResults>);
12
12
  interface _ForwardResults {
13
13
  run: Run;
14
14
  example: Example;
@@ -19,9 +19,18 @@ class DynamicRunEvaluator {
19
19
  return evaluator(run, example);
20
20
  });
21
21
  }
22
+ isEvaluationResults(x) {
23
+ return (typeof x === "object" &&
24
+ x != null &&
25
+ "results" in x &&
26
+ Array.isArray(x.results) &&
27
+ x.results.length > 0);
28
+ }
22
29
  coerceEvaluationResults(results, sourceRunId) {
23
- if ("results" in results) {
24
- throw new Error("EvaluationResults not supported yet.");
30
+ if (this.isEvaluationResults(results)) {
31
+ return {
32
+ results: results.results.map((r) => this.coerceEvaluationResult(r, sourceRunId, false)),
33
+ };
25
34
  }
26
35
  return this.coerceEvaluationResult(results, sourceRunId, true);
27
36
  }
@@ -70,7 +70,7 @@ export type EvaluationResults = {
70
70
  results: Array<EvaluationResult>;
71
71
  };
72
72
  export interface RunEvaluator {
73
- evaluateRun(run: Run, example?: Example, options?: Partial<RunTreeConfig>): Promise<EvaluationResult>;
73
+ evaluateRun(run: Run, example?: Example, options?: Partial<RunTreeConfig>): Promise<EvaluationResult | EvaluationResults>;
74
74
  }
75
75
  export type RunEvaluatorLike = ((run: Run, example?: Example) => Promise<EvaluationResult | EvaluationResults>) | ((run: Run, example?: Example) => EvaluationResult | EvaluationResults);
76
76
  /**
@@ -79,6 +79,7 @@ export type RunEvaluatorLike = ((run: Run, example?: Example) => Promise<Evaluat
79
79
  export declare class DynamicRunEvaluator<Func extends (...args: any[]) => any> implements RunEvaluator {
80
80
  func: Func;
81
81
  constructor(evaluator: Func);
82
+ private isEvaluationResults;
82
83
  private coerceEvaluationResults;
83
84
  private coerceEvaluationResult;
84
85
  /**
@@ -87,6 +88,6 @@ export declare class DynamicRunEvaluator<Func extends (...args: any[]) => any> i
87
88
  * @param example The optional example to use for evaluation.
88
89
  * @returns A promise that extracts to the evaluation result.
89
90
  */
90
- evaluateRun(run: Run, example?: Example, options?: Partial<RunTreeConfig>): Promise<EvaluationResult>;
91
+ evaluateRun(run: Run, example?: Example, options?: Partial<RunTreeConfig>): Promise<EvaluationResult | EvaluationResults>;
91
92
  }
92
93
  export declare function runEvaluator(func: RunEvaluatorLike): RunEvaluator;
@@ -16,9 +16,18 @@ export class DynamicRunEvaluator {
16
16
  return evaluator(run, example);
17
17
  });
18
18
  }
19
+ isEvaluationResults(x) {
20
+ return (typeof x === "object" &&
21
+ x != null &&
22
+ "results" in x &&
23
+ Array.isArray(x.results) &&
24
+ x.results.length > 0);
25
+ }
19
26
  coerceEvaluationResults(results, sourceRunId) {
20
- if ("results" in results) {
21
- throw new Error("EvaluationResults not supported yet.");
27
+ if (this.isEvaluationResults(results)) {
28
+ return {
29
+ results: results.results.map((r) => this.coerceEvaluationResult(r, sourceRunId, false)),
30
+ };
22
31
  }
23
32
  return this.coerceEvaluationResult(results, sourceRunId, true);
24
33
  }
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.31";
9
+ exports.__version__ = "0.1.33";
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, RetrieverOutput, } from "./schemas.js";
3
3
  export { RunTree, type RunTreeConfig } from "./run_trees.js";
4
- export declare const __version__ = "0.1.31";
4
+ export declare const __version__ = "0.1.33";
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.31";
4
+ export const __version__ = "0.1.33";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langsmith",
3
- "version": "0.1.31",
3
+ "version": "0.1.33",
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": [
@@ -37,6 +37,10 @@
37
37
  "wrappers.js",
38
38
  "wrappers.d.ts",
39
39
  "wrappers.d.cts",
40
+ "anonymizer.cjs",
41
+ "anonymizer.js",
42
+ "anonymizer.d.ts",
43
+ "anonymizer.d.cts",
40
44
  "wrappers/openai.cjs",
41
45
  "wrappers/openai.js",
42
46
  "wrappers/openai.d.ts",
@@ -91,6 +95,7 @@
91
95
  "dependencies": {
92
96
  "@types/uuid": "^9.0.1",
93
97
  "commander": "^10.0.1",
98
+ "lodash.set": "^4.3.2",
94
99
  "p-queue": "^6.6.2",
95
100
  "p-retry": "4",
96
101
  "uuid": "^9.0.0"
@@ -104,6 +109,7 @@
104
109
  "@langchain/langgraph": "^0.0.19",
105
110
  "@tsconfig/recommended": "^1.0.2",
106
111
  "@types/jest": "^29.5.1",
112
+ "@types/lodash.set": "^4.3.9",
107
113
  "@typescript-eslint/eslint-plugin": "^5.59.8",
108
114
  "@typescript-eslint/parser": "^5.59.8",
109
115
  "babel-jest": "^29.5.0",
@@ -228,6 +234,15 @@
228
234
  "import": "./wrappers.js",
229
235
  "require": "./wrappers.cjs"
230
236
  },
237
+ "./anonymizer": {
238
+ "types": {
239
+ "import": "./anonymizer.d.ts",
240
+ "require": "./anonymizer.d.cts",
241
+ "default": "./anonymizer.d.ts"
242
+ },
243
+ "import": "./anonymizer.js",
244
+ "require": "./anonymizer.cjs"
245
+ },
231
246
  "./wrappers/openai": {
232
247
  "types": {
233
248
  "import": "./wrappers/openai.d.ts",