langsmith 0.5.18 → 0.5.20
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 +24 -0
- package/dist/client.d.ts +5 -0
- package/dist/client.js +24 -0
- package/dist/experimental/anthropic/context.cjs +5 -0
- package/dist/experimental/anthropic/context.js +5 -0
- package/dist/experimental/anthropic/index.cjs +1 -0
- package/dist/experimental/anthropic/index.js +1 -0
- package/dist/experimental/sandbox/client.cjs +234 -11
- package/dist/experimental/sandbox/client.d.ts +76 -4
- package/dist/experimental/sandbox/client.js +234 -11
- package/dist/experimental/sandbox/index.cjs +0 -3
- package/dist/experimental/sandbox/index.d.ts +1 -1
- package/dist/experimental/sandbox/index.js +0 -3
- package/dist/experimental/sandbox/sandbox.cjs +65 -1
- package/dist/experimental/sandbox/sandbox.d.ts +35 -5
- package/dist/experimental/sandbox/sandbox.js +65 -1
- package/dist/experimental/sandbox/types.d.ts +92 -1
- package/dist/experimental/vercel/index.cjs +11 -0
- package/dist/experimental/vercel/index.js +12 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
package/dist/client.cjs
CHANGED
|
@@ -679,6 +679,24 @@ class Client {
|
|
|
679
679
|
}
|
|
680
680
|
return outputs;
|
|
681
681
|
}
|
|
682
|
+
/**
|
|
683
|
+
* Filter content from new_token events to prevent streaming LLM output
|
|
684
|
+
* from being uploaded via events.
|
|
685
|
+
*/
|
|
686
|
+
_filterNewTokenEvents(events) {
|
|
687
|
+
if (!events || events.length === 0) {
|
|
688
|
+
return events;
|
|
689
|
+
}
|
|
690
|
+
return events.map((event) => {
|
|
691
|
+
if (event.name === "new_token") {
|
|
692
|
+
// Remove the kwargs containing the token data
|
|
693
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
694
|
+
const { kwargs: _, ...rest } = event;
|
|
695
|
+
return rest;
|
|
696
|
+
}
|
|
697
|
+
return event;
|
|
698
|
+
});
|
|
699
|
+
}
|
|
682
700
|
async prepareRunCreateOrUpdateInputs(run) {
|
|
683
701
|
const runParams = { ...run };
|
|
684
702
|
if (runParams.inputs !== undefined) {
|
|
@@ -687,6 +705,9 @@ class Client {
|
|
|
687
705
|
if (runParams.outputs !== undefined) {
|
|
688
706
|
runParams.outputs = await this.processOutputs(runParams.outputs);
|
|
689
707
|
}
|
|
708
|
+
if (runParams.events !== undefined) {
|
|
709
|
+
runParams.events = this._filterNewTokenEvents(runParams.events);
|
|
710
|
+
}
|
|
690
711
|
return runParams;
|
|
691
712
|
}
|
|
692
713
|
async _getResponse(path, queryParams) {
|
|
@@ -1531,6 +1552,9 @@ class Client {
|
|
|
1531
1552
|
if (run.outputs) {
|
|
1532
1553
|
run.outputs = await this.processOutputs(run.outputs);
|
|
1533
1554
|
}
|
|
1555
|
+
if (run.events) {
|
|
1556
|
+
run.events = this._filterNewTokenEvents(run.events);
|
|
1557
|
+
}
|
|
1534
1558
|
// TODO: Untangle types
|
|
1535
1559
|
const data = { ...run, id: runId };
|
|
1536
1560
|
if (!this._filterForSampling([data], true).length) {
|
package/dist/client.d.ts
CHANGED
|
@@ -445,6 +445,11 @@ export declare class Client implements LangSmithTracingClientInterface {
|
|
|
445
445
|
private _getPlatformEndpointPath;
|
|
446
446
|
private processInputs;
|
|
447
447
|
private processOutputs;
|
|
448
|
+
/**
|
|
449
|
+
* Filter content from new_token events to prevent streaming LLM output
|
|
450
|
+
* from being uploaded via events.
|
|
451
|
+
*/
|
|
452
|
+
private _filterNewTokenEvents;
|
|
448
453
|
private prepareRunCreateOrUpdateInputs;
|
|
449
454
|
private _getResponse;
|
|
450
455
|
private _get;
|
package/dist/client.js
CHANGED
|
@@ -641,6 +641,24 @@ export class Client {
|
|
|
641
641
|
}
|
|
642
642
|
return outputs;
|
|
643
643
|
}
|
|
644
|
+
/**
|
|
645
|
+
* Filter content from new_token events to prevent streaming LLM output
|
|
646
|
+
* from being uploaded via events.
|
|
647
|
+
*/
|
|
648
|
+
_filterNewTokenEvents(events) {
|
|
649
|
+
if (!events || events.length === 0) {
|
|
650
|
+
return events;
|
|
651
|
+
}
|
|
652
|
+
return events.map((event) => {
|
|
653
|
+
if (event.name === "new_token") {
|
|
654
|
+
// Remove the kwargs containing the token data
|
|
655
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
656
|
+
const { kwargs: _, ...rest } = event;
|
|
657
|
+
return rest;
|
|
658
|
+
}
|
|
659
|
+
return event;
|
|
660
|
+
});
|
|
661
|
+
}
|
|
644
662
|
async prepareRunCreateOrUpdateInputs(run) {
|
|
645
663
|
const runParams = { ...run };
|
|
646
664
|
if (runParams.inputs !== undefined) {
|
|
@@ -649,6 +667,9 @@ export class Client {
|
|
|
649
667
|
if (runParams.outputs !== undefined) {
|
|
650
668
|
runParams.outputs = await this.processOutputs(runParams.outputs);
|
|
651
669
|
}
|
|
670
|
+
if (runParams.events !== undefined) {
|
|
671
|
+
runParams.events = this._filterNewTokenEvents(runParams.events);
|
|
672
|
+
}
|
|
652
673
|
return runParams;
|
|
653
674
|
}
|
|
654
675
|
async _getResponse(path, queryParams) {
|
|
@@ -1493,6 +1514,9 @@ export class Client {
|
|
|
1493
1514
|
if (run.outputs) {
|
|
1494
1515
|
run.outputs = await this.processOutputs(run.outputs);
|
|
1495
1516
|
}
|
|
1517
|
+
if (run.events) {
|
|
1518
|
+
run.events = this._filterNewTokenEvents(run.events);
|
|
1519
|
+
}
|
|
1496
1520
|
// TODO: Untangle types
|
|
1497
1521
|
const data = { ...run, id: runId };
|
|
1498
1522
|
if (!this._filterForSampling([data], true).length) {
|
|
@@ -133,6 +133,11 @@ class StreamManager {
|
|
|
133
133
|
run_type: "chain",
|
|
134
134
|
inputs: block.input,
|
|
135
135
|
start_time: eventTime,
|
|
136
|
+
extra: {
|
|
137
|
+
metadata: {
|
|
138
|
+
ls_agent_type: "subagent",
|
|
139
|
+
},
|
|
140
|
+
},
|
|
136
141
|
}) ?? this.tools[block.id];
|
|
137
142
|
this.namespaces[block.id] ??= this.tools[block.id];
|
|
138
143
|
}
|
|
@@ -130,6 +130,11 @@ export class StreamManager {
|
|
|
130
130
|
run_type: "chain",
|
|
131
131
|
inputs: block.input,
|
|
132
132
|
start_time: eventTime,
|
|
133
|
+
extra: {
|
|
134
|
+
metadata: {
|
|
135
|
+
ls_agent_type: "subagent",
|
|
136
|
+
},
|
|
137
|
+
},
|
|
133
138
|
}) ?? this.tools[block.id];
|
|
134
139
|
this.namespaces[block.id] ??= this.tools[block.id];
|
|
135
140
|
}
|
|
@@ -112,6 +112,7 @@ function wrapClaudeAgentQuery(queryFn, defaultThis, baseConfig) {
|
|
|
112
112
|
...baseConfig,
|
|
113
113
|
metadata: {
|
|
114
114
|
ls_integration: "claude-agent-sdk-js",
|
|
115
|
+
ls_agent_type: "root",
|
|
115
116
|
...baseConfig?.metadata,
|
|
116
117
|
},
|
|
117
118
|
__deferredSerializableArgOptions: { maxDepth: 1 },
|
|
@@ -109,6 +109,7 @@ function wrapClaudeAgentQuery(queryFn, defaultThis, baseConfig) {
|
|
|
109
109
|
...baseConfig,
|
|
110
110
|
metadata: {
|
|
111
111
|
ls_integration: "claude-agent-sdk-js",
|
|
112
|
+
ls_agent_type: "root",
|
|
112
113
|
...baseConfig?.metadata,
|
|
113
114
|
},
|
|
114
115
|
__deferredSerializableArgOptions: { maxDepth: 1 },
|
|
@@ -10,6 +10,27 @@ const async_caller_js_1 = require("../../utils/async_caller.cjs");
|
|
|
10
10
|
const sandbox_js_1 = require("./sandbox.cjs");
|
|
11
11
|
const errors_js_1 = require("./errors.cjs");
|
|
12
12
|
const helpers_js_1 = require("./helpers.cjs");
|
|
13
|
+
/**
|
|
14
|
+
* Sleep that can be interrupted by an AbortSignal.
|
|
15
|
+
* Resolves after `ms` milliseconds or rejects immediately if the signal fires.
|
|
16
|
+
*/
|
|
17
|
+
function sleepWithSignal(ms, signal) {
|
|
18
|
+
if (!signal) {
|
|
19
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
20
|
+
}
|
|
21
|
+
signal.throwIfAborted();
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
const timer = setTimeout(() => {
|
|
24
|
+
signal.removeEventListener("abort", onAbort);
|
|
25
|
+
resolve();
|
|
26
|
+
}, ms);
|
|
27
|
+
function onAbort() {
|
|
28
|
+
clearTimeout(timer);
|
|
29
|
+
reject(signal.reason);
|
|
30
|
+
}
|
|
31
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
32
|
+
});
|
|
33
|
+
}
|
|
13
34
|
/**
|
|
14
35
|
* Get the default sandbox API endpoint from environment.
|
|
15
36
|
*
|
|
@@ -53,6 +74,8 @@ function getDefaultApiKey() {
|
|
|
53
74
|
* await sandbox.delete();
|
|
54
75
|
* }
|
|
55
76
|
* ```
|
|
77
|
+
*
|
|
78
|
+
* @experimental This feature is experimental, and breaking changes are expected.
|
|
56
79
|
*/
|
|
57
80
|
class SandboxClient {
|
|
58
81
|
constructor(config = {}) {
|
|
@@ -113,6 +136,25 @@ class SandboxClient {
|
|
|
113
136
|
getApiKey() {
|
|
114
137
|
return this._apiKey;
|
|
115
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* JSON POST helper. Sends JSON body, checks response status,
|
|
141
|
+
* and returns the Response for further processing.
|
|
142
|
+
* Throws on non-ok responses via handleClientHttpError.
|
|
143
|
+
* Callers can add specific status checks (e.g. 404) before calling this.
|
|
144
|
+
* @internal
|
|
145
|
+
*/
|
|
146
|
+
async _postJson(url, body, options) {
|
|
147
|
+
const response = await this._fetch(url, {
|
|
148
|
+
method: "POST",
|
|
149
|
+
headers: { "Content-Type": "application/json" },
|
|
150
|
+
body: JSON.stringify(body),
|
|
151
|
+
signal: options?.signal,
|
|
152
|
+
});
|
|
153
|
+
if (!response.ok) {
|
|
154
|
+
await (0, helpers_js_1.handleClientHttpError)(response);
|
|
155
|
+
}
|
|
156
|
+
return response;
|
|
157
|
+
}
|
|
116
158
|
// =========================================================================
|
|
117
159
|
// Volume Operations
|
|
118
160
|
// =========================================================================
|
|
@@ -538,14 +580,25 @@ class SandboxClient {
|
|
|
538
580
|
* ```
|
|
539
581
|
*/
|
|
540
582
|
async createSandbox(templateName, options = {}) {
|
|
541
|
-
const { name, timeout = 30, waitForReady = true, ttlSeconds, idleTtlSeconds, } = options;
|
|
583
|
+
const { snapshotId, name, timeout = 30, waitForReady = true, ttlSeconds, idleTtlSeconds, vCpus, memBytes, fsCapacityBytes, } = options;
|
|
584
|
+
if (!templateName && !snapshotId) {
|
|
585
|
+
throw new Error("Either templateName or snapshotId is required");
|
|
586
|
+
}
|
|
587
|
+
if (templateName && snapshotId) {
|
|
588
|
+
throw new Error("Cannot specify both templateName and snapshotId");
|
|
589
|
+
}
|
|
542
590
|
(0, helpers_js_1.validateTtl)(ttlSeconds, "ttlSeconds");
|
|
543
591
|
(0, helpers_js_1.validateTtl)(idleTtlSeconds, "idleTtlSeconds");
|
|
544
592
|
const url = `${this._baseUrl}/boxes`;
|
|
545
593
|
const payload = {
|
|
546
|
-
template_name: templateName,
|
|
547
594
|
wait_for_ready: waitForReady,
|
|
548
595
|
};
|
|
596
|
+
if (templateName) {
|
|
597
|
+
payload.template_name = templateName;
|
|
598
|
+
}
|
|
599
|
+
if (snapshotId) {
|
|
600
|
+
payload.snapshot_id = snapshotId;
|
|
601
|
+
}
|
|
549
602
|
if (waitForReady) {
|
|
550
603
|
payload.timeout = timeout;
|
|
551
604
|
}
|
|
@@ -558,6 +611,15 @@ class SandboxClient {
|
|
|
558
611
|
if (idleTtlSeconds !== undefined) {
|
|
559
612
|
payload.idle_ttl_seconds = idleTtlSeconds;
|
|
560
613
|
}
|
|
614
|
+
if (vCpus !== undefined) {
|
|
615
|
+
payload.vcpus = vCpus;
|
|
616
|
+
}
|
|
617
|
+
if (memBytes !== undefined) {
|
|
618
|
+
payload.mem_bytes = memBytes;
|
|
619
|
+
}
|
|
620
|
+
if (fsCapacityBytes !== undefined) {
|
|
621
|
+
payload.fs_capacity_bytes = fsCapacityBytes;
|
|
622
|
+
}
|
|
561
623
|
const httpTimeout = waitForReady ? (timeout + 30) * 1000 : 30 * 1000;
|
|
562
624
|
const response = await this._fetch(url, {
|
|
563
625
|
method: "POST",
|
|
@@ -580,9 +642,9 @@ class SandboxClient {
|
|
|
580
642
|
* @returns Sandbox.
|
|
581
643
|
* @throws LangSmithResourceNotFoundError if sandbox not found.
|
|
582
644
|
*/
|
|
583
|
-
async getSandbox(name) {
|
|
645
|
+
async getSandbox(name, options) {
|
|
584
646
|
const url = `${this._baseUrl}/boxes/${encodeURIComponent(name)}`;
|
|
585
|
-
const response = await this._fetch(url);
|
|
647
|
+
const response = await this._fetch(url, { signal: options?.signal });
|
|
586
648
|
if (!response.ok) {
|
|
587
649
|
if (response.status === 404) {
|
|
588
650
|
throw new errors_js_1.LangSmithResourceNotFoundError(`Sandbox '${name}' not found`, "sandbox");
|
|
@@ -677,9 +739,9 @@ class SandboxClient {
|
|
|
677
739
|
* @returns ResourceStatus with status and optional status_message.
|
|
678
740
|
* @throws LangSmithResourceNotFoundError if sandbox not found.
|
|
679
741
|
*/
|
|
680
|
-
async getSandboxStatus(name) {
|
|
742
|
+
async getSandboxStatus(name, options) {
|
|
681
743
|
const url = `${this._baseUrl}/boxes/${encodeURIComponent(name)}/status`;
|
|
682
|
-
const response = await this._fetch(url);
|
|
744
|
+
const response = await this._fetch(url, { signal: options?.signal });
|
|
683
745
|
if (!response.ok) {
|
|
684
746
|
if (response.status === 404) {
|
|
685
747
|
throw new errors_js_1.LangSmithResourceNotFoundError(`Sandbox '${name}' not found`, "sandbox");
|
|
@@ -709,22 +771,183 @@ class SandboxClient {
|
|
|
709
771
|
* ```
|
|
710
772
|
*/
|
|
711
773
|
async waitForSandbox(name, options = {}) {
|
|
712
|
-
const { timeout = 120, pollInterval = 1.0 } = options;
|
|
774
|
+
const { timeout = 120, pollInterval = 1.0, signal } = options;
|
|
713
775
|
const deadline = Date.now() + timeout * 1000;
|
|
714
776
|
let lastStatus = "provisioning";
|
|
715
777
|
while (Date.now() < deadline) {
|
|
716
|
-
|
|
778
|
+
signal?.throwIfAborted();
|
|
779
|
+
const statusResult = await this.getSandboxStatus(name, { signal });
|
|
717
780
|
lastStatus = statusResult.status;
|
|
718
781
|
if (statusResult.status === "ready") {
|
|
719
|
-
return this.getSandbox(name);
|
|
782
|
+
return this.getSandbox(name, { signal });
|
|
720
783
|
}
|
|
721
784
|
if (statusResult.status === "failed") {
|
|
722
785
|
throw new errors_js_1.LangSmithResourceCreationError(statusResult.status_message ?? `Sandbox '${name}' creation failed`, "sandbox");
|
|
723
786
|
}
|
|
724
|
-
// Wait before polling again
|
|
725
|
-
|
|
787
|
+
// Wait before polling again, capped to remaining time + jitter
|
|
788
|
+
const remaining = deadline - Date.now();
|
|
789
|
+
const jitter = pollInterval * 200 * (Math.random() - 0.5); // ±10%
|
|
790
|
+
const delay = Math.min(pollInterval * 1000 + jitter, remaining);
|
|
791
|
+
if (delay > 0) {
|
|
792
|
+
await sleepWithSignal(delay, signal);
|
|
793
|
+
}
|
|
726
794
|
}
|
|
727
795
|
throw new errors_js_1.LangSmithResourceTimeoutError(`Sandbox '${name}' did not become ready within ${timeout}s`, "sandbox", lastStatus);
|
|
728
796
|
}
|
|
797
|
+
/**
|
|
798
|
+
* Start a stopped sandbox and wait until ready.
|
|
799
|
+
*
|
|
800
|
+
* @param name - Sandbox name.
|
|
801
|
+
* @param options - Options with timeout.
|
|
802
|
+
* @returns Sandbox in "ready" status.
|
|
803
|
+
*/
|
|
804
|
+
async startSandbox(name, options = {}) {
|
|
805
|
+
const { timeout = 120, signal } = options;
|
|
806
|
+
const url = `${this._baseUrl}/boxes/${encodeURIComponent(name)}/start`;
|
|
807
|
+
await this._postJson(url, {}, { signal });
|
|
808
|
+
return this.waitForSandbox(name, { timeout, signal });
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Stop a running sandbox (preserves sandbox files for later restart).
|
|
812
|
+
*
|
|
813
|
+
* @param name - Sandbox name.
|
|
814
|
+
*/
|
|
815
|
+
async stopSandbox(name) {
|
|
816
|
+
const url = `${this._baseUrl}/boxes/${encodeURIComponent(name)}/stop`;
|
|
817
|
+
await this._postJson(url, {});
|
|
818
|
+
}
|
|
819
|
+
// =========================================================================
|
|
820
|
+
// Snapshot Operations
|
|
821
|
+
// =========================================================================
|
|
822
|
+
/**
|
|
823
|
+
* Build a snapshot from a Docker image.
|
|
824
|
+
*
|
|
825
|
+
* Blocks until the snapshot is ready (polls with 2s interval).
|
|
826
|
+
*
|
|
827
|
+
* @param name - Snapshot name.
|
|
828
|
+
* @param dockerImage - Docker image to build from (e.g., "python:3.12-slim").
|
|
829
|
+
* @param fsCapacityBytes - Filesystem capacity in bytes.
|
|
830
|
+
* @param options - Additional options (registry credentials, timeout).
|
|
831
|
+
* @returns Snapshot in "ready" status.
|
|
832
|
+
*/
|
|
833
|
+
async createSnapshot(name, dockerImage, fsCapacityBytes, options = {}) {
|
|
834
|
+
const { registryId, registryUrl, registryUsername, registryPassword, timeout = 60, signal, } = options;
|
|
835
|
+
const url = `${this._baseUrl}/snapshots`;
|
|
836
|
+
const payload = {
|
|
837
|
+
name,
|
|
838
|
+
docker_image: dockerImage,
|
|
839
|
+
fs_capacity_bytes: fsCapacityBytes,
|
|
840
|
+
};
|
|
841
|
+
if (registryId !== undefined) {
|
|
842
|
+
payload.registry_id = registryId;
|
|
843
|
+
}
|
|
844
|
+
if (registryUrl !== undefined) {
|
|
845
|
+
payload.registry_url = registryUrl;
|
|
846
|
+
}
|
|
847
|
+
if (registryUsername !== undefined) {
|
|
848
|
+
payload.registry_username = registryUsername;
|
|
849
|
+
}
|
|
850
|
+
if (registryPassword !== undefined) {
|
|
851
|
+
payload.registry_password = registryPassword;
|
|
852
|
+
}
|
|
853
|
+
const response = await this._postJson(url, payload, { signal });
|
|
854
|
+
const snapshot = (await response.json());
|
|
855
|
+
return this.waitForSnapshot(snapshot.id, { timeout, signal });
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Capture a snapshot from a running sandbox.
|
|
859
|
+
*
|
|
860
|
+
* Blocks until the snapshot is ready (polls with 2s interval).
|
|
861
|
+
*
|
|
862
|
+
* @param sandboxName - Name of the sandbox to capture from.
|
|
863
|
+
* @param name - Snapshot name.
|
|
864
|
+
* @param options - Capture options (checkpoint, timeout).
|
|
865
|
+
* @returns Snapshot in "ready" status.
|
|
866
|
+
*/
|
|
867
|
+
async captureSnapshot(sandboxName, name, options = {}) {
|
|
868
|
+
const { checkpoint, timeout = 60, signal } = options;
|
|
869
|
+
const url = `${this._baseUrl}/boxes/${encodeURIComponent(sandboxName)}/snapshot`;
|
|
870
|
+
const payload = { name };
|
|
871
|
+
if (checkpoint !== undefined) {
|
|
872
|
+
payload.checkpoint = checkpoint;
|
|
873
|
+
}
|
|
874
|
+
const response = await this._postJson(url, payload, { signal });
|
|
875
|
+
const snapshot = (await response.json());
|
|
876
|
+
return this.waitForSnapshot(snapshot.id, { timeout, signal });
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Get a snapshot by ID.
|
|
880
|
+
*
|
|
881
|
+
* @param snapshotId - Snapshot UUID.
|
|
882
|
+
* @returns Snapshot.
|
|
883
|
+
*/
|
|
884
|
+
async getSnapshot(snapshotId, options) {
|
|
885
|
+
const url = `${this._baseUrl}/snapshots/${encodeURIComponent(snapshotId)}`;
|
|
886
|
+
const response = await this._fetch(url, { signal: options?.signal });
|
|
887
|
+
if (!response.ok) {
|
|
888
|
+
if (response.status === 404) {
|
|
889
|
+
throw new errors_js_1.LangSmithResourceNotFoundError(`Snapshot '${snapshotId}' not found`, "snapshot");
|
|
890
|
+
}
|
|
891
|
+
await (0, helpers_js_1.handleClientHttpError)(response);
|
|
892
|
+
}
|
|
893
|
+
return (await response.json());
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* List all snapshots.
|
|
897
|
+
*
|
|
898
|
+
* @returns List of Snapshots.
|
|
899
|
+
*/
|
|
900
|
+
async listSnapshots() {
|
|
901
|
+
const url = `${this._baseUrl}/snapshots`;
|
|
902
|
+
const response = await this._fetch(url);
|
|
903
|
+
if (!response.ok) {
|
|
904
|
+
await (0, helpers_js_1.handleClientHttpError)(response);
|
|
905
|
+
}
|
|
906
|
+
const data = await response.json();
|
|
907
|
+
return (data.snapshots ?? []);
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* Delete a snapshot.
|
|
911
|
+
*
|
|
912
|
+
* @param snapshotId - Snapshot UUID.
|
|
913
|
+
*/
|
|
914
|
+
async deleteSnapshot(snapshotId) {
|
|
915
|
+
const url = `${this._baseUrl}/snapshots/${encodeURIComponent(snapshotId)}`;
|
|
916
|
+
const response = await this._fetch(url, { method: "DELETE" });
|
|
917
|
+
if (!response.ok) {
|
|
918
|
+
await (0, helpers_js_1.handleClientHttpError)(response);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Poll until a snapshot reaches "ready" or "failed" status.
|
|
923
|
+
*
|
|
924
|
+
* @param snapshotId - Snapshot UUID.
|
|
925
|
+
* @param options - Polling options (timeout, pollInterval).
|
|
926
|
+
* @returns Snapshot in "ready" status.
|
|
927
|
+
*/
|
|
928
|
+
async waitForSnapshot(snapshotId, options = {}) {
|
|
929
|
+
const { timeout = 300, pollInterval = 2.0, signal } = options;
|
|
930
|
+
const deadline = Date.now() + timeout * 1000;
|
|
931
|
+
let lastStatus = "building";
|
|
932
|
+
while (Date.now() < deadline) {
|
|
933
|
+
signal?.throwIfAborted();
|
|
934
|
+
const snapshot = await this.getSnapshot(snapshotId, { signal });
|
|
935
|
+
lastStatus = snapshot.status;
|
|
936
|
+
if (snapshot.status === "ready") {
|
|
937
|
+
return snapshot;
|
|
938
|
+
}
|
|
939
|
+
if (snapshot.status === "failed") {
|
|
940
|
+
throw new errors_js_1.LangSmithResourceCreationError(snapshot.status_message ?? `Snapshot '${snapshotId}' build failed`, "snapshot");
|
|
941
|
+
}
|
|
942
|
+
// Cap sleep to remaining time + jitter
|
|
943
|
+
const remaining = deadline - Date.now();
|
|
944
|
+
const jitter = pollInterval * 200 * (Math.random() - 0.5); // ±10%
|
|
945
|
+
const delay = Math.min(pollInterval * 1000 + jitter, remaining);
|
|
946
|
+
if (delay > 0) {
|
|
947
|
+
await sleepWithSignal(delay, signal);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
throw new errors_js_1.LangSmithResourceTimeoutError(`Snapshot '${snapshotId}' did not become ready within ${timeout}s`, "snapshot", lastStatus);
|
|
951
|
+
}
|
|
729
952
|
}
|
|
730
953
|
exports.SandboxClient = SandboxClient;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Main SandboxClient class for interacting with the sandbox server API.
|
|
3
3
|
*/
|
|
4
|
-
import type { CreatePoolOptions, CreateSandboxOptions, CreateTemplateOptions, CreateVolumeOptions, Pool, ResourceStatus, SandboxClientConfig, SandboxTemplate, UpdatePoolOptions, UpdateSandboxOptions, UpdateTemplateOptions, UpdateVolumeOptions, Volume, WaitForSandboxOptions } from "./types.js";
|
|
4
|
+
import type { CaptureSnapshotOptions, CreatePoolOptions, CreateSandboxOptions, CreateSnapshotOptions, CreateTemplateOptions, CreateVolumeOptions, Pool, ResourceStatus, SandboxClientConfig, SandboxTemplate, Snapshot, StartSandboxOptions, UpdatePoolOptions, UpdateSandboxOptions, UpdateTemplateOptions, UpdateVolumeOptions, Volume, WaitForSandboxOptions, WaitForSnapshotOptions } from "./types.js";
|
|
5
5
|
import { Sandbox } from "./sandbox.js";
|
|
6
6
|
/**
|
|
7
7
|
* Client for interacting with the Sandbox Server API.
|
|
@@ -30,6 +30,8 @@ import { Sandbox } from "./sandbox.js";
|
|
|
30
30
|
* await sandbox.delete();
|
|
31
31
|
* }
|
|
32
32
|
* ```
|
|
33
|
+
*
|
|
34
|
+
* @experimental This feature is experimental, and breaking changes are expected.
|
|
33
35
|
*/
|
|
34
36
|
export declare class SandboxClient {
|
|
35
37
|
private _baseUrl;
|
|
@@ -204,7 +206,7 @@ export declare class SandboxClient {
|
|
|
204
206
|
* }
|
|
205
207
|
* ```
|
|
206
208
|
*/
|
|
207
|
-
createSandbox(templateName
|
|
209
|
+
createSandbox(templateName?: string, options?: CreateSandboxOptions): Promise<Sandbox>;
|
|
208
210
|
/**
|
|
209
211
|
* Get a Sandbox by name.
|
|
210
212
|
*
|
|
@@ -214,7 +216,9 @@ export declare class SandboxClient {
|
|
|
214
216
|
* @returns Sandbox.
|
|
215
217
|
* @throws LangSmithResourceNotFoundError if sandbox not found.
|
|
216
218
|
*/
|
|
217
|
-
getSandbox(name: string
|
|
219
|
+
getSandbox(name: string, options?: {
|
|
220
|
+
signal?: AbortSignal;
|
|
221
|
+
}): Promise<Sandbox>;
|
|
218
222
|
/**
|
|
219
223
|
* List all Sandboxes.
|
|
220
224
|
*
|
|
@@ -256,7 +260,9 @@ export declare class SandboxClient {
|
|
|
256
260
|
* @returns ResourceStatus with status and optional status_message.
|
|
257
261
|
* @throws LangSmithResourceNotFoundError if sandbox not found.
|
|
258
262
|
*/
|
|
259
|
-
getSandboxStatus(name: string
|
|
263
|
+
getSandboxStatus(name: string, options?: {
|
|
264
|
+
signal?: AbortSignal;
|
|
265
|
+
}): Promise<ResourceStatus>;
|
|
260
266
|
/**
|
|
261
267
|
* Wait for a sandbox to become ready.
|
|
262
268
|
*
|
|
@@ -278,4 +284,70 @@ export declare class SandboxClient {
|
|
|
278
284
|
* ```
|
|
279
285
|
*/
|
|
280
286
|
waitForSandbox(name: string, options?: WaitForSandboxOptions): Promise<Sandbox>;
|
|
287
|
+
/**
|
|
288
|
+
* Start a stopped sandbox and wait until ready.
|
|
289
|
+
*
|
|
290
|
+
* @param name - Sandbox name.
|
|
291
|
+
* @param options - Options with timeout.
|
|
292
|
+
* @returns Sandbox in "ready" status.
|
|
293
|
+
*/
|
|
294
|
+
startSandbox(name: string, options?: StartSandboxOptions): Promise<Sandbox>;
|
|
295
|
+
/**
|
|
296
|
+
* Stop a running sandbox (preserves sandbox files for later restart).
|
|
297
|
+
*
|
|
298
|
+
* @param name - Sandbox name.
|
|
299
|
+
*/
|
|
300
|
+
stopSandbox(name: string): Promise<void>;
|
|
301
|
+
/**
|
|
302
|
+
* Build a snapshot from a Docker image.
|
|
303
|
+
*
|
|
304
|
+
* Blocks until the snapshot is ready (polls with 2s interval).
|
|
305
|
+
*
|
|
306
|
+
* @param name - Snapshot name.
|
|
307
|
+
* @param dockerImage - Docker image to build from (e.g., "python:3.12-slim").
|
|
308
|
+
* @param fsCapacityBytes - Filesystem capacity in bytes.
|
|
309
|
+
* @param options - Additional options (registry credentials, timeout).
|
|
310
|
+
* @returns Snapshot in "ready" status.
|
|
311
|
+
*/
|
|
312
|
+
createSnapshot(name: string, dockerImage: string, fsCapacityBytes: number, options?: CreateSnapshotOptions): Promise<Snapshot>;
|
|
313
|
+
/**
|
|
314
|
+
* Capture a snapshot from a running sandbox.
|
|
315
|
+
*
|
|
316
|
+
* Blocks until the snapshot is ready (polls with 2s interval).
|
|
317
|
+
*
|
|
318
|
+
* @param sandboxName - Name of the sandbox to capture from.
|
|
319
|
+
* @param name - Snapshot name.
|
|
320
|
+
* @param options - Capture options (checkpoint, timeout).
|
|
321
|
+
* @returns Snapshot in "ready" status.
|
|
322
|
+
*/
|
|
323
|
+
captureSnapshot(sandboxName: string, name: string, options?: CaptureSnapshotOptions): Promise<Snapshot>;
|
|
324
|
+
/**
|
|
325
|
+
* Get a snapshot by ID.
|
|
326
|
+
*
|
|
327
|
+
* @param snapshotId - Snapshot UUID.
|
|
328
|
+
* @returns Snapshot.
|
|
329
|
+
*/
|
|
330
|
+
getSnapshot(snapshotId: string, options?: {
|
|
331
|
+
signal?: AbortSignal;
|
|
332
|
+
}): Promise<Snapshot>;
|
|
333
|
+
/**
|
|
334
|
+
* List all snapshots.
|
|
335
|
+
*
|
|
336
|
+
* @returns List of Snapshots.
|
|
337
|
+
*/
|
|
338
|
+
listSnapshots(): Promise<Snapshot[]>;
|
|
339
|
+
/**
|
|
340
|
+
* Delete a snapshot.
|
|
341
|
+
*
|
|
342
|
+
* @param snapshotId - Snapshot UUID.
|
|
343
|
+
*/
|
|
344
|
+
deleteSnapshot(snapshotId: string): Promise<void>;
|
|
345
|
+
/**
|
|
346
|
+
* Poll until a snapshot reaches "ready" or "failed" status.
|
|
347
|
+
*
|
|
348
|
+
* @param snapshotId - Snapshot UUID.
|
|
349
|
+
* @param options - Polling options (timeout, pollInterval).
|
|
350
|
+
* @returns Snapshot in "ready" status.
|
|
351
|
+
*/
|
|
352
|
+
waitForSnapshot(snapshotId: string, options?: WaitForSnapshotOptions): Promise<Snapshot>;
|
|
281
353
|
}
|