langsmith 0.7.3 → 0.7.4
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 +3 -1
- package/dist/client.js +3 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/sandbox/client.cjs +270 -5
- package/dist/sandbox/client.d.ts +16 -1
- package/dist/sandbox/client.js +270 -5
- package/dist/sandbox/command_handle.cjs +1 -1
- package/dist/sandbox/command_handle.js +1 -1
- package/dist/sandbox/index.d.ts +1 -1
- package/dist/sandbox/types.d.ts +31 -0
- package/package.json +8 -7
package/dist/client.cjs
CHANGED
|
@@ -4927,7 +4927,9 @@ class Client {
|
|
|
4927
4927
|
});
|
|
4928
4928
|
const data = (await response.json());
|
|
4929
4929
|
const commitHash = data.commit.commit_hash;
|
|
4930
|
-
|
|
4930
|
+
const settings = await this._getSettings();
|
|
4931
|
+
const query = new URLSearchParams({ organizationId: settings.id });
|
|
4932
|
+
return `${this.getHostUrl()}/context/${name}/${commitHash.slice(0, 8)}?${query.toString()}`;
|
|
4931
4933
|
}
|
|
4932
4934
|
async _deleteDirectory(identifier) {
|
|
4933
4935
|
const [owner, name] = (0, prompts_js_1.parseHubIdentifier)(identifier);
|
package/dist/client.js
CHANGED
|
@@ -4889,7 +4889,9 @@ export class Client {
|
|
|
4889
4889
|
});
|
|
4890
4890
|
const data = (await response.json());
|
|
4891
4891
|
const commitHash = data.commit.commit_hash;
|
|
4892
|
-
|
|
4892
|
+
const settings = await this._getSettings();
|
|
4893
|
+
const query = new URLSearchParams({ organizationId: settings.id });
|
|
4894
|
+
return `${this.getHostUrl()}/context/${name}/${commitHash.slice(0, 8)}?${query.toString()}`;
|
|
4893
4895
|
}
|
|
4894
4896
|
async _deleteDirectory(identifier) {
|
|
4895
4897
|
const [owner, name] = parseHubIdentifier(identifier);
|
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.7.
|
|
21
|
+
exports.__version__ = "0.7.4";
|
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.7.
|
|
8
|
+
export declare const __version__ = "0.7.4";
|
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.7.
|
|
8
|
+
export const __version__ = "0.7.4";
|
package/dist/sandbox/client.cjs
CHANGED
|
@@ -10,6 +10,7 @@ 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
|
+
const index_js_1 = require("../utils/uuid/src/index.cjs");
|
|
13
14
|
/**
|
|
14
15
|
* Sleep that can be interrupted by an AbortSignal.
|
|
15
16
|
* Resolves after `ms` milliseconds or rejects immediately if the signal fires.
|
|
@@ -18,17 +19,18 @@ function sleepWithSignal(ms, signal) {
|
|
|
18
19
|
if (!signal) {
|
|
19
20
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
20
21
|
}
|
|
21
|
-
signal
|
|
22
|
+
const abortSignal = signal;
|
|
23
|
+
abortSignal.throwIfAborted();
|
|
22
24
|
return new Promise((resolve, reject) => {
|
|
23
25
|
const timer = setTimeout(() => {
|
|
24
|
-
|
|
26
|
+
abortSignal.removeEventListener("abort", onAbort);
|
|
25
27
|
resolve();
|
|
26
28
|
}, ms);
|
|
27
29
|
function onAbort() {
|
|
28
30
|
clearTimeout(timer);
|
|
29
|
-
reject(
|
|
31
|
+
reject(abortSignal.reason);
|
|
30
32
|
}
|
|
31
|
-
|
|
33
|
+
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
32
34
|
});
|
|
33
35
|
}
|
|
34
36
|
/**
|
|
@@ -47,6 +49,192 @@ function getDefaultApiEndpoint() {
|
|
|
47
49
|
function getDefaultApiKey() {
|
|
48
50
|
return (0, env_js_1.getLangSmithEnvironmentVariable)("API_KEY");
|
|
49
51
|
}
|
|
52
|
+
function shellQuote(value) {
|
|
53
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
54
|
+
}
|
|
55
|
+
function writeString(header, value, offset, length) {
|
|
56
|
+
header.write(value.slice(0, length), offset, length, "utf8");
|
|
57
|
+
}
|
|
58
|
+
function writeOctal(header, value, offset, length) {
|
|
59
|
+
const octal = value.toString(8).padStart(length - 1, "0");
|
|
60
|
+
header.write(octal.slice(-length + 1) + "\0", offset, length, "ascii");
|
|
61
|
+
}
|
|
62
|
+
function splitTarPath(name) {
|
|
63
|
+
if (Buffer.byteLength(name) <= 100) {
|
|
64
|
+
return { name, prefix: "" };
|
|
65
|
+
}
|
|
66
|
+
const parts = name.split("/");
|
|
67
|
+
for (let i = 1; i < parts.length; i += 1) {
|
|
68
|
+
const prefix = parts.slice(0, i).join("/");
|
|
69
|
+
const basename = parts.slice(i).join("/");
|
|
70
|
+
if (Buffer.byteLength(prefix) <= 155 &&
|
|
71
|
+
Buffer.byteLength(basename) <= 100) {
|
|
72
|
+
return { name: basename, prefix };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
throw new Error(`Docker build context path is too long for tar: ${name}`);
|
|
76
|
+
}
|
|
77
|
+
function makeTarHeader(args) {
|
|
78
|
+
const header = Buffer.alloc(512, 0);
|
|
79
|
+
const split = splitTarPath(args.name);
|
|
80
|
+
writeString(header, split.name, 0, 100);
|
|
81
|
+
writeOctal(header, args.mode, 100, 8);
|
|
82
|
+
writeOctal(header, 0, 108, 8);
|
|
83
|
+
writeOctal(header, 0, 116, 8);
|
|
84
|
+
writeOctal(header, args.size, 124, 12);
|
|
85
|
+
writeOctal(header, Math.floor(args.mtimeMs / 1000), 136, 12);
|
|
86
|
+
header.fill(" ", 148, 156);
|
|
87
|
+
writeString(header, args.type === "directory" ? "5" : args.type === "symlink" ? "2" : "0", 156, 1);
|
|
88
|
+
if (args.linkName) {
|
|
89
|
+
writeString(header, args.linkName, 157, 100);
|
|
90
|
+
}
|
|
91
|
+
writeString(header, "ustar", 257, 6);
|
|
92
|
+
writeString(header, "00", 263, 2);
|
|
93
|
+
if (split.prefix) {
|
|
94
|
+
writeString(header, split.prefix, 345, 155);
|
|
95
|
+
}
|
|
96
|
+
let checksum = 0;
|
|
97
|
+
for (const byte of header) {
|
|
98
|
+
checksum += byte;
|
|
99
|
+
}
|
|
100
|
+
header.write(checksum.toString(8).padStart(6, "0") + "\0 ", 148, 8, "ascii");
|
|
101
|
+
return header;
|
|
102
|
+
}
|
|
103
|
+
async function makeDockerContextTar(contextPath) {
|
|
104
|
+
const fs = await import("node:fs/promises");
|
|
105
|
+
const path = await import("node:path");
|
|
106
|
+
const contextRoot = path.resolve(contextPath);
|
|
107
|
+
const chunks = [];
|
|
108
|
+
async function addEntry(absPath) {
|
|
109
|
+
const rel = path.relative(contextRoot, absPath);
|
|
110
|
+
if (!rel || rel.split(path.sep).includes(".git")) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const tarPath = rel.split(path.sep).join("/");
|
|
114
|
+
const stat = await fs.lstat(absPath);
|
|
115
|
+
if (stat.isDirectory()) {
|
|
116
|
+
chunks.push(makeTarHeader({
|
|
117
|
+
name: tarPath.endsWith("/") ? tarPath : `${tarPath}/`,
|
|
118
|
+
mode: stat.mode & 0o777,
|
|
119
|
+
size: 0,
|
|
120
|
+
type: "directory",
|
|
121
|
+
mtimeMs: stat.mtimeMs,
|
|
122
|
+
}));
|
|
123
|
+
const entries = await fs.readdir(absPath);
|
|
124
|
+
for (const entry of entries.sort()) {
|
|
125
|
+
await addEntry(path.join(absPath, entry));
|
|
126
|
+
}
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (stat.isSymbolicLink()) {
|
|
130
|
+
chunks.push(makeTarHeader({
|
|
131
|
+
name: tarPath,
|
|
132
|
+
mode: stat.mode & 0o777,
|
|
133
|
+
size: 0,
|
|
134
|
+
type: "symlink",
|
|
135
|
+
linkName: await fs.readlink(absPath),
|
|
136
|
+
mtimeMs: stat.mtimeMs,
|
|
137
|
+
}));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (!stat.isFile()) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const content = await fs.readFile(absPath);
|
|
144
|
+
chunks.push(makeTarHeader({
|
|
145
|
+
name: tarPath,
|
|
146
|
+
mode: stat.mode & 0o777,
|
|
147
|
+
size: content.byteLength,
|
|
148
|
+
type: "file",
|
|
149
|
+
mtimeMs: stat.mtimeMs,
|
|
150
|
+
}), content);
|
|
151
|
+
const padding = (512 - (content.byteLength % 512)) % 512;
|
|
152
|
+
if (padding) {
|
|
153
|
+
chunks.push(Buffer.alloc(padding, 0));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const rootEntries = await fs.readdir(contextRoot);
|
|
157
|
+
for (const entry of rootEntries.sort()) {
|
|
158
|
+
await addEntry(path.join(contextRoot, entry));
|
|
159
|
+
}
|
|
160
|
+
chunks.push(Buffer.alloc(1024, 0));
|
|
161
|
+
return new Uint8Array(Buffer.concat(chunks));
|
|
162
|
+
}
|
|
163
|
+
async function resolveDockerfileContext(dockerfile, context) {
|
|
164
|
+
const fs = await import("node:fs/promises");
|
|
165
|
+
const path = await import("node:path");
|
|
166
|
+
const contextPath = path.resolve(context);
|
|
167
|
+
const dockerfilePath = path.resolve(contextPath, dockerfile);
|
|
168
|
+
const contextStat = await fs.stat(contextPath);
|
|
169
|
+
if (!contextStat.isDirectory()) {
|
|
170
|
+
throw new Error(`context must be a directory: ${contextPath}`);
|
|
171
|
+
}
|
|
172
|
+
const dockerfileStat = await fs.stat(dockerfilePath);
|
|
173
|
+
if (!dockerfileStat.isFile()) {
|
|
174
|
+
throw new Error(`dockerfile must be a file: ${dockerfilePath}`);
|
|
175
|
+
}
|
|
176
|
+
const dockerfileRel = path.relative(contextPath, dockerfilePath);
|
|
177
|
+
if (dockerfileRel === "" ||
|
|
178
|
+
dockerfileRel.startsWith("..") ||
|
|
179
|
+
path.isAbsolute(dockerfileRel)) {
|
|
180
|
+
throw new Error("dockerfile must be inside context");
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
contextPath,
|
|
184
|
+
dockerfileRel: dockerfileRel.split(path.sep).join("/"),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function makeDockerfileBuildCommand(args) {
|
|
188
|
+
const dockerfileRemote = `${args.remoteContext}/${args.dockerfileRel}`;
|
|
189
|
+
const dockerfileDir = dockerfileRemote.split("/").slice(0, -1).join("/");
|
|
190
|
+
const dockerfileName = dockerfileRemote.split("/").at(-1) ?? "Dockerfile";
|
|
191
|
+
const socketPath = `${args.buildkitRun}/buildkitd.sock`;
|
|
192
|
+
const buildctl = [
|
|
193
|
+
"buildctl",
|
|
194
|
+
"--addr",
|
|
195
|
+
`unix://${socketPath}`,
|
|
196
|
+
"build",
|
|
197
|
+
"--progress=plain",
|
|
198
|
+
"--frontend",
|
|
199
|
+
"dockerfile.v0",
|
|
200
|
+
"--local",
|
|
201
|
+
`context=${args.remoteContext}`,
|
|
202
|
+
"--local",
|
|
203
|
+
`dockerfile=${dockerfileDir}`,
|
|
204
|
+
"--opt",
|
|
205
|
+
`filename=${dockerfileName}`,
|
|
206
|
+
"--output",
|
|
207
|
+
`type=docker,name=${args.imageRef}`,
|
|
208
|
+
];
|
|
209
|
+
if (args.target !== undefined) {
|
|
210
|
+
buildctl.push("--opt", `target=${args.target}`);
|
|
211
|
+
}
|
|
212
|
+
for (const [key, value] of Object.entries(args.buildArgs ?? {}).sort()) {
|
|
213
|
+
buildctl.push("--opt", `build-arg:${key}=${value}`);
|
|
214
|
+
}
|
|
215
|
+
return [
|
|
216
|
+
"set -euo pipefail",
|
|
217
|
+
`mkdir -p ${shellQuote(args.buildkitRoot)} ${shellQuote(args.buildkitRun)}`,
|
|
218
|
+
`buildkitd --addr ${shellQuote(`unix://${socketPath}`)} --root ${shellQuote(args.buildkitRoot)} --oci-worker=true --containerd-worker=false --oci-worker-snapshotter=native --oci-worker-binary buildkit-runc > ${shellQuote(`${args.buildkitRun}/buildkitd.log`)} 2>&1 &`,
|
|
219
|
+
"buildkitd_pid=$!",
|
|
220
|
+
'cleanup() { kill "$buildkitd_pid" >/dev/null 2>&1 || true; }',
|
|
221
|
+
"trap cleanup EXIT",
|
|
222
|
+
"for i in $(seq 1 300); do",
|
|
223
|
+
` if buildctl --addr ${shellQuote(`unix://${socketPath}`)} debug workers >/dev/null 2>&1; then break; fi`,
|
|
224
|
+
` if ! kill -0 "$buildkitd_pid" >/dev/null 2>&1; then cat ${shellQuote(`${args.buildkitRun}/buildkitd.log`)}; exit 1; fi`,
|
|
225
|
+
` if [ "$i" = 300 ]; then cat ${shellQuote(`${args.buildkitRun}/buildkitd.log`)}; exit 1; fi`,
|
|
226
|
+
" sleep 0.1",
|
|
227
|
+
"done",
|
|
228
|
+
"for i in $(seq 1 300); do",
|
|
229
|
+
" if docker info >/dev/null 2>&1; then break; fi",
|
|
230
|
+
' if [ "$i" = 300 ]; then docker info; exit 1; fi',
|
|
231
|
+
" sleep 0.1",
|
|
232
|
+
"done",
|
|
233
|
+
`${buildctl.map(shellQuote).join(" ")} | docker load`,
|
|
234
|
+
`rm -rf ${shellQuote(args.buildkitRoot)} || true`,
|
|
235
|
+
"",
|
|
236
|
+
].join("\n");
|
|
237
|
+
}
|
|
50
238
|
/**
|
|
51
239
|
* Client for interacting with the Sandbox Server API.
|
|
52
240
|
*
|
|
@@ -453,6 +641,77 @@ class SandboxClient {
|
|
|
453
641
|
const snapshot = (await response.json());
|
|
454
642
|
return this.waitForSnapshot(snapshot.id, { timeout, signal });
|
|
455
643
|
}
|
|
644
|
+
/**
|
|
645
|
+
* Build a snapshot from a local Dockerfile context.
|
|
646
|
+
*
|
|
647
|
+
* Creates a temporary builder sandbox, uploads the Docker build context,
|
|
648
|
+
* runs BuildKit inside the sandbox, and captures the built image as a
|
|
649
|
+
* LangSmith snapshot.
|
|
650
|
+
*
|
|
651
|
+
* @param name - Snapshot name.
|
|
652
|
+
* @param dockerfile - Local Dockerfile path, relative to context by default.
|
|
653
|
+
* @param fsCapacityBytes - Filesystem capacity in bytes.
|
|
654
|
+
* @param options - Build context, args, target, build log callback, builder
|
|
655
|
+
* vCPUs/memory, timeout.
|
|
656
|
+
* @returns Snapshot in "ready" status.
|
|
657
|
+
*/
|
|
658
|
+
async createSnapshotFromDockerfile(name, dockerfile, fsCapacityBytes, options = {}) {
|
|
659
|
+
const { context = ".", buildArgs, target, onBuildLog, vCpus, memBytes, timeout = 60, } = options;
|
|
660
|
+
const { contextPath, dockerfileRel } = await resolveDockerfileContext(dockerfile, context);
|
|
661
|
+
const builderName = `snapshot-builder-${(0, index_js_1.v4)().replace(/-/g, "").slice(0, 12)}`;
|
|
662
|
+
// Stage the build on the capacity-backed root filesystem, not /tmp.
|
|
663
|
+
// Inside the sandbox /tmp is a RAM-backed tmpfs that fsCapacityBytes does
|
|
664
|
+
// not size, and BuildKit's native snapshotter writes a full copy of every
|
|
665
|
+
// layer under its root, so a /tmp build exhausts guest RAM and fails with
|
|
666
|
+
// "No space left on device".
|
|
667
|
+
const buildRoot = `/var/lib/langsmith-build/${(0, index_js_1.v4)()
|
|
668
|
+
.replace(/-/g, "")
|
|
669
|
+
.slice(0, 12)}`;
|
|
670
|
+
const remoteContext = `${buildRoot}/context`;
|
|
671
|
+
const remoteTar = `${buildRoot}/context.tar`;
|
|
672
|
+
const imageRef = `langsmith-snapshot-build:${(0, index_js_1.v4)().replace(/-/g, "")}`;
|
|
673
|
+
const buildkitRoot = `${buildRoot}/buildkit-root`;
|
|
674
|
+
const buildkitRun = `${buildRoot}/buildkit-run`;
|
|
675
|
+
const builder = await this.createSandbox({
|
|
676
|
+
name: builderName,
|
|
677
|
+
timeout,
|
|
678
|
+
vCpus,
|
|
679
|
+
memBytes,
|
|
680
|
+
fsCapacityBytes,
|
|
681
|
+
});
|
|
682
|
+
try {
|
|
683
|
+
await builder.write(remoteTar, await makeDockerContextTar(contextPath), timeout);
|
|
684
|
+
await builder.run([
|
|
685
|
+
`rm -rf ${shellQuote(remoteContext)}`,
|
|
686
|
+
`mkdir -p ${shellQuote(remoteContext)}`,
|
|
687
|
+
`tar -xf ${shellQuote(remoteTar)} -C ${shellQuote(remoteContext)}`,
|
|
688
|
+
].join(" && "), { timeout });
|
|
689
|
+
const result = await builder.run(makeDockerfileBuildCommand({
|
|
690
|
+
remoteContext,
|
|
691
|
+
dockerfileRel,
|
|
692
|
+
imageRef,
|
|
693
|
+
buildkitRoot,
|
|
694
|
+
buildkitRun,
|
|
695
|
+
buildArgs,
|
|
696
|
+
target,
|
|
697
|
+
}), {
|
|
698
|
+
timeout,
|
|
699
|
+
onStdout: onBuildLog,
|
|
700
|
+
onStderr: onBuildLog,
|
|
701
|
+
});
|
|
702
|
+
if (result.exit_code !== 0) {
|
|
703
|
+
throw new errors_js_1.LangSmithResourceCreationError("Dockerfile snapshot build failed", "snapshot");
|
|
704
|
+
}
|
|
705
|
+
return await this.captureSnapshot(builder.name, name, {
|
|
706
|
+
dockerImage: imageRef,
|
|
707
|
+
fsCapacityBytes,
|
|
708
|
+
timeout,
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
finally {
|
|
712
|
+
await builder.delete();
|
|
713
|
+
}
|
|
714
|
+
}
|
|
456
715
|
/**
|
|
457
716
|
* Capture a snapshot from a running sandbox.
|
|
458
717
|
*
|
|
@@ -464,9 +723,15 @@ class SandboxClient {
|
|
|
464
723
|
* @returns Snapshot in "ready" status.
|
|
465
724
|
*/
|
|
466
725
|
async captureSnapshot(sandboxName, name, options = {}) {
|
|
467
|
-
const { timeout = 60, signal } = options;
|
|
726
|
+
const { dockerImage, fsCapacityBytes, timeout = 60, signal } = options;
|
|
468
727
|
const url = `${this._baseUrl}/boxes/${encodeURIComponent(sandboxName)}/snapshot`;
|
|
469
728
|
const payload = { name };
|
|
729
|
+
if (dockerImage !== undefined) {
|
|
730
|
+
payload.docker_image = dockerImage;
|
|
731
|
+
}
|
|
732
|
+
if (fsCapacityBytes !== undefined) {
|
|
733
|
+
payload.fs_capacity_bytes = fsCapacityBytes;
|
|
734
|
+
}
|
|
470
735
|
const response = await this._postJson(url, payload, { signal });
|
|
471
736
|
const snapshot = (await response.json());
|
|
472
737
|
return this.waitForSnapshot(snapshot.id, { timeout, signal });
|
package/dist/sandbox/client.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Main SandboxClient class for interacting with the sandbox server API.
|
|
3
3
|
*/
|
|
4
|
-
import type { CaptureSnapshotOptions, CreateSandboxOptions, CreateSnapshotOptions, ListSnapshotsOptions, ResourceStatus, SandboxClientConfig, Snapshot, StartSandboxOptions, UpdateSandboxOptions, WaitForSandboxOptions, WaitForSnapshotOptions } from "./types.js";
|
|
4
|
+
import type { CaptureSnapshotOptions, CreateDockerfileSnapshotOptions, CreateSandboxOptions, CreateSnapshotOptions, ListSnapshotsOptions, ResourceStatus, SandboxClientConfig, Snapshot, StartSandboxOptions, UpdateSandboxOptions, WaitForSandboxOptions, WaitForSnapshotOptions } from "./types.js";
|
|
5
5
|
import { Sandbox } from "./sandbox.js";
|
|
6
6
|
/**
|
|
7
7
|
* Client for interacting with the Sandbox Server API.
|
|
@@ -173,6 +173,21 @@ export declare class SandboxClient {
|
|
|
173
173
|
* @returns Snapshot in "ready" status.
|
|
174
174
|
*/
|
|
175
175
|
createSnapshot(name: string, dockerImage: string, fsCapacityBytes: number, options?: CreateSnapshotOptions): Promise<Snapshot>;
|
|
176
|
+
/**
|
|
177
|
+
* Build a snapshot from a local Dockerfile context.
|
|
178
|
+
*
|
|
179
|
+
* Creates a temporary builder sandbox, uploads the Docker build context,
|
|
180
|
+
* runs BuildKit inside the sandbox, and captures the built image as a
|
|
181
|
+
* LangSmith snapshot.
|
|
182
|
+
*
|
|
183
|
+
* @param name - Snapshot name.
|
|
184
|
+
* @param dockerfile - Local Dockerfile path, relative to context by default.
|
|
185
|
+
* @param fsCapacityBytes - Filesystem capacity in bytes.
|
|
186
|
+
* @param options - Build context, args, target, build log callback, builder
|
|
187
|
+
* vCPUs/memory, timeout.
|
|
188
|
+
* @returns Snapshot in "ready" status.
|
|
189
|
+
*/
|
|
190
|
+
createSnapshotFromDockerfile(name: string, dockerfile: string, fsCapacityBytes: number, options?: CreateDockerfileSnapshotOptions): Promise<Snapshot>;
|
|
176
191
|
/**
|
|
177
192
|
* Capture a snapshot from a running sandbox.
|
|
178
193
|
*
|
package/dist/sandbox/client.js
CHANGED
|
@@ -7,6 +7,7 @@ import { AsyncCaller } from "../utils/async_caller.js";
|
|
|
7
7
|
import { Sandbox } from "./sandbox.js";
|
|
8
8
|
import { LangSmithResourceCreationError, LangSmithResourceNameConflictError, LangSmithResourceNotFoundError, LangSmithResourceTimeoutError, LangSmithSandboxAPIError, LangSmithValidationError, } from "./errors.js";
|
|
9
9
|
import { handleClientHttpError, handleSandboxCreationError, validateTtl, } from "./helpers.js";
|
|
10
|
+
import { v4 as uuidv4 } from "../utils/uuid/src/index.js";
|
|
10
11
|
/**
|
|
11
12
|
* Sleep that can be interrupted by an AbortSignal.
|
|
12
13
|
* Resolves after `ms` milliseconds or rejects immediately if the signal fires.
|
|
@@ -15,17 +16,18 @@ function sleepWithSignal(ms, signal) {
|
|
|
15
16
|
if (!signal) {
|
|
16
17
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
17
18
|
}
|
|
18
|
-
signal
|
|
19
|
+
const abortSignal = signal;
|
|
20
|
+
abortSignal.throwIfAborted();
|
|
19
21
|
return new Promise((resolve, reject) => {
|
|
20
22
|
const timer = setTimeout(() => {
|
|
21
|
-
|
|
23
|
+
abortSignal.removeEventListener("abort", onAbort);
|
|
22
24
|
resolve();
|
|
23
25
|
}, ms);
|
|
24
26
|
function onAbort() {
|
|
25
27
|
clearTimeout(timer);
|
|
26
|
-
reject(
|
|
28
|
+
reject(abortSignal.reason);
|
|
27
29
|
}
|
|
28
|
-
|
|
30
|
+
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
29
31
|
});
|
|
30
32
|
}
|
|
31
33
|
/**
|
|
@@ -44,6 +46,192 @@ function getDefaultApiEndpoint() {
|
|
|
44
46
|
function getDefaultApiKey() {
|
|
45
47
|
return getLangSmithEnvironmentVariable("API_KEY");
|
|
46
48
|
}
|
|
49
|
+
function shellQuote(value) {
|
|
50
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
51
|
+
}
|
|
52
|
+
function writeString(header, value, offset, length) {
|
|
53
|
+
header.write(value.slice(0, length), offset, length, "utf8");
|
|
54
|
+
}
|
|
55
|
+
function writeOctal(header, value, offset, length) {
|
|
56
|
+
const octal = value.toString(8).padStart(length - 1, "0");
|
|
57
|
+
header.write(octal.slice(-length + 1) + "\0", offset, length, "ascii");
|
|
58
|
+
}
|
|
59
|
+
function splitTarPath(name) {
|
|
60
|
+
if (Buffer.byteLength(name) <= 100) {
|
|
61
|
+
return { name, prefix: "" };
|
|
62
|
+
}
|
|
63
|
+
const parts = name.split("/");
|
|
64
|
+
for (let i = 1; i < parts.length; i += 1) {
|
|
65
|
+
const prefix = parts.slice(0, i).join("/");
|
|
66
|
+
const basename = parts.slice(i).join("/");
|
|
67
|
+
if (Buffer.byteLength(prefix) <= 155 &&
|
|
68
|
+
Buffer.byteLength(basename) <= 100) {
|
|
69
|
+
return { name: basename, prefix };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
throw new Error(`Docker build context path is too long for tar: ${name}`);
|
|
73
|
+
}
|
|
74
|
+
function makeTarHeader(args) {
|
|
75
|
+
const header = Buffer.alloc(512, 0);
|
|
76
|
+
const split = splitTarPath(args.name);
|
|
77
|
+
writeString(header, split.name, 0, 100);
|
|
78
|
+
writeOctal(header, args.mode, 100, 8);
|
|
79
|
+
writeOctal(header, 0, 108, 8);
|
|
80
|
+
writeOctal(header, 0, 116, 8);
|
|
81
|
+
writeOctal(header, args.size, 124, 12);
|
|
82
|
+
writeOctal(header, Math.floor(args.mtimeMs / 1000), 136, 12);
|
|
83
|
+
header.fill(" ", 148, 156);
|
|
84
|
+
writeString(header, args.type === "directory" ? "5" : args.type === "symlink" ? "2" : "0", 156, 1);
|
|
85
|
+
if (args.linkName) {
|
|
86
|
+
writeString(header, args.linkName, 157, 100);
|
|
87
|
+
}
|
|
88
|
+
writeString(header, "ustar", 257, 6);
|
|
89
|
+
writeString(header, "00", 263, 2);
|
|
90
|
+
if (split.prefix) {
|
|
91
|
+
writeString(header, split.prefix, 345, 155);
|
|
92
|
+
}
|
|
93
|
+
let checksum = 0;
|
|
94
|
+
for (const byte of header) {
|
|
95
|
+
checksum += byte;
|
|
96
|
+
}
|
|
97
|
+
header.write(checksum.toString(8).padStart(6, "0") + "\0 ", 148, 8, "ascii");
|
|
98
|
+
return header;
|
|
99
|
+
}
|
|
100
|
+
async function makeDockerContextTar(contextPath) {
|
|
101
|
+
const fs = await import("node:fs/promises");
|
|
102
|
+
const path = await import("node:path");
|
|
103
|
+
const contextRoot = path.resolve(contextPath);
|
|
104
|
+
const chunks = [];
|
|
105
|
+
async function addEntry(absPath) {
|
|
106
|
+
const rel = path.relative(contextRoot, absPath);
|
|
107
|
+
if (!rel || rel.split(path.sep).includes(".git")) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const tarPath = rel.split(path.sep).join("/");
|
|
111
|
+
const stat = await fs.lstat(absPath);
|
|
112
|
+
if (stat.isDirectory()) {
|
|
113
|
+
chunks.push(makeTarHeader({
|
|
114
|
+
name: tarPath.endsWith("/") ? tarPath : `${tarPath}/`,
|
|
115
|
+
mode: stat.mode & 0o777,
|
|
116
|
+
size: 0,
|
|
117
|
+
type: "directory",
|
|
118
|
+
mtimeMs: stat.mtimeMs,
|
|
119
|
+
}));
|
|
120
|
+
const entries = await fs.readdir(absPath);
|
|
121
|
+
for (const entry of entries.sort()) {
|
|
122
|
+
await addEntry(path.join(absPath, entry));
|
|
123
|
+
}
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (stat.isSymbolicLink()) {
|
|
127
|
+
chunks.push(makeTarHeader({
|
|
128
|
+
name: tarPath,
|
|
129
|
+
mode: stat.mode & 0o777,
|
|
130
|
+
size: 0,
|
|
131
|
+
type: "symlink",
|
|
132
|
+
linkName: await fs.readlink(absPath),
|
|
133
|
+
mtimeMs: stat.mtimeMs,
|
|
134
|
+
}));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (!stat.isFile()) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const content = await fs.readFile(absPath);
|
|
141
|
+
chunks.push(makeTarHeader({
|
|
142
|
+
name: tarPath,
|
|
143
|
+
mode: stat.mode & 0o777,
|
|
144
|
+
size: content.byteLength,
|
|
145
|
+
type: "file",
|
|
146
|
+
mtimeMs: stat.mtimeMs,
|
|
147
|
+
}), content);
|
|
148
|
+
const padding = (512 - (content.byteLength % 512)) % 512;
|
|
149
|
+
if (padding) {
|
|
150
|
+
chunks.push(Buffer.alloc(padding, 0));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const rootEntries = await fs.readdir(contextRoot);
|
|
154
|
+
for (const entry of rootEntries.sort()) {
|
|
155
|
+
await addEntry(path.join(contextRoot, entry));
|
|
156
|
+
}
|
|
157
|
+
chunks.push(Buffer.alloc(1024, 0));
|
|
158
|
+
return new Uint8Array(Buffer.concat(chunks));
|
|
159
|
+
}
|
|
160
|
+
async function resolveDockerfileContext(dockerfile, context) {
|
|
161
|
+
const fs = await import("node:fs/promises");
|
|
162
|
+
const path = await import("node:path");
|
|
163
|
+
const contextPath = path.resolve(context);
|
|
164
|
+
const dockerfilePath = path.resolve(contextPath, dockerfile);
|
|
165
|
+
const contextStat = await fs.stat(contextPath);
|
|
166
|
+
if (!contextStat.isDirectory()) {
|
|
167
|
+
throw new Error(`context must be a directory: ${contextPath}`);
|
|
168
|
+
}
|
|
169
|
+
const dockerfileStat = await fs.stat(dockerfilePath);
|
|
170
|
+
if (!dockerfileStat.isFile()) {
|
|
171
|
+
throw new Error(`dockerfile must be a file: ${dockerfilePath}`);
|
|
172
|
+
}
|
|
173
|
+
const dockerfileRel = path.relative(contextPath, dockerfilePath);
|
|
174
|
+
if (dockerfileRel === "" ||
|
|
175
|
+
dockerfileRel.startsWith("..") ||
|
|
176
|
+
path.isAbsolute(dockerfileRel)) {
|
|
177
|
+
throw new Error("dockerfile must be inside context");
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
contextPath,
|
|
181
|
+
dockerfileRel: dockerfileRel.split(path.sep).join("/"),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function makeDockerfileBuildCommand(args) {
|
|
185
|
+
const dockerfileRemote = `${args.remoteContext}/${args.dockerfileRel}`;
|
|
186
|
+
const dockerfileDir = dockerfileRemote.split("/").slice(0, -1).join("/");
|
|
187
|
+
const dockerfileName = dockerfileRemote.split("/").at(-1) ?? "Dockerfile";
|
|
188
|
+
const socketPath = `${args.buildkitRun}/buildkitd.sock`;
|
|
189
|
+
const buildctl = [
|
|
190
|
+
"buildctl",
|
|
191
|
+
"--addr",
|
|
192
|
+
`unix://${socketPath}`,
|
|
193
|
+
"build",
|
|
194
|
+
"--progress=plain",
|
|
195
|
+
"--frontend",
|
|
196
|
+
"dockerfile.v0",
|
|
197
|
+
"--local",
|
|
198
|
+
`context=${args.remoteContext}`,
|
|
199
|
+
"--local",
|
|
200
|
+
`dockerfile=${dockerfileDir}`,
|
|
201
|
+
"--opt",
|
|
202
|
+
`filename=${dockerfileName}`,
|
|
203
|
+
"--output",
|
|
204
|
+
`type=docker,name=${args.imageRef}`,
|
|
205
|
+
];
|
|
206
|
+
if (args.target !== undefined) {
|
|
207
|
+
buildctl.push("--opt", `target=${args.target}`);
|
|
208
|
+
}
|
|
209
|
+
for (const [key, value] of Object.entries(args.buildArgs ?? {}).sort()) {
|
|
210
|
+
buildctl.push("--opt", `build-arg:${key}=${value}`);
|
|
211
|
+
}
|
|
212
|
+
return [
|
|
213
|
+
"set -euo pipefail",
|
|
214
|
+
`mkdir -p ${shellQuote(args.buildkitRoot)} ${shellQuote(args.buildkitRun)}`,
|
|
215
|
+
`buildkitd --addr ${shellQuote(`unix://${socketPath}`)} --root ${shellQuote(args.buildkitRoot)} --oci-worker=true --containerd-worker=false --oci-worker-snapshotter=native --oci-worker-binary buildkit-runc > ${shellQuote(`${args.buildkitRun}/buildkitd.log`)} 2>&1 &`,
|
|
216
|
+
"buildkitd_pid=$!",
|
|
217
|
+
'cleanup() { kill "$buildkitd_pid" >/dev/null 2>&1 || true; }',
|
|
218
|
+
"trap cleanup EXIT",
|
|
219
|
+
"for i in $(seq 1 300); do",
|
|
220
|
+
` if buildctl --addr ${shellQuote(`unix://${socketPath}`)} debug workers >/dev/null 2>&1; then break; fi`,
|
|
221
|
+
` if ! kill -0 "$buildkitd_pid" >/dev/null 2>&1; then cat ${shellQuote(`${args.buildkitRun}/buildkitd.log`)}; exit 1; fi`,
|
|
222
|
+
` if [ "$i" = 300 ]; then cat ${shellQuote(`${args.buildkitRun}/buildkitd.log`)}; exit 1; fi`,
|
|
223
|
+
" sleep 0.1",
|
|
224
|
+
"done",
|
|
225
|
+
"for i in $(seq 1 300); do",
|
|
226
|
+
" if docker info >/dev/null 2>&1; then break; fi",
|
|
227
|
+
' if [ "$i" = 300 ]; then docker info; exit 1; fi',
|
|
228
|
+
" sleep 0.1",
|
|
229
|
+
"done",
|
|
230
|
+
`${buildctl.map(shellQuote).join(" ")} | docker load`,
|
|
231
|
+
`rm -rf ${shellQuote(args.buildkitRoot)} || true`,
|
|
232
|
+
"",
|
|
233
|
+
].join("\n");
|
|
234
|
+
}
|
|
47
235
|
/**
|
|
48
236
|
* Client for interacting with the Sandbox Server API.
|
|
49
237
|
*
|
|
@@ -450,6 +638,77 @@ export class SandboxClient {
|
|
|
450
638
|
const snapshot = (await response.json());
|
|
451
639
|
return this.waitForSnapshot(snapshot.id, { timeout, signal });
|
|
452
640
|
}
|
|
641
|
+
/**
|
|
642
|
+
* Build a snapshot from a local Dockerfile context.
|
|
643
|
+
*
|
|
644
|
+
* Creates a temporary builder sandbox, uploads the Docker build context,
|
|
645
|
+
* runs BuildKit inside the sandbox, and captures the built image as a
|
|
646
|
+
* LangSmith snapshot.
|
|
647
|
+
*
|
|
648
|
+
* @param name - Snapshot name.
|
|
649
|
+
* @param dockerfile - Local Dockerfile path, relative to context by default.
|
|
650
|
+
* @param fsCapacityBytes - Filesystem capacity in bytes.
|
|
651
|
+
* @param options - Build context, args, target, build log callback, builder
|
|
652
|
+
* vCPUs/memory, timeout.
|
|
653
|
+
* @returns Snapshot in "ready" status.
|
|
654
|
+
*/
|
|
655
|
+
async createSnapshotFromDockerfile(name, dockerfile, fsCapacityBytes, options = {}) {
|
|
656
|
+
const { context = ".", buildArgs, target, onBuildLog, vCpus, memBytes, timeout = 60, } = options;
|
|
657
|
+
const { contextPath, dockerfileRel } = await resolveDockerfileContext(dockerfile, context);
|
|
658
|
+
const builderName = `snapshot-builder-${uuidv4().replace(/-/g, "").slice(0, 12)}`;
|
|
659
|
+
// Stage the build on the capacity-backed root filesystem, not /tmp.
|
|
660
|
+
// Inside the sandbox /tmp is a RAM-backed tmpfs that fsCapacityBytes does
|
|
661
|
+
// not size, and BuildKit's native snapshotter writes a full copy of every
|
|
662
|
+
// layer under its root, so a /tmp build exhausts guest RAM and fails with
|
|
663
|
+
// "No space left on device".
|
|
664
|
+
const buildRoot = `/var/lib/langsmith-build/${uuidv4()
|
|
665
|
+
.replace(/-/g, "")
|
|
666
|
+
.slice(0, 12)}`;
|
|
667
|
+
const remoteContext = `${buildRoot}/context`;
|
|
668
|
+
const remoteTar = `${buildRoot}/context.tar`;
|
|
669
|
+
const imageRef = `langsmith-snapshot-build:${uuidv4().replace(/-/g, "")}`;
|
|
670
|
+
const buildkitRoot = `${buildRoot}/buildkit-root`;
|
|
671
|
+
const buildkitRun = `${buildRoot}/buildkit-run`;
|
|
672
|
+
const builder = await this.createSandbox({
|
|
673
|
+
name: builderName,
|
|
674
|
+
timeout,
|
|
675
|
+
vCpus,
|
|
676
|
+
memBytes,
|
|
677
|
+
fsCapacityBytes,
|
|
678
|
+
});
|
|
679
|
+
try {
|
|
680
|
+
await builder.write(remoteTar, await makeDockerContextTar(contextPath), timeout);
|
|
681
|
+
await builder.run([
|
|
682
|
+
`rm -rf ${shellQuote(remoteContext)}`,
|
|
683
|
+
`mkdir -p ${shellQuote(remoteContext)}`,
|
|
684
|
+
`tar -xf ${shellQuote(remoteTar)} -C ${shellQuote(remoteContext)}`,
|
|
685
|
+
].join(" && "), { timeout });
|
|
686
|
+
const result = await builder.run(makeDockerfileBuildCommand({
|
|
687
|
+
remoteContext,
|
|
688
|
+
dockerfileRel,
|
|
689
|
+
imageRef,
|
|
690
|
+
buildkitRoot,
|
|
691
|
+
buildkitRun,
|
|
692
|
+
buildArgs,
|
|
693
|
+
target,
|
|
694
|
+
}), {
|
|
695
|
+
timeout,
|
|
696
|
+
onStdout: onBuildLog,
|
|
697
|
+
onStderr: onBuildLog,
|
|
698
|
+
});
|
|
699
|
+
if (result.exit_code !== 0) {
|
|
700
|
+
throw new LangSmithResourceCreationError("Dockerfile snapshot build failed", "snapshot");
|
|
701
|
+
}
|
|
702
|
+
return await this.captureSnapshot(builder.name, name, {
|
|
703
|
+
dockerImage: imageRef,
|
|
704
|
+
fsCapacityBytes,
|
|
705
|
+
timeout,
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
finally {
|
|
709
|
+
await builder.delete();
|
|
710
|
+
}
|
|
711
|
+
}
|
|
453
712
|
/**
|
|
454
713
|
* Capture a snapshot from a running sandbox.
|
|
455
714
|
*
|
|
@@ -461,9 +720,15 @@ export class SandboxClient {
|
|
|
461
720
|
* @returns Snapshot in "ready" status.
|
|
462
721
|
*/
|
|
463
722
|
async captureSnapshot(sandboxName, name, options = {}) {
|
|
464
|
-
const { timeout = 60, signal } = options;
|
|
723
|
+
const { dockerImage, fsCapacityBytes, timeout = 60, signal } = options;
|
|
465
724
|
const url = `${this._baseUrl}/boxes/${encodeURIComponent(sandboxName)}/snapshot`;
|
|
466
725
|
const payload = { name };
|
|
726
|
+
if (dockerImage !== undefined) {
|
|
727
|
+
payload.docker_image = dockerImage;
|
|
728
|
+
}
|
|
729
|
+
if (fsCapacityBytes !== undefined) {
|
|
730
|
+
payload.fs_capacity_bytes = fsCapacityBytes;
|
|
731
|
+
}
|
|
467
732
|
const response = await this._postJson(url, payload, { signal });
|
|
468
733
|
const snapshot = (await response.json());
|
|
469
734
|
return this.waitForSnapshot(snapshot.id, { timeout, signal });
|
|
@@ -202,7 +202,7 @@ class CommandHandle {
|
|
|
202
202
|
return;
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
|
-
|
|
205
|
+
throw new errors_js_1.LangSmithSandboxConnectionError("Command stream ended without exit message");
|
|
206
206
|
}
|
|
207
207
|
/**
|
|
208
208
|
* Async iterate over output chunks with auto-reconnect on transient errors.
|
|
@@ -199,7 +199,7 @@ export class CommandHandle {
|
|
|
199
199
|
return;
|
|
200
200
|
}
|
|
201
201
|
}
|
|
202
|
-
|
|
202
|
+
throw new LangSmithSandboxConnectionError("Command stream ended without exit message");
|
|
203
203
|
}
|
|
204
204
|
/**
|
|
205
205
|
* Async iterate over output chunks with auto-reconnect on transient errors.
|
package/dist/sandbox/index.d.ts
CHANGED
|
@@ -30,5 +30,5 @@
|
|
|
30
30
|
export { SandboxClient } from "./client.js";
|
|
31
31
|
export { Sandbox } from "./sandbox.js";
|
|
32
32
|
export { CommandHandle } from "./command_handle.js";
|
|
33
|
-
export type { ExecutionResult, OutputChunk, WsMessage, WsRunOptions, ResourceStatus, Snapshot, SandboxData, SandboxClientConfig, RunOptions, CreateSandboxOptions, SandboxAccessControl, SandboxProxyConfig, CreateSnapshotOptions, CaptureSnapshotOptions, ListSnapshotsOptions, WaitForSnapshotOptions, StartSandboxOptions, UpdateSandboxOptions, WaitForSandboxOptions, } from "./types.js";
|
|
33
|
+
export type { ExecutionResult, OutputChunk, WsMessage, WsRunOptions, ResourceStatus, Snapshot, SandboxData, SandboxClientConfig, RunOptions, CreateSandboxOptions, SandboxAccessControl, SandboxProxyConfig, CreateSnapshotOptions, CreateDockerfileSnapshotOptions, CaptureSnapshotOptions, ListSnapshotsOptions, WaitForSnapshotOptions, StartSandboxOptions, UpdateSandboxOptions, WaitForSandboxOptions, } from "./types.js";
|
|
34
34
|
export { LangSmithSandboxError, LangSmithSandboxAPIError, LangSmithSandboxAuthenticationError, LangSmithSandboxConnectionError, LangSmithSandboxServerReloadError, LangSmithResourceNotFoundError, LangSmithResourceTimeoutError, LangSmithResourceInUseError, LangSmithResourceAlreadyExistsError, LangSmithResourceNameConflictError, LangSmithValidationError, LangSmithQuotaExceededError, LangSmithResourceCreationError, LangSmithSandboxCreationError, LangSmithSandboxNotReadyError, LangSmithSandboxOperationError, LangSmithCommandTimeoutError, LangSmithDataplaneNotConfiguredError, } from "./errors.js";
|
package/dist/sandbox/types.d.ts
CHANGED
|
@@ -333,10 +333,41 @@ export interface CreateSnapshotOptions {
|
|
|
333
333
|
/** AbortSignal for cancellation. */
|
|
334
334
|
signal?: AbortSignal;
|
|
335
335
|
}
|
|
336
|
+
/**
|
|
337
|
+
* Options for creating a snapshot from a local Dockerfile context.
|
|
338
|
+
*/
|
|
339
|
+
export interface CreateDockerfileSnapshotOptions {
|
|
340
|
+
/** Local Docker build context directory. Default: current working directory. */
|
|
341
|
+
context?: string;
|
|
342
|
+
/** Docker build args passed as BuildKit build-arg opts. */
|
|
343
|
+
buildArgs?: Record<string, string>;
|
|
344
|
+
/** Optional Dockerfile target stage. */
|
|
345
|
+
target?: string;
|
|
346
|
+
/** Callback for Docker build stdout/stderr chunks. */
|
|
347
|
+
onBuildLog?: (data: string) => void;
|
|
348
|
+
/**
|
|
349
|
+
* Number of vCPUs for the temporary builder sandbox. The build runs
|
|
350
|
+
* BuildKit plus the native snapshotter's layer copies inside it, which
|
|
351
|
+
* contend for a single core by default, so an extra vCPU can cut a cold
|
|
352
|
+
* build's wall time substantially.
|
|
353
|
+
*/
|
|
354
|
+
vCpus?: number;
|
|
355
|
+
/** Memory in bytes for the temporary builder sandbox. */
|
|
356
|
+
memBytes?: number;
|
|
357
|
+
/** Timeout in seconds for builder sandbox operations. Default: 60. */
|
|
358
|
+
timeout?: number;
|
|
359
|
+
}
|
|
336
360
|
/**
|
|
337
361
|
* Options for capturing a snapshot from a running sandbox.
|
|
338
362
|
*/
|
|
339
363
|
export interface CaptureSnapshotOptions {
|
|
364
|
+
/**
|
|
365
|
+
* Docker image tag inside the sandbox to export into the snapshot instead
|
|
366
|
+
* of capturing the live root filesystem.
|
|
367
|
+
*/
|
|
368
|
+
dockerImage?: string;
|
|
369
|
+
/** Filesystem capacity in bytes for Docker image export. */
|
|
370
|
+
fsCapacityBytes?: number;
|
|
340
371
|
/** Timeout in seconds when waiting for ready. Default: 60. */
|
|
341
372
|
timeout?: number;
|
|
342
373
|
/** AbortSignal for cancellation. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "langsmith",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.4",
|
|
4
4
|
"description": "Client library to connect to the LangSmith Observability and Evaluation Platform.",
|
|
5
5
|
"packageManager": "pnpm@10.33.0",
|
|
6
6
|
"files": [
|
|
@@ -159,8 +159,8 @@
|
|
|
159
159
|
"@ai-sdk/openai": "4.0.0-canary.59",
|
|
160
160
|
"@ai-sdk/provider": "4.0.0-canary.16",
|
|
161
161
|
"@ai-sdk/anthropic": "4.0.0-canary.55",
|
|
162
|
-
"@anthropic-ai/claude-agent-sdk": "^0.
|
|
163
|
-
"@anthropic-ai/sdk": "^0.
|
|
162
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.150",
|
|
163
|
+
"@anthropic-ai/sdk": "^0.98.0",
|
|
164
164
|
"@babel/preset-env": "^7.22.4",
|
|
165
165
|
"@faker-js/faker": "^8.4.1",
|
|
166
166
|
"@google/genai": "^2.0.1",
|
|
@@ -169,15 +169,16 @@
|
|
|
169
169
|
"@langchain/core": "^0.3.72",
|
|
170
170
|
"@langchain/langgraph": "^0.3.6",
|
|
171
171
|
"@langchain/openai": "^0.6.17",
|
|
172
|
-
"@openai/agents": "^0.
|
|
172
|
+
"@openai/agents": "^0.11.5",
|
|
173
173
|
"@opentelemetry/api": "^1.9.0",
|
|
174
|
-
"@opentelemetry/auto-instrumentations-node": "^0.
|
|
174
|
+
"@opentelemetry/auto-instrumentations-node": "^0.76.0",
|
|
175
175
|
"@opentelemetry/context-async-hooks": "^2.6.1",
|
|
176
|
-
"@opentelemetry/sdk-node": "^0.
|
|
176
|
+
"@opentelemetry/sdk-node": "^0.218.0",
|
|
177
177
|
"@opentelemetry/sdk-trace-base": "^2.0.0",
|
|
178
178
|
"@opentelemetry/sdk-trace-node": "^2.0.0",
|
|
179
179
|
"@tsconfig/recommended": "^1.0.2",
|
|
180
180
|
"@types/jest": "^29.5.1",
|
|
181
|
+
"@types/node": "^25.9.1",
|
|
181
182
|
"@types/node-fetch": "^2.6.12",
|
|
182
183
|
"@types/semver": "^7.7.1",
|
|
183
184
|
"@types/ws": "^8.18.1",
|
|
@@ -200,7 +201,7 @@
|
|
|
200
201
|
"ts-node": "^10.9.1",
|
|
201
202
|
"typedoc": "^0.28.16",
|
|
202
203
|
"typedoc-plugin-expand-object-like-types": "^0.1.2",
|
|
203
|
-
"typescript": "^
|
|
204
|
+
"typescript": "^6.0.3",
|
|
204
205
|
"vitest": "^3.1.3",
|
|
205
206
|
"ws": "^8.19.0",
|
|
206
207
|
"zod": "^4.3.6"
|