langsmith 0.8.7 → 0.8.8
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/_openapi_client/client.cjs +8 -0
- package/dist/_openapi_client/client.d.ts +3 -0
- package/dist/_openapi_client/client.js +8 -0
- package/dist/_openapi_client/resources/annotation-queues/annotation-queues.cjs +147 -0
- package/dist/_openapi_client/resources/annotation-queues/annotation-queues.d.ts +372 -0
- package/dist/_openapi_client/resources/annotation-queues/annotation-queues.js +110 -0
- package/dist/_openapi_client/resources/annotation-queues/items.cjs +84 -0
- package/dist/_openapi_client/resources/annotation-queues/items.d.ts +207 -0
- package/dist/_openapi_client/resources/annotation-queues/items.js +80 -0
- package/dist/_openapi_client/resources/annotation-queues/runs.cjs +61 -0
- package/dist/_openapi_client/resources/annotation-queues/runs.d.ts +185 -0
- package/dist/_openapi_client/resources/annotation-queues/runs.js +57 -0
- package/dist/_openapi_client/resources/datasets/experiment-runs.cjs +1 -1
- package/dist/_openapi_client/resources/datasets/experiment-runs.js +1 -1
- package/dist/_openapi_client/resources/index.cjs +3 -1
- package/dist/_openapi_client/resources/index.d.ts +1 -0
- package/dist/_openapi_client/resources/index.js +1 -0
- package/dist/_openapi_client/resources/info.cjs +3 -1
- package/dist/_openapi_client/resources/info.d.ts +16 -22
- package/dist/_openapi_client/resources/info.js +3 -1
- package/dist/_openapi_client/resources/issues.cjs +2 -2
- package/dist/_openapi_client/resources/issues.js +2 -2
- package/dist/_openapi_client/resources/online-evaluators.cjs +7 -7
- package/dist/_openapi_client/resources/online-evaluators.d.ts +8 -0
- package/dist/_openapi_client/resources/online-evaluators.js +7 -7
- package/dist/_openapi_client/resources/public/runs.cjs +2 -2
- package/dist/_openapi_client/resources/public/runs.d.ts +1 -1
- package/dist/_openapi_client/resources/public/runs.js +2 -2
- package/dist/_openapi_client/resources/runs/runs.cjs +3 -3
- package/dist/_openapi_client/resources/runs/runs.d.ts +9 -4
- package/dist/_openapi_client/resources/runs/runs.js +3 -3
- package/dist/_openapi_client/resources/runs/share.cjs +2 -2
- package/dist/_openapi_client/resources/runs/share.js +2 -2
- package/dist/_openapi_client/resources/sandboxes/boxes.cjs +10 -10
- package/dist/_openapi_client/resources/sandboxes/boxes.d.ts +40 -0
- package/dist/_openapi_client/resources/sandboxes/boxes.js +10 -10
- package/dist/_openapi_client/resources/sandboxes/registries.cjs +5 -5
- package/dist/_openapi_client/resources/sandboxes/registries.js +5 -5
- package/dist/_openapi_client/resources/sandboxes/sandboxes.d.ts +17 -0
- package/dist/_openapi_client/resources/sandboxes/snapshots.cjs +4 -4
- package/dist/_openapi_client/resources/sandboxes/snapshots.d.ts +12 -0
- package/dist/_openapi_client/resources/sandboxes/snapshots.js +4 -4
- package/dist/_openapi_client/resources/threads.cjs +3 -3
- package/dist/_openapi_client/resources/threads.d.ts +2 -1
- package/dist/_openapi_client/resources/threads.js +3 -3
- package/dist/_openapi_client/resources/traces.cjs +2 -2
- package/dist/_openapi_client/resources/traces.d.ts +1 -1
- package/dist/_openapi_client/resources/traces.js +2 -2
- package/dist/client.cjs +57 -21
- package/dist/client.d.ts +58 -22
- package/dist/client.js +57 -21
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/sandbox/command_handle.cjs +4 -4
- package/dist/sandbox/command_handle.js +5 -5
- package/dist/sandbox/errors.cjs +16 -1
- package/dist/sandbox/errors.d.ts +11 -0
- package/dist/sandbox/errors.js +14 -0
- package/dist/sandbox/index.cjs +2 -1
- package/dist/sandbox/index.d.ts +1 -1
- package/dist/sandbox/index.js +1 -1
- package/dist/sandbox/sandbox.cjs +22 -4
- package/dist/sandbox/sandbox.js +24 -6
- package/dist/sandbox/ws_execute.cjs +75 -8
- package/dist/sandbox/ws_execute.d.ts +8 -0
- package/dist/sandbox/ws_execute.js +72 -8
- package/package.json +1 -1
- package/dist/utils/constants.cjs +0 -4
- package/dist/utils/constants.d.ts +0 -1
- package/dist/utils/constants.js +0 -1
package/dist/sandbox/errors.js
CHANGED
|
@@ -351,6 +351,20 @@ export class LangSmithStreamEndedBeforeStartedError extends LangSmithSandboxOper
|
|
|
351
351
|
this.name = "LangSmithStreamEndedBeforeStartedError";
|
|
352
352
|
}
|
|
353
353
|
}
|
|
354
|
+
/**
|
|
355
|
+
* Thrown when the socket fails or times out before the WebSocket handshake.
|
|
356
|
+
*
|
|
357
|
+
* Distinct from its parent because it is safely retryable: the execute frame
|
|
358
|
+
* was never sent, so re-issuing the same command_id cannot double-run a
|
|
359
|
+
* command. run() retries this with backoff; a plain connection error (a
|
|
360
|
+
* rejected handshake) is permanent and propagates immediately.
|
|
361
|
+
*/
|
|
362
|
+
export class LangSmithSandboxConnectTimeoutError extends LangSmithSandboxConnectionError {
|
|
363
|
+
constructor(message) {
|
|
364
|
+
super(message);
|
|
365
|
+
this.name = "LangSmithSandboxConnectTimeoutError";
|
|
366
|
+
}
|
|
367
|
+
}
|
|
354
368
|
/**
|
|
355
369
|
* Raised when the sandbox server is reloading (close code 1001).
|
|
356
370
|
*
|
package/dist/sandbox/index.cjs
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* @packageDocumentation
|
|
30
30
|
*/
|
|
31
31
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
-
exports.LangSmithDataplaneNotConfiguredError = exports.LangSmithCommandTimeoutError = exports.LangSmithSandboxOperationError = exports.LangSmithSandboxNotReadyError = exports.LangSmithSandboxCreationError = exports.LangSmithResourceCreationError = exports.LangSmithQuotaExceededError = exports.LangSmithValidationError = exports.LangSmithResourceNameConflictError = exports.LangSmithResourceAlreadyExistsError = exports.LangSmithResourceInUseError = exports.LangSmithResourceTimeoutError = exports.LangSmithResourceNotFoundError = exports.LangSmithSandboxServerReloadError = exports.LangSmithSandboxConnectionError = exports.LangSmithSandboxAuthenticationError = exports.LangSmithSandboxAPIError = exports.LangSmithSandboxError = exports.s3Mount = exports.mountConfig = exports.gitMount = exports.gcsMount = exports.workspaceSecret = exports.proxyConfig = exports.opaqueSecret = exports.gcpAuth = exports.awsAuth = exports.CommandHandle = exports.Sandbox = exports.SandboxClient = void 0;
|
|
32
|
+
exports.LangSmithDataplaneNotConfiguredError = exports.LangSmithCommandTimeoutError = exports.LangSmithSandboxOperationError = exports.LangSmithSandboxNotReadyError = exports.LangSmithSandboxCreationError = exports.LangSmithResourceCreationError = exports.LangSmithQuotaExceededError = exports.LangSmithValidationError = exports.LangSmithResourceNameConflictError = exports.LangSmithResourceAlreadyExistsError = exports.LangSmithResourceInUseError = exports.LangSmithResourceTimeoutError = exports.LangSmithResourceNotFoundError = exports.LangSmithSandboxServerReloadError = exports.LangSmithSandboxConnectTimeoutError = exports.LangSmithSandboxConnectionError = exports.LangSmithSandboxAuthenticationError = exports.LangSmithSandboxAPIError = exports.LangSmithSandboxError = exports.s3Mount = exports.mountConfig = exports.gitMount = exports.gcsMount = exports.workspaceSecret = exports.proxyConfig = exports.opaqueSecret = exports.gcpAuth = exports.awsAuth = exports.CommandHandle = exports.Sandbox = exports.SandboxClient = void 0;
|
|
33
33
|
// Main classes
|
|
34
34
|
var client_js_1 = require("./client.cjs");
|
|
35
35
|
Object.defineProperty(exports, "SandboxClient", { enumerable: true, get: function () { return client_js_1.SandboxClient; } });
|
|
@@ -55,6 +55,7 @@ Object.defineProperty(exports, "LangSmithSandboxError", { enumerable: true, get:
|
|
|
55
55
|
Object.defineProperty(exports, "LangSmithSandboxAPIError", { enumerable: true, get: function () { return errors_js_1.LangSmithSandboxAPIError; } });
|
|
56
56
|
Object.defineProperty(exports, "LangSmithSandboxAuthenticationError", { enumerable: true, get: function () { return errors_js_1.LangSmithSandboxAuthenticationError; } });
|
|
57
57
|
Object.defineProperty(exports, "LangSmithSandboxConnectionError", { enumerable: true, get: function () { return errors_js_1.LangSmithSandboxConnectionError; } });
|
|
58
|
+
Object.defineProperty(exports, "LangSmithSandboxConnectTimeoutError", { enumerable: true, get: function () { return errors_js_1.LangSmithSandboxConnectTimeoutError; } });
|
|
58
59
|
Object.defineProperty(exports, "LangSmithSandboxServerReloadError", { enumerable: true, get: function () { return errors_js_1.LangSmithSandboxServerReloadError; } });
|
|
59
60
|
// Resource errors (type-based with resourceType attribute)
|
|
60
61
|
Object.defineProperty(exports, "LangSmithResourceNotFoundError", { enumerable: true, get: function () { return errors_js_1.LangSmithResourceNotFoundError; } });
|
package/dist/sandbox/index.d.ts
CHANGED
|
@@ -33,4 +33,4 @@ export { CommandHandle } from "./command_handle.js";
|
|
|
33
33
|
export { awsAuth, gcpAuth, opaqueSecret, proxyConfig, workspaceSecret, } from "./proxy_config.js";
|
|
34
34
|
export { gcsMount, gitMount, mountConfig, s3Mount } from "./mounts.js";
|
|
35
35
|
export type { ExecutionResult, OutputChunk, WsMessage, WsRunOptions, ResourceStatus, Snapshot, SandboxData, SandboxClientConfig, RunOptions, CreateSandboxOptions, SandboxAccessControl, SandboxAwsAuthRule, SandboxAwsMountAuthConfig, SandboxGcpAuthRule, SandboxGcpMountAuthConfig, SandboxMountAuth, SandboxMountAuthConfig, SandboxMountConfig, SandboxProxyConfig, SandboxProxyRule, SandboxProxySecret, SandboxMount, MountCacheConfig, GCSMountConfig, GCSMountSpec, GitMountConfig, GitMountRefSpec, GitMountSpec, S3MountConfig, S3MountSpec, CreateSnapshotOptions, CreateDockerfileSnapshotOptions, CaptureSnapshotOptions, ListSnapshotsOptions, WaitForSnapshotOptions, StartSandboxOptions, UpdateSandboxOptions, WaitForSandboxOptions, } from "./types.js";
|
|
36
|
-
export { LangSmithSandboxError, LangSmithSandboxAPIError, LangSmithSandboxAuthenticationError, LangSmithSandboxConnectionError, LangSmithSandboxServerReloadError, LangSmithResourceNotFoundError, LangSmithResourceTimeoutError, LangSmithResourceInUseError, LangSmithResourceAlreadyExistsError, LangSmithResourceNameConflictError, LangSmithValidationError, LangSmithQuotaExceededError, LangSmithResourceCreationError, LangSmithSandboxCreationError, LangSmithSandboxNotReadyError, LangSmithSandboxOperationError, LangSmithCommandTimeoutError, LangSmithDataplaneNotConfiguredError, } from "./errors.js";
|
|
36
|
+
export { LangSmithSandboxError, LangSmithSandboxAPIError, LangSmithSandboxAuthenticationError, LangSmithSandboxConnectionError, LangSmithSandboxConnectTimeoutError, LangSmithSandboxServerReloadError, LangSmithResourceNotFoundError, LangSmithResourceTimeoutError, LangSmithResourceInUseError, LangSmithResourceAlreadyExistsError, LangSmithResourceNameConflictError, LangSmithValidationError, LangSmithQuotaExceededError, LangSmithResourceCreationError, LangSmithSandboxCreationError, LangSmithSandboxNotReadyError, LangSmithSandboxOperationError, LangSmithCommandTimeoutError, LangSmithDataplaneNotConfiguredError, } from "./errors.js";
|
package/dist/sandbox/index.js
CHANGED
|
@@ -36,7 +36,7 @@ export { gcsMount, gitMount, mountConfig, s3Mount } from "./mounts.js";
|
|
|
36
36
|
// Errors
|
|
37
37
|
export {
|
|
38
38
|
// Base and connection errors
|
|
39
|
-
LangSmithSandboxError, LangSmithSandboxAPIError, LangSmithSandboxAuthenticationError, LangSmithSandboxConnectionError, LangSmithSandboxServerReloadError,
|
|
39
|
+
LangSmithSandboxError, LangSmithSandboxAPIError, LangSmithSandboxAuthenticationError, LangSmithSandboxConnectionError, LangSmithSandboxConnectTimeoutError, LangSmithSandboxServerReloadError,
|
|
40
40
|
// Resource errors (type-based with resourceType attribute)
|
|
41
41
|
LangSmithResourceNotFoundError, LangSmithResourceTimeoutError, LangSmithResourceInUseError, LangSmithResourceAlreadyExistsError, LangSmithResourceNameConflictError,
|
|
42
42
|
// Validation and quota errors
|
package/dist/sandbox/sandbox.cjs
CHANGED
|
@@ -234,6 +234,7 @@ class Sandbox {
|
|
|
234
234
|
// spawning a second one.
|
|
235
235
|
const execCommandId = commandId ?? (0, uuid_js_1.uuid7)();
|
|
236
236
|
let attempt = 0;
|
|
237
|
+
const deadline = (0, ws_execute_js_1.connectDeadline)();
|
|
237
238
|
// eslint-disable-next-line no-constant-condition
|
|
238
239
|
while (true) {
|
|
239
240
|
const [stream, control] = await (0, ws_execute_js_1.runWsStream)(dataplaneUrl, this._client.getApiKey(), command, {
|
|
@@ -246,6 +247,7 @@ class Sandbox {
|
|
|
246
247
|
killOnDisconnect,
|
|
247
248
|
ttlSeconds,
|
|
248
249
|
pty,
|
|
250
|
+
openTimeout: (0, ws_execute_js_1.openTimeoutFor)(deadline),
|
|
249
251
|
...(Object.keys(clientHeaders).length > 0
|
|
250
252
|
? { headers: clientHeaders }
|
|
251
253
|
: {}),
|
|
@@ -259,16 +261,31 @@ class Sandbox {
|
|
|
259
261
|
return handle;
|
|
260
262
|
}
|
|
261
263
|
catch (e) {
|
|
262
|
-
// Idempotent re-issue (same command_id)
|
|
263
|
-
|
|
264
|
+
// Idempotent re-issue (same command_id): neither an early close nor a
|
|
265
|
+
// failed connect can have started a second command.
|
|
266
|
+
if (!(e instanceof errors_js_1.LangSmithStreamEndedBeforeStartedError) &&
|
|
267
|
+
!(e instanceof errors_js_1.LangSmithSandboxConnectTimeoutError)) {
|
|
264
268
|
throw e;
|
|
265
269
|
}
|
|
266
270
|
attempt++;
|
|
267
271
|
if (attempt > command_handle_js_1.CommandHandle.MAX_AUTO_RECONNECTS) {
|
|
268
272
|
throw e;
|
|
269
273
|
}
|
|
270
|
-
|
|
274
|
+
let delay = Math.min(command_handle_js_1.CommandHandle.BACKOFF_BASE * 2 ** (attempt - 1), command_handle_js_1.CommandHandle.BACKOFF_MAX);
|
|
275
|
+
const remaining = (0, ws_execute_js_1.remainingBudget)(deadline);
|
|
276
|
+
if (remaining !== undefined) {
|
|
277
|
+
if (remaining <= 0) {
|
|
278
|
+
throw e;
|
|
279
|
+
}
|
|
280
|
+
delay = Math.min(delay, remaining);
|
|
281
|
+
}
|
|
271
282
|
await new Promise((r) => setTimeout(r, delay * 1000));
|
|
283
|
+
// Never start an attempt with no budget left: openTimeoutFor would
|
|
284
|
+
// return 0, which ws forwards to Node as "no timeout at all".
|
|
285
|
+
const left = (0, ws_execute_js_1.remainingBudget)(deadline);
|
|
286
|
+
if (left !== undefined && left <= 0) {
|
|
287
|
+
throw e;
|
|
288
|
+
}
|
|
272
289
|
}
|
|
273
290
|
}
|
|
274
291
|
}
|
|
@@ -426,8 +443,9 @@ class Sandbox {
|
|
|
426
443
|
*/
|
|
427
444
|
async stop() {
|
|
428
445
|
await this._client.stopSandbox(this.name);
|
|
446
|
+
// dataplane_url stays set: it is stable across stop/start and a request on
|
|
447
|
+
// it resumes the sandbox.
|
|
429
448
|
this.status = "stopped";
|
|
430
|
-
this.dataplane_url = undefined;
|
|
431
449
|
}
|
|
432
450
|
/**
|
|
433
451
|
* Capture a snapshot from this sandbox.
|
package/dist/sandbox/sandbox.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* Sandbox class for interacting with a specific sandbox instance.
|
|
3
3
|
*/
|
|
4
4
|
import { uuid7 } from "../uuid.js";
|
|
5
|
-
import { LangSmithDataplaneNotConfiguredError, LangSmithStreamEndedBeforeStartedError, } from "./errors.js";
|
|
5
|
+
import { LangSmithDataplaneNotConfiguredError, LangSmithSandboxConnectTimeoutError, LangSmithStreamEndedBeforeStartedError, } from "./errors.js";
|
|
6
6
|
import { handleSandboxHttpError } from "./helpers.js";
|
|
7
7
|
import { CommandHandle } from "./command_handle.js";
|
|
8
|
-
import { isWsAvailable, reconnectWsStream, runWsStream } from "./ws_execute.js";
|
|
8
|
+
import { connectDeadline, isWsAvailable, openTimeoutFor, reconnectWsStream, remainingBudget, runWsStream, } from "./ws_execute.js";
|
|
9
9
|
/**
|
|
10
10
|
* Represents an active sandbox for running commands and file operations.
|
|
11
11
|
*
|
|
@@ -231,6 +231,7 @@ export class Sandbox {
|
|
|
231
231
|
// spawning a second one.
|
|
232
232
|
const execCommandId = commandId ?? uuid7();
|
|
233
233
|
let attempt = 0;
|
|
234
|
+
const deadline = connectDeadline();
|
|
234
235
|
// eslint-disable-next-line no-constant-condition
|
|
235
236
|
while (true) {
|
|
236
237
|
const [stream, control] = await runWsStream(dataplaneUrl, this._client.getApiKey(), command, {
|
|
@@ -243,6 +244,7 @@ export class Sandbox {
|
|
|
243
244
|
killOnDisconnect,
|
|
244
245
|
ttlSeconds,
|
|
245
246
|
pty,
|
|
247
|
+
openTimeout: openTimeoutFor(deadline),
|
|
246
248
|
...(Object.keys(clientHeaders).length > 0
|
|
247
249
|
? { headers: clientHeaders }
|
|
248
250
|
: {}),
|
|
@@ -256,16 +258,31 @@ export class Sandbox {
|
|
|
256
258
|
return handle;
|
|
257
259
|
}
|
|
258
260
|
catch (e) {
|
|
259
|
-
// Idempotent re-issue (same command_id)
|
|
260
|
-
|
|
261
|
+
// Idempotent re-issue (same command_id): neither an early close nor a
|
|
262
|
+
// failed connect can have started a second command.
|
|
263
|
+
if (!(e instanceof LangSmithStreamEndedBeforeStartedError) &&
|
|
264
|
+
!(e instanceof LangSmithSandboxConnectTimeoutError)) {
|
|
261
265
|
throw e;
|
|
262
266
|
}
|
|
263
267
|
attempt++;
|
|
264
268
|
if (attempt > CommandHandle.MAX_AUTO_RECONNECTS) {
|
|
265
269
|
throw e;
|
|
266
270
|
}
|
|
267
|
-
|
|
271
|
+
let delay = Math.min(CommandHandle.BACKOFF_BASE * 2 ** (attempt - 1), CommandHandle.BACKOFF_MAX);
|
|
272
|
+
const remaining = remainingBudget(deadline);
|
|
273
|
+
if (remaining !== undefined) {
|
|
274
|
+
if (remaining <= 0) {
|
|
275
|
+
throw e;
|
|
276
|
+
}
|
|
277
|
+
delay = Math.min(delay, remaining);
|
|
278
|
+
}
|
|
268
279
|
await new Promise((r) => setTimeout(r, delay * 1000));
|
|
280
|
+
// Never start an attempt with no budget left: openTimeoutFor would
|
|
281
|
+
// return 0, which ws forwards to Node as "no timeout at all".
|
|
282
|
+
const left = remainingBudget(deadline);
|
|
283
|
+
if (left !== undefined && left <= 0) {
|
|
284
|
+
throw e;
|
|
285
|
+
}
|
|
269
286
|
}
|
|
270
287
|
}
|
|
271
288
|
}
|
|
@@ -423,8 +440,9 @@ export class Sandbox {
|
|
|
423
440
|
*/
|
|
424
441
|
async stop() {
|
|
425
442
|
await this._client.stopSandbox(this.name);
|
|
443
|
+
// dataplane_url stays set: it is stable across stop/start and a request on
|
|
444
|
+
// it resumes the sandbox.
|
|
426
445
|
this.status = "stopped";
|
|
427
|
-
this.dataplane_url = undefined;
|
|
428
446
|
}
|
|
429
447
|
/**
|
|
430
448
|
* Capture a snapshot from this sandbox.
|
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
* Install with: npm install ws
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.WSStreamControl = void 0;
|
|
9
|
+
exports.WSStreamControl = exports.WS_CONNECT_BUDGET = exports.WS_OPEN_TIMEOUT = void 0;
|
|
10
|
+
exports.connectDeadline = connectDeadline;
|
|
11
|
+
exports.remainingBudget = remainingBudget;
|
|
12
|
+
exports.openTimeoutFor = openTimeoutFor;
|
|
10
13
|
exports.isWsAvailable = isWsAvailable;
|
|
11
14
|
exports.buildWsUrl = buildWsUrl;
|
|
12
15
|
exports.buildAuthHeaders = buildAuthHeaders;
|
|
@@ -14,6 +17,46 @@ exports.raiseForWsError = raiseForWsError;
|
|
|
14
17
|
exports.runWsStream = runWsStream;
|
|
15
18
|
exports.reconnectWsStream = reconnectWsStream;
|
|
16
19
|
const errors_js_1 = require("./errors.cjs");
|
|
20
|
+
const env_js_1 = require("../utils/env.cjs");
|
|
21
|
+
/** Read a WebSocket timeout override, in seconds. `<= 0` disables it. */
|
|
22
|
+
function envTimeout(name, defaultSeconds) {
|
|
23
|
+
const raw = (0, env_js_1.getLangSmithEnvironmentVariable)(name);
|
|
24
|
+
if (raw === undefined || raw.trim() === "")
|
|
25
|
+
return defaultSeconds;
|
|
26
|
+
const value = Number(raw);
|
|
27
|
+
if (!Number.isFinite(value)) {
|
|
28
|
+
console.warn(`Ignoring invalid LANGSMITH_${name}=${raw}, using ${defaultSeconds}`);
|
|
29
|
+
return defaultSeconds;
|
|
30
|
+
}
|
|
31
|
+
return value > 0 ? value : undefined;
|
|
32
|
+
}
|
|
33
|
+
// A slow cold start is recovered by the retry loop in run(), not by waiting here.
|
|
34
|
+
exports.WS_OPEN_TIMEOUT = envTimeout("SANDBOX_WS_TIMEOUT_OPEN", 30);
|
|
35
|
+
// Ceiling on the whole connect phase. Without it, retrying a blackholed
|
|
36
|
+
// handshake costs MAX_AUTO_RECONNECTS + 1 full open timeouts plus backoff.
|
|
37
|
+
exports.WS_CONNECT_BUDGET = envTimeout("SANDBOX_WS_TIMEOUT_CONNECT_BUDGET", 120);
|
|
38
|
+
function nowSeconds() {
|
|
39
|
+
return performance.now() / 1000;
|
|
40
|
+
}
|
|
41
|
+
/** Instant after which connect attempts must stop, if bounded. */
|
|
42
|
+
function connectDeadline() {
|
|
43
|
+
return exports.WS_CONNECT_BUDGET === undefined
|
|
44
|
+
? undefined
|
|
45
|
+
: nowSeconds() + exports.WS_CONNECT_BUDGET;
|
|
46
|
+
}
|
|
47
|
+
/** Seconds left in the connect budget, or undefined when unbounded. */
|
|
48
|
+
function remainingBudget(deadline) {
|
|
49
|
+
return deadline === undefined ? undefined : deadline - nowSeconds();
|
|
50
|
+
}
|
|
51
|
+
/** Per-attempt open timeout, clamped so it cannot outlive `deadline`. */
|
|
52
|
+
function openTimeoutFor(deadline) {
|
|
53
|
+
if (deadline === undefined)
|
|
54
|
+
return exports.WS_OPEN_TIMEOUT;
|
|
55
|
+
const remaining = Math.max(deadline - nowSeconds(), 0);
|
|
56
|
+
return exports.WS_OPEN_TIMEOUT === undefined
|
|
57
|
+
? remaining
|
|
58
|
+
: Math.min(exports.WS_OPEN_TIMEOUT, remaining);
|
|
59
|
+
}
|
|
17
60
|
// Load the optional 'ws' package once, at module load, instead of importing on
|
|
18
61
|
// every call. Resolves to the WebSocket constructor, or null if 'ws' isn't
|
|
19
62
|
// installed. run() reads isWsAvailable() to choose WS vs the HTTP fallback.
|
|
@@ -140,18 +183,42 @@ function raiseForWsError(msg, commandId = "") {
|
|
|
140
183
|
/**
|
|
141
184
|
* Create a ws WebSocket connection and return a promise that resolves when open
|
|
142
185
|
* or rejects on error.
|
|
186
|
+
*
|
|
187
|
+
* A socket-level failure rejects with the retryable connect-timeout error; a
|
|
188
|
+
* non-101 response rejects with the permanent connection error.
|
|
143
189
|
*/
|
|
144
|
-
async function connectWs(url, headers) {
|
|
190
|
+
async function connectWs(url, headers, openTimeout = exports.WS_OPEN_TIMEOUT) {
|
|
145
191
|
const { WebSocket: WS } = await ensureWs();
|
|
146
192
|
return new Promise((resolve, reject) => {
|
|
147
|
-
const ws = new WS(url, {
|
|
193
|
+
const ws = new WS(url, {
|
|
194
|
+
headers,
|
|
195
|
+
handshakeTimeout: openTimeout === undefined ? undefined : openTimeout * 1000,
|
|
196
|
+
});
|
|
197
|
+
let settled = false;
|
|
148
198
|
ws.on("open", () => {
|
|
149
|
-
|
|
199
|
+
if (settled)
|
|
200
|
+
return;
|
|
201
|
+
settled = true;
|
|
150
202
|
resolve(ws);
|
|
151
203
|
});
|
|
204
|
+
// Attaching this suppresses ws's own "error" for a non-101 response, so the
|
|
205
|
+
// rejected upgrade cannot be misreported as a retryable socket failure. ws
|
|
206
|
+
// leaves cleanup to the handler once a listener is present.
|
|
207
|
+
ws.on("unexpected-response", (req, res) => {
|
|
208
|
+
res.resume?.();
|
|
209
|
+
req.destroy?.();
|
|
210
|
+
if (settled)
|
|
211
|
+
return;
|
|
212
|
+
settled = true;
|
|
213
|
+
reject(new errors_js_1.LangSmithSandboxConnectionError(`WebSocket upgrade to ${url} rejected by server (HTTP ${res.statusCode})`));
|
|
214
|
+
});
|
|
215
|
+
// Stays attached after settling so a late error has a listener and cannot
|
|
216
|
+
// become an unhandled 'error' event.
|
|
152
217
|
ws.on("error", (err) => {
|
|
153
|
-
|
|
154
|
-
|
|
218
|
+
if (settled)
|
|
219
|
+
return;
|
|
220
|
+
settled = true;
|
|
221
|
+
reject(new errors_js_1.LangSmithSandboxConnectTimeoutError(`Failed to connect to sandbox WebSocket: ${err.message}`));
|
|
155
222
|
});
|
|
156
223
|
});
|
|
157
224
|
}
|
|
@@ -249,14 +316,14 @@ async function* readWsMessages(ws) {
|
|
|
249
316
|
* data arrives in addition to yielding the messages.
|
|
250
317
|
*/
|
|
251
318
|
async function runWsStream(dataplaneUrl, apiKey, command, options = {}) {
|
|
252
|
-
const { timeout = 60, env, cwd, shell = "/bin/bash", onStdout, onStderr, commandId, idleTimeout = 300, killOnDisconnect = false, ttlSeconds = 600, pty, headers: extraHeaders, } = options;
|
|
319
|
+
const { timeout = 60, env, cwd, shell = "/bin/bash", onStdout, onStderr, commandId, idleTimeout = 300, killOnDisconnect = false, ttlSeconds = 600, pty, headers: extraHeaders, openTimeout = exports.WS_OPEN_TIMEOUT, } = options;
|
|
253
320
|
const wsUrl = buildWsUrl(dataplaneUrl);
|
|
254
321
|
const headers = buildAuthHeaders(apiKey, extraHeaders);
|
|
255
322
|
const control = new WSStreamControl();
|
|
256
323
|
async function* stream() {
|
|
257
324
|
let ws;
|
|
258
325
|
try {
|
|
259
|
-
ws = await connectWs(wsUrl, headers);
|
|
326
|
+
ws = await connectWs(wsUrl, headers, openTimeout);
|
|
260
327
|
control._bind(ws);
|
|
261
328
|
// Send execute request
|
|
262
329
|
const payload = {
|
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
* Install with: npm install ws
|
|
6
6
|
*/
|
|
7
7
|
import type { WsMessage, WsRunOptions } from "./types.js";
|
|
8
|
+
export declare const WS_OPEN_TIMEOUT: number | undefined;
|
|
9
|
+
export declare const WS_CONNECT_BUDGET: number | undefined;
|
|
10
|
+
/** Instant after which connect attempts must stop, if bounded. */
|
|
11
|
+
export declare function connectDeadline(): number | undefined;
|
|
12
|
+
/** Seconds left in the connect budget, or undefined when unbounded. */
|
|
13
|
+
export declare function remainingBudget(deadline: number | undefined): number | undefined;
|
|
14
|
+
/** Per-attempt open timeout, clamped so it cannot outlive `deadline`. */
|
|
15
|
+
export declare function openTimeoutFor(deadline: number | undefined): number | undefined;
|
|
8
16
|
type WsWebSocket = any;
|
|
9
17
|
/** Whether the optional `ws` package could be loaded (resolved once). */
|
|
10
18
|
export declare function isWsAvailable(): Promise<boolean>;
|
|
@@ -4,7 +4,47 @@
|
|
|
4
4
|
* Uses the `ws` npm package (optional peer dependency).
|
|
5
5
|
* Install with: npm install ws
|
|
6
6
|
*/
|
|
7
|
-
import { LangSmithCommandTimeoutError, LangSmithSandboxConnectionError, LangSmithSandboxOperationError, LangSmithSandboxServerReloadError, } from "./errors.js";
|
|
7
|
+
import { LangSmithCommandTimeoutError, LangSmithSandboxConnectionError, LangSmithSandboxConnectTimeoutError, LangSmithSandboxOperationError, LangSmithSandboxServerReloadError, } from "./errors.js";
|
|
8
|
+
import { getLangSmithEnvironmentVariable } from "../utils/env.js";
|
|
9
|
+
/** Read a WebSocket timeout override, in seconds. `<= 0` disables it. */
|
|
10
|
+
function envTimeout(name, defaultSeconds) {
|
|
11
|
+
const raw = getLangSmithEnvironmentVariable(name);
|
|
12
|
+
if (raw === undefined || raw.trim() === "")
|
|
13
|
+
return defaultSeconds;
|
|
14
|
+
const value = Number(raw);
|
|
15
|
+
if (!Number.isFinite(value)) {
|
|
16
|
+
console.warn(`Ignoring invalid LANGSMITH_${name}=${raw}, using ${defaultSeconds}`);
|
|
17
|
+
return defaultSeconds;
|
|
18
|
+
}
|
|
19
|
+
return value > 0 ? value : undefined;
|
|
20
|
+
}
|
|
21
|
+
// A slow cold start is recovered by the retry loop in run(), not by waiting here.
|
|
22
|
+
export const WS_OPEN_TIMEOUT = envTimeout("SANDBOX_WS_TIMEOUT_OPEN", 30);
|
|
23
|
+
// Ceiling on the whole connect phase. Without it, retrying a blackholed
|
|
24
|
+
// handshake costs MAX_AUTO_RECONNECTS + 1 full open timeouts plus backoff.
|
|
25
|
+
export const WS_CONNECT_BUDGET = envTimeout("SANDBOX_WS_TIMEOUT_CONNECT_BUDGET", 120);
|
|
26
|
+
function nowSeconds() {
|
|
27
|
+
return performance.now() / 1000;
|
|
28
|
+
}
|
|
29
|
+
/** Instant after which connect attempts must stop, if bounded. */
|
|
30
|
+
export function connectDeadline() {
|
|
31
|
+
return WS_CONNECT_BUDGET === undefined
|
|
32
|
+
? undefined
|
|
33
|
+
: nowSeconds() + WS_CONNECT_BUDGET;
|
|
34
|
+
}
|
|
35
|
+
/** Seconds left in the connect budget, or undefined when unbounded. */
|
|
36
|
+
export function remainingBudget(deadline) {
|
|
37
|
+
return deadline === undefined ? undefined : deadline - nowSeconds();
|
|
38
|
+
}
|
|
39
|
+
/** Per-attempt open timeout, clamped so it cannot outlive `deadline`. */
|
|
40
|
+
export function openTimeoutFor(deadline) {
|
|
41
|
+
if (deadline === undefined)
|
|
42
|
+
return WS_OPEN_TIMEOUT;
|
|
43
|
+
const remaining = Math.max(deadline - nowSeconds(), 0);
|
|
44
|
+
return WS_OPEN_TIMEOUT === undefined
|
|
45
|
+
? remaining
|
|
46
|
+
: Math.min(WS_OPEN_TIMEOUT, remaining);
|
|
47
|
+
}
|
|
8
48
|
// Load the optional 'ws' package once, at module load, instead of importing on
|
|
9
49
|
// every call. Resolves to the WebSocket constructor, or null if 'ws' isn't
|
|
10
50
|
// installed. run() reads isWsAvailable() to choose WS vs the HTTP fallback.
|
|
@@ -130,18 +170,42 @@ export function raiseForWsError(msg, commandId = "") {
|
|
|
130
170
|
/**
|
|
131
171
|
* Create a ws WebSocket connection and return a promise that resolves when open
|
|
132
172
|
* or rejects on error.
|
|
173
|
+
*
|
|
174
|
+
* A socket-level failure rejects with the retryable connect-timeout error; a
|
|
175
|
+
* non-101 response rejects with the permanent connection error.
|
|
133
176
|
*/
|
|
134
|
-
async function connectWs(url, headers) {
|
|
177
|
+
async function connectWs(url, headers, openTimeout = WS_OPEN_TIMEOUT) {
|
|
135
178
|
const { WebSocket: WS } = await ensureWs();
|
|
136
179
|
return new Promise((resolve, reject) => {
|
|
137
|
-
const ws = new WS(url, {
|
|
180
|
+
const ws = new WS(url, {
|
|
181
|
+
headers,
|
|
182
|
+
handshakeTimeout: openTimeout === undefined ? undefined : openTimeout * 1000,
|
|
183
|
+
});
|
|
184
|
+
let settled = false;
|
|
138
185
|
ws.on("open", () => {
|
|
139
|
-
|
|
186
|
+
if (settled)
|
|
187
|
+
return;
|
|
188
|
+
settled = true;
|
|
140
189
|
resolve(ws);
|
|
141
190
|
});
|
|
191
|
+
// Attaching this suppresses ws's own "error" for a non-101 response, so the
|
|
192
|
+
// rejected upgrade cannot be misreported as a retryable socket failure. ws
|
|
193
|
+
// leaves cleanup to the handler once a listener is present.
|
|
194
|
+
ws.on("unexpected-response", (req, res) => {
|
|
195
|
+
res.resume?.();
|
|
196
|
+
req.destroy?.();
|
|
197
|
+
if (settled)
|
|
198
|
+
return;
|
|
199
|
+
settled = true;
|
|
200
|
+
reject(new LangSmithSandboxConnectionError(`WebSocket upgrade to ${url} rejected by server (HTTP ${res.statusCode})`));
|
|
201
|
+
});
|
|
202
|
+
// Stays attached after settling so a late error has a listener and cannot
|
|
203
|
+
// become an unhandled 'error' event.
|
|
142
204
|
ws.on("error", (err) => {
|
|
143
|
-
|
|
144
|
-
|
|
205
|
+
if (settled)
|
|
206
|
+
return;
|
|
207
|
+
settled = true;
|
|
208
|
+
reject(new LangSmithSandboxConnectTimeoutError(`Failed to connect to sandbox WebSocket: ${err.message}`));
|
|
145
209
|
});
|
|
146
210
|
});
|
|
147
211
|
}
|
|
@@ -239,14 +303,14 @@ async function* readWsMessages(ws) {
|
|
|
239
303
|
* data arrives in addition to yielding the messages.
|
|
240
304
|
*/
|
|
241
305
|
export async function runWsStream(dataplaneUrl, apiKey, command, options = {}) {
|
|
242
|
-
const { timeout = 60, env, cwd, shell = "/bin/bash", onStdout, onStderr, commandId, idleTimeout = 300, killOnDisconnect = false, ttlSeconds = 600, pty, headers: extraHeaders, } = options;
|
|
306
|
+
const { timeout = 60, env, cwd, shell = "/bin/bash", onStdout, onStderr, commandId, idleTimeout = 300, killOnDisconnect = false, ttlSeconds = 600, pty, headers: extraHeaders, openTimeout = WS_OPEN_TIMEOUT, } = options;
|
|
243
307
|
const wsUrl = buildWsUrl(dataplaneUrl);
|
|
244
308
|
const headers = buildAuthHeaders(apiKey, extraHeaders);
|
|
245
309
|
const control = new WSStreamControl();
|
|
246
310
|
async function* stream() {
|
|
247
311
|
let ws;
|
|
248
312
|
try {
|
|
249
|
-
ws = await connectWs(wsUrl, headers);
|
|
313
|
+
ws = await connectWs(wsUrl, headers, openTimeout);
|
|
250
314
|
control._bind(ws);
|
|
251
315
|
// Send execute request
|
|
252
316
|
const payload = {
|
package/package.json
CHANGED
package/dist/utils/constants.cjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const _MIN_BACKEND_VERSION = "0.16.14rc1";
|
package/dist/utils/constants.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const _MIN_BACKEND_VERSION = "0.16.14rc1";
|