langsmith 0.5.22 → 0.5.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 +365 -19
- package/dist/client.d.ts +116 -1
- package/dist/client.js +369 -23
- package/dist/evaluation/_runner.cjs +4 -7
- package/dist/evaluation/_runner.js +2 -5
- 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 +54 -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/error.cjs +7 -0
- package/dist/utils/error.d.ts +1 -0
- package/dist/utils/error.js +6 -0
- package/dist/utils/fast-safe-stringify/index.cjs +203 -0
- package/dist/utils/fast-safe-stringify/index.d.ts +46 -0
- package/dist/utils/fast-safe-stringify/index.js +202 -0
- 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/prompts.cjs +7 -2
- package/dist/utils/prompts.d.ts +6 -1
- package/dist/utils/prompts.js +6 -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 +7 -5
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
|
*/
|
|
@@ -189,7 +190,19 @@ class AutoBatchQueue {
|
|
|
189
190
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise
|
|
190
191
|
itemPromiseResolve = resolve;
|
|
191
192
|
});
|
|
192
|
-
|
|
193
|
+
// By default we compute the exact serialized size here by stringifying
|
|
194
|
+
// the payload. This is expensive: JSON.stringify on large payloads
|
|
195
|
+
// blocks the event loop on the user's hot path.
|
|
196
|
+
//
|
|
197
|
+
// Opting into LANGSMITH_PERF_OPTIMIZATION=true switches to a cheap
|
|
198
|
+
// structural estimate instead. The estimate is only used for soft
|
|
199
|
+
// memory accounting (queue size limit and downstream async caller
|
|
200
|
+
// memory tracking), never for anything correctness-critical -- the
|
|
201
|
+
// real serialization still happens later, off the hot path, when the
|
|
202
|
+
// batch is assembled for sending.
|
|
203
|
+
const size = (0, env_js_1.getLangSmithEnvironmentVariable)("PERF_OPTIMIZATION") === "true"
|
|
204
|
+
? (0, index_js_3.estimateSerializedSize)(item.item).size
|
|
205
|
+
: (0, index_js_3.serialize)(item.item, `Serializing run with id: ${item.item.id}`).length;
|
|
193
206
|
// Check if adding this item would exceed the size limit
|
|
194
207
|
// Allow the run if the queue is empty (to support large single traces)
|
|
195
208
|
if (this.sizeBytes + size > this.maxSizeBytes && this.items.length > 0) {
|
|
@@ -253,9 +266,67 @@ class AutoBatchQueue {
|
|
|
253
266
|
}
|
|
254
267
|
exports.AutoBatchQueue = AutoBatchQueue;
|
|
255
268
|
class Client {
|
|
269
|
+
get tracingMode() {
|
|
270
|
+
return this._tracingMode;
|
|
271
|
+
}
|
|
256
272
|
get _fetch() {
|
|
257
273
|
return this.fetchImplementation || (0, fetch_js_1._getFetchImplementation)(this.debug);
|
|
258
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
|
+
}
|
|
259
330
|
constructor(config = {}) {
|
|
260
331
|
Object.defineProperty(this, "apiKey", {
|
|
261
332
|
enumerable: true,
|
|
@@ -420,12 +491,35 @@ class Client {
|
|
|
420
491
|
writable: true,
|
|
421
492
|
value: false
|
|
422
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
|
+
});
|
|
423
511
|
Object.defineProperty(this, "langSmithToOTELTranslator", {
|
|
424
512
|
enumerable: true,
|
|
425
513
|
configurable: true,
|
|
426
514
|
writable: true,
|
|
427
515
|
value: void 0
|
|
428
516
|
});
|
|
517
|
+
Object.defineProperty(this, "_tracingMode", {
|
|
518
|
+
enumerable: true,
|
|
519
|
+
configurable: true,
|
|
520
|
+
writable: true,
|
|
521
|
+
value: "langsmith"
|
|
522
|
+
});
|
|
429
523
|
Object.defineProperty(this, "fetchImplementation", {
|
|
430
524
|
enumerable: true,
|
|
431
525
|
configurable: true,
|
|
@@ -545,7 +639,8 @@ class Client {
|
|
|
545
639
|
this.batchSizeLimit = config.batchSizeLimit;
|
|
546
640
|
this.fetchOptions = config.fetchOptions || {};
|
|
547
641
|
this.manualFlushMode = config.manualFlushMode ?? this.manualFlushMode;
|
|
548
|
-
|
|
642
|
+
this._tracingMode = (0, env_js_1.resolveTracingMode)(config.tracingMode);
|
|
643
|
+
if (this._tracingMode === "otel") {
|
|
549
644
|
this.langSmithToOTELTranslator = new translator_js_1.LangSmithToOTELTranslator();
|
|
550
645
|
}
|
|
551
646
|
// Cache metadata env vars once during construction to avoid repeatedly scanning process.env
|
|
@@ -1084,18 +1179,18 @@ class Client {
|
|
|
1084
1179
|
const sizeLimit = await this._getBatchSizeLimit();
|
|
1085
1180
|
if (this.autoBatchQueue.sizeBytes > sizeLimitBytes ||
|
|
1086
1181
|
this.autoBatchQueue.items.length > sizeLimit) {
|
|
1087
|
-
|
|
1182
|
+
this._trackDrain(this.drainAutoBatchQueue({
|
|
1088
1183
|
batchSizeLimitBytes: sizeLimitBytes,
|
|
1089
1184
|
batchSizeLimit: sizeLimit,
|
|
1090
|
-
});
|
|
1185
|
+
}));
|
|
1091
1186
|
}
|
|
1092
1187
|
if (this.autoBatchQueue.items.length > 0) {
|
|
1093
1188
|
this.autoBatchTimeout = setTimeout(() => {
|
|
1094
1189
|
this.autoBatchTimeout = undefined;
|
|
1095
|
-
|
|
1190
|
+
this._trackDrain(this.drainAutoBatchQueue({
|
|
1096
1191
|
batchSizeLimitBytes: sizeLimitBytes,
|
|
1097
1192
|
batchSizeLimit: sizeLimit,
|
|
1098
|
-
});
|
|
1193
|
+
}));
|
|
1099
1194
|
}, this.autoBatchAggregationDelayMs);
|
|
1100
1195
|
}
|
|
1101
1196
|
return itemPromise;
|
|
@@ -1275,7 +1370,7 @@ class Client {
|
|
|
1275
1370
|
.map((item) => item.id)
|
|
1276
1371
|
.concat(batchChunks.patch.map((item) => item.id))
|
|
1277
1372
|
.join(",");
|
|
1278
|
-
await this._postBatchIngestRuns(
|
|
1373
|
+
await this._postBatchIngestRuns(await this._serializeBody(batchChunks, `Ingesting runs with ids: ${runIds}`), options);
|
|
1279
1374
|
}
|
|
1280
1375
|
}
|
|
1281
1376
|
async _postBatchIngestRuns(body, options) {
|
|
@@ -1376,7 +1471,7 @@ class Client {
|
|
|
1376
1471
|
const { inputs, outputs, events, extra, error, serialized, attachments, ...payload } = originalPayload;
|
|
1377
1472
|
const fields = { inputs, outputs, events, extra, error, serialized };
|
|
1378
1473
|
// encode the main run payload
|
|
1379
|
-
const stringifiedPayload =
|
|
1474
|
+
const stringifiedPayload = await this._serializeBody(payload, `Serializing for multipart ingestion of run with id: ${payload.id}`);
|
|
1380
1475
|
accumulatedParts.push({
|
|
1381
1476
|
name: `${method}.${payload.id}`,
|
|
1382
1477
|
payload: new Blob([stringifiedPayload], {
|
|
@@ -1388,7 +1483,7 @@ class Client {
|
|
|
1388
1483
|
if (value === undefined) {
|
|
1389
1484
|
continue;
|
|
1390
1485
|
}
|
|
1391
|
-
const stringifiedValue =
|
|
1486
|
+
const stringifiedValue = await this._serializeBody(value, `Serializing ${key} for multipart ingestion of run with id: ${payload.id}`);
|
|
1392
1487
|
accumulatedParts.push({
|
|
1393
1488
|
name: `${method}.${payload.id}.${key}`,
|
|
1394
1489
|
payload: new Blob([stringifiedValue], {
|
|
@@ -3821,7 +3916,7 @@ class Client {
|
|
|
3821
3916
|
return json.commits[0].commit_hash;
|
|
3822
3917
|
}
|
|
3823
3918
|
async _likeOrUnlikePrompt(promptIdentifier, like) {
|
|
3824
|
-
const [owner, promptName, _] = (0, prompts_js_1.
|
|
3919
|
+
const [owner, promptName, _] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
|
|
3825
3920
|
const body = JSON.stringify({ like: like });
|
|
3826
3921
|
const response = await this.caller.call(async () => {
|
|
3827
3922
|
const res = await this._fetch(`${this.apiUrl}/likes/${owner}/${promptName}`, {
|
|
@@ -3840,7 +3935,7 @@ class Client {
|
|
|
3840
3935
|
return response.json();
|
|
3841
3936
|
}
|
|
3842
3937
|
async _getPromptUrl(promptIdentifier) {
|
|
3843
|
-
const [owner, promptName, commitHash] = (0, prompts_js_1.
|
|
3938
|
+
const [owner, promptName, commitHash] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
|
|
3844
3939
|
if (!(await this._currentTenantIsOwner(owner))) {
|
|
3845
3940
|
if (commitHash !== "latest") {
|
|
3846
3941
|
return `${this.getHostUrl()}/hub/${owner}/${promptName}/${commitHash.substring(0, 8)}`;
|
|
@@ -3932,7 +4027,7 @@ class Client {
|
|
|
3932
4027
|
* ```
|
|
3933
4028
|
*/
|
|
3934
4029
|
async *listCommits(promptIdentifier) {
|
|
3935
|
-
const [owner, promptName, _] = (0, prompts_js_1.
|
|
4030
|
+
const [owner, promptName, _] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
|
|
3936
4031
|
for await (const commits of this._getPaginated(`/commits/${owner}/${promptName}/`, new URLSearchParams(), (res) => res.commits)) {
|
|
3937
4032
|
yield* commits;
|
|
3938
4033
|
}
|
|
@@ -3995,7 +4090,7 @@ class Client {
|
|
|
3995
4090
|
* ```
|
|
3996
4091
|
*/
|
|
3997
4092
|
async getPrompt(promptIdentifier) {
|
|
3998
|
-
const [owner, promptName, _] = (0, prompts_js_1.
|
|
4093
|
+
const [owner, promptName, _] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
|
|
3999
4094
|
const response = await this.caller.call(async () => {
|
|
4000
4095
|
const res = await this._fetch(`${this.apiUrl}/repos/${owner}/${promptName}`, {
|
|
4001
4096
|
method: "GET",
|
|
@@ -4052,7 +4147,7 @@ class Client {
|
|
|
4052
4147
|
You can add a handle by creating a public prompt at:\n
|
|
4053
4148
|
https://smith.langchain.com/prompts`);
|
|
4054
4149
|
}
|
|
4055
|
-
const [owner, promptName, _] = (0, prompts_js_1.
|
|
4150
|
+
const [owner, promptName, _] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
|
|
4056
4151
|
if (!(await this._currentTenantIsOwner(owner))) {
|
|
4057
4152
|
throw await this._ownerConflictError("create a prompt", owner);
|
|
4058
4153
|
}
|
|
@@ -4111,7 +4206,7 @@ class Client {
|
|
|
4111
4206
|
if (!(await this.promptExists(promptIdentifier))) {
|
|
4112
4207
|
throw new Error("Prompt does not exist, you must create it first.");
|
|
4113
4208
|
}
|
|
4114
|
-
const [owner, promptName, _] = (0, prompts_js_1.
|
|
4209
|
+
const [owner, promptName, _] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
|
|
4115
4210
|
const resolvedParentCommitHash = options?.parentCommitHash === "latest" || !options?.parentCommitHash
|
|
4116
4211
|
? await this._getLatestCommitHash(`${owner}/${promptName}`)
|
|
4117
4212
|
: options?.parentCommitHash;
|
|
@@ -4309,7 +4404,7 @@ class Client {
|
|
|
4309
4404
|
if (!(await this.promptExists(promptIdentifier))) {
|
|
4310
4405
|
throw new Error("Prompt does not exist, you must create it first.");
|
|
4311
4406
|
}
|
|
4312
|
-
const [owner, promptName] = (0, prompts_js_1.
|
|
4407
|
+
const [owner, promptName] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
|
|
4313
4408
|
if (!(await this._currentTenantIsOwner(owner))) {
|
|
4314
4409
|
throw await this._ownerConflictError("update a prompt", owner);
|
|
4315
4410
|
}
|
|
@@ -4349,7 +4444,7 @@ class Client {
|
|
|
4349
4444
|
if (!(await this.promptExists(promptIdentifier))) {
|
|
4350
4445
|
throw new Error("Prompt does not exist, you must create it first.");
|
|
4351
4446
|
}
|
|
4352
|
-
const [owner, promptName, _] = (0, prompts_js_1.
|
|
4447
|
+
const [owner, promptName, _] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
|
|
4353
4448
|
if (!(await this._currentTenantIsOwner(owner))) {
|
|
4354
4449
|
throw await this._ownerConflictError("delete a prompt", owner);
|
|
4355
4450
|
}
|
|
@@ -4377,7 +4472,7 @@ class Client {
|
|
|
4377
4472
|
* Fetch a prompt commit directly from the API (bypassing cache).
|
|
4378
4473
|
*/
|
|
4379
4474
|
async _fetchPromptFromApi(promptIdentifier, options) {
|
|
4380
|
-
const [owner, promptName, commitHash] = (0, prompts_js_1.
|
|
4475
|
+
const [owner, promptName, commitHash] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
|
|
4381
4476
|
const response = await this.caller.call(async () => {
|
|
4382
4477
|
const res = await this._fetch(`${this.apiUrl}/commits/${owner}/${promptName}/${commitHash}${options?.includeModel ? "?include_model=true" : ""}`, {
|
|
4383
4478
|
method: "GET",
|
|
@@ -4457,6 +4552,251 @@ class Client {
|
|
|
4457
4552
|
});
|
|
4458
4553
|
return url;
|
|
4459
4554
|
}
|
|
4555
|
+
/**
|
|
4556
|
+
* Check if an agent repo exists.
|
|
4557
|
+
*/
|
|
4558
|
+
async agentExists(identifier) {
|
|
4559
|
+
const [owner, name] = (0, prompts_js_1.parseHubIdentifier)(identifier);
|
|
4560
|
+
return this._repoExists(owner, name);
|
|
4561
|
+
}
|
|
4562
|
+
/**
|
|
4563
|
+
* Check if a skill repo exists.
|
|
4564
|
+
*/
|
|
4565
|
+
async skillExists(identifier) {
|
|
4566
|
+
const [owner, name] = (0, prompts_js_1.parseHubIdentifier)(identifier);
|
|
4567
|
+
return this._repoExists(owner, name);
|
|
4568
|
+
}
|
|
4569
|
+
/**
|
|
4570
|
+
* Pull an agent directory from Hub.
|
|
4571
|
+
* @param identifier The identifier (owner/name[:version]).
|
|
4572
|
+
* @param options.version Commit hash or tag; overrides identifier's version.
|
|
4573
|
+
*/
|
|
4574
|
+
async pullAgent(identifier, options) {
|
|
4575
|
+
return (await this._pullDirectory(identifier, "agent", options?.version));
|
|
4576
|
+
}
|
|
4577
|
+
/**
|
|
4578
|
+
* Pull a skill directory from Hub.
|
|
4579
|
+
*/
|
|
4580
|
+
async pullSkill(identifier, options) {
|
|
4581
|
+
return (await this._pullDirectory(identifier, "skill", options?.version));
|
|
4582
|
+
}
|
|
4583
|
+
/**
|
|
4584
|
+
* Push an agent to Hub. Creates the repo if missing, patches metadata if
|
|
4585
|
+
* provided, then commits the given files.
|
|
4586
|
+
* @returns The URL of the resulting commit.
|
|
4587
|
+
*/
|
|
4588
|
+
async pushAgent(identifier, options) {
|
|
4589
|
+
return this._pushDirectory(identifier, "agent", options);
|
|
4590
|
+
}
|
|
4591
|
+
/**
|
|
4592
|
+
* Push a skill to Hub.
|
|
4593
|
+
*/
|
|
4594
|
+
async pushSkill(identifier, options) {
|
|
4595
|
+
return this._pushDirectory(identifier, "skill", options);
|
|
4596
|
+
}
|
|
4597
|
+
/**
|
|
4598
|
+
* Delete an agent and all its owned child file repos.
|
|
4599
|
+
*/
|
|
4600
|
+
async deleteAgent(identifier) {
|
|
4601
|
+
return this._deleteDirectory(identifier);
|
|
4602
|
+
}
|
|
4603
|
+
/**
|
|
4604
|
+
* Delete a skill and all its owned child file repos.
|
|
4605
|
+
*/
|
|
4606
|
+
async deleteSkill(identifier) {
|
|
4607
|
+
return this._deleteDirectory(identifier);
|
|
4608
|
+
}
|
|
4609
|
+
/**
|
|
4610
|
+
* List agent repos. Yields one at a time, auto-paginating.
|
|
4611
|
+
*/
|
|
4612
|
+
async *listAgents(options) {
|
|
4613
|
+
yield* this._listReposByType("agent", options);
|
|
4614
|
+
}
|
|
4615
|
+
/**
|
|
4616
|
+
* List skill repos. Yields one at a time, auto-paginating.
|
|
4617
|
+
*/
|
|
4618
|
+
async *listSkills(options) {
|
|
4619
|
+
yield* this._listReposByType("skill", options);
|
|
4620
|
+
}
|
|
4621
|
+
async *_listReposByType(repoType, options) {
|
|
4622
|
+
const params = new URLSearchParams();
|
|
4623
|
+
params.append("repo_type", repoType);
|
|
4624
|
+
params.append("is_archived", (!!options?.isArchived).toString());
|
|
4625
|
+
if (options?.isPublic !== undefined) {
|
|
4626
|
+
params.append("is_public", options.isPublic.toString());
|
|
4627
|
+
}
|
|
4628
|
+
if (options?.query) {
|
|
4629
|
+
params.append("query", options.query);
|
|
4630
|
+
}
|
|
4631
|
+
for await (const repos of this._getPaginated("/repos", params, (res) => res.repos)) {
|
|
4632
|
+
yield* repos;
|
|
4633
|
+
}
|
|
4634
|
+
}
|
|
4635
|
+
async _pullDirectory(identifier, repoType, version) {
|
|
4636
|
+
const [owner, name, parsedVersion] = (0, prompts_js_1.parseHubIdentifier)(identifier);
|
|
4637
|
+
const resolvedVersion = version ?? (parsedVersion !== "latest" ? parsedVersion : undefined);
|
|
4638
|
+
const url = new URL(`${this.apiUrl}/v1/platform/hub/repos/${owner}/${name}/directories`);
|
|
4639
|
+
url.searchParams.set("repo_type", repoType);
|
|
4640
|
+
if (resolvedVersion) {
|
|
4641
|
+
url.searchParams.set("commit", resolvedVersion);
|
|
4642
|
+
}
|
|
4643
|
+
const response = await this.caller.call(async () => {
|
|
4644
|
+
const res = await this._fetch(url.toString(), {
|
|
4645
|
+
method: "GET",
|
|
4646
|
+
headers: this._mergedHeaders,
|
|
4647
|
+
signal: AbortSignal.timeout(this.timeout_ms),
|
|
4648
|
+
...this.fetchOptions,
|
|
4649
|
+
});
|
|
4650
|
+
await (0, error_js_1.raiseForStatus)(res, "pull directory");
|
|
4651
|
+
return res;
|
|
4652
|
+
});
|
|
4653
|
+
return (await response.json());
|
|
4654
|
+
}
|
|
4655
|
+
async _pushDirectory(identifier, repoType, options) {
|
|
4656
|
+
if (options.parentCommit !== undefined &&
|
|
4657
|
+
(options.parentCommit.length < 8 || options.parentCommit.length > 64)) {
|
|
4658
|
+
throw new Error("parent_commit must be 8-64 characters");
|
|
4659
|
+
}
|
|
4660
|
+
const [owner, name] = (0, prompts_js_1.parseHubIdentifier)(identifier);
|
|
4661
|
+
if (!(await this._currentTenantIsOwner(owner))) {
|
|
4662
|
+
throw await this._ownerConflictError(`push ${repoType}`, owner);
|
|
4663
|
+
}
|
|
4664
|
+
if (await this._repoExists(owner, name)) {
|
|
4665
|
+
if (options.description !== undefined ||
|
|
4666
|
+
options.readme !== undefined ||
|
|
4667
|
+
options.tags !== undefined ||
|
|
4668
|
+
options.isPublic !== undefined) {
|
|
4669
|
+
await this._updateRepoMetadata(owner, name, options);
|
|
4670
|
+
}
|
|
4671
|
+
}
|
|
4672
|
+
else {
|
|
4673
|
+
const REPO_HANDLE_PATTERN = /^[a-z][a-z0-9-_]*$/;
|
|
4674
|
+
if (!REPO_HANDLE_PATTERN.test(name)) {
|
|
4675
|
+
throw new Error(`Invalid repo_handle ${JSON.stringify(name)}: must match ${REPO_HANDLE_PATTERN}`);
|
|
4676
|
+
}
|
|
4677
|
+
await this._createRepo(name, repoType, options);
|
|
4678
|
+
}
|
|
4679
|
+
const body = { files: options.files };
|
|
4680
|
+
if (options.parentCommit) {
|
|
4681
|
+
body.parent_commit = options.parentCommit;
|
|
4682
|
+
}
|
|
4683
|
+
const response = await this.caller.call(async () => {
|
|
4684
|
+
const res = await this._fetch(`${this.apiUrl}/v1/platform/hub/repos/${owner}/${name}/directories/commits`, {
|
|
4685
|
+
method: "POST",
|
|
4686
|
+
headers: {
|
|
4687
|
+
...this._mergedHeaders,
|
|
4688
|
+
"Content-Type": "application/json",
|
|
4689
|
+
},
|
|
4690
|
+
signal: AbortSignal.timeout(this.timeout_ms),
|
|
4691
|
+
...this.fetchOptions,
|
|
4692
|
+
body: JSON.stringify(body),
|
|
4693
|
+
});
|
|
4694
|
+
await (0, error_js_1.raiseForStatus)(res, `push ${repoType}`);
|
|
4695
|
+
return res;
|
|
4696
|
+
});
|
|
4697
|
+
const data = (await response.json());
|
|
4698
|
+
const commitHash = data.commit.commit_hash;
|
|
4699
|
+
return `${this.getHostUrl()}/hub/${owner}/${name}:${commitHash.slice(0, 8)}`;
|
|
4700
|
+
}
|
|
4701
|
+
async _deleteDirectory(identifier) {
|
|
4702
|
+
const [owner, name] = (0, prompts_js_1.parseHubIdentifier)(identifier);
|
|
4703
|
+
if (!(await this._currentTenantIsOwner(owner))) {
|
|
4704
|
+
throw await this._ownerConflictError("delete", owner);
|
|
4705
|
+
}
|
|
4706
|
+
await this.caller.call(async () => {
|
|
4707
|
+
const res = await this._fetch(`${this.apiUrl}/v1/platform/hub/repos/${owner}/${name}/directories`, {
|
|
4708
|
+
method: "DELETE",
|
|
4709
|
+
headers: this._mergedHeaders,
|
|
4710
|
+
signal: AbortSignal.timeout(this.timeout_ms),
|
|
4711
|
+
...this.fetchOptions,
|
|
4712
|
+
});
|
|
4713
|
+
await (0, error_js_1.raiseForStatus)(res, "delete directory");
|
|
4714
|
+
return res;
|
|
4715
|
+
});
|
|
4716
|
+
}
|
|
4717
|
+
async _repoExists(owner, name) {
|
|
4718
|
+
try {
|
|
4719
|
+
await this.caller.call(async () => {
|
|
4720
|
+
const res = await this._fetch(`${this.apiUrl}/repos/${owner}/${name}`, {
|
|
4721
|
+
method: "GET",
|
|
4722
|
+
headers: this._mergedHeaders,
|
|
4723
|
+
signal: AbortSignal.timeout(this.timeout_ms),
|
|
4724
|
+
...this.fetchOptions,
|
|
4725
|
+
});
|
|
4726
|
+
await (0, error_js_1.raiseForStatus)(res, "check repo exists");
|
|
4727
|
+
return res;
|
|
4728
|
+
});
|
|
4729
|
+
return true;
|
|
4730
|
+
}
|
|
4731
|
+
catch (e) {
|
|
4732
|
+
if ((0, error_js_1.isLangSmithNotFoundError)(e)) {
|
|
4733
|
+
return false;
|
|
4734
|
+
}
|
|
4735
|
+
throw e;
|
|
4736
|
+
}
|
|
4737
|
+
}
|
|
4738
|
+
async _createRepo(name, repoType, options) {
|
|
4739
|
+
const body = {
|
|
4740
|
+
repo_handle: name,
|
|
4741
|
+
repo_type: repoType,
|
|
4742
|
+
is_public: !!options.isPublic,
|
|
4743
|
+
};
|
|
4744
|
+
if (options.description !== undefined)
|
|
4745
|
+
body.description = options.description;
|
|
4746
|
+
if (options.readme !== undefined)
|
|
4747
|
+
body.readme = options.readme;
|
|
4748
|
+
if (options.tags !== undefined)
|
|
4749
|
+
body.tags = options.tags;
|
|
4750
|
+
try {
|
|
4751
|
+
await this.caller.call(async () => {
|
|
4752
|
+
const res = await this._fetch(`${this.apiUrl}/repos/`, {
|
|
4753
|
+
method: "POST",
|
|
4754
|
+
headers: {
|
|
4755
|
+
...this._mergedHeaders,
|
|
4756
|
+
"Content-Type": "application/json",
|
|
4757
|
+
},
|
|
4758
|
+
signal: AbortSignal.timeout(this.timeout_ms),
|
|
4759
|
+
...this.fetchOptions,
|
|
4760
|
+
body: JSON.stringify(body),
|
|
4761
|
+
});
|
|
4762
|
+
await (0, error_js_1.raiseForStatus)(res, `create ${repoType}`);
|
|
4763
|
+
return res;
|
|
4764
|
+
});
|
|
4765
|
+
}
|
|
4766
|
+
catch (e) {
|
|
4767
|
+
if ((0, error_js_1.isLangSmithConflictError)(e)) {
|
|
4768
|
+
return;
|
|
4769
|
+
}
|
|
4770
|
+
throw e;
|
|
4771
|
+
}
|
|
4772
|
+
}
|
|
4773
|
+
async _updateRepoMetadata(owner, name, options) {
|
|
4774
|
+
const body = {};
|
|
4775
|
+
if (options.description !== undefined)
|
|
4776
|
+
body.description = options.description;
|
|
4777
|
+
if (options.readme !== undefined)
|
|
4778
|
+
body.readme = options.readme;
|
|
4779
|
+
if (options.tags !== undefined)
|
|
4780
|
+
body.tags = options.tags;
|
|
4781
|
+
if (options.isPublic !== undefined)
|
|
4782
|
+
body.is_public = options.isPublic;
|
|
4783
|
+
if (Object.keys(body).length === 0)
|
|
4784
|
+
return;
|
|
4785
|
+
await this.caller.call(async () => {
|
|
4786
|
+
const res = await this._fetch(`${this.apiUrl}/repos/${owner}/${name}`, {
|
|
4787
|
+
method: "PATCH",
|
|
4788
|
+
headers: {
|
|
4789
|
+
...this._mergedHeaders,
|
|
4790
|
+
"Content-Type": "application/json",
|
|
4791
|
+
},
|
|
4792
|
+
signal: AbortSignal.timeout(this.timeout_ms),
|
|
4793
|
+
...this.fetchOptions,
|
|
4794
|
+
body: JSON.stringify(body),
|
|
4795
|
+
});
|
|
4796
|
+
await (0, error_js_1.raiseForStatus)(res, "update repo metadata");
|
|
4797
|
+
return res;
|
|
4798
|
+
});
|
|
4799
|
+
}
|
|
4460
4800
|
/**
|
|
4461
4801
|
* Clone a public dataset to your own langsmith tenant.
|
|
4462
4802
|
* This operation is idempotent. If you already have a dataset with the given name,
|
|
@@ -4588,6 +4928,12 @@ class Client {
|
|
|
4588
4928
|
* ```
|
|
4589
4929
|
*/
|
|
4590
4930
|
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
4931
|
+
// Wait for any in-flight drains (whose serialize work may still be
|
|
4932
|
+
// in-progress on a worker thread and not yet registered with the
|
|
4933
|
+
// batchIngestCaller queue) before checking queue idleness.
|
|
4934
|
+
while (this._pendingDrains.size > 0) {
|
|
4935
|
+
await Promise.all([...this._pendingDrains]);
|
|
4936
|
+
}
|
|
4591
4937
|
await Promise.all([
|
|
4592
4938
|
...this.autoBatchQueue.items.map(({ itemPromise }) => itemPromise),
|
|
4593
4939
|
this.batchIngestCaller.queue.onIdle(),
|