langsmith 0.5.23 → 0.5.25
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 +102 -11
- package/dist/client.d.ts +38 -0
- package/dist/client.js +103 -12
- package/dist/evaluation/_runner.cjs +3 -3
- package/dist/evaluation/_runner.js +1 -1
- package/dist/evaluation/evaluate_comparative.cjs +10 -10
- package/dist/evaluation/evaluate_comparative.js +1 -1
- package/dist/evaluation/evaluator.cjs +2 -2
- package/dist/evaluation/evaluator.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/run_trees.cjs +8 -7
- package/dist/run_trees.d.ts +7 -0
- package/dist/run_trees.js +7 -6
- package/dist/schemas.d.ts +4 -0
- package/dist/singletons/otel.cjs +3 -2
- package/dist/singletons/otel.js +4 -3
- package/dist/traceable.cjs +1 -2
- package/dist/traceable.js +1 -2
- package/dist/utils/_uuid.cjs +2 -2
- package/dist/utils/_uuid.js +1 -1
- package/dist/utils/env.cjs +33 -0
- package/dist/utils/env.d.ts +9 -0
- package/dist/utils/env.js +32 -0
- package/dist/utils/fast-safe-stringify/index.cjs +10 -35
- package/dist/utils/fast-safe-stringify/index.d.ts +14 -1
- package/dist/utils/fast-safe-stringify/index.js +10 -35
- package/dist/utils/jestlike/index.cjs +5 -5
- package/dist/utils/jestlike/index.js +1 -1
- package/dist/utils/jestlike/vendor/evaluatedBy.cjs +3 -3
- package/dist/utils/jestlike/vendor/evaluatedBy.js +1 -1
- package/dist/utils/serialize_worker.cjs +389 -0
- package/dist/utils/serialize_worker.d.ts +67 -0
- package/dist/utils/serialize_worker.js +383 -0
- package/dist/utils/uuid/src/index.cjs +24 -0
- package/dist/utils/uuid/src/index.d.ts +10 -0
- package/dist/utils/uuid/src/index.js +9 -0
- package/dist/utils/uuid/src/max.cjs +3 -0
- package/dist/utils/uuid/src/max.d.ts +2 -0
- package/dist/utils/uuid/src/max.js +1 -0
- package/dist/utils/uuid/src/nil.cjs +3 -0
- package/dist/utils/uuid/src/nil.d.ts +2 -0
- package/dist/utils/uuid/src/nil.js +1 -0
- package/dist/utils/uuid/src/parse.cjs +23 -0
- package/dist/utils/uuid/src/parse.d.ts +3 -0
- package/dist/utils/uuid/src/parse.js +18 -0
- package/dist/utils/uuid/src/regex.cjs +3 -0
- package/dist/utils/uuid/src/regex.d.ts +2 -0
- package/dist/utils/uuid/src/regex.js +1 -0
- package/dist/utils/uuid/src/rng.cjs +10 -0
- package/dist/utils/uuid/src/rng.d.ts +1 -0
- package/dist/utils/uuid/src/rng.js +7 -0
- package/dist/utils/uuid/src/sha1.cjs +75 -0
- package/dist/utils/uuid/src/sha1.d.ts +2 -0
- package/dist/utils/uuid/src/sha1.js +73 -0
- package/dist/utils/uuid/src/stringify.cjs +55 -0
- package/dist/utils/uuid/src/stringify.d.ts +3 -0
- package/dist/utils/uuid/src/stringify.js +49 -0
- package/dist/utils/uuid/src/types.cjs +2 -0
- package/dist/utils/uuid/src/types.d.ts +22 -0
- package/dist/utils/uuid/src/types.js +1 -0
- package/dist/utils/uuid/src/v35.cjs +52 -0
- package/dist/utils/uuid/src/v35.d.ts +7 -0
- package/dist/utils/uuid/src/v35.js +44 -0
- package/dist/utils/uuid/src/v4.cjs +40 -0
- package/dist/utils/uuid/src/v4.d.ts +4 -0
- package/dist/utils/uuid/src/v4.js +35 -0
- package/dist/utils/uuid/src/v5.cjs +50 -0
- package/dist/utils/uuid/src/v5.d.ts +9 -0
- package/dist/utils/uuid/src/v5.js +9 -0
- package/dist/utils/uuid/src/v7.cjs +88 -0
- package/dist/utils/uuid/src/v7.d.ts +9 -0
- package/dist/utils/uuid/src/v7.js +82 -0
- package/dist/utils/uuid/src/validate.cjs +10 -0
- package/dist/utils/uuid/src/validate.d.ts +2 -0
- package/dist/utils/uuid/src/validate.js +5 -0
- package/dist/utils/uuid/src/version.cjs +13 -0
- package/dist/utils/uuid/src/version.d.ts +2 -0
- package/dist/utils/uuid/src/version.js +8 -0
- package/dist/utils/worker_threads.browser.cjs +16 -0
- package/dist/utils/worker_threads.browser.d.ts +14 -0
- package/dist/utils/worker_threads.browser.js +13 -0
- package/dist/utils/worker_threads.cjs +16 -0
- package/dist/utils/worker_threads.d.ts +13 -0
- package/dist/utils/worker_threads.js +13 -0
- package/dist/uuid.cjs +2 -2
- package/dist/uuid.js +1 -1
- package/package.json +4 -4
package/dist/client.cjs
CHANGED
|
@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.Client = exports.AutoBatchQueue = exports.DEFAULT_MAX_SIZE_BYTES = exports.DEFAULT_UNCOMPRESSED_BATCH_SIZE_LIMIT_BYTES = void 0;
|
|
37
37
|
exports.mergeRuntimeEnvIntoRun = mergeRuntimeEnvIntoRun;
|
|
38
|
-
const uuid = __importStar(require("uuid"));
|
|
38
|
+
const uuid = __importStar(require("./utils/uuid/src/index.cjs"));
|
|
39
39
|
const translator_js_1 = require("./experimental/otel/translator.cjs");
|
|
40
40
|
const otel_js_1 = require("./singletons/otel.cjs");
|
|
41
41
|
const async_caller_js_1 = require("./utils/async_caller.cjs");
|
|
@@ -50,6 +50,7 @@ const index_js_2 = require("./utils/prompt_cache/index.cjs");
|
|
|
50
50
|
const fsUtils = __importStar(require("./utils/fs.cjs"));
|
|
51
51
|
const fetch_js_1 = require("./singletons/fetch.cjs");
|
|
52
52
|
const index_js_3 = require("./utils/fast-safe-stringify/index.cjs");
|
|
53
|
+
const serialize_worker_js_1 = require("./utils/serialize_worker.cjs");
|
|
53
54
|
/**
|
|
54
55
|
* Catches timestamps without a timezone suffix.
|
|
55
56
|
*/
|
|
@@ -200,7 +201,7 @@ class AutoBatchQueue {
|
|
|
200
201
|
// real serialization still happens later, off the hot path, when the
|
|
201
202
|
// batch is assembled for sending.
|
|
202
203
|
const size = (0, env_js_1.getLangSmithEnvironmentVariable)("PERF_OPTIMIZATION") === "true"
|
|
203
|
-
? (0, index_js_3.estimateSerializedSize)(item.item)
|
|
204
|
+
? (0, index_js_3.estimateSerializedSize)(item.item).size
|
|
204
205
|
: (0, index_js_3.serialize)(item.item, `Serializing run with id: ${item.item.id}`).length;
|
|
205
206
|
// Check if adding this item would exceed the size limit
|
|
206
207
|
// Allow the run if the queue is empty (to support large single traces)
|
|
@@ -265,9 +266,67 @@ class AutoBatchQueue {
|
|
|
265
266
|
}
|
|
266
267
|
exports.AutoBatchQueue = AutoBatchQueue;
|
|
267
268
|
class Client {
|
|
269
|
+
get tracingMode() {
|
|
270
|
+
return this._tracingMode;
|
|
271
|
+
}
|
|
268
272
|
get _fetch() {
|
|
269
273
|
return this.fetchImplementation || (0, fetch_js_1._getFetchImplementation)(this.debug);
|
|
270
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* Serialize a payload for tracing, optionally offloading the work to a
|
|
277
|
+
* Node worker thread when LANGSMITH_PERF_OPTIMIZATION=true and the runtime
|
|
278
|
+
* supports worker_threads.
|
|
279
|
+
*
|
|
280
|
+
* Falls back to synchronous serialization when:
|
|
281
|
+
* - the perf flag is off
|
|
282
|
+
* - manualFlushMode is enabled (serverless: worker boot cost > benefit)
|
|
283
|
+
* - worker_threads is unavailable (non-Node runtimes)
|
|
284
|
+
* - the payload contains values that can't be structured-cloned across
|
|
285
|
+
* threads (functions, non-cloneable class instances, streams, etc.)
|
|
286
|
+
* - the worker throws for any other reason
|
|
287
|
+
*
|
|
288
|
+
* In all fallback cases the returned bytes are identical to the sync path.
|
|
289
|
+
*/
|
|
290
|
+
_trackDrain(promise) {
|
|
291
|
+
this._pendingDrains.add(promise);
|
|
292
|
+
promise.finally(() => {
|
|
293
|
+
this._pendingDrains.delete(promise);
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
async _serializeBody(payload, errorContext) {
|
|
297
|
+
const perfOptIn = (0, env_js_1.getLangSmithEnvironmentVariable)("PERF_OPTIMIZATION") === "true";
|
|
298
|
+
if (!perfOptIn || this.manualFlushMode) {
|
|
299
|
+
return (0, index_js_3.serialize)(payload, errorContext);
|
|
300
|
+
}
|
|
301
|
+
// Shape-aware gate: worker offload pays for itself only when the
|
|
302
|
+
// payload is dominated by one or more large strings (V8 can refcount
|
|
303
|
+
// those across isolates instead of copying). For structure-heavy
|
|
304
|
+
// payloads -- many keys, deep nesting, lots of small strings -- the
|
|
305
|
+
// structuredClone walk plus thread-hop cost exceeds the JSON.stringify
|
|
306
|
+
// cost we would pay inline, so we fall through to sync serialize.
|
|
307
|
+
if (!(0, serialize_worker_js_1.hasLargeString)(payload)) {
|
|
308
|
+
return (0, index_js_3.serialize)(payload, errorContext);
|
|
309
|
+
}
|
|
310
|
+
if (this._serializeWorker === undefined) {
|
|
311
|
+
this._serializeWorker = (0, serialize_worker_js_1.getSharedSerializeWorker)();
|
|
312
|
+
}
|
|
313
|
+
if (this._serializeWorker === null) {
|
|
314
|
+
return (0, index_js_3.serialize)(payload, errorContext);
|
|
315
|
+
}
|
|
316
|
+
try {
|
|
317
|
+
const bytes = await this._serializeWorker.serialize(payload);
|
|
318
|
+
if (bytes === null) {
|
|
319
|
+
// Worker subsystem unavailable; cache the null to skip re-entry.
|
|
320
|
+
this._serializeWorker = null;
|
|
321
|
+
return (0, index_js_3.serialize)(payload, errorContext);
|
|
322
|
+
}
|
|
323
|
+
return bytes;
|
|
324
|
+
}
|
|
325
|
+
catch {
|
|
326
|
+
// DataCloneError, worker crash, etc. Fall back silently.
|
|
327
|
+
return (0, index_js_3.serialize)(payload, errorContext);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
271
330
|
constructor(config = {}) {
|
|
272
331
|
Object.defineProperty(this, "apiKey", {
|
|
273
332
|
enumerable: true,
|
|
@@ -432,12 +491,35 @@ class Client {
|
|
|
432
491
|
writable: true,
|
|
433
492
|
value: false
|
|
434
493
|
});
|
|
494
|
+
Object.defineProperty(this, "_serializeWorker", {
|
|
495
|
+
enumerable: true,
|
|
496
|
+
configurable: true,
|
|
497
|
+
writable: true,
|
|
498
|
+
value: void 0
|
|
499
|
+
});
|
|
500
|
+
/**
|
|
501
|
+
* Tracks in-flight drainAutoBatchQueue promises so awaitPendingTraceBatches
|
|
502
|
+
* can wait on them even if the flush involves async work (worker-thread
|
|
503
|
+
* serialize) that hasn't yet registered with batchIngestCaller.queue.
|
|
504
|
+
*/
|
|
505
|
+
Object.defineProperty(this, "_pendingDrains", {
|
|
506
|
+
enumerable: true,
|
|
507
|
+
configurable: true,
|
|
508
|
+
writable: true,
|
|
509
|
+
value: new Set()
|
|
510
|
+
});
|
|
435
511
|
Object.defineProperty(this, "langSmithToOTELTranslator", {
|
|
436
512
|
enumerable: true,
|
|
437
513
|
configurable: true,
|
|
438
514
|
writable: true,
|
|
439
515
|
value: void 0
|
|
440
516
|
});
|
|
517
|
+
Object.defineProperty(this, "_tracingMode", {
|
|
518
|
+
enumerable: true,
|
|
519
|
+
configurable: true,
|
|
520
|
+
writable: true,
|
|
521
|
+
value: "langsmith"
|
|
522
|
+
});
|
|
441
523
|
Object.defineProperty(this, "fetchImplementation", {
|
|
442
524
|
enumerable: true,
|
|
443
525
|
configurable: true,
|
|
@@ -557,7 +639,8 @@ class Client {
|
|
|
557
639
|
this.batchSizeLimit = config.batchSizeLimit;
|
|
558
640
|
this.fetchOptions = config.fetchOptions || {};
|
|
559
641
|
this.manualFlushMode = config.manualFlushMode ?? this.manualFlushMode;
|
|
560
|
-
|
|
642
|
+
this._tracingMode = (0, env_js_1.resolveTracingMode)(config.tracingMode);
|
|
643
|
+
if (this._tracingMode === "otel") {
|
|
561
644
|
this.langSmithToOTELTranslator = new translator_js_1.LangSmithToOTELTranslator();
|
|
562
645
|
}
|
|
563
646
|
// Cache metadata env vars once during construction to avoid repeatedly scanning process.env
|
|
@@ -1096,18 +1179,18 @@ class Client {
|
|
|
1096
1179
|
const sizeLimit = await this._getBatchSizeLimit();
|
|
1097
1180
|
if (this.autoBatchQueue.sizeBytes > sizeLimitBytes ||
|
|
1098
1181
|
this.autoBatchQueue.items.length > sizeLimit) {
|
|
1099
|
-
|
|
1182
|
+
this._trackDrain(this.drainAutoBatchQueue({
|
|
1100
1183
|
batchSizeLimitBytes: sizeLimitBytes,
|
|
1101
1184
|
batchSizeLimit: sizeLimit,
|
|
1102
|
-
});
|
|
1185
|
+
}));
|
|
1103
1186
|
}
|
|
1104
1187
|
if (this.autoBatchQueue.items.length > 0) {
|
|
1105
1188
|
this.autoBatchTimeout = setTimeout(() => {
|
|
1106
1189
|
this.autoBatchTimeout = undefined;
|
|
1107
|
-
|
|
1190
|
+
this._trackDrain(this.drainAutoBatchQueue({
|
|
1108
1191
|
batchSizeLimitBytes: sizeLimitBytes,
|
|
1109
1192
|
batchSizeLimit: sizeLimit,
|
|
1110
|
-
});
|
|
1193
|
+
}));
|
|
1111
1194
|
}, this.autoBatchAggregationDelayMs);
|
|
1112
1195
|
}
|
|
1113
1196
|
return itemPromise;
|
|
@@ -1116,7 +1199,7 @@ class Client {
|
|
|
1116
1199
|
const response = await this.caller.call(async () => {
|
|
1117
1200
|
const res = await this._fetch(`${this.apiUrl}/info`, {
|
|
1118
1201
|
method: "GET",
|
|
1119
|
-
headers: { Accept: "application/json" },
|
|
1202
|
+
headers: { ...this._mergedHeaders, Accept: "application/json" },
|
|
1120
1203
|
signal: AbortSignal.timeout(SERVER_INFO_REQUEST_TIMEOUT_MS),
|
|
1121
1204
|
...this.fetchOptions,
|
|
1122
1205
|
});
|
|
@@ -1287,7 +1370,7 @@ class Client {
|
|
|
1287
1370
|
.map((item) => item.id)
|
|
1288
1371
|
.concat(batchChunks.patch.map((item) => item.id))
|
|
1289
1372
|
.join(",");
|
|
1290
|
-
await this._postBatchIngestRuns(
|
|
1373
|
+
await this._postBatchIngestRuns(await this._serializeBody(batchChunks, `Ingesting runs with ids: ${runIds}`), options);
|
|
1291
1374
|
}
|
|
1292
1375
|
}
|
|
1293
1376
|
async _postBatchIngestRuns(body, options) {
|
|
@@ -1388,7 +1471,7 @@ class Client {
|
|
|
1388
1471
|
const { inputs, outputs, events, extra, error, serialized, attachments, ...payload } = originalPayload;
|
|
1389
1472
|
const fields = { inputs, outputs, events, extra, error, serialized };
|
|
1390
1473
|
// encode the main run payload
|
|
1391
|
-
const stringifiedPayload =
|
|
1474
|
+
const stringifiedPayload = await this._serializeBody(payload, `Serializing for multipart ingestion of run with id: ${payload.id}`);
|
|
1392
1475
|
accumulatedParts.push({
|
|
1393
1476
|
name: `${method}.${payload.id}`,
|
|
1394
1477
|
payload: new Blob([stringifiedPayload], {
|
|
@@ -1400,7 +1483,7 @@ class Client {
|
|
|
1400
1483
|
if (value === undefined) {
|
|
1401
1484
|
continue;
|
|
1402
1485
|
}
|
|
1403
|
-
const stringifiedValue =
|
|
1486
|
+
const stringifiedValue = await this._serializeBody(value, `Serializing ${key} for multipart ingestion of run with id: ${payload.id}`);
|
|
1404
1487
|
accumulatedParts.push({
|
|
1405
1488
|
name: `${method}.${payload.id}.${key}`,
|
|
1406
1489
|
payload: new Blob([stringifiedValue], {
|
|
@@ -4407,6 +4490,8 @@ class Client {
|
|
|
4407
4490
|
commit_hash: result.commit_hash,
|
|
4408
4491
|
manifest: result.manifest,
|
|
4409
4492
|
examples: result.examples,
|
|
4493
|
+
hub_model_config: result.model_config,
|
|
4494
|
+
hub_model_provider: result.model_provider,
|
|
4410
4495
|
};
|
|
4411
4496
|
}
|
|
4412
4497
|
async pullPromptCommit(promptIdentifier, options) {
|
|
@@ -4845,6 +4930,12 @@ class Client {
|
|
|
4845
4930
|
* ```
|
|
4846
4931
|
*/
|
|
4847
4932
|
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
4933
|
+
// Wait for any in-flight drains (whose serialize work may still be
|
|
4934
|
+
// in-progress on a worker thread and not yet registered with the
|
|
4935
|
+
// batchIngestCaller queue) before checking queue idleness.
|
|
4936
|
+
while (this._pendingDrains.size > 0) {
|
|
4937
|
+
await Promise.all([...this._pendingDrains]);
|
|
4938
|
+
}
|
|
4848
4939
|
await Promise.all([
|
|
4849
4940
|
...this.autoBatchQueue.items.map(({ itemPromise }) => itemPromise),
|
|
4850
4941
|
this.batchIngestCaller.queue.onIdle(),
|
package/dist/client.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { OTELContext } from "./experimental/otel/types.js";
|
|
2
2
|
import { AsyncCallerParams } from "./utils/async_caller.js";
|
|
3
3
|
import { ComparativeExperiment, DataType, Dataset, DatasetDiffInfo, DatasetShareSchema, Example, ExampleCreate, ExampleUpdate, ExampleUpdateWithoutId, Feedback, FeedbackConfig, FeedbackIngestToken, KVMap, LangChainBaseMessage, LangSmithSettings, LikePromptResponse, Prompt, PromptCommit, PromptSortField, Run, RunCreate, RunUpdate, ScoreType, TimeDelta, TracerSession, TracerSessionResult, ValueType, AnnotationQueue, RunWithAnnotationQueueInfo, Attachments, UploadExamplesResponse, UpdateExamplesResponse, DatasetVersion, AnnotationQueueWithDetails, AnnotationQueueRubricItem, FeedbackConfigSchema, AgentContext, SkillContext, Entry } from "./schemas.js";
|
|
4
|
+
import { type TracingMode } from "./utils/env.js";
|
|
4
5
|
import { EvaluationResult, EvaluationResults } from "./evaluation/evaluator.js";
|
|
5
6
|
import { PromptCache } from "./utils/prompt_cache/index.js";
|
|
6
7
|
export interface ClientConfig {
|
|
@@ -56,6 +57,17 @@ export interface ClientConfig {
|
|
|
56
57
|
* By default, prompt caching is enabled globally.
|
|
57
58
|
*/
|
|
58
59
|
disablePromptCache?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Where to send traces. One of:
|
|
62
|
+
*
|
|
63
|
+
* - `"langsmith"` (default) — LangSmith REST API only.
|
|
64
|
+
* - `"otel"` — OpenTelemetry export only.
|
|
65
|
+
*
|
|
66
|
+
* Falls back to the `LANGSMITH_TRACING_MODE` env var, then to the
|
|
67
|
+
* legacy `OTEL_ENABLED` / `LANGSMITH_OTEL_ENABLED` env vars, then to
|
|
68
|
+
* `"langsmith"`.
|
|
69
|
+
*/
|
|
70
|
+
tracingMode?: TracingMode;
|
|
59
71
|
/**
|
|
60
72
|
* Additional HTTP headers to include in all requests.
|
|
61
73
|
* These headers will be merged with the default headers (User-Agent,
|
|
@@ -414,11 +426,37 @@ export declare class Client implements LangSmithTracingClientInterface {
|
|
|
414
426
|
private _serverInfo;
|
|
415
427
|
private _getServerInfoPromise?;
|
|
416
428
|
private manualFlushMode;
|
|
429
|
+
private _serializeWorker?;
|
|
430
|
+
/**
|
|
431
|
+
* Tracks in-flight drainAutoBatchQueue promises so awaitPendingTraceBatches
|
|
432
|
+
* can wait on them even if the flush involves async work (worker-thread
|
|
433
|
+
* serialize) that hasn't yet registered with batchIngestCaller.queue.
|
|
434
|
+
*/
|
|
435
|
+
private _pendingDrains;
|
|
417
436
|
private langSmithToOTELTranslator?;
|
|
437
|
+
private _tracingMode;
|
|
438
|
+
get tracingMode(): TracingMode;
|
|
418
439
|
private fetchImplementation?;
|
|
419
440
|
private cachedLSEnvVarsForMetadata?;
|
|
420
441
|
private _promptCache?;
|
|
421
442
|
private get _fetch();
|
|
443
|
+
/**
|
|
444
|
+
* Serialize a payload for tracing, optionally offloading the work to a
|
|
445
|
+
* Node worker thread when LANGSMITH_PERF_OPTIMIZATION=true and the runtime
|
|
446
|
+
* supports worker_threads.
|
|
447
|
+
*
|
|
448
|
+
* Falls back to synchronous serialization when:
|
|
449
|
+
* - the perf flag is off
|
|
450
|
+
* - manualFlushMode is enabled (serverless: worker boot cost > benefit)
|
|
451
|
+
* - worker_threads is unavailable (non-Node runtimes)
|
|
452
|
+
* - the payload contains values that can't be structured-cloned across
|
|
453
|
+
* threads (functions, non-cloneable class instances, streams, etc.)
|
|
454
|
+
* - the worker throws for any other reason
|
|
455
|
+
*
|
|
456
|
+
* In all fallback cases the returned bytes are identical to the sync path.
|
|
457
|
+
*/
|
|
458
|
+
private _trackDrain;
|
|
459
|
+
private _serializeBody;
|
|
422
460
|
private multipartStreamingDisabled;
|
|
423
461
|
private _multipartDisabled;
|
|
424
462
|
private _runCompressionDisabled;
|
package/dist/client.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import * as uuid from "uuid";
|
|
1
|
+
import * as uuid from "./utils/uuid/src/index.js";
|
|
2
2
|
import { LangSmithToOTELTranslator, } from "./experimental/otel/translator.js";
|
|
3
3
|
import { getDefaultOTLPTracerComponents, getOTELTrace, getOTELContext, } from "./singletons/otel.js";
|
|
4
4
|
import { AsyncCaller } from "./utils/async_caller.js";
|
|
5
5
|
import { convertLangChainMessageToExample, isLangChainMessage, } from "./utils/messages.js";
|
|
6
|
-
import { getEnvironmentVariable, getLangSmithEnvVarsMetadata, getLangSmithEnvironmentVariable, getRuntimeEnvironment,
|
|
6
|
+
import { getEnvironmentVariable, getLangSmithEnvVarsMetadata, getLangSmithEnvironmentVariable, getRuntimeEnvironment, getEnv, resolveTracingMode, } from "./utils/env.js";
|
|
7
7
|
import { __version__ } from "./index.js";
|
|
8
8
|
import { assertUuid } from "./utils/_uuid.js";
|
|
9
9
|
import { warnOnce } from "./utils/warn.js";
|
|
@@ -13,6 +13,7 @@ import { promptCacheSingleton, } from "./utils/prompt_cache/index.js";
|
|
|
13
13
|
import * as fsUtils from "./utils/fs.js";
|
|
14
14
|
import { _shouldStreamForGlobalFetchImplementation, _getFetchImplementation, } from "./singletons/fetch.js";
|
|
15
15
|
import { serialize as serializePayloadForTracing, estimateSerializedSize, } from "./utils/fast-safe-stringify/index.js";
|
|
16
|
+
import { getSharedSerializeWorker, hasLargeString, } from "./utils/serialize_worker.js";
|
|
16
17
|
/**
|
|
17
18
|
* Catches timestamps without a timezone suffix.
|
|
18
19
|
*/
|
|
@@ -163,7 +164,7 @@ export class AutoBatchQueue {
|
|
|
163
164
|
// real serialization still happens later, off the hot path, when the
|
|
164
165
|
// batch is assembled for sending.
|
|
165
166
|
const size = getLangSmithEnvironmentVariable("PERF_OPTIMIZATION") === "true"
|
|
166
|
-
? estimateSerializedSize(item.item)
|
|
167
|
+
? estimateSerializedSize(item.item).size
|
|
167
168
|
: serializePayloadForTracing(item.item, `Serializing run with id: ${item.item.id}`).length;
|
|
168
169
|
// Check if adding this item would exceed the size limit
|
|
169
170
|
// Allow the run if the queue is empty (to support large single traces)
|
|
@@ -227,9 +228,67 @@ export class AutoBatchQueue {
|
|
|
227
228
|
}
|
|
228
229
|
}
|
|
229
230
|
export class Client {
|
|
231
|
+
get tracingMode() {
|
|
232
|
+
return this._tracingMode;
|
|
233
|
+
}
|
|
230
234
|
get _fetch() {
|
|
231
235
|
return this.fetchImplementation || _getFetchImplementation(this.debug);
|
|
232
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Serialize a payload for tracing, optionally offloading the work to a
|
|
239
|
+
* Node worker thread when LANGSMITH_PERF_OPTIMIZATION=true and the runtime
|
|
240
|
+
* supports worker_threads.
|
|
241
|
+
*
|
|
242
|
+
* Falls back to synchronous serialization when:
|
|
243
|
+
* - the perf flag is off
|
|
244
|
+
* - manualFlushMode is enabled (serverless: worker boot cost > benefit)
|
|
245
|
+
* - worker_threads is unavailable (non-Node runtimes)
|
|
246
|
+
* - the payload contains values that can't be structured-cloned across
|
|
247
|
+
* threads (functions, non-cloneable class instances, streams, etc.)
|
|
248
|
+
* - the worker throws for any other reason
|
|
249
|
+
*
|
|
250
|
+
* In all fallback cases the returned bytes are identical to the sync path.
|
|
251
|
+
*/
|
|
252
|
+
_trackDrain(promise) {
|
|
253
|
+
this._pendingDrains.add(promise);
|
|
254
|
+
promise.finally(() => {
|
|
255
|
+
this._pendingDrains.delete(promise);
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
async _serializeBody(payload, errorContext) {
|
|
259
|
+
const perfOptIn = getLangSmithEnvironmentVariable("PERF_OPTIMIZATION") === "true";
|
|
260
|
+
if (!perfOptIn || this.manualFlushMode) {
|
|
261
|
+
return serializePayloadForTracing(payload, errorContext);
|
|
262
|
+
}
|
|
263
|
+
// Shape-aware gate: worker offload pays for itself only when the
|
|
264
|
+
// payload is dominated by one or more large strings (V8 can refcount
|
|
265
|
+
// those across isolates instead of copying). For structure-heavy
|
|
266
|
+
// payloads -- many keys, deep nesting, lots of small strings -- the
|
|
267
|
+
// structuredClone walk plus thread-hop cost exceeds the JSON.stringify
|
|
268
|
+
// cost we would pay inline, so we fall through to sync serialize.
|
|
269
|
+
if (!hasLargeString(payload)) {
|
|
270
|
+
return serializePayloadForTracing(payload, errorContext);
|
|
271
|
+
}
|
|
272
|
+
if (this._serializeWorker === undefined) {
|
|
273
|
+
this._serializeWorker = getSharedSerializeWorker();
|
|
274
|
+
}
|
|
275
|
+
if (this._serializeWorker === null) {
|
|
276
|
+
return serializePayloadForTracing(payload, errorContext);
|
|
277
|
+
}
|
|
278
|
+
try {
|
|
279
|
+
const bytes = await this._serializeWorker.serialize(payload);
|
|
280
|
+
if (bytes === null) {
|
|
281
|
+
// Worker subsystem unavailable; cache the null to skip re-entry.
|
|
282
|
+
this._serializeWorker = null;
|
|
283
|
+
return serializePayloadForTracing(payload, errorContext);
|
|
284
|
+
}
|
|
285
|
+
return bytes;
|
|
286
|
+
}
|
|
287
|
+
catch {
|
|
288
|
+
// DataCloneError, worker crash, etc. Fall back silently.
|
|
289
|
+
return serializePayloadForTracing(payload, errorContext);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
233
292
|
constructor(config = {}) {
|
|
234
293
|
Object.defineProperty(this, "apiKey", {
|
|
235
294
|
enumerable: true,
|
|
@@ -394,12 +453,35 @@ export class Client {
|
|
|
394
453
|
writable: true,
|
|
395
454
|
value: false
|
|
396
455
|
});
|
|
456
|
+
Object.defineProperty(this, "_serializeWorker", {
|
|
457
|
+
enumerable: true,
|
|
458
|
+
configurable: true,
|
|
459
|
+
writable: true,
|
|
460
|
+
value: void 0
|
|
461
|
+
});
|
|
462
|
+
/**
|
|
463
|
+
* Tracks in-flight drainAutoBatchQueue promises so awaitPendingTraceBatches
|
|
464
|
+
* can wait on them even if the flush involves async work (worker-thread
|
|
465
|
+
* serialize) that hasn't yet registered with batchIngestCaller.queue.
|
|
466
|
+
*/
|
|
467
|
+
Object.defineProperty(this, "_pendingDrains", {
|
|
468
|
+
enumerable: true,
|
|
469
|
+
configurable: true,
|
|
470
|
+
writable: true,
|
|
471
|
+
value: new Set()
|
|
472
|
+
});
|
|
397
473
|
Object.defineProperty(this, "langSmithToOTELTranslator", {
|
|
398
474
|
enumerable: true,
|
|
399
475
|
configurable: true,
|
|
400
476
|
writable: true,
|
|
401
477
|
value: void 0
|
|
402
478
|
});
|
|
479
|
+
Object.defineProperty(this, "_tracingMode", {
|
|
480
|
+
enumerable: true,
|
|
481
|
+
configurable: true,
|
|
482
|
+
writable: true,
|
|
483
|
+
value: "langsmith"
|
|
484
|
+
});
|
|
403
485
|
Object.defineProperty(this, "fetchImplementation", {
|
|
404
486
|
enumerable: true,
|
|
405
487
|
configurable: true,
|
|
@@ -519,7 +601,8 @@ export class Client {
|
|
|
519
601
|
this.batchSizeLimit = config.batchSizeLimit;
|
|
520
602
|
this.fetchOptions = config.fetchOptions || {};
|
|
521
603
|
this.manualFlushMode = config.manualFlushMode ?? this.manualFlushMode;
|
|
522
|
-
|
|
604
|
+
this._tracingMode = resolveTracingMode(config.tracingMode);
|
|
605
|
+
if (this._tracingMode === "otel") {
|
|
523
606
|
this.langSmithToOTELTranslator = new LangSmithToOTELTranslator();
|
|
524
607
|
}
|
|
525
608
|
// Cache metadata env vars once during construction to avoid repeatedly scanning process.env
|
|
@@ -1058,18 +1141,18 @@ export class Client {
|
|
|
1058
1141
|
const sizeLimit = await this._getBatchSizeLimit();
|
|
1059
1142
|
if (this.autoBatchQueue.sizeBytes > sizeLimitBytes ||
|
|
1060
1143
|
this.autoBatchQueue.items.length > sizeLimit) {
|
|
1061
|
-
|
|
1144
|
+
this._trackDrain(this.drainAutoBatchQueue({
|
|
1062
1145
|
batchSizeLimitBytes: sizeLimitBytes,
|
|
1063
1146
|
batchSizeLimit: sizeLimit,
|
|
1064
|
-
});
|
|
1147
|
+
}));
|
|
1065
1148
|
}
|
|
1066
1149
|
if (this.autoBatchQueue.items.length > 0) {
|
|
1067
1150
|
this.autoBatchTimeout = setTimeout(() => {
|
|
1068
1151
|
this.autoBatchTimeout = undefined;
|
|
1069
|
-
|
|
1152
|
+
this._trackDrain(this.drainAutoBatchQueue({
|
|
1070
1153
|
batchSizeLimitBytes: sizeLimitBytes,
|
|
1071
1154
|
batchSizeLimit: sizeLimit,
|
|
1072
|
-
});
|
|
1155
|
+
}));
|
|
1073
1156
|
}, this.autoBatchAggregationDelayMs);
|
|
1074
1157
|
}
|
|
1075
1158
|
return itemPromise;
|
|
@@ -1078,7 +1161,7 @@ export class Client {
|
|
|
1078
1161
|
const response = await this.caller.call(async () => {
|
|
1079
1162
|
const res = await this._fetch(`${this.apiUrl}/info`, {
|
|
1080
1163
|
method: "GET",
|
|
1081
|
-
headers: { Accept: "application/json" },
|
|
1164
|
+
headers: { ...this._mergedHeaders, Accept: "application/json" },
|
|
1082
1165
|
signal: AbortSignal.timeout(SERVER_INFO_REQUEST_TIMEOUT_MS),
|
|
1083
1166
|
...this.fetchOptions,
|
|
1084
1167
|
});
|
|
@@ -1249,7 +1332,7 @@ export class Client {
|
|
|
1249
1332
|
.map((item) => item.id)
|
|
1250
1333
|
.concat(batchChunks.patch.map((item) => item.id))
|
|
1251
1334
|
.join(",");
|
|
1252
|
-
await this._postBatchIngestRuns(
|
|
1335
|
+
await this._postBatchIngestRuns(await this._serializeBody(batchChunks, `Ingesting runs with ids: ${runIds}`), options);
|
|
1253
1336
|
}
|
|
1254
1337
|
}
|
|
1255
1338
|
async _postBatchIngestRuns(body, options) {
|
|
@@ -1350,7 +1433,7 @@ export class Client {
|
|
|
1350
1433
|
const { inputs, outputs, events, extra, error, serialized, attachments, ...payload } = originalPayload;
|
|
1351
1434
|
const fields = { inputs, outputs, events, extra, error, serialized };
|
|
1352
1435
|
// encode the main run payload
|
|
1353
|
-
const stringifiedPayload =
|
|
1436
|
+
const stringifiedPayload = await this._serializeBody(payload, `Serializing for multipart ingestion of run with id: ${payload.id}`);
|
|
1354
1437
|
accumulatedParts.push({
|
|
1355
1438
|
name: `${method}.${payload.id}`,
|
|
1356
1439
|
payload: new Blob([stringifiedPayload], {
|
|
@@ -1362,7 +1445,7 @@ export class Client {
|
|
|
1362
1445
|
if (value === undefined) {
|
|
1363
1446
|
continue;
|
|
1364
1447
|
}
|
|
1365
|
-
const stringifiedValue =
|
|
1448
|
+
const stringifiedValue = await this._serializeBody(value, `Serializing ${key} for multipart ingestion of run with id: ${payload.id}`);
|
|
1366
1449
|
accumulatedParts.push({
|
|
1367
1450
|
name: `${method}.${payload.id}.${key}`,
|
|
1368
1451
|
payload: new Blob([stringifiedValue], {
|
|
@@ -4369,6 +4452,8 @@ export class Client {
|
|
|
4369
4452
|
commit_hash: result.commit_hash,
|
|
4370
4453
|
manifest: result.manifest,
|
|
4371
4454
|
examples: result.examples,
|
|
4455
|
+
hub_model_config: result.model_config,
|
|
4456
|
+
hub_model_provider: result.model_provider,
|
|
4372
4457
|
};
|
|
4373
4458
|
}
|
|
4374
4459
|
async pullPromptCommit(promptIdentifier, options) {
|
|
@@ -4807,6 +4892,12 @@ export class Client {
|
|
|
4807
4892
|
* ```
|
|
4808
4893
|
*/
|
|
4809
4894
|
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
4895
|
+
// Wait for any in-flight drains (whose serialize work may still be
|
|
4896
|
+
// in-progress on a worker thread and not yet registered with the
|
|
4897
|
+
// batchIngestCaller queue) before checking queue idleness.
|
|
4898
|
+
while (this._pendingDrains.size > 0) {
|
|
4899
|
+
await Promise.all([...this._pendingDrains]);
|
|
4900
|
+
}
|
|
4810
4901
|
await Promise.all([
|
|
4811
4902
|
...this.autoBatchQueue.items.map(({ itemPromise }) => itemPromise),
|
|
4812
4903
|
this.batchIngestCaller.queue.onIdle(),
|
|
@@ -13,7 +13,7 @@ const env_js_1 = require("../utils/env.cjs");
|
|
|
13
13
|
const error_js_1 = require("../utils/error.cjs");
|
|
14
14
|
const _random_name_js_1 = require("./_random_name.cjs");
|
|
15
15
|
const evaluator_js_1 = require("./evaluator.cjs");
|
|
16
|
-
const
|
|
16
|
+
const index_js_2 = require("../utils/uuid/src/index.cjs");
|
|
17
17
|
const evaluate_comparative_js_1 = require("./evaluate_comparative.cjs");
|
|
18
18
|
const p_queue_js_1 = require("../utils/p-queue.cjs");
|
|
19
19
|
// Implementation signature
|
|
@@ -202,7 +202,7 @@ class _ExperimentManager {
|
|
|
202
202
|
this._experimentName = (0, _random_name_js_1.randomName)();
|
|
203
203
|
}
|
|
204
204
|
else if (typeof args.experiment === "string") {
|
|
205
|
-
this._experimentName = `${args.experiment}-${(0,
|
|
205
|
+
this._experimentName = `${args.experiment}-${(0, index_js_2.v4)().slice(0, 8)}`;
|
|
206
206
|
}
|
|
207
207
|
else {
|
|
208
208
|
if (!args.experiment.name) {
|
|
@@ -276,7 +276,7 @@ class _ExperimentManager {
|
|
|
276
276
|
catch (e) {
|
|
277
277
|
// Naming collision
|
|
278
278
|
if (e?.name === "LangSmithConflictError") {
|
|
279
|
-
const ent = (0,
|
|
279
|
+
const ent = (0, index_js_2.v4)().slice(0, 6);
|
|
280
280
|
this._experimentName = `${originalExperimentName}-${ent}`;
|
|
281
281
|
}
|
|
282
282
|
else {
|
|
@@ -7,7 +7,7 @@ import { getLangSmithEnvVarsMetadata } from "../utils/env.js";
|
|
|
7
7
|
import { printErrorStackTrace } from "../utils/error.js";
|
|
8
8
|
import { randomName } from "./_random_name.js";
|
|
9
9
|
import { runEvaluator, } from "./evaluator.js";
|
|
10
|
-
import { v4 as uuidv4 } from "uuid";
|
|
10
|
+
import { v4 as uuidv4 } from "../utils/uuid/src/index.js";
|
|
11
11
|
import { evaluateComparative, } from "./evaluate_comparative.js";
|
|
12
12
|
import { PQueue } from "../utils/p-queue.js";
|
|
13
13
|
// Implementation signature
|
|
@@ -4,22 +4,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.evaluateComparative = evaluateComparative;
|
|
7
|
-
const
|
|
8
|
-
const
|
|
7
|
+
const index_js_1 = require("../utils/uuid/src/index.cjs");
|
|
8
|
+
const index_js_2 = require("../index.cjs");
|
|
9
9
|
const shuffle_js_1 = require("../utils/shuffle.cjs");
|
|
10
10
|
const async_caller_js_1 = require("../utils/async_caller.cjs");
|
|
11
|
-
const
|
|
11
|
+
const index_js_3 = __importDefault(require("../utils/p-retry/index.cjs"));
|
|
12
12
|
const traceable_js_1 = require("../traceable.cjs");
|
|
13
13
|
function isExperimentResultsList(value) {
|
|
14
14
|
return value.some((x) => typeof x !== "string");
|
|
15
15
|
}
|
|
16
16
|
async function loadExperiment(client, experiment) {
|
|
17
17
|
const value = typeof experiment === "string" ? experiment : experiment.experimentName;
|
|
18
|
-
return client.readProject((0,
|
|
18
|
+
return client.readProject((0, index_js_1.validate)(value) ? { projectId: value } : { projectName: value });
|
|
19
19
|
}
|
|
20
20
|
async function loadTraces(client, experiment, options) {
|
|
21
21
|
const executionOrder = options.loadNested ? undefined : 1;
|
|
22
|
-
const runs = await client.listRuns((0,
|
|
22
|
+
const runs = await client.listRuns((0, index_js_1.validate)(experiment)
|
|
23
23
|
? { projectId: experiment, executionOrder }
|
|
24
24
|
: { projectName: experiment, executionOrder });
|
|
25
25
|
const treeMap = {};
|
|
@@ -56,7 +56,7 @@ async function evaluateComparative(experiments, options) {
|
|
|
56
56
|
if (options.maxConcurrency && options.maxConcurrency < 0) {
|
|
57
57
|
throw new Error("maxConcurrency must be a positive number.");
|
|
58
58
|
}
|
|
59
|
-
const client = options.client ?? new
|
|
59
|
+
const client = options.client ?? new index_js_2.Client();
|
|
60
60
|
const resolvedExperiments = await Promise.all(experiments);
|
|
61
61
|
const projects = await (() => {
|
|
62
62
|
if (!isExperimentResultsList(resolvedExperiments)) {
|
|
@@ -64,7 +64,7 @@ async function evaluateComparative(experiments, options) {
|
|
|
64
64
|
}
|
|
65
65
|
// if we know the number of runs beforehand, check if the
|
|
66
66
|
// number of runs in the project matches the expected number of runs
|
|
67
|
-
return Promise.all(resolvedExperiments.map((experiment) => (0,
|
|
67
|
+
return Promise.all(resolvedExperiments.map((experiment) => (0, index_js_3.default)(async () => {
|
|
68
68
|
const project = await loadExperiment(client, experiment);
|
|
69
69
|
if (project.run_count !== experiment?.results.length) {
|
|
70
70
|
throw new Error("Experiment is missing runs. Retrying.");
|
|
@@ -83,16 +83,16 @@ async function evaluateComparative(experiments, options) {
|
|
|
83
83
|
console.warn("Detected multiple dataset versions used by experiments, which may lead to inaccurate results.");
|
|
84
84
|
}
|
|
85
85
|
const datasetVersion = projects.at(0)?.extra?.metadata?.dataset_version;
|
|
86
|
-
const id = (0,
|
|
86
|
+
const id = (0, index_js_1.v4)();
|
|
87
87
|
const experimentName = (() => {
|
|
88
88
|
if (!options.experimentPrefix) {
|
|
89
89
|
const names = projects
|
|
90
90
|
.map((p) => p.name)
|
|
91
91
|
.filter(Boolean)
|
|
92
92
|
.join(" vs. ");
|
|
93
|
-
return `${names}-${(0,
|
|
93
|
+
return `${names}-${(0, index_js_1.v4)().slice(0, 4)}`;
|
|
94
94
|
}
|
|
95
|
-
return `${options.experimentPrefix}-${(0,
|
|
95
|
+
return `${options.experimentPrefix}-${(0, index_js_1.v4)().slice(0, 4)}`;
|
|
96
96
|
})();
|
|
97
97
|
// TODO: add URL to the comparative experiment
|
|
98
98
|
console.log(`Starting pairwise evaluation of: ${experimentName}`);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DynamicRunEvaluator = void 0;
|
|
4
4
|
exports.runEvaluator = runEvaluator;
|
|
5
|
-
const
|
|
5
|
+
const index_js_1 = require("../utils/uuid/src/index.cjs");
|
|
6
6
|
const traceable_js_1 = require("../traceable.cjs");
|
|
7
7
|
/**
|
|
8
8
|
* Wraps an evaluator function + implements the RunEvaluator interface.
|
|
@@ -67,7 +67,7 @@ class DynamicRunEvaluator {
|
|
|
67
67
|
* @returns A promise that extracts to the evaluation result.
|
|
68
68
|
*/
|
|
69
69
|
async evaluateRun(run, example, options) {
|
|
70
|
-
let sourceRunId = (0,
|
|
70
|
+
let sourceRunId = (0, index_js_1.v7)();
|
|
71
71
|
const metadata = {
|
|
72
72
|
targetRunId: run.id,
|
|
73
73
|
};
|
package/dist/index.cjs
CHANGED
|
@@ -18,4 +18,4 @@ Object.defineProperty(exports, "PromptCache", { enumerable: true, get: function
|
|
|
18
18
|
Object.defineProperty(exports, "configureGlobalPromptCache", { enumerable: true, get: function () { return index_js_1.configureGlobalPromptCache; } });
|
|
19
19
|
Object.defineProperty(exports, "promptCacheSingleton", { enumerable: true, get: function () { return index_js_1.promptCacheSingleton; } });
|
|
20
20
|
// Update using pnpm bump-version
|
|
21
|
-
exports.__version__ = "0.5.
|
|
21
|
+
exports.__version__ = "0.5.25";
|
package/dist/index.d.ts
CHANGED
|
@@ -5,4 +5,4 @@ export { overrideFetchImplementation } from "./singletons/fetch.js";
|
|
|
5
5
|
export { getDefaultProjectName } from "./utils/project.js";
|
|
6
6
|
export { uuid7, uuid7FromTime } from "./uuid.js";
|
|
7
7
|
export { Cache, PromptCache, type CacheConfig, type CacheMetrics, configureGlobalPromptCache, promptCacheSingleton, } from "./utils/prompt_cache/index.js";
|
|
8
|
-
export declare const __version__ = "0.5.
|
|
8
|
+
export declare const __version__ = "0.5.25";
|
package/dist/index.js
CHANGED
|
@@ -5,4 +5,4 @@ export { getDefaultProjectName } from "./utils/project.js";
|
|
|
5
5
|
export { uuid7, uuid7FromTime } from "./uuid.js";
|
|
6
6
|
export { Cache, PromptCache, configureGlobalPromptCache, promptCacheSingleton, } from "./utils/prompt_cache/index.js";
|
|
7
7
|
// Update using pnpm bump-version
|
|
8
|
-
export const __version__ = "0.5.
|
|
8
|
+
export const __version__ = "0.5.25";
|