langsmith 0.10.11 → 0.10.13
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/CHANGELOG.md +28 -0
- package/README.md +198 -198
- package/lib/address.d.mts +35 -56
- package/lib/address.d.mts.map +1 -1
- package/lib/address.d.ts +35 -56
- package/lib/address.d.ts.map +1 -1
- package/lib/address.js +91 -156
- package/lib/address.js.map +1 -1
- package/lib/address.mjs +83 -151
- package/lib/address.mjs.map +1 -1
- package/lib/client.d.mts +1 -1
- package/lib/client.d.mts.map +1 -1
- package/lib/client.d.ts +1 -1
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +11 -10
- package/lib/client.js.map +1 -1
- package/lib/client.mjs +4 -3
- package/lib/client.mjs.map +1 -1
- package/lib/run_trees.d.mts +1 -1
- package/lib/run_trees.d.mts.map +1 -1
- package/lib/run_trees.d.ts +1 -1
- package/lib/run_trees.d.ts.map +1 -1
- package/lib/run_trees.js +11 -9
- package/lib/run_trees.js.map +1 -1
- package/lib/run_trees.mjs +7 -5
- package/lib/run_trees.mjs.map +1 -1
- package/lib/sandbox/index.d.mts +1 -1
- package/lib/sandbox/index.d.mts.map +1 -1
- package/lib/sandbox/index.d.ts +1 -1
- package/lib/sandbox/index.d.ts.map +1 -1
- package/lib/sandbox/index.js.map +1 -1
- package/lib/sandbox/index.mjs.map +1 -1
- package/lib/sandbox/mounts.d.mts +3 -2
- package/lib/sandbox/mounts.d.mts.map +1 -1
- package/lib/sandbox/mounts.d.ts +3 -2
- package/lib/sandbox/mounts.d.ts.map +1 -1
- package/lib/sandbox/mounts.js +6 -1
- package/lib/sandbox/mounts.js.map +1 -1
- package/lib/sandbox/mounts.mjs +6 -1
- package/lib/sandbox/mounts.mjs.map +1 -1
- package/lib/sandbox/types.d.mts +7 -0
- package/lib/sandbox/types.d.mts.map +1 -1
- package/lib/sandbox/types.d.ts +7 -0
- package/lib/sandbox/types.d.ts.map +1 -1
- package/lib/sandbox/verify.d.mts.map +1 -1
- package/lib/sandbox/verify.d.ts.map +1 -1
- package/lib/sandbox/verify.js +81 -19
- package/lib/sandbox/verify.js.map +1 -1
- package/lib/sandbox/verify.mjs +81 -19
- package/lib/sandbox/verify.mjs.map +1 -1
- package/lib/utils/addressing.d.mts +1 -1
- package/lib/utils/addressing.d.mts.map +1 -1
- package/lib/utils/addressing.d.ts +1 -1
- package/lib/utils/addressing.d.ts.map +1 -1
- package/lib/utils/addressing.js +4 -4
- package/lib/utils/addressing.js.map +1 -1
- package/lib/utils/addressing.mjs +5 -5
- package/lib/utils/addressing.mjs.map +1 -1
- package/package.json +22 -10
- package/resources/annotation-queues/annotation-queues.d.mts +33 -2
- package/resources/annotation-queues/annotation-queues.d.mts.map +1 -1
- package/resources/annotation-queues/annotation-queues.d.ts +33 -2
- package/resources/annotation-queues/annotation-queues.d.ts.map +1 -1
- package/resources/annotation-queues/annotation-queues.js.map +1 -1
- package/resources/annotation-queues/annotation-queues.mjs.map +1 -1
- package/resources/runs/runs.d.mts +14 -1
- package/resources/runs/runs.d.mts.map +1 -1
- package/resources/runs/runs.d.ts +14 -1
- package/resources/runs/runs.d.ts.map +1 -1
- package/resources/runs/runs.js +4 -0
- package/resources/runs/runs.js.map +1 -1
- package/resources/runs/runs.mjs +4 -0
- package/resources/runs/runs.mjs.map +1 -1
- package/resources/sandboxes/boxes.d.mts +32 -0
- package/resources/sandboxes/boxes.d.mts.map +1 -1
- package/resources/sandboxes/boxes.d.ts +32 -0
- package/resources/sandboxes/boxes.d.ts.map +1 -1
- package/resources/sandboxes/sandboxes.d.mts +32 -0
- package/resources/sandboxes/sandboxes.d.mts.map +1 -1
- package/resources/sandboxes/sandboxes.d.ts +32 -0
- package/resources/sandboxes/sandboxes.d.ts.map +1 -1
- package/resources/sandboxes/sandboxes.js.map +1 -1
- package/resources/sandboxes/sandboxes.mjs.map +1 -1
- package/resources/threads/threads.d.mts +13 -1
- package/resources/threads/threads.d.mts.map +1 -1
- package/resources/threads/threads.d.ts +13 -1
- package/resources/threads/threads.d.ts.map +1 -1
- package/resources/threads/threads.js +4 -0
- package/resources/threads/threads.js.map +1 -1
- package/resources/threads/threads.mjs +4 -0
- package/resources/threads/threads.mjs.map +1 -1
- package/resources/traces.d.mts +34 -16
- package/resources/traces.d.mts.map +1 -1
- package/resources/traces.d.ts +34 -16
- package/resources/traces.d.ts.map +1 -1
- package/resources/traces.js +7 -1
- package/resources/traces.js.map +1 -1
- package/resources/traces.mjs +7 -1
- package/resources/traces.mjs.map +1 -1
- package/src/lib/address.ts +138 -180
- package/src/lib/client.ts +4 -5
- package/src/lib/run_trees.ts +8 -5
- package/src/lib/sandbox/README.md +3 -2
- package/src/lib/sandbox/index.ts +1 -0
- package/src/lib/sandbox/mounts.ts +8 -0
- package/src/lib/sandbox/types.ts +8 -0
- package/src/lib/sandbox/verify.ts +107 -28
- package/src/lib/utils/addressing.ts +8 -5
- package/src/resources/annotation-queues/annotation-queues.ts +46 -2
- package/src/resources/runs/runs.ts +15 -1
- package/src/resources/sandboxes/boxes.ts +40 -0
- package/src/resources/sandboxes/sandboxes.ts +40 -0
- package/src/resources/threads/threads.ts +14 -1
- package/src/resources/traces.ts +38 -16
- package/src/version.ts +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
package/src/lib/client.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { addressToLrn } from "./address.js";
|
|
1
2
|
import * as uuid from "./utils/uuid/src/index.js";
|
|
2
3
|
import { getOpenAPIBaseUrl } from "./utils/api_url.js";
|
|
3
4
|
import type { OTELContext } from "./experimental/otel/types.js";
|
|
@@ -1861,7 +1862,7 @@ export class Client implements LangSmithTracingClientInterface {
|
|
|
1861
1862
|
|
|
1862
1863
|
/**
|
|
1863
1864
|
* (beta) Access the sessions resource, which resolves an address to the
|
|
1864
|
-
* project its traces go to: `client.sessions.resolve(address
|
|
1865
|
+
* project its traces go to: `client.sessions.resolve(address)`.
|
|
1865
1866
|
*/
|
|
1866
1867
|
public get sessions(): Sessions {
|
|
1867
1868
|
this._checkStainlessVersion("0.18.0");
|
|
@@ -3359,9 +3360,7 @@ export class Client implements LangSmithTracingClientInterface {
|
|
|
3359
3360
|
|
|
3360
3361
|
/** The ID of the project an address names, with one request. */
|
|
3361
3362
|
private async _resolveAddress(address: Address): Promise<string> {
|
|
3362
|
-
const { session_id: sessionId } = await this.sessions.resolve(
|
|
3363
|
-
address.toApiAddress(),
|
|
3364
|
-
);
|
|
3363
|
+
const { session_id: sessionId } = await this.sessions.resolve(address);
|
|
3365
3364
|
assertUuid(sessionId);
|
|
3366
3365
|
return sessionId;
|
|
3367
3366
|
}
|
|
@@ -5672,7 +5671,7 @@ export class Client implements LangSmithTracingClientInterface {
|
|
|
5672
5671
|
return feedback as Feedback;
|
|
5673
5672
|
}
|
|
5674
5673
|
const body = JSON.stringify(
|
|
5675
|
-
address ? { ...feedback, address: address
|
|
5674
|
+
address ? { ...feedback, address: addressToLrn(address) } : feedback,
|
|
5676
5675
|
);
|
|
5677
5676
|
const url = `${this.apiUrl}/feedback`;
|
|
5678
5677
|
await this.caller.call(async () => {
|
package/src/lib/run_trees.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { addressToLrn, addressFromLrn } from "./address.js";
|
|
1
2
|
import { Client } from "./client.js";
|
|
2
3
|
import { isEnvTracingEnabled } from "./env.js";
|
|
3
4
|
import {
|
|
@@ -33,7 +34,7 @@ import {
|
|
|
33
34
|
rejectConflicting,
|
|
34
35
|
resolveFromEnv,
|
|
35
36
|
} from "./utils/addressing.js";
|
|
36
|
-
import { AgentAddress, EnvAddressError, ensureAgent } from "./address.js";
|
|
37
|
+
import { type AgentAddress, EnvAddressError, ensureAgent } from "./address.js";
|
|
37
38
|
|
|
38
39
|
const TIMESTAMP_LENGTH = 36;
|
|
39
40
|
// DNS namespace for UUID v5 (same as Python's uuid.NAMESPACE_DNS)
|
|
@@ -197,7 +198,7 @@ const HEADER_SAFE_REPLICA_FIELDS = new Set([
|
|
|
197
198
|
/** Rebuilds an untrusted header address; `undefined` if unusable. */
|
|
198
199
|
function addressFromHeader(value: unknown): AgentAddress | undefined {
|
|
199
200
|
try {
|
|
200
|
-
return
|
|
201
|
+
return addressFromLrn(value);
|
|
201
202
|
} catch {
|
|
202
203
|
// Values are untrusted, so not logged.
|
|
203
204
|
console.warn("Ignoring an unusable address in a `baggage` header.");
|
|
@@ -301,7 +302,7 @@ class Baggage {
|
|
|
301
302
|
}
|
|
302
303
|
if (this.address) {
|
|
303
304
|
items.push(
|
|
304
|
-
`langsmith-address=${encodeURIComponent(this.address
|
|
305
|
+
`langsmith-address=${encodeURIComponent(addressToLrn(this.address))}`,
|
|
305
306
|
);
|
|
306
307
|
}
|
|
307
308
|
|
|
@@ -752,7 +753,8 @@ export class RunTree implements BaseRun {
|
|
|
752
753
|
primary === undefined &&
|
|
753
754
|
!this.replicas?.some((r) => r.primary === true) &&
|
|
754
755
|
projectName === this.project_name &&
|
|
755
|
-
address
|
|
756
|
+
(address ? addressToLrn(address) : undefined) ===
|
|
757
|
+
(this.address ? addressToLrn(this.address) : undefined)
|
|
756
758
|
) {
|
|
757
759
|
return {
|
|
758
760
|
...baseRun,
|
|
@@ -760,7 +762,8 @@ export class RunTree implements BaseRun {
|
|
|
760
762
|
address,
|
|
761
763
|
};
|
|
762
764
|
}
|
|
763
|
-
const seed =
|
|
765
|
+
const seed =
|
|
766
|
+
projectName ?? (address ? addressToLrn(address) : undefined) ?? "agent//";
|
|
764
767
|
|
|
765
768
|
// Apply reroot logic before ID remapping
|
|
766
769
|
if (reroot) {
|
|
@@ -679,8 +679,9 @@ try {
|
|
|
679
679
|
|
|
680
680
|
## Verifying Requests from LangSmith
|
|
681
681
|
|
|
682
|
-
`SandboxTokenVerifier` checks
|
|
683
|
-
|
|
682
|
+
`SandboxTokenVerifier` checks signatures against LangSmith's JWKS (derived from
|
|
683
|
+
`LANGSMITH_ENDPOINT`, cached); user tokens may use any JOSE asymmetric algorithm
|
|
684
|
+
(RS*, PS*, ES\*, EdDSA), chosen by the JWKS key. It uses Web Crypto, so it needs a
|
|
684
685
|
runtime with Ed25519 support (Node.js 20+, Deno, Bun, Cloudflare Workers). The
|
|
685
686
|
JWKS must be served over HTTPS (localhost is exempt); for a plain-HTTP
|
|
686
687
|
self-hosted deployment on a trusted network, pass `allowInsecureJwks: true`.
|
package/src/lib/sandbox/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
GitMountRefSpec,
|
|
5
5
|
GitMountSpec,
|
|
6
6
|
MountCacheConfig,
|
|
7
|
+
MountEncryptionConfig,
|
|
7
8
|
SandboxAwsAuthRule,
|
|
8
9
|
SandboxGcpAuthRule,
|
|
9
10
|
S3MountSpec,
|
|
@@ -98,6 +99,7 @@ export function s3Mount({
|
|
|
98
99
|
pathStyle = false,
|
|
99
100
|
readOnly,
|
|
100
101
|
cache,
|
|
102
|
+
encryption,
|
|
101
103
|
}: {
|
|
102
104
|
id: string;
|
|
103
105
|
mountPath: string;
|
|
@@ -108,6 +110,7 @@ export function s3Mount({
|
|
|
108
110
|
pathStyle?: boolean;
|
|
109
111
|
readOnly?: boolean;
|
|
110
112
|
cache?: MountCacheConfig;
|
|
113
|
+
encryption?: MountEncryptionConfig;
|
|
111
114
|
}): S3MountSpec {
|
|
112
115
|
const mount: S3MountSpec = {
|
|
113
116
|
id: requireNonEmptyString(id, "id"),
|
|
@@ -129,6 +132,11 @@ export function s3Mount({
|
|
|
129
132
|
if (cache !== undefined) {
|
|
130
133
|
mount.cache = { ...cache };
|
|
131
134
|
}
|
|
135
|
+
if (encryption !== undefined) {
|
|
136
|
+
mount.encryption = {
|
|
137
|
+
kms_key: requireNonEmptyString(encryption.kms_key, "encryption.kms_key"),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
132
140
|
return mount;
|
|
133
141
|
}
|
|
134
142
|
|
package/src/lib/sandbox/types.ts
CHANGED
|
@@ -603,10 +603,18 @@ export interface S3MountConfig {
|
|
|
603
603
|
path_style?: boolean;
|
|
604
604
|
}
|
|
605
605
|
|
|
606
|
+
/** Customer-managed key that uploads through a mount are encrypted with. */
|
|
607
|
+
export interface MountEncryptionConfig {
|
|
608
|
+
/** KMS key ARN in the mount's region; uploads use SSE-KMS with this key. */
|
|
609
|
+
kms_key: string;
|
|
610
|
+
}
|
|
611
|
+
|
|
606
612
|
/** S3-backed sandbox mount specification. */
|
|
607
613
|
export interface S3MountSpec extends SandboxBucketMountBase {
|
|
608
614
|
/** Mount type. */
|
|
609
615
|
type: "s3";
|
|
616
|
+
/** Optional customer-managed key for uploads. */
|
|
617
|
+
encryption?: MountEncryptionConfig;
|
|
610
618
|
/** S3 mount configuration. */
|
|
611
619
|
s3: S3MountConfig;
|
|
612
620
|
}
|
|
@@ -99,7 +99,72 @@ interface Jwk {
|
|
|
99
99
|
kty?: string;
|
|
100
100
|
crv?: string;
|
|
101
101
|
kid?: string;
|
|
102
|
+
alg?: string;
|
|
102
103
|
x?: string;
|
|
104
|
+
y?: string;
|
|
105
|
+
n?: string;
|
|
106
|
+
e?: string;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
interface VerificationKey {
|
|
110
|
+
alg: string;
|
|
111
|
+
key: CryptoKey;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
interface AlgorithmSpec {
|
|
115
|
+
kty: string;
|
|
116
|
+
crv?: string;
|
|
117
|
+
importParams: AlgorithmIdentifier | RsaHashedImportParams | EcKeyImportParams;
|
|
118
|
+
verifyParams: AlgorithmIdentifier | RsaPssParams | EcdsaParams;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Every JOSE asymmetric algorithm; the JWKS key picks which one a token may use.
|
|
122
|
+
const ALGORITHMS: Record<string, AlgorithmSpec> = {
|
|
123
|
+
EdDSA: {
|
|
124
|
+
kty: "OKP",
|
|
125
|
+
crv: "Ed25519",
|
|
126
|
+
importParams: { name: "Ed25519" },
|
|
127
|
+
verifyParams: { name: "Ed25519" },
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
for (const [bits, curve] of [
|
|
131
|
+
[256, "P-256"],
|
|
132
|
+
[384, "P-384"],
|
|
133
|
+
[512, "P-521"],
|
|
134
|
+
] as const) {
|
|
135
|
+
const hash = `SHA-${bits}`;
|
|
136
|
+
ALGORITHMS[`RS${bits}`] = {
|
|
137
|
+
kty: "RSA",
|
|
138
|
+
importParams: { name: "RSASSA-PKCS1-v1_5", hash },
|
|
139
|
+
verifyParams: { name: "RSASSA-PKCS1-v1_5" },
|
|
140
|
+
};
|
|
141
|
+
// JWS RSA-PSS uses a salt as long as the hash.
|
|
142
|
+
ALGORITHMS[`PS${bits}`] = {
|
|
143
|
+
kty: "RSA",
|
|
144
|
+
importParams: { name: "RSA-PSS", hash },
|
|
145
|
+
verifyParams: { name: "RSA-PSS", saltLength: bits / 8 },
|
|
146
|
+
};
|
|
147
|
+
// The JWS ECDSA signature is raw r||s, which Web Crypto verifies directly.
|
|
148
|
+
ALGORITHMS[`ES${bits}`] = {
|
|
149
|
+
kty: "EC",
|
|
150
|
+
crv: curve,
|
|
151
|
+
importParams: { name: "ECDSA", namedCurve: curve },
|
|
152
|
+
verifyParams: { name: "ECDSA", hash },
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const USER_TOKEN_ALGORITHMS = Object.keys(ALGORITHMS);
|
|
157
|
+
const CALLBACK_ALGORITHMS = ["EdDSA"];
|
|
158
|
+
|
|
159
|
+
/** A JWK's algorithm: its `alg` member if supported, else derived from its key type. */
|
|
160
|
+
function jwkAlgorithm(jwk: Jwk): string | undefined {
|
|
161
|
+
if (jwk.alg !== undefined) {
|
|
162
|
+
return USER_TOKEN_ALGORITHMS.includes(jwk.alg) ? jwk.alg : undefined;
|
|
163
|
+
}
|
|
164
|
+
if (jwk.kty === "RSA") return "RS256";
|
|
165
|
+
return USER_TOKEN_ALGORITHMS.find(
|
|
166
|
+
(alg) => ALGORITHMS[alg].kty === jwk.kty && ALGORITHMS[alg].crv === jwk.crv,
|
|
167
|
+
);
|
|
103
168
|
}
|
|
104
169
|
|
|
105
170
|
function fail(message: string): never {
|
|
@@ -202,7 +267,7 @@ function timingSafeEqual(a: string, b: string): boolean {
|
|
|
202
267
|
export class SandboxTokenVerifier {
|
|
203
268
|
private readonly jwksUrl: string;
|
|
204
269
|
private readonly timeoutMs: number;
|
|
205
|
-
private keys = new Map<string,
|
|
270
|
+
private keys = new Map<string, VerificationKey>();
|
|
206
271
|
private fetchedAt = 0;
|
|
207
272
|
private inflight?: Promise<void>;
|
|
208
273
|
|
|
@@ -233,6 +298,7 @@ export class SandboxTokenVerifier {
|
|
|
233
298
|
): Promise<SandboxUser> {
|
|
234
299
|
const claims = await this.verify(
|
|
235
300
|
token,
|
|
301
|
+
USER_TOKEN_ALGORITHMS,
|
|
236
302
|
serviceHost(options.audience),
|
|
237
303
|
options.issuer,
|
|
238
304
|
);
|
|
@@ -260,6 +326,7 @@ export class SandboxTokenVerifier {
|
|
|
260
326
|
): Promise<SandboxCallback> {
|
|
261
327
|
const claims = await this.verify(
|
|
262
328
|
options.signature,
|
|
329
|
+
CALLBACK_ALGORITHMS,
|
|
263
330
|
options.aud,
|
|
264
331
|
options.issuer,
|
|
265
332
|
);
|
|
@@ -293,6 +360,7 @@ export class SandboxTokenVerifier {
|
|
|
293
360
|
|
|
294
361
|
private async verify(
|
|
295
362
|
token: string,
|
|
363
|
+
algorithms: string[],
|
|
296
364
|
audience: AudienceMatcher | undefined,
|
|
297
365
|
issuer: string | undefined,
|
|
298
366
|
): Promise<Claims> {
|
|
@@ -308,14 +376,19 @@ export class SandboxTokenVerifier {
|
|
|
308
376
|
} catch (e) {
|
|
309
377
|
fail(`malformed token: ${(e as Error).message}`);
|
|
310
378
|
}
|
|
311
|
-
if (header.alg
|
|
379
|
+
if (!algorithms.includes(header.alg as string)) {
|
|
312
380
|
fail(`unexpected signing algorithm: ${JSON.stringify(header.alg)}`);
|
|
313
381
|
}
|
|
314
382
|
if (typeof header.kid !== "string" || !header.kid) fail("token has no kid");
|
|
315
383
|
|
|
316
|
-
const key = await this.key(header.kid);
|
|
384
|
+
const { alg, key } = await this.key(header.kid);
|
|
385
|
+
// The key belongs to one algorithm; the token must claim that one.
|
|
386
|
+
if (header.alg !== alg) {
|
|
387
|
+
fail(`unexpected signing algorithm: ${JSON.stringify(header.alg)}`);
|
|
388
|
+
}
|
|
317
389
|
const signed = new TextEncoder().encode(`${parts[0]}.${parts[1]}`);
|
|
318
|
-
|
|
390
|
+
const { verifyParams } = ALGORITHMS[alg];
|
|
391
|
+
if (!(await subtle().verify(verifyParams, key, signature, signed))) {
|
|
319
392
|
fail("invalid signature");
|
|
320
393
|
}
|
|
321
394
|
|
|
@@ -347,7 +420,7 @@ export class SandboxTokenVerifier {
|
|
|
347
420
|
return claims;
|
|
348
421
|
}
|
|
349
422
|
|
|
350
|
-
private async key(kid: string): Promise<
|
|
423
|
+
private async key(kid: string): Promise<VerificationKey> {
|
|
351
424
|
const age = Date.now() - this.fetchedAt;
|
|
352
425
|
const fresh = this.keys.has(kid) && age < JWKS_TTL_MS;
|
|
353
426
|
if (!fresh && (this.fetchedAt === 0 || age >= JWKS_MIN_REFRESH_MS)) {
|
|
@@ -356,17 +429,9 @@ export class SandboxTokenVerifier {
|
|
|
356
429
|
});
|
|
357
430
|
await this.inflight;
|
|
358
431
|
}
|
|
359
|
-
const
|
|
360
|
-
if (!
|
|
361
|
-
|
|
362
|
-
return await key;
|
|
363
|
-
} catch (e) {
|
|
364
|
-
fail(
|
|
365
|
-
`signing key ${kid} is not a usable Ed25519 key: ${
|
|
366
|
-
(e as Error).message
|
|
367
|
-
}`,
|
|
368
|
-
);
|
|
369
|
-
}
|
|
432
|
+
const entry = this.keys.get(kid);
|
|
433
|
+
if (!entry) fail(`unknown signing key: ${kid}`);
|
|
434
|
+
return entry;
|
|
370
435
|
}
|
|
371
436
|
|
|
372
437
|
private async refresh(): Promise<void> {
|
|
@@ -386,20 +451,34 @@ export class SandboxTokenVerifier {
|
|
|
386
451
|
if (!Array.isArray(entries)) {
|
|
387
452
|
fail("invalid JWKS: expected an object with a keys array");
|
|
388
453
|
}
|
|
389
|
-
const keys = new Map<string,
|
|
454
|
+
const keys = new Map<string, VerificationKey>();
|
|
455
|
+
const webCrypto = subtle();
|
|
390
456
|
for (const jwk of entries as (Jwk | null)[]) {
|
|
391
|
-
if (typeof jwk !== "object" || jwk === null) continue;
|
|
392
|
-
|
|
457
|
+
if (typeof jwk !== "object" || jwk === null || !jwk.kid) continue;
|
|
458
|
+
const alg = jwkAlgorithm(jwk);
|
|
459
|
+
if (!alg) continue;
|
|
460
|
+
const { kty, crv, importParams } = ALGORITHMS[alg];
|
|
461
|
+
if (jwk.kty !== kty || jwk.crv !== crv) continue;
|
|
462
|
+
try {
|
|
463
|
+
// Import with the key's own algorithm: Web Crypto fixes the RSA hash here.
|
|
464
|
+
const key = await webCrypto.importKey(
|
|
465
|
+
"jwk",
|
|
466
|
+
{
|
|
467
|
+
kty: jwk.kty,
|
|
468
|
+
crv: jwk.crv,
|
|
469
|
+
x: jwk.x,
|
|
470
|
+
y: jwk.y,
|
|
471
|
+
n: jwk.n,
|
|
472
|
+
e: jwk.e,
|
|
473
|
+
},
|
|
474
|
+
importParams,
|
|
475
|
+
false,
|
|
476
|
+
["verify"],
|
|
477
|
+
);
|
|
478
|
+
keys.set(jwk.kid, { alg, key });
|
|
479
|
+
} catch {
|
|
393
480
|
continue;
|
|
394
|
-
|
|
395
|
-
"jwk",
|
|
396
|
-
{ kty: "OKP", crv: "Ed25519", x: jwk.x },
|
|
397
|
-
{ name: "Ed25519" },
|
|
398
|
-
false,
|
|
399
|
-
["verify"],
|
|
400
|
-
);
|
|
401
|
-
imported.catch(() => undefined);
|
|
402
|
-
keys.set(jwk.kid, imported);
|
|
481
|
+
}
|
|
403
482
|
}
|
|
404
483
|
this.keys = keys;
|
|
405
484
|
this.fetchedAt = Date.now();
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
-
AgentAddress,
|
|
2
|
+
type AgentAddress,
|
|
3
|
+
addressFromEnv,
|
|
4
|
+
addressFromLrn,
|
|
5
|
+
addressToLrn,
|
|
3
6
|
EnvAddressError,
|
|
4
7
|
ensureAgent,
|
|
5
8
|
envNames,
|
|
@@ -47,7 +50,7 @@ function getEnvProject(): string | undefined {
|
|
|
47
50
|
*/
|
|
48
51
|
export function resolveFromEnv(): Tier {
|
|
49
52
|
const project = getEnvProject();
|
|
50
|
-
const address =
|
|
53
|
+
const address = addressFromEnv();
|
|
51
54
|
if (project && address) {
|
|
52
55
|
throw new EnvAddressError(
|
|
53
56
|
"LANGSMITH_AGENT_* and a project are both set in the environment.",
|
|
@@ -92,7 +95,7 @@ export function applyToPayload(
|
|
|
92
95
|
// is sent. It is parsed again, so a malformed one cannot fail the whole
|
|
93
96
|
// batch in the backend.
|
|
94
97
|
try {
|
|
95
|
-
address =
|
|
98
|
+
address = addressFromLrn(payload.address);
|
|
96
99
|
} catch {
|
|
97
100
|
throw new Error("The run's `address` is not a valid agent address.");
|
|
98
101
|
}
|
|
@@ -103,7 +106,7 @@ export function applyToPayload(
|
|
|
103
106
|
const namedProject =
|
|
104
107
|
payload.session_id != null || payload.session_name != null;
|
|
105
108
|
if (!address && !update && !namedProject) {
|
|
106
|
-
address =
|
|
109
|
+
address = addressFromEnv();
|
|
107
110
|
}
|
|
108
111
|
if (!address) {
|
|
109
112
|
return;
|
|
@@ -112,7 +115,7 @@ export function applyToPayload(
|
|
|
112
115
|
"Sending runs to an `address` is in beta and enabled per workspace; a " +
|
|
113
116
|
"workspace without it rejects the runs.",
|
|
114
117
|
);
|
|
115
|
-
payload.address = address
|
|
118
|
+
payload.address = addressToLrn(address);
|
|
116
119
|
if (!namedProject) {
|
|
117
120
|
delete payload.session_name;
|
|
118
121
|
delete payload.session_id;
|
|
@@ -194,6 +194,8 @@ export interface AnnotationQueueRubricItemSchema {
|
|
|
194
194
|
|
|
195
195
|
description?: string | null;
|
|
196
196
|
|
|
197
|
+
feedback_config?: AnnotationQueueRubricItemSchema.FeedbackConfig | null;
|
|
198
|
+
|
|
197
199
|
feedback_config_id?: string | null;
|
|
198
200
|
|
|
199
201
|
is_assertion?: boolean | null;
|
|
@@ -207,6 +209,48 @@ export interface AnnotationQueueRubricItemSchema {
|
|
|
207
209
|
value_descriptions?: { [key: string]: string } | null;
|
|
208
210
|
}
|
|
209
211
|
|
|
212
|
+
export namespace AnnotationQueueRubricItemSchema {
|
|
213
|
+
export interface FeedbackConfig {
|
|
214
|
+
id: string;
|
|
215
|
+
|
|
216
|
+
feedback_config: FeedbackConfig.FeedbackConfig;
|
|
217
|
+
|
|
218
|
+
feedback_key: string;
|
|
219
|
+
|
|
220
|
+
modified_at: string;
|
|
221
|
+
|
|
222
|
+
tenant_id: string;
|
|
223
|
+
|
|
224
|
+
is_lower_score_better?: boolean | null;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export namespace FeedbackConfig {
|
|
228
|
+
export interface FeedbackConfig {
|
|
229
|
+
/**
|
|
230
|
+
* Enum for feedback types.
|
|
231
|
+
*/
|
|
232
|
+
type: 'continuous' | 'categorical' | 'freeform';
|
|
233
|
+
|
|
234
|
+
categories?: Array<FeedbackConfig.Category> | null;
|
|
235
|
+
|
|
236
|
+
max?: number | null;
|
|
237
|
+
|
|
238
|
+
min?: number | null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export namespace FeedbackConfig {
|
|
242
|
+
/**
|
|
243
|
+
* Specific value and label pair for feedback
|
|
244
|
+
*/
|
|
245
|
+
export interface Category {
|
|
246
|
+
value: number;
|
|
247
|
+
|
|
248
|
+
label?: string | null;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
210
254
|
/**
|
|
211
255
|
* AnnotationQueue schema.
|
|
212
256
|
*/
|
|
@@ -519,7 +563,7 @@ export interface AnnotationQueueUpdateParams {
|
|
|
519
563
|
|
|
520
564
|
description?: string | null;
|
|
521
565
|
|
|
522
|
-
enable_reservations?: boolean;
|
|
566
|
+
enable_reservations?: boolean | DatasetsAPI.Missing;
|
|
523
567
|
|
|
524
568
|
metadata?: { [key: string]: unknown } | DatasetsAPI.Missing | null;
|
|
525
569
|
|
|
@@ -527,7 +571,7 @@ export interface AnnotationQueueUpdateParams {
|
|
|
527
571
|
|
|
528
572
|
num_reviewers_per_item?: number | DatasetsAPI.Missing | null;
|
|
529
573
|
|
|
530
|
-
reservation_minutes?: number | null;
|
|
574
|
+
reservation_minutes?: number | DatasetsAPI.Missing | null;
|
|
531
575
|
|
|
532
576
|
reviewer_access_mode?: 'any' | 'assigned' | null;
|
|
533
577
|
|
|
@@ -41,6 +41,10 @@ export class Runs extends APIResource {
|
|
|
41
41
|
* start_time. Supports filters, cursor pagination, and `selects` to select fields
|
|
42
42
|
* to return.
|
|
43
43
|
*
|
|
44
|
+
* When `ai_search` is set, `Accept: text/event-stream` is required; requests
|
|
45
|
+
* without it return 406. AI search is unavailable on deployments that route
|
|
46
|
+
* queries to the v1 backend and returns 501 there.
|
|
47
|
+
*
|
|
44
48
|
* Self-hosted deployments require LangSmith `v0.16` or later.
|
|
45
49
|
*
|
|
46
50
|
* @example
|
|
@@ -872,6 +876,15 @@ export interface RunGetURLParams {
|
|
|
872
876
|
}
|
|
873
877
|
|
|
874
878
|
export interface RunQueryV2Params extends ItemsCursorPostPaginationParams {
|
|
879
|
+
/**
|
|
880
|
+
* Body param: `ai_search` is a plain-language criterion evaluated against the
|
|
881
|
+
* messages from the agent trajectory scoped to the run. AND-ed with the ordinary
|
|
882
|
+
* filters. Requires semantic filtering enabled for the deployment. Must contain
|
|
883
|
+
* nonempty text of at most 2000 UTF-8 bytes. Not supported on public dataset
|
|
884
|
+
* queries.
|
|
885
|
+
*/
|
|
886
|
+
ai_search?: string;
|
|
887
|
+
|
|
875
888
|
/**
|
|
876
889
|
* Body param: `filter` narrows results to runs matching this LangSmith filter
|
|
877
890
|
* expression, evaluated against each individual run. For example: and(eq(run_type,
|
|
@@ -972,7 +985,8 @@ export interface RunQueryV2Params extends ItemsCursorPostPaginationParams {
|
|
|
972
985
|
tree_filter?: string;
|
|
973
986
|
|
|
974
987
|
/**
|
|
975
|
-
* Header param: application/json or text/event-stream
|
|
988
|
+
* Header param: application/json, or text/event-stream (required when ai_search is
|
|
989
|
+
* set)
|
|
976
990
|
*/
|
|
977
991
|
Accept?: string;
|
|
978
992
|
}
|
|
@@ -404,6 +404,8 @@ export namespace BoxCreateParams {
|
|
|
404
404
|
|
|
405
405
|
contexthub?: SandboxapiS3BucketMountSpec.Contexthub;
|
|
406
406
|
|
|
407
|
+
encryption?: SandboxapiS3BucketMountSpec.Encryption;
|
|
408
|
+
|
|
407
409
|
gcs?: SandboxapiS3BucketMountSpec.Gcs;
|
|
408
410
|
|
|
409
411
|
git?: SandboxapiS3BucketMountSpec.Git;
|
|
@@ -445,6 +447,14 @@ export namespace BoxCreateParams {
|
|
|
445
447
|
initial_pull_only?: boolean;
|
|
446
448
|
}
|
|
447
449
|
|
|
450
|
+
export interface Encryption {
|
|
451
|
+
/**
|
|
452
|
+
* KMSKey is the key to encrypt uploads with. S3 mounts take a KMS key ARN in the
|
|
453
|
+
* mount's region.
|
|
454
|
+
*/
|
|
455
|
+
kms_key: string;
|
|
456
|
+
}
|
|
457
|
+
|
|
448
458
|
export interface Gcs {
|
|
449
459
|
bucket: string;
|
|
450
460
|
|
|
@@ -481,6 +491,8 @@ export namespace BoxCreateParams {
|
|
|
481
491
|
|
|
482
492
|
contexthub?: SandboxapiGcsBucketMountSpec.Contexthub;
|
|
483
493
|
|
|
494
|
+
encryption?: SandboxapiGcsBucketMountSpec.Encryption;
|
|
495
|
+
|
|
484
496
|
git?: SandboxapiGcsBucketMountSpec.Git;
|
|
485
497
|
|
|
486
498
|
read_only?: boolean;
|
|
@@ -516,6 +528,14 @@ export namespace BoxCreateParams {
|
|
|
516
528
|
initial_pull_only?: boolean;
|
|
517
529
|
}
|
|
518
530
|
|
|
531
|
+
export interface Encryption {
|
|
532
|
+
/**
|
|
533
|
+
* KMSKey is the key to encrypt uploads with. S3 mounts take a KMS key ARN in the
|
|
534
|
+
* mount's region.
|
|
535
|
+
*/
|
|
536
|
+
kms_key: string;
|
|
537
|
+
}
|
|
538
|
+
|
|
519
539
|
export interface Git {
|
|
520
540
|
remote_url: string;
|
|
521
541
|
|
|
@@ -558,6 +578,8 @@ export namespace BoxCreateParams {
|
|
|
558
578
|
|
|
559
579
|
contexthub?: SandboxapiGitRepoMountSpec.Contexthub;
|
|
560
580
|
|
|
581
|
+
encryption?: SandboxapiGitRepoMountSpec.Encryption;
|
|
582
|
+
|
|
561
583
|
gcs?: SandboxapiGitRepoMountSpec.Gcs;
|
|
562
584
|
|
|
563
585
|
read_only?: boolean;
|
|
@@ -603,6 +625,14 @@ export namespace BoxCreateParams {
|
|
|
603
625
|
initial_pull_only?: boolean;
|
|
604
626
|
}
|
|
605
627
|
|
|
628
|
+
export interface Encryption {
|
|
629
|
+
/**
|
|
630
|
+
* KMSKey is the key to encrypt uploads with. S3 mounts take a KMS key ARN in the
|
|
631
|
+
* mount's region.
|
|
632
|
+
*/
|
|
633
|
+
kms_key: string;
|
|
634
|
+
}
|
|
635
|
+
|
|
606
636
|
export interface Gcs {
|
|
607
637
|
bucket: string;
|
|
608
638
|
|
|
@@ -633,6 +663,8 @@ export namespace BoxCreateParams {
|
|
|
633
663
|
|
|
634
664
|
cache?: SandboxapiContextHubRepoMountSpec.Cache;
|
|
635
665
|
|
|
666
|
+
encryption?: SandboxapiContextHubRepoMountSpec.Encryption;
|
|
667
|
+
|
|
636
668
|
gcs?: SandboxapiContextHubRepoMountSpec.Gcs;
|
|
637
669
|
|
|
638
670
|
git?: SandboxapiContextHubRepoMountSpec.Git;
|
|
@@ -664,6 +696,14 @@ export namespace BoxCreateParams {
|
|
|
664
696
|
writeback_seconds?: number;
|
|
665
697
|
}
|
|
666
698
|
|
|
699
|
+
export interface Encryption {
|
|
700
|
+
/**
|
|
701
|
+
* KMSKey is the key to encrypt uploads with. S3 mounts take a KMS key ARN in the
|
|
702
|
+
* mount's region.
|
|
703
|
+
*/
|
|
704
|
+
kms_key: string;
|
|
705
|
+
}
|
|
706
|
+
|
|
667
707
|
export interface Gcs {
|
|
668
708
|
bucket: string;
|
|
669
709
|
|