langsmith 0.8.10 → 0.8.12
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/resources/issues.d.ts +6 -0
- package/dist/_openapi_client/resources/online-evaluators.cjs +2 -2
- package/dist/_openapi_client/resources/online-evaluators.d.ts +7 -2
- package/dist/_openapi_client/resources/online-evaluators.js +2 -2
- package/dist/_openapi_client/resources/public/runs.d.ts +1 -1
- package/dist/_openapi_client/resources/sandboxes/boxes.cjs +2 -0
- package/dist/_openapi_client/resources/sandboxes/boxes.d.ts +25 -0
- package/dist/_openapi_client/resources/sandboxes/boxes.js +2 -0
- package/dist/_openapi_client/resources/sandboxes/sandboxes.d.ts +7 -2
- package/dist/_openapi_client/resources/sandboxes/sandboxes.js +1 -1
- package/dist/_openapi_client/resources/sandboxes/snapshots.cjs +12 -2
- package/dist/_openapi_client/resources/sandboxes/snapshots.d.ts +25 -3
- package/dist/_openapi_client/resources/sandboxes/snapshots.js +12 -2
- package/dist/_openapi_client/resources/threads.cjs +1 -1
- package/dist/_openapi_client/resources/threads.d.ts +26 -1
- package/dist/_openapi_client/resources/threads.js +1 -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 +66 -12
- package/dist/sandbox/client.d.ts +31 -5
- package/dist/sandbox/client.js +66 -12
- package/dist/sandbox/errors.cjs +17 -6
- package/dist/sandbox/errors.d.ts +12 -5
- package/dist/sandbox/errors.js +15 -5
- package/dist/sandbox/index.cjs +3 -1
- package/dist/sandbox/index.d.ts +3 -3
- package/dist/sandbox/index.js +2 -2
- package/dist/sandbox/mounts.cjs +25 -2
- package/dist/sandbox/mounts.d.ts +12 -1
- package/dist/sandbox/mounts.js +24 -2
- package/dist/sandbox/proxy_config.cjs +28 -4
- package/dist/sandbox/proxy_config.d.ts +4 -2
- package/dist/sandbox/proxy_config.js +28 -4
- package/dist/sandbox/sandbox.cjs +27 -1
- package/dist/sandbox/sandbox.d.ts +25 -1
- package/dist/sandbox/sandbox.js +28 -2
- package/dist/sandbox/types.d.ts +71 -1
- package/dist/sandbox/ws_execute.cjs +7 -3
- package/dist/sandbox/ws_execute.js +8 -4
- package/dist/wrappers/anthropic.cjs +39 -6
- package/dist/wrappers/anthropic.js +39 -6
- package/package.json +12 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Sandbox class for interacting with a specific sandbox instance.
|
|
3
3
|
*/
|
|
4
|
-
import type { CaptureSnapshotOptions, ExecutionResult, RunOptions, Snapshot, StartSandboxOptions } from "./types.js";
|
|
4
|
+
import type { CaptureSnapshotOptions, DownloadURL, ExecutionResult, GenerateDownloadURLOptions, RunOptions, Snapshot, StartSandboxOptions } from "./types.js";
|
|
5
5
|
import { CommandHandle } from "./command_handle.js";
|
|
6
6
|
/**
|
|
7
7
|
* Represents an active sandbox for running commands and file operations.
|
|
@@ -155,6 +155,30 @@ export declare class Sandbox {
|
|
|
155
155
|
* ```
|
|
156
156
|
*/
|
|
157
157
|
read(path: string, timeout?: number): Promise<Uint8Array>;
|
|
158
|
+
/**
|
|
159
|
+
* Create a link that downloads one file from this sandbox.
|
|
160
|
+
*
|
|
161
|
+
* The link carries its own token, so anyone holding the URL can fetch that
|
|
162
|
+
* one file without a LangSmith credential. Fetching wakes a stopped sandbox.
|
|
163
|
+
*
|
|
164
|
+
* Do not modify the file after minting a link for it. The link is pinned to
|
|
165
|
+
* a path, not to a snapshot of the contents, so a later write to that path
|
|
166
|
+
* may or may not be reflected in what the link serves. Write a new file and
|
|
167
|
+
* mint a new link when the contents change.
|
|
168
|
+
*
|
|
169
|
+
* @param path - File path inside the sandbox.
|
|
170
|
+
* @param options - Expiry and response header overrides.
|
|
171
|
+
* @returns The link and its expiry, if any.
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* ```typescript
|
|
175
|
+
* const link = await sandbox.generateDownloadURL("/tmp/report.pdf", {
|
|
176
|
+
* expiresInSeconds: 3600,
|
|
177
|
+
* });
|
|
178
|
+
* console.log(link.download_url);
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
generateDownloadURL(path: string, options?: GenerateDownloadURLOptions): Promise<DownloadURL>;
|
|
158
182
|
/**
|
|
159
183
|
* Delete this sandbox.
|
|
160
184
|
*
|
package/dist/sandbox/sandbox.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Sandbox class for interacting with a specific sandbox instance.
|
|
3
3
|
*/
|
|
4
4
|
import { uuid7 } from "../uuid.js";
|
|
5
|
-
import { LangSmithDataplaneNotConfiguredError,
|
|
5
|
+
import { LangSmithDataplaneNotConfiguredError, LangSmithSandboxRetryableConnectionError, LangSmithStreamEndedBeforeStartedError, } from "./errors.js";
|
|
6
6
|
import { handleSandboxHttpError } from "./helpers.js";
|
|
7
7
|
import { CommandHandle } from "./command_handle.js";
|
|
8
8
|
import { connectDeadline, isWsAvailable, openTimeoutFor, reconnectWsStream, remainingBudget, runWsStream, } from "./ws_execute.js";
|
|
@@ -261,7 +261,7 @@ export class Sandbox {
|
|
|
261
261
|
// Idempotent re-issue (same command_id): neither an early close nor a
|
|
262
262
|
// failed connect can have started a second command.
|
|
263
263
|
if (!(e instanceof LangSmithStreamEndedBeforeStartedError) &&
|
|
264
|
-
!(e instanceof
|
|
264
|
+
!(e instanceof LangSmithSandboxRetryableConnectionError)) {
|
|
265
265
|
throw e;
|
|
266
266
|
}
|
|
267
267
|
attempt++;
|
|
@@ -407,6 +407,32 @@ export class Sandbox {
|
|
|
407
407
|
const buffer = await response.arrayBuffer();
|
|
408
408
|
return new Uint8Array(buffer);
|
|
409
409
|
}
|
|
410
|
+
/**
|
|
411
|
+
* Create a link that downloads one file from this sandbox.
|
|
412
|
+
*
|
|
413
|
+
* The link carries its own token, so anyone holding the URL can fetch that
|
|
414
|
+
* one file without a LangSmith credential. Fetching wakes a stopped sandbox.
|
|
415
|
+
*
|
|
416
|
+
* Do not modify the file after minting a link for it. The link is pinned to
|
|
417
|
+
* a path, not to a snapshot of the contents, so a later write to that path
|
|
418
|
+
* may or may not be reflected in what the link serves. Write a new file and
|
|
419
|
+
* mint a new link when the contents change.
|
|
420
|
+
*
|
|
421
|
+
* @param path - File path inside the sandbox.
|
|
422
|
+
* @param options - Expiry and response header overrides.
|
|
423
|
+
* @returns The link and its expiry, if any.
|
|
424
|
+
*
|
|
425
|
+
* @example
|
|
426
|
+
* ```typescript
|
|
427
|
+
* const link = await sandbox.generateDownloadURL("/tmp/report.pdf", {
|
|
428
|
+
* expiresInSeconds: 3600,
|
|
429
|
+
* });
|
|
430
|
+
* console.log(link.download_url);
|
|
431
|
+
* ```
|
|
432
|
+
*/
|
|
433
|
+
async generateDownloadURL(path, options = {}) {
|
|
434
|
+
return this._client.generateDownloadURL(this.name, path, options);
|
|
435
|
+
}
|
|
410
436
|
/**
|
|
411
437
|
* Delete this sandbox.
|
|
412
438
|
*
|
package/dist/sandbox/types.d.ts
CHANGED
|
@@ -270,6 +270,13 @@ export interface SandboxAwsAuthRule {
|
|
|
270
270
|
type: "aws";
|
|
271
271
|
/** Whether the rule is enabled. */
|
|
272
272
|
enabled?: boolean;
|
|
273
|
+
/**
|
|
274
|
+
* Plaintext environment variables set for every command in the sandbox while
|
|
275
|
+
* the rule is enabled, for tools that refuse to run unless a credential
|
|
276
|
+
* variable is present even though the proxy injects the real credential on
|
|
277
|
+
* the wire.
|
|
278
|
+
*/
|
|
279
|
+
env_vars?: Record<string, string>;
|
|
273
280
|
/** AWS credentials used by the proxy signer. */
|
|
274
281
|
aws: {
|
|
275
282
|
access_key_id: SandboxProxySecret;
|
|
@@ -284,6 +291,13 @@ export interface SandboxGcpAuthRule {
|
|
|
284
291
|
type: "gcp";
|
|
285
292
|
/** Whether the rule is enabled. */
|
|
286
293
|
enabled?: boolean;
|
|
294
|
+
/**
|
|
295
|
+
* Plaintext environment variables set for every command in the sandbox while
|
|
296
|
+
* the rule is enabled, for tools that refuse to run unless a credential
|
|
297
|
+
* variable is present even though the proxy injects the real credential on
|
|
298
|
+
* the wire.
|
|
299
|
+
*/
|
|
300
|
+
env_vars?: Record<string, string>;
|
|
287
301
|
/** GCP service-account credential and OAuth scopes. */
|
|
288
302
|
gcp: {
|
|
289
303
|
service_account_json: SandboxProxySecret;
|
|
@@ -386,8 +400,29 @@ export interface GitMountSpec {
|
|
|
386
400
|
/** Git mount configuration. */
|
|
387
401
|
git: GitMountConfig;
|
|
388
402
|
}
|
|
403
|
+
/** Context Hub configuration for a sandbox mount. */
|
|
404
|
+
export interface ContextHubMountConfig {
|
|
405
|
+
/**
|
|
406
|
+
* Context Hub repository to sync, as `owner/repo` (e.g. `-/my-agent`, where
|
|
407
|
+
* `-` is the current workspace).
|
|
408
|
+
*/
|
|
409
|
+
repo: string;
|
|
410
|
+
/** Sync once at startup instead of polling for updates. */
|
|
411
|
+
initial_pull_only?: boolean;
|
|
412
|
+
}
|
|
413
|
+
/** Read-only Context Hub-backed sandbox mount specification. */
|
|
414
|
+
export interface ContextHubMountSpec {
|
|
415
|
+
/** Stable mount identifier. */
|
|
416
|
+
id: string;
|
|
417
|
+
/** Mount type. */
|
|
418
|
+
type: "contexthub";
|
|
419
|
+
/** Absolute path inside the sandbox where the repo tree appears. */
|
|
420
|
+
mount_path: string;
|
|
421
|
+
/** Context Hub mount configuration. */
|
|
422
|
+
contexthub: ContextHubMountConfig;
|
|
423
|
+
}
|
|
389
424
|
/** Sandbox mount specification. */
|
|
390
|
-
export type SandboxMount = S3MountSpec | GCSMountSpec | GitMountSpec;
|
|
425
|
+
export type SandboxMount = S3MountSpec | GCSMountSpec | GitMountSpec | ContextHubMountSpec;
|
|
391
426
|
/** AWS credentials used by the backend to authenticate S3 mounts. */
|
|
392
427
|
export interface SandboxAwsMountAuthConfig {
|
|
393
428
|
access_key_id: SandboxProxySecret;
|
|
@@ -524,6 +559,41 @@ export interface CaptureSnapshotOptions {
|
|
|
524
559
|
/** AbortSignal for cancellation. */
|
|
525
560
|
signal?: AbortSignal;
|
|
526
561
|
}
|
|
562
|
+
/**
|
|
563
|
+
* How a download link asks the browser to handle the file.
|
|
564
|
+
*/
|
|
565
|
+
export type DownloadContentDisposition = "attachment" | "inline";
|
|
566
|
+
/**
|
|
567
|
+
* Options for minting a sandbox file download link.
|
|
568
|
+
*/
|
|
569
|
+
export interface GenerateDownloadURLOptions {
|
|
570
|
+
/**
|
|
571
|
+
* Link TTL in seconds. Omit for a link that never expires.
|
|
572
|
+
*/
|
|
573
|
+
expiresInSeconds?: number;
|
|
574
|
+
/** Content-Type to serve the file as. */
|
|
575
|
+
contentType?: string;
|
|
576
|
+
/** Content-Disposition to serve the file with. */
|
|
577
|
+
contentDisposition?: DownloadContentDisposition;
|
|
578
|
+
/** AbortSignal for cancellation. */
|
|
579
|
+
signal?: AbortSignal;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* A link that downloads one sandbox file with no LangSmith credential.
|
|
583
|
+
*
|
|
584
|
+
* The link is pinned to the sandbox, the file path, and the response headers,
|
|
585
|
+
* so it cannot be repointed at another file. It is pinned to the path rather
|
|
586
|
+
* than to a snapshot of the contents, so the file must not be modified while
|
|
587
|
+
* the link is in use.
|
|
588
|
+
*/
|
|
589
|
+
export interface DownloadURL {
|
|
590
|
+
/** The full URL to fetch. Supports GET, HEAD, and Range. */
|
|
591
|
+
download_url: string;
|
|
592
|
+
/** The signed token embedded in `download_url`. */
|
|
593
|
+
token: string;
|
|
594
|
+
/** Expiry timestamp, or null for a link that never expires. */
|
|
595
|
+
expires_at: string | null;
|
|
596
|
+
}
|
|
527
597
|
/**
|
|
528
598
|
* Options for listing snapshots. All fields are optional and independent.
|
|
529
599
|
*
|
|
@@ -35,6 +35,7 @@ exports.WS_OPEN_TIMEOUT = envTimeout("SANDBOX_WS_TIMEOUT_OPEN", 30);
|
|
|
35
35
|
// Ceiling on the whole connect phase. Without it, retrying a blackholed
|
|
36
36
|
// handshake costs MAX_AUTO_RECONNECTS + 1 full open timeouts plus backoff.
|
|
37
37
|
exports.WS_CONNECT_BUDGET = envTimeout("SANDBOX_WS_TIMEOUT_CONNECT_BUDGET", 120);
|
|
38
|
+
const RETRYABLE_HANDSHAKE_STATUS_CODES = new Set([500, 502, 503, 504]);
|
|
38
39
|
function nowSeconds() {
|
|
39
40
|
return performance.now() / 1000;
|
|
40
41
|
}
|
|
@@ -184,8 +185,8 @@ function raiseForWsError(msg, commandId = "") {
|
|
|
184
185
|
* Create a ws WebSocket connection and return a promise that resolves when open
|
|
185
186
|
* or rejects on error.
|
|
186
187
|
*
|
|
187
|
-
*
|
|
188
|
-
* non-101
|
|
188
|
+
* Socket-level failures and transient 5xx responses are retryable. Other
|
|
189
|
+
* non-101 responses are permanent connection errors.
|
|
189
190
|
*/
|
|
190
191
|
async function connectWs(url, headers, openTimeout = exports.WS_OPEN_TIMEOUT) {
|
|
191
192
|
const { WebSocket: WS } = await ensureWs();
|
|
@@ -210,7 +211,10 @@ async function connectWs(url, headers, openTimeout = exports.WS_OPEN_TIMEOUT) {
|
|
|
210
211
|
if (settled)
|
|
211
212
|
return;
|
|
212
213
|
settled = true;
|
|
213
|
-
|
|
214
|
+
const ErrorType = RETRYABLE_HANDSHAKE_STATUS_CODES.has(res.statusCode ?? 0)
|
|
215
|
+
? errors_js_1.LangSmithSandboxRetryableConnectionError
|
|
216
|
+
: errors_js_1.LangSmithSandboxConnectionError;
|
|
217
|
+
reject(new ErrorType(`WebSocket upgrade to ${url} rejected by server (HTTP ${res.statusCode})`));
|
|
214
218
|
});
|
|
215
219
|
// Stays attached after settling so a late error has a listener and cannot
|
|
216
220
|
// become an unhandled 'error' event.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Uses the `ws` npm package (optional peer dependency).
|
|
5
5
|
* Install with: npm install ws
|
|
6
6
|
*/
|
|
7
|
-
import { LangSmithCommandTimeoutError, LangSmithSandboxConnectionError, LangSmithSandboxConnectTimeoutError, LangSmithSandboxOperationError, LangSmithSandboxServerReloadError, } from "./errors.js";
|
|
7
|
+
import { LangSmithCommandTimeoutError, LangSmithSandboxConnectionError, LangSmithSandboxConnectTimeoutError, LangSmithSandboxOperationError, LangSmithSandboxRetryableConnectionError, LangSmithSandboxServerReloadError, } from "./errors.js";
|
|
8
8
|
import { getLangSmithEnvironmentVariable } from "../utils/env.js";
|
|
9
9
|
/** Read a WebSocket timeout override, in seconds. `<= 0` disables it. */
|
|
10
10
|
function envTimeout(name, defaultSeconds) {
|
|
@@ -23,6 +23,7 @@ export const WS_OPEN_TIMEOUT = envTimeout("SANDBOX_WS_TIMEOUT_OPEN", 30);
|
|
|
23
23
|
// Ceiling on the whole connect phase. Without it, retrying a blackholed
|
|
24
24
|
// handshake costs MAX_AUTO_RECONNECTS + 1 full open timeouts plus backoff.
|
|
25
25
|
export const WS_CONNECT_BUDGET = envTimeout("SANDBOX_WS_TIMEOUT_CONNECT_BUDGET", 120);
|
|
26
|
+
const RETRYABLE_HANDSHAKE_STATUS_CODES = new Set([500, 502, 503, 504]);
|
|
26
27
|
function nowSeconds() {
|
|
27
28
|
return performance.now() / 1000;
|
|
28
29
|
}
|
|
@@ -171,8 +172,8 @@ export function raiseForWsError(msg, commandId = "") {
|
|
|
171
172
|
* Create a ws WebSocket connection and return a promise that resolves when open
|
|
172
173
|
* or rejects on error.
|
|
173
174
|
*
|
|
174
|
-
*
|
|
175
|
-
* non-101
|
|
175
|
+
* Socket-level failures and transient 5xx responses are retryable. Other
|
|
176
|
+
* non-101 responses are permanent connection errors.
|
|
176
177
|
*/
|
|
177
178
|
async function connectWs(url, headers, openTimeout = WS_OPEN_TIMEOUT) {
|
|
178
179
|
const { WebSocket: WS } = await ensureWs();
|
|
@@ -197,7 +198,10 @@ async function connectWs(url, headers, openTimeout = WS_OPEN_TIMEOUT) {
|
|
|
197
198
|
if (settled)
|
|
198
199
|
return;
|
|
199
200
|
settled = true;
|
|
200
|
-
|
|
201
|
+
const ErrorType = RETRYABLE_HANDSHAKE_STATUS_CODES.has(res.statusCode ?? 0)
|
|
202
|
+
? LangSmithSandboxRetryableConnectionError
|
|
203
|
+
: LangSmithSandboxConnectionError;
|
|
204
|
+
reject(new ErrorType(`WebSocket upgrade to ${url} rejected by server (HTTP ${res.statusCode})`));
|
|
201
205
|
});
|
|
202
206
|
// Stays attached after settling so a late error has a listener and cannot
|
|
203
207
|
// become an unhandled 'error' event.
|
|
@@ -5,7 +5,36 @@ exports.createUsageMetadata = createUsageMetadata;
|
|
|
5
5
|
const anthropic_managed_js_1 = require("./utils/anthropic_managed.cjs");
|
|
6
6
|
const traceable_js_1 = require("../traceable.cjs");
|
|
7
7
|
const usage_js_1 = require("../utils/usage.cjs");
|
|
8
|
+
const index_js_1 = require("../anonymizer/index.cjs");
|
|
8
9
|
const TRACED_INVOCATION_KEYS = ["top_k", "top_p", "stream", "thinking"];
|
|
10
|
+
/** Keys of an MCP server definition that are safe to trace. */
|
|
11
|
+
const MCP_SERVER_SAFE_KEYS = new Set([
|
|
12
|
+
"name",
|
|
13
|
+
"type",
|
|
14
|
+
"url",
|
|
15
|
+
"tool_configuration",
|
|
16
|
+
]);
|
|
17
|
+
/**
|
|
18
|
+
* Mask credentials in `mcp_servers` before tracing. Anything outside
|
|
19
|
+
* {@link MCP_SERVER_SAFE_KEYS} keeps its name but gets
|
|
20
|
+
* {@link SECRET_PLACEHOLDER} as its value, so the trace shows the field was set
|
|
21
|
+
* without exposing it, and fields the API adds later are masked by default.
|
|
22
|
+
*
|
|
23
|
+
* Returns new objects; the caller's are also sent to Anthropic.
|
|
24
|
+
*/
|
|
25
|
+
function redactMcpServers(servers) {
|
|
26
|
+
if (!Array.isArray(servers))
|
|
27
|
+
return servers;
|
|
28
|
+
return servers.map((server) => {
|
|
29
|
+
if (typeof server !== "object" || server == null || Array.isArray(server)) {
|
|
30
|
+
return server;
|
|
31
|
+
}
|
|
32
|
+
return Object.fromEntries(Object.entries(server).map(([key, value]) => [
|
|
33
|
+
key,
|
|
34
|
+
MCP_SERVER_SAFE_KEYS.has(key) ? value : index_js_1.SECRET_PLACEHOLDER,
|
|
35
|
+
]));
|
|
36
|
+
});
|
|
37
|
+
}
|
|
9
38
|
/**
|
|
10
39
|
* Create usage metadata from Anthropic's token usage format.
|
|
11
40
|
*/
|
|
@@ -222,11 +251,15 @@ const wrapAnthropic = (anthropic, options) => {
|
|
|
222
251
|
* This provides parity with the Python SDK behavior and enables system prompts
|
|
223
252
|
* to be viewed and edited in the LangSmith playground.
|
|
224
253
|
*/
|
|
225
|
-
function
|
|
254
|
+
function processTracedInputs(params) {
|
|
255
|
+
// Copy first: `params` also goes to the API and must keep its real values.
|
|
256
|
+
const processed = { ...params };
|
|
257
|
+
if (processed.mcp_servers != null) {
|
|
258
|
+
processed.mcp_servers = redactMcpServers(processed.mcp_servers);
|
|
259
|
+
}
|
|
226
260
|
if (!params.system) {
|
|
227
|
-
return
|
|
261
|
+
return processed;
|
|
228
262
|
}
|
|
229
|
-
const processed = { ...params };
|
|
230
263
|
// Handle both string and ContentBlock[] formats
|
|
231
264
|
const systemContent = Array.isArray(params.system)
|
|
232
265
|
? params.system
|
|
@@ -248,7 +281,7 @@ const wrapAnthropic = (anthropic, options) => {
|
|
|
248
281
|
run_type: "llm",
|
|
249
282
|
aggregator: messageAggregator,
|
|
250
283
|
argsConfigPath: [1, "langsmithExtra"],
|
|
251
|
-
processInputs:
|
|
284
|
+
processInputs: processTracedInputs,
|
|
252
285
|
getInvocationParams: (payload) => {
|
|
253
286
|
if (typeof payload !== "object" || payload == null)
|
|
254
287
|
return undefined;
|
|
@@ -315,7 +348,7 @@ const wrapAnthropic = (anthropic, options) => {
|
|
|
315
348
|
run_type: "llm",
|
|
316
349
|
aggregator: messageAggregator,
|
|
317
350
|
argsConfigPath: [1, "langsmithExtra"],
|
|
318
|
-
processInputs:
|
|
351
|
+
processInputs: processTracedInputs,
|
|
319
352
|
getInvocationParams: messagesCreateConfig.getInvocationParams,
|
|
320
353
|
processOutputs: processMessageOutput,
|
|
321
354
|
...cleanedOptions,
|
|
@@ -341,7 +374,7 @@ const wrapAnthropic = (anthropic, options) => {
|
|
|
341
374
|
run_type: "llm",
|
|
342
375
|
aggregator: messageAggregator,
|
|
343
376
|
argsConfigPath: [1, "langsmithExtra"],
|
|
344
|
-
processInputs:
|
|
377
|
+
processInputs: processTracedInputs,
|
|
345
378
|
getInvocationParams: messagesCreateConfig.getInvocationParams,
|
|
346
379
|
processOutputs: processMessageOutput,
|
|
347
380
|
...cleanedOptions,
|
|
@@ -1,7 +1,36 @@
|
|
|
1
1
|
import { wrapManagedAgentSessionEvents } from "./utils/anthropic_managed.js";
|
|
2
2
|
import { isTraceableFunction, traceable, } from "../traceable.js";
|
|
3
3
|
import { convertAnthropicUsageToInputTokenDetails } from "../utils/usage.js";
|
|
4
|
+
import { SECRET_PLACEHOLDER } from "../anonymizer/index.js";
|
|
4
5
|
const TRACED_INVOCATION_KEYS = ["top_k", "top_p", "stream", "thinking"];
|
|
6
|
+
/** Keys of an MCP server definition that are safe to trace. */
|
|
7
|
+
const MCP_SERVER_SAFE_KEYS = new Set([
|
|
8
|
+
"name",
|
|
9
|
+
"type",
|
|
10
|
+
"url",
|
|
11
|
+
"tool_configuration",
|
|
12
|
+
]);
|
|
13
|
+
/**
|
|
14
|
+
* Mask credentials in `mcp_servers` before tracing. Anything outside
|
|
15
|
+
* {@link MCP_SERVER_SAFE_KEYS} keeps its name but gets
|
|
16
|
+
* {@link SECRET_PLACEHOLDER} as its value, so the trace shows the field was set
|
|
17
|
+
* without exposing it, and fields the API adds later are masked by default.
|
|
18
|
+
*
|
|
19
|
+
* Returns new objects; the caller's are also sent to Anthropic.
|
|
20
|
+
*/
|
|
21
|
+
function redactMcpServers(servers) {
|
|
22
|
+
if (!Array.isArray(servers))
|
|
23
|
+
return servers;
|
|
24
|
+
return servers.map((server) => {
|
|
25
|
+
if (typeof server !== "object" || server == null || Array.isArray(server)) {
|
|
26
|
+
return server;
|
|
27
|
+
}
|
|
28
|
+
return Object.fromEntries(Object.entries(server).map(([key, value]) => [
|
|
29
|
+
key,
|
|
30
|
+
MCP_SERVER_SAFE_KEYS.has(key) ? value : SECRET_PLACEHOLDER,
|
|
31
|
+
]));
|
|
32
|
+
});
|
|
33
|
+
}
|
|
5
34
|
/**
|
|
6
35
|
* Create usage metadata from Anthropic's token usage format.
|
|
7
36
|
*/
|
|
@@ -218,11 +247,15 @@ export const wrapAnthropic = (anthropic, options) => {
|
|
|
218
247
|
* This provides parity with the Python SDK behavior and enables system prompts
|
|
219
248
|
* to be viewed and edited in the LangSmith playground.
|
|
220
249
|
*/
|
|
221
|
-
function
|
|
250
|
+
function processTracedInputs(params) {
|
|
251
|
+
// Copy first: `params` also goes to the API and must keep its real values.
|
|
252
|
+
const processed = { ...params };
|
|
253
|
+
if (processed.mcp_servers != null) {
|
|
254
|
+
processed.mcp_servers = redactMcpServers(processed.mcp_servers);
|
|
255
|
+
}
|
|
222
256
|
if (!params.system) {
|
|
223
|
-
return
|
|
257
|
+
return processed;
|
|
224
258
|
}
|
|
225
|
-
const processed = { ...params };
|
|
226
259
|
// Handle both string and ContentBlock[] formats
|
|
227
260
|
const systemContent = Array.isArray(params.system)
|
|
228
261
|
? params.system
|
|
@@ -244,7 +277,7 @@ export const wrapAnthropic = (anthropic, options) => {
|
|
|
244
277
|
run_type: "llm",
|
|
245
278
|
aggregator: messageAggregator,
|
|
246
279
|
argsConfigPath: [1, "langsmithExtra"],
|
|
247
|
-
processInputs:
|
|
280
|
+
processInputs: processTracedInputs,
|
|
248
281
|
getInvocationParams: (payload) => {
|
|
249
282
|
if (typeof payload !== "object" || payload == null)
|
|
250
283
|
return undefined;
|
|
@@ -311,7 +344,7 @@ export const wrapAnthropic = (anthropic, options) => {
|
|
|
311
344
|
run_type: "llm",
|
|
312
345
|
aggregator: messageAggregator,
|
|
313
346
|
argsConfigPath: [1, "langsmithExtra"],
|
|
314
|
-
processInputs:
|
|
347
|
+
processInputs: processTracedInputs,
|
|
315
348
|
getInvocationParams: messagesCreateConfig.getInvocationParams,
|
|
316
349
|
processOutputs: processMessageOutput,
|
|
317
350
|
...cleanedOptions,
|
|
@@ -337,7 +370,7 @@ export const wrapAnthropic = (anthropic, options) => {
|
|
|
337
370
|
run_type: "llm",
|
|
338
371
|
aggregator: messageAggregator,
|
|
339
372
|
argsConfigPath: [1, "langsmithExtra"],
|
|
340
|
-
processInputs:
|
|
373
|
+
processInputs: processTracedInputs,
|
|
341
374
|
getInvocationParams: messagesCreateConfig.getInvocationParams,
|
|
342
375
|
processOutputs: processMessageOutput,
|
|
343
376
|
...cleanedOptions,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "langsmith",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.12",
|
|
4
4
|
"description": "Client library to connect to the LangSmith Observability and Evaluation Platform.",
|
|
5
5
|
"packageManager": "pnpm@10.33.0",
|
|
6
6
|
"files": [
|
|
@@ -481,7 +481,17 @@
|
|
|
481
481
|
},
|
|
482
482
|
"pnpm": {
|
|
483
483
|
"overrides": {
|
|
484
|
-
"
|
|
484
|
+
"@hono/node-server": "2.0.10",
|
|
485
|
+
"brace-expansion@<1.1.16": "1.1.16",
|
|
486
|
+
"brace-expansion@>=2.0.0 <2.1.2": "2.1.2",
|
|
487
|
+
"brace-expansion@>=3.0.0 <5.0.7": "5.0.7",
|
|
488
|
+
"fast-uri": "3.1.5",
|
|
489
|
+
"postcss": "8.5.23",
|
|
490
|
+
"js-yaml": "4.3.1",
|
|
491
|
+
"hono": "4.12.34",
|
|
492
|
+
"ip-address": "10.3.1",
|
|
493
|
+
"undici@>=7.0.0 <8.0.0": "7.29.0",
|
|
494
|
+
"undici@>=8.0.0 <9.0.0": "8.9.0",
|
|
485
495
|
"vite": "^7.3.2",
|
|
486
496
|
"form-data": "4.0.6",
|
|
487
497
|
"langsmith": "0.6.0",
|