langsmith 0.1.23 → 0.1.24
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 +46 -5
- package/dist/client.d.ts +17 -3
- package/dist/client.js +46 -5
- package/dist/evaluation/_runner.cjs +25 -7
- package/dist/evaluation/_runner.d.ts +4 -4
- package/dist/evaluation/_runner.js +25 -7
- package/dist/evaluation/evaluate_comparative.cjs +202 -0
- package/dist/evaluation/evaluate_comparative.d.ts +51 -0
- package/dist/evaluation/evaluate_comparative.js +195 -0
- package/dist/evaluation/evaluator.cjs +10 -13
- package/dist/evaluation/evaluator.js +11 -14
- package/dist/evaluation/index.cjs +3 -1
- package/dist/evaluation/index.d.ts +1 -0
- package/dist/evaluation/index.js +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/run_trees.cjs +1 -0
- package/dist/run_trees.js +1 -0
- package/dist/schemas.d.ts +17 -0
- package/dist/traceable.cjs +209 -23
- package/dist/traceable.js +206 -20
- package/dist/utils/shuffle.cjs +15 -0
- package/dist/utils/shuffle.d.ts +1 -0
- package/dist/utils/shuffle.js +11 -0
- package/package.json +1 -1
package/dist/traceable.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import { RunTree, isRunTree, isRunnableConfigLike, } from "./run_trees.js";
|
|
3
3
|
import { getEnvironmentVariable } from "./utils/env.js";
|
|
4
4
|
function isPromiseMethod(x) {
|
|
@@ -13,6 +13,22 @@ const isAsyncIterable = (x) => x != null &&
|
|
|
13
13
|
typeof x === "object" &&
|
|
14
14
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
15
|
typeof x[Symbol.asyncIterator] === "function";
|
|
16
|
+
const GeneratorFunction = function* () { }.constructor;
|
|
17
|
+
const isIteratorLike = (x) => x != null &&
|
|
18
|
+
typeof x === "object" &&
|
|
19
|
+
"next" in x &&
|
|
20
|
+
typeof x.next === "function";
|
|
21
|
+
const isGenerator = (x) =>
|
|
22
|
+
// eslint-disable-next-line no-instanceof/no-instanceof
|
|
23
|
+
x != null && typeof x === "function" && x instanceof GeneratorFunction;
|
|
24
|
+
const isThenable = (x) => x != null &&
|
|
25
|
+
typeof x === "object" &&
|
|
26
|
+
"then" in x &&
|
|
27
|
+
typeof x.then === "function";
|
|
28
|
+
const isReadableStream = (x) => x != null &&
|
|
29
|
+
typeof x === "object" &&
|
|
30
|
+
"getReader" in x &&
|
|
31
|
+
typeof x.getReader === "function";
|
|
16
32
|
const tracingIsEnabled = (tracingEnabled) => {
|
|
17
33
|
if (tracingEnabled !== undefined) {
|
|
18
34
|
return tracingEnabled;
|
|
@@ -52,6 +68,141 @@ const getTracingRunTree = (runTree, inputs) => {
|
|
|
52
68
|
runTree.inputs = handleRunInputs(inputs);
|
|
53
69
|
return runTree;
|
|
54
70
|
};
|
|
71
|
+
// idea: store the state of the promise outside
|
|
72
|
+
// but only when the promise is "consumed"
|
|
73
|
+
const getSerializablePromise = (arg) => {
|
|
74
|
+
const proxyState = { current: undefined };
|
|
75
|
+
const promiseProxy = new Proxy(arg, {
|
|
76
|
+
get(target, prop, receiver) {
|
|
77
|
+
if (prop === "then") {
|
|
78
|
+
const boundThen = arg[prop].bind(arg);
|
|
79
|
+
return (resolve, reject = (x) => {
|
|
80
|
+
throw x;
|
|
81
|
+
}) => {
|
|
82
|
+
return boundThen((value) => {
|
|
83
|
+
proxyState.current = ["resolve", value];
|
|
84
|
+
return resolve(value);
|
|
85
|
+
}, (error) => {
|
|
86
|
+
proxyState.current = ["reject", error];
|
|
87
|
+
return reject(error);
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (prop === "catch") {
|
|
92
|
+
const boundCatch = arg[prop].bind(arg);
|
|
93
|
+
return (reject) => {
|
|
94
|
+
return boundCatch((error) => {
|
|
95
|
+
proxyState.current = ["reject", error];
|
|
96
|
+
return reject(error);
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (prop === "toJSON") {
|
|
101
|
+
return () => {
|
|
102
|
+
if (!proxyState.current)
|
|
103
|
+
return undefined;
|
|
104
|
+
const [type, value] = proxyState.current ?? [];
|
|
105
|
+
if (type === "resolve")
|
|
106
|
+
return value;
|
|
107
|
+
return { error: value };
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return Reflect.get(target, prop, receiver);
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
return promiseProxy;
|
|
114
|
+
};
|
|
115
|
+
const convertSerializableArg = (arg) => {
|
|
116
|
+
if (isReadableStream(arg)) {
|
|
117
|
+
const proxyState = [];
|
|
118
|
+
const transform = new TransformStream({
|
|
119
|
+
start: () => void 0,
|
|
120
|
+
transform: (chunk, controller) => {
|
|
121
|
+
proxyState.push(chunk);
|
|
122
|
+
controller.enqueue(chunk);
|
|
123
|
+
},
|
|
124
|
+
flush: () => void 0,
|
|
125
|
+
});
|
|
126
|
+
const pipeThrough = arg.pipeThrough(transform);
|
|
127
|
+
Object.assign(pipeThrough, { toJSON: () => proxyState });
|
|
128
|
+
return pipeThrough;
|
|
129
|
+
}
|
|
130
|
+
if (isAsyncIterable(arg)) {
|
|
131
|
+
const proxyState = { current: [] };
|
|
132
|
+
return new Proxy(arg, {
|
|
133
|
+
get(target, prop, receiver) {
|
|
134
|
+
if (prop === Symbol.asyncIterator) {
|
|
135
|
+
return () => {
|
|
136
|
+
const boundIterator = arg[Symbol.asyncIterator].bind(arg);
|
|
137
|
+
const iterator = boundIterator();
|
|
138
|
+
return new Proxy(iterator, {
|
|
139
|
+
get(target, prop, receiver) {
|
|
140
|
+
if (prop === "next" || prop === "return" || prop === "throw") {
|
|
141
|
+
const bound = iterator.next.bind(iterator);
|
|
142
|
+
return (...args) => {
|
|
143
|
+
// @ts-expect-error TS cannot infer the argument types for the bound function
|
|
144
|
+
const wrapped = getSerializablePromise(bound(...args));
|
|
145
|
+
proxyState.current.push(wrapped);
|
|
146
|
+
return wrapped;
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
if (prop === "return" || prop === "throw") {
|
|
150
|
+
return iterator.next.bind(iterator);
|
|
151
|
+
}
|
|
152
|
+
return Reflect.get(target, prop, receiver);
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
if (prop === "toJSON") {
|
|
158
|
+
return () => {
|
|
159
|
+
const onlyNexts = proxyState.current;
|
|
160
|
+
const serialized = onlyNexts.map((next) => next.toJSON());
|
|
161
|
+
const chunks = serialized.reduce((memo, next) => {
|
|
162
|
+
if (next?.value)
|
|
163
|
+
memo.push(next.value);
|
|
164
|
+
return memo;
|
|
165
|
+
}, []);
|
|
166
|
+
return chunks;
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
return Reflect.get(target, prop, receiver);
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
if (!Array.isArray(arg) && isIteratorLike(arg)) {
|
|
174
|
+
const proxyState = [];
|
|
175
|
+
return new Proxy(arg, {
|
|
176
|
+
get(target, prop, receiver) {
|
|
177
|
+
if (prop === "next" || prop === "return" || prop === "throw") {
|
|
178
|
+
const bound = arg[prop]?.bind(arg);
|
|
179
|
+
return (...args) => {
|
|
180
|
+
// @ts-expect-error TS cannot infer the argument types for the bound function
|
|
181
|
+
const next = bound?.(...args);
|
|
182
|
+
if (next != null)
|
|
183
|
+
proxyState.push(next);
|
|
184
|
+
return next;
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
if (prop === "toJSON") {
|
|
188
|
+
return () => {
|
|
189
|
+
const chunks = proxyState.reduce((memo, next) => {
|
|
190
|
+
if (next.value)
|
|
191
|
+
memo.push(next.value);
|
|
192
|
+
return memo;
|
|
193
|
+
}, []);
|
|
194
|
+
return chunks;
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
return Reflect.get(target, prop, receiver);
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
if (isThenable(arg)) {
|
|
202
|
+
return getSerializablePromise(arg);
|
|
203
|
+
}
|
|
204
|
+
return arg;
|
|
205
|
+
};
|
|
55
206
|
/**
|
|
56
207
|
* Higher-order function that takes function as input and returns a
|
|
57
208
|
* "TraceableFunction" - a wrapped version of the input that
|
|
@@ -115,8 +266,13 @@ export function traceable(wrappedFunc, config) {
|
|
|
115
266
|
...runTreeConfig,
|
|
116
267
|
};
|
|
117
268
|
}
|
|
269
|
+
// TODO: deal with possible nested promises and async iterables
|
|
270
|
+
const processedArgs = args;
|
|
271
|
+
for (let i = 0; i < processedArgs.length; i++) {
|
|
272
|
+
processedArgs[i] = convertSerializableArg(processedArgs[i]);
|
|
273
|
+
}
|
|
118
274
|
const [currentRunTree, rawInputs] = (() => {
|
|
119
|
-
const [firstArg, ...restArgs] =
|
|
275
|
+
const [firstArg, ...restArgs] = processedArgs;
|
|
120
276
|
// used for handoff between LangChain.JS and traceable functions
|
|
121
277
|
if (isRunnableConfigLike(firstArg)) {
|
|
122
278
|
return [
|
|
@@ -144,12 +300,12 @@ export function traceable(wrappedFunc, config) {
|
|
|
144
300
|
const prevRunFromStore = asyncLocalStorage.getStore();
|
|
145
301
|
if (prevRunFromStore) {
|
|
146
302
|
return [
|
|
147
|
-
getTracingRunTree(prevRunFromStore.createChild(ensuredConfig),
|
|
148
|
-
|
|
303
|
+
getTracingRunTree(prevRunFromStore.createChild(ensuredConfig), processedArgs),
|
|
304
|
+
processedArgs,
|
|
149
305
|
];
|
|
150
306
|
}
|
|
151
|
-
const currentRunTree = getTracingRunTree(new RunTree(ensuredConfig),
|
|
152
|
-
return [currentRunTree,
|
|
307
|
+
const currentRunTree = getTracingRunTree(new RunTree(ensuredConfig), processedArgs);
|
|
308
|
+
return [currentRunTree, processedArgs];
|
|
153
309
|
})();
|
|
154
310
|
return asyncLocalStorage.run(currentRunTree, () => {
|
|
155
311
|
const postRunPromise = currentRunTree?.postRun();
|
|
@@ -205,6 +361,17 @@ export function traceable(wrappedFunc, config) {
|
|
|
205
361
|
await postRunPromise;
|
|
206
362
|
await currentRunTree?.patchRun();
|
|
207
363
|
}
|
|
364
|
+
function gatherAll(iterator) {
|
|
365
|
+
const chunks = [];
|
|
366
|
+
// eslint-disable-next-line no-constant-condition
|
|
367
|
+
while (true) {
|
|
368
|
+
const next = iterator.next();
|
|
369
|
+
chunks.push(next);
|
|
370
|
+
if (next.done)
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
return chunks;
|
|
374
|
+
}
|
|
208
375
|
let returnValue;
|
|
209
376
|
try {
|
|
210
377
|
returnValue = wrappedFunc(...rawInputs);
|
|
@@ -223,15 +390,30 @@ export function traceable(wrappedFunc, config) {
|
|
|
223
390
|
const snapshot = AsyncLocalStorage.snapshot();
|
|
224
391
|
return resolve(wrapAsyncGeneratorForTracing(rawOutput, snapshot));
|
|
225
392
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
393
|
+
if (isGenerator(wrappedFunc) && isIteratorLike(rawOutput)) {
|
|
394
|
+
const chunks = gatherAll(rawOutput);
|
|
395
|
+
await currentRunTree?.end(handleRunOutputs(await handleChunks(chunks.reduce((memo, { value, done }) => {
|
|
396
|
+
if (!done || typeof value !== "undefined") {
|
|
397
|
+
memo.push(value);
|
|
398
|
+
}
|
|
399
|
+
return memo;
|
|
400
|
+
}, []))));
|
|
401
|
+
await handleEnd();
|
|
402
|
+
return (function* () {
|
|
403
|
+
for (const ret of chunks) {
|
|
404
|
+
if (ret.done)
|
|
405
|
+
return ret.value;
|
|
406
|
+
yield ret.value;
|
|
407
|
+
}
|
|
408
|
+
})();
|
|
409
|
+
}
|
|
410
|
+
try {
|
|
411
|
+
await currentRunTree?.end(handleRunOutputs(rawOutput));
|
|
412
|
+
await handleEnd();
|
|
413
|
+
}
|
|
414
|
+
finally {
|
|
415
|
+
// eslint-disable-next-line no-unsafe-finally
|
|
416
|
+
return rawOutput;
|
|
235
417
|
}
|
|
236
418
|
}, async (error) => {
|
|
237
419
|
await currentRunTree?.end(undefined, String(error));
|
|
@@ -281,11 +463,15 @@ export function isTraceableFunction(x
|
|
|
281
463
|
return typeof x === "function" && "langsmith:traceable" in x;
|
|
282
464
|
}
|
|
283
465
|
function isKVMap(x) {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
466
|
+
if (typeof x !== "object" || x == null) {
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
const prototype = Object.getPrototypeOf(x);
|
|
470
|
+
return ((prototype === null ||
|
|
471
|
+
prototype === Object.prototype ||
|
|
472
|
+
Object.getPrototypeOf(prototype) === null) &&
|
|
473
|
+
!(Symbol.toStringTag in x) &&
|
|
474
|
+
!(Symbol.iterator in x));
|
|
289
475
|
}
|
|
290
476
|
export function wrapFunctionAndEnsureTraceable(target, options, name = "target") {
|
|
291
477
|
if (typeof target === "function") {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.shuffle = void 0;
|
|
4
|
+
function shuffle(array) {
|
|
5
|
+
let currentIndex = array.length;
|
|
6
|
+
while (currentIndex !== 0) {
|
|
7
|
+
const randomIndex = Math.floor(Math.random() * currentIndex);
|
|
8
|
+
currentIndex -= 1;
|
|
9
|
+
const tmp = array[currentIndex];
|
|
10
|
+
array[currentIndex] = array[randomIndex];
|
|
11
|
+
array[randomIndex] = tmp;
|
|
12
|
+
}
|
|
13
|
+
return array;
|
|
14
|
+
}
|
|
15
|
+
exports.shuffle = shuffle;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function shuffle<T extends unknown[]>(array: T): T;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function shuffle(array) {
|
|
2
|
+
let currentIndex = array.length;
|
|
3
|
+
while (currentIndex !== 0) {
|
|
4
|
+
const randomIndex = Math.floor(Math.random() * currentIndex);
|
|
5
|
+
currentIndex -= 1;
|
|
6
|
+
const tmp = array[currentIndex];
|
|
7
|
+
array[currentIndex] = array[randomIndex];
|
|
8
|
+
array[randomIndex] = tmp;
|
|
9
|
+
}
|
|
10
|
+
return array;
|
|
11
|
+
}
|