langsmith 0.1.32 → 0.1.34
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 +1 -0
- package/anonymizer.d.cts +1 -0
- package/anonymizer.d.ts +1 -0
- package/anonymizer.js +1 -0
- package/dist/anonymizer/index.cjs +102 -0
- package/dist/anonymizer/index.d.ts +16 -0
- package/dist/anonymizer/index.js +95 -0
- package/dist/client.cjs +28 -2
- package/dist/client.d.ts +14 -2
- package/dist/client.js +28 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +16 -1
package/anonymizer.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./dist/anonymizer/index.cjs');
|
package/anonymizer.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './dist/anonymizer/index.js'
|
package/anonymizer.d.ts
ADDED
|
@@ -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,102 @@
|
|
|
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
|
+
return JSON.parse(JSON.stringify(data));
|
|
41
|
+
}
|
|
42
|
+
function createAnonymizer(replacer, options) {
|
|
43
|
+
return (data) => {
|
|
44
|
+
let mutateValue = deepClone(data);
|
|
45
|
+
const nodes = extractStringNodes(mutateValue, {
|
|
46
|
+
maxDepth: options?.maxDepth,
|
|
47
|
+
});
|
|
48
|
+
const processor = Array.isArray(replacer)
|
|
49
|
+
? (() => {
|
|
50
|
+
const replacers = replacer.map(({ pattern, type, replace }) => {
|
|
51
|
+
if (type != null && type !== "pattern")
|
|
52
|
+
throw new Error("Invalid anonymizer type");
|
|
53
|
+
return [
|
|
54
|
+
typeof pattern === "string"
|
|
55
|
+
? new RegExp(pattern, "g")
|
|
56
|
+
: pattern,
|
|
57
|
+
replace ?? "[redacted]",
|
|
58
|
+
];
|
|
59
|
+
});
|
|
60
|
+
if (replacers.length === 0)
|
|
61
|
+
throw new Error("No replacers provided");
|
|
62
|
+
return {
|
|
63
|
+
maskNodes: (nodes) => {
|
|
64
|
+
return nodes.reduce((memo, item) => {
|
|
65
|
+
const newValue = replacers.reduce((value, [regex, replace]) => {
|
|
66
|
+
const result = value.replace(regex, replace);
|
|
67
|
+
// make sure we reset the state of regex
|
|
68
|
+
regex.lastIndex = 0;
|
|
69
|
+
return result;
|
|
70
|
+
}, item.value);
|
|
71
|
+
if (newValue !== item.value) {
|
|
72
|
+
memo.push({ value: newValue, path: item.path });
|
|
73
|
+
}
|
|
74
|
+
return memo;
|
|
75
|
+
}, []);
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
})()
|
|
79
|
+
: typeof replacer === "function"
|
|
80
|
+
? {
|
|
81
|
+
maskNodes: (nodes) => nodes.reduce((memo, item) => {
|
|
82
|
+
const newValue = replacer(item.value, item.path);
|
|
83
|
+
if (newValue !== item.value) {
|
|
84
|
+
memo.push({ value: newValue, path: item.path });
|
|
85
|
+
}
|
|
86
|
+
return memo;
|
|
87
|
+
}, []),
|
|
88
|
+
}
|
|
89
|
+
: replacer;
|
|
90
|
+
const toUpdate = processor.maskNodes(nodes);
|
|
91
|
+
for (const node of toUpdate) {
|
|
92
|
+
if (node.path === "") {
|
|
93
|
+
mutateValue = node.value;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
(0, lodash_set_1.default)(mutateValue, node.path, node.value);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return mutateValue;
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
exports.createAnonymizer = createAnonymizer;
|
|
@@ -0,0 +1,16 @@
|
|
|
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
|
+
}): <T>(data: T) => T;
|
|
@@ -0,0 +1,95 @@
|
|
|
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
|
+
return JSON.parse(JSON.stringify(data));
|
|
35
|
+
}
|
|
36
|
+
export function createAnonymizer(replacer, options) {
|
|
37
|
+
return (data) => {
|
|
38
|
+
let mutateValue = deepClone(data);
|
|
39
|
+
const nodes = extractStringNodes(mutateValue, {
|
|
40
|
+
maxDepth: options?.maxDepth,
|
|
41
|
+
});
|
|
42
|
+
const processor = Array.isArray(replacer)
|
|
43
|
+
? (() => {
|
|
44
|
+
const replacers = replacer.map(({ pattern, type, replace }) => {
|
|
45
|
+
if (type != null && type !== "pattern")
|
|
46
|
+
throw new Error("Invalid anonymizer type");
|
|
47
|
+
return [
|
|
48
|
+
typeof pattern === "string"
|
|
49
|
+
? new RegExp(pattern, "g")
|
|
50
|
+
: pattern,
|
|
51
|
+
replace ?? "[redacted]",
|
|
52
|
+
];
|
|
53
|
+
});
|
|
54
|
+
if (replacers.length === 0)
|
|
55
|
+
throw new Error("No replacers provided");
|
|
56
|
+
return {
|
|
57
|
+
maskNodes: (nodes) => {
|
|
58
|
+
return nodes.reduce((memo, item) => {
|
|
59
|
+
const newValue = replacers.reduce((value, [regex, replace]) => {
|
|
60
|
+
const result = value.replace(regex, replace);
|
|
61
|
+
// make sure we reset the state of regex
|
|
62
|
+
regex.lastIndex = 0;
|
|
63
|
+
return result;
|
|
64
|
+
}, item.value);
|
|
65
|
+
if (newValue !== item.value) {
|
|
66
|
+
memo.push({ value: newValue, path: item.path });
|
|
67
|
+
}
|
|
68
|
+
return memo;
|
|
69
|
+
}, []);
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
})()
|
|
73
|
+
: typeof replacer === "function"
|
|
74
|
+
? {
|
|
75
|
+
maskNodes: (nodes) => nodes.reduce((memo, item) => {
|
|
76
|
+
const newValue = replacer(item.value, item.path);
|
|
77
|
+
if (newValue !== item.value) {
|
|
78
|
+
memo.push({ value: newValue, path: item.path });
|
|
79
|
+
}
|
|
80
|
+
return memo;
|
|
81
|
+
}, []),
|
|
82
|
+
}
|
|
83
|
+
: replacer;
|
|
84
|
+
const toUpdate = processor.maskNodes(nodes);
|
|
85
|
+
for (const node of toUpdate) {
|
|
86
|
+
if (node.path === "") {
|
|
87
|
+
mutateValue = node.value;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
set(mutateValue, node.path, node.value);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return mutateValue;
|
|
94
|
+
};
|
|
95
|
+
}
|
package/dist/client.cjs
CHANGED
|
@@ -279,8 +279,10 @@ class Client {
|
|
|
279
279
|
...(config.callerOptions ?? {}),
|
|
280
280
|
onFailedResponseHook: handle429,
|
|
281
281
|
});
|
|
282
|
-
this.hideInputs =
|
|
283
|
-
|
|
282
|
+
this.hideInputs =
|
|
283
|
+
config.hideInputs ?? config.anonymizer ?? defaultConfig.hideInputs;
|
|
284
|
+
this.hideOutputs =
|
|
285
|
+
config.hideOutputs ?? config.anonymizer ?? defaultConfig.hideOutputs;
|
|
284
286
|
this.autoBatchTracing = config.autoBatchTracing ?? this.autoBatchTracing;
|
|
285
287
|
this.pendingAutoBatchedRunLimit =
|
|
286
288
|
config.pendingAutoBatchedRunLimit ?? this.pendingAutoBatchedRunLimit;
|
|
@@ -1424,6 +1426,30 @@ class Client {
|
|
|
1424
1426
|
yield* datasets;
|
|
1425
1427
|
}
|
|
1426
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
|
+
}
|
|
1427
1453
|
async deleteDataset({ datasetId, datasetName, }) {
|
|
1428
1454
|
let path = "/datasets";
|
|
1429
1455
|
let datasetId_ = datasetId;
|
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
|
-
|
|
11
|
-
|
|
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;
|
package/dist/client.js
CHANGED
|
@@ -252,8 +252,10 @@ export class Client {
|
|
|
252
252
|
...(config.callerOptions ?? {}),
|
|
253
253
|
onFailedResponseHook: handle429,
|
|
254
254
|
});
|
|
255
|
-
this.hideInputs =
|
|
256
|
-
|
|
255
|
+
this.hideInputs =
|
|
256
|
+
config.hideInputs ?? config.anonymizer ?? defaultConfig.hideInputs;
|
|
257
|
+
this.hideOutputs =
|
|
258
|
+
config.hideOutputs ?? config.anonymizer ?? defaultConfig.hideOutputs;
|
|
257
259
|
this.autoBatchTracing = config.autoBatchTracing ?? this.autoBatchTracing;
|
|
258
260
|
this.pendingAutoBatchedRunLimit =
|
|
259
261
|
config.pendingAutoBatchedRunLimit ?? this.pendingAutoBatchedRunLimit;
|
|
@@ -1397,6 +1399,30 @@ export class Client {
|
|
|
1397
1399
|
yield* datasets;
|
|
1398
1400
|
}
|
|
1399
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
|
+
}
|
|
1400
1426
|
async deleteDataset({ datasetId, datasetName, }) {
|
|
1401
1427
|
let path = "/datasets";
|
|
1402
1428
|
let datasetId_ = datasetId;
|
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.
|
|
9
|
+
exports.__version__ = "0.1.34";
|
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.
|
|
4
|
+
export declare const __version__ = "0.1.34";
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "langsmith",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.34",
|
|
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",
|