nexus-ai-pro 2.3.0 → 2.5.0
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/API_STABILITY.md +271 -0
- package/CHANGELOG.md +330 -1
- package/README.md +18 -4
- package/SECURITY.md +209 -0
- package/dist/adapter-kit/contracts.d.ts +87 -0
- package/dist/adapter-kit/contracts.js +237 -0
- package/dist/adapter-kit/errors.d.ts +39 -0
- package/dist/adapter-kit/errors.js +52 -0
- package/dist/adapter-kit/index.d.ts +144 -0
- package/dist/adapter-kit/index.js +178 -0
- package/dist/adapter-kit/semver.d.ts +12 -0
- package/dist/adapter-kit/semver.js +55 -0
- package/dist/agent/middleware/pii.js +2 -33
- package/dist/context-hub/hub.d.ts +53 -0
- package/dist/context-hub/hub.js +108 -3
- package/dist/context-hub/index.d.ts +2 -1
- package/dist/context-hub/index.js +2 -1
- package/dist/context-hub/signing.d.ts +95 -0
- package/dist/context-hub/signing.js +88 -0
- package/dist/core/secure-stream.js +4 -3
- package/dist/doctor/index.d.ts +4 -2
- package/dist/doctor/index.js +29 -0
- package/dist/embeddings/adapters.js +2 -1
- package/dist/evals/judge.js +2 -5
- package/dist/evaluate/cache.d.ts +0 -14
- package/dist/evaluate/cache.js +0 -30
- package/dist/evaluate/file-cache.d.ts +15 -0
- package/dist/evaluate/file-cache.js +30 -0
- package/dist/evaluate/index.d.ts +2 -1
- package/dist/evaluate/index.js +2 -1
- package/dist/evaluate/review.js +2 -2
- package/dist/evaluate/run.js +2 -2
- package/dist/evaluate/threads.d.ts +187 -0
- package/dist/evaluate/threads.js +187 -0
- package/dist/evaluate/version.js +3 -3
- package/dist/graph/graph.js +2 -2
- package/dist/graph/node-cache.js +3 -3
- package/dist/hallucination/rag.d.ts +4 -1
- package/dist/hallucination/rag.js +6 -1
- package/dist/images/asset-stores.js +2 -3
- package/dist/images/asset-support.js +2 -2
- package/dist/images/assets.js +1 -2
- package/dist/images/evals.js +4 -4
- package/dist/insights/proposals.js +1 -2
- package/dist/insights/regressions.d.ts +26 -2
- package/dist/insights/regressions.js +129 -3
- package/dist/loaders/git.js +8 -1
- package/dist/loaders/web.js +7 -1
- package/dist/mcp/client.d.ts +14 -0
- package/dist/mcp/client.js +59 -1
- package/dist/mcp/registry.d.ts +6 -0
- package/dist/mcp/registry.js +7 -0
- package/dist/operations/adapters.d.ts +2 -1
- package/dist/operations/runner.js +2 -0
- package/dist/operations/stats.js +11 -4
- package/dist/postgres/index.d.ts +2 -1
- package/dist/postgres/index.js +4 -0
- package/dist/postgres/operations.js +11 -4
- package/dist/postgres/store.d.ts +7 -0
- package/dist/postgres/store.js +21 -0
- package/dist/postgres/tenancy.d.ts +43 -0
- package/dist/postgres/tenancy.js +71 -0
- package/dist/prompts/file.js +28 -11
- package/dist/protocols/a2a.d.ts +235 -0
- package/dist/protocols/a2a.js +386 -0
- package/dist/protocols/acp.d.ts +87 -0
- package/dist/protocols/acp.js +322 -0
- package/dist/protocols/ag-ui.d.ts +120 -0
- package/dist/protocols/ag-ui.js +180 -0
- package/dist/protocols/shared.d.ts +80 -0
- package/dist/protocols/shared.js +49 -0
- package/dist/providers/anthropic.js +1 -0
- package/dist/providers/cohere.js +2 -2
- package/dist/providers/google.js +2 -11
- package/dist/providers/openai.js +1 -0
- package/dist/rag/file-ingestion.js +15 -9
- package/dist/runtime/detect.d.ts +17 -0
- package/dist/runtime/detect.js +16 -0
- package/dist/runtime/index.d.ts +19 -0
- package/dist/runtime/index.js +7 -0
- package/dist/security/injection-detector.d.ts +2 -0
- package/dist/security/injection-detector.js +7 -1
- package/dist/security/input-guard.js +5 -2
- package/dist/security/output-guard.js +8 -4
- package/dist/security/pii-detector.js +22 -6
- package/dist/security/prompt-hardening.js +6 -2
- package/dist/security/semantic-injection-classifier.d.ts +2 -1
- package/dist/security/semantic-injection-classifier.js +31 -35
- package/dist/security/upload-scanner.d.ts +8 -0
- package/dist/security/upload-scanner.js +51 -9
- package/dist/server/deployments.d.ts +188 -11
- package/dist/server/deployments.js +474 -36
- package/dist/server/metrics.d.ts +2 -0
- package/dist/server/metrics.js +7 -0
- package/dist/server/runs.d.ts +18 -1
- package/dist/server/runs.js +66 -7
- package/dist/server/server.js +9 -1
- package/dist/server/state.d.ts +12 -2
- package/dist/server/state.js +15 -0
- package/dist/sqlite/operations.js +15 -4
- package/dist/sqlite/store.d.ts +7 -0
- package/dist/sqlite/store.js +28 -0
- package/dist/store/memory.d.ts +8 -0
- package/dist/store/memory.js +12 -1
- package/dist/store/redis.d.ts +13 -0
- package/dist/store/redis.js +38 -0
- package/dist/testing/record.js +2 -2
- package/dist/tracing/instrument.js +1 -0
- package/dist/tracing/stores.js +2 -1
- package/dist/tracing/tracer.d.ts +4 -0
- package/dist/tracing/tracer.js +8 -2
- package/dist/types/config.d.ts +20 -0
- package/dist/types/operations.d.ts +7 -0
- package/dist/types/server.d.ts +67 -2
- package/dist/types/store.d.ts +9 -0
- package/dist/utils/base64.d.ts +4 -0
- package/dist/utils/base64.js +21 -0
- package/dist/utils/checksums.d.ts +22 -0
- package/dist/utils/checksums.js +39 -0
- package/dist/utils/ids.d.ts +8 -0
- package/dist/utils/ids.js +26 -2
- package/dist/utils/sha256.d.ts +22 -0
- package/dist/utils/sha256.js +120 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist-cjs/adapter-kit/contracts.d.ts +87 -0
- package/dist-cjs/adapter-kit/contracts.js +243 -0
- package/dist-cjs/adapter-kit/errors.d.ts +39 -0
- package/dist-cjs/adapter-kit/errors.js +57 -0
- package/dist-cjs/adapter-kit/index.d.ts +144 -0
- package/dist-cjs/adapter-kit/index.js +194 -0
- package/dist-cjs/adapter-kit/semver.d.ts +12 -0
- package/dist-cjs/adapter-kit/semver.js +59 -0
- package/dist-cjs/agent/middleware/pii.js +4 -35
- package/dist-cjs/context-hub/hub.d.ts +53 -0
- package/dist-cjs/context-hub/hub.js +110 -4
- package/dist-cjs/context-hub/index.d.ts +2 -1
- package/dist-cjs/context-hub/index.js +7 -1
- package/dist-cjs/context-hub/signing.d.ts +95 -0
- package/dist-cjs/context-hub/signing.js +96 -0
- package/dist-cjs/core/secure-stream.js +4 -3
- package/dist-cjs/doctor/index.d.ts +4 -2
- package/dist-cjs/doctor/index.js +29 -0
- package/dist-cjs/embeddings/adapters.js +2 -1
- package/dist-cjs/evals/judge.js +2 -5
- package/dist-cjs/evaluate/cache.d.ts +0 -14
- package/dist-cjs/evaluate/cache.js +1 -65
- package/dist-cjs/evaluate/file-cache.d.ts +15 -0
- package/dist-cjs/evaluate/file-cache.js +67 -0
- package/dist-cjs/evaluate/index.d.ts +2 -1
- package/dist-cjs/evaluate/index.js +3 -2
- package/dist-cjs/evaluate/review.js +2 -2
- package/dist-cjs/evaluate/run.js +2 -2
- package/dist-cjs/evaluate/threads.d.ts +187 -0
- package/dist-cjs/evaluate/threads.js +198 -0
- package/dist-cjs/evaluate/version.js +3 -3
- package/dist-cjs/graph/graph.js +2 -2
- package/dist-cjs/graph/node-cache.js +3 -3
- package/dist-cjs/hallucination/rag.d.ts +4 -1
- package/dist-cjs/hallucination/rag.js +6 -1
- package/dist-cjs/images/asset-stores.js +2 -3
- package/dist-cjs/images/asset-support.js +2 -2
- package/dist-cjs/images/assets.js +1 -2
- package/dist-cjs/images/evals.js +4 -4
- package/dist-cjs/insights/proposals.js +1 -2
- package/dist-cjs/insights/regressions.d.ts +26 -2
- package/dist-cjs/insights/regressions.js +129 -3
- package/dist-cjs/loaders/git.js +8 -1
- package/dist-cjs/loaders/web.js +7 -1
- package/dist-cjs/mcp/client.d.ts +14 -0
- package/dist-cjs/mcp/client.js +60 -1
- package/dist-cjs/mcp/registry.d.ts +6 -0
- package/dist-cjs/mcp/registry.js +7 -0
- package/dist-cjs/operations/adapters.d.ts +2 -1
- package/dist-cjs/operations/runner.js +2 -0
- package/dist-cjs/operations/stats.js +11 -4
- package/dist-cjs/postgres/index.d.ts +2 -1
- package/dist-cjs/postgres/index.js +8 -1
- package/dist-cjs/postgres/operations.js +11 -4
- package/dist-cjs/postgres/store.d.ts +7 -0
- package/dist-cjs/postgres/store.js +21 -0
- package/dist-cjs/postgres/tenancy.d.ts +43 -0
- package/dist-cjs/postgres/tenancy.js +110 -0
- package/dist-cjs/prompts/file.js +28 -11
- package/dist-cjs/protocols/a2a.d.ts +235 -0
- package/dist-cjs/protocols/a2a.js +393 -0
- package/dist-cjs/protocols/acp.d.ts +87 -0
- package/dist-cjs/protocols/acp.js +326 -0
- package/dist-cjs/protocols/ag-ui.d.ts +120 -0
- package/dist-cjs/protocols/ag-ui.js +186 -0
- package/dist-cjs/protocols/shared.d.ts +80 -0
- package/dist-cjs/protocols/shared.js +59 -0
- package/dist-cjs/providers/anthropic.js +1 -0
- package/dist-cjs/providers/cohere.js +2 -2
- package/dist-cjs/providers/google.js +3 -12
- package/dist-cjs/providers/openai.js +1 -0
- package/dist-cjs/rag/file-ingestion.js +14 -8
- package/dist-cjs/runtime/detect.d.ts +17 -0
- package/dist-cjs/runtime/detect.js +19 -0
- package/dist-cjs/runtime/index.d.ts +19 -0
- package/dist-cjs/runtime/index.js +31 -0
- package/dist-cjs/security/injection-detector.d.ts +2 -0
- package/dist-cjs/security/injection-detector.js +7 -1
- package/dist-cjs/security/input-guard.js +5 -2
- package/dist-cjs/security/output-guard.js +8 -4
- package/dist-cjs/security/pii-detector.js +22 -6
- package/dist-cjs/security/prompt-hardening.js +6 -2
- package/dist-cjs/security/semantic-injection-classifier.d.ts +2 -1
- package/dist-cjs/security/semantic-injection-classifier.js +31 -35
- package/dist-cjs/security/upload-scanner.d.ts +8 -0
- package/dist-cjs/security/upload-scanner.js +53 -9
- package/dist-cjs/server/deployments.d.ts +188 -11
- package/dist-cjs/server/deployments.js +474 -36
- package/dist-cjs/server/metrics.d.ts +2 -0
- package/dist-cjs/server/metrics.js +8 -0
- package/dist-cjs/server/runs.d.ts +18 -1
- package/dist-cjs/server/runs.js +65 -6
- package/dist-cjs/server/server.js +9 -1
- package/dist-cjs/server/state.d.ts +12 -2
- package/dist-cjs/server/state.js +15 -0
- package/dist-cjs/sqlite/operations.js +15 -4
- package/dist-cjs/sqlite/store.d.ts +7 -0
- package/dist-cjs/sqlite/store.js +28 -0
- package/dist-cjs/store/memory.d.ts +8 -0
- package/dist-cjs/store/memory.js +12 -1
- package/dist-cjs/store/redis.d.ts +13 -0
- package/dist-cjs/store/redis.js +38 -0
- package/dist-cjs/testing/record.js +2 -2
- package/dist-cjs/tracing/instrument.js +1 -0
- package/dist-cjs/tracing/stores.js +2 -1
- package/dist-cjs/tracing/tracer.d.ts +4 -0
- package/dist-cjs/tracing/tracer.js +8 -2
- package/dist-cjs/types/config.d.ts +20 -0
- package/dist-cjs/types/operations.d.ts +7 -0
- package/dist-cjs/types/server.d.ts +67 -2
- package/dist-cjs/types/store.d.ts +9 -0
- package/dist-cjs/utils/base64.d.ts +4 -0
- package/dist-cjs/utils/base64.js +25 -0
- package/dist-cjs/utils/checksums.d.ts +22 -0
- package/dist-cjs/utils/checksums.js +46 -0
- package/dist-cjs/utils/ids.d.ts +8 -0
- package/dist-cjs/utils/ids.js +28 -2
- package/dist-cjs/utils/sha256.d.ts +22 -0
- package/dist-cjs/utils/sha256.js +125 -0
- package/dist-cjs/version.d.ts +1 -1
- package/dist-cjs/version.js +1 -1
- package/llms.txt +5 -2
- package/package.json +102 -6
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { PromptDefinition } from '../types/prompts.js';
|
|
2
|
+
/** A JSON Web Key, as Web Crypto exports one. */
|
|
3
|
+
export interface ContextJsonWebKey {
|
|
4
|
+
/** The key type: `OKP` for Ed25519. */
|
|
5
|
+
kty?: string;
|
|
6
|
+
/** The curve: `Ed25519`. */
|
|
7
|
+
crv?: string;
|
|
8
|
+
/** The public key, in base64url. */
|
|
9
|
+
x?: string;
|
|
10
|
+
/** The private key, in base64url, on a private key only. */
|
|
11
|
+
d?: string;
|
|
12
|
+
/** Anything else the exporter wrote. */
|
|
13
|
+
[field: string]: unknown;
|
|
14
|
+
}
|
|
15
|
+
/** A Web Crypto key, as `crypto.subtle` returns one. Any runtime's `CryptoKey` is one. */
|
|
16
|
+
export interface ContextCryptoKey {
|
|
17
|
+
/** Its algorithm. */
|
|
18
|
+
readonly algorithm: unknown;
|
|
19
|
+
/** What it may do. */
|
|
20
|
+
readonly usages: readonly string[];
|
|
21
|
+
}
|
|
22
|
+
/** One signature on a bundle version. Stored with it, and carried by its export. */
|
|
23
|
+
export interface ContextSignature {
|
|
24
|
+
/** The id of the key that signed, which a keyring looks up. */
|
|
25
|
+
keyId: string;
|
|
26
|
+
/** Always `Ed25519`. */
|
|
27
|
+
algorithm: 'Ed25519';
|
|
28
|
+
/**
|
|
29
|
+
* What was signed: `sha256:` and the full SHA-256, in hex, of the bundle's content and the content
|
|
30
|
+
* of every prompt it pins. A bundle's version is only the first 12 hex digits, enough to name a
|
|
31
|
+
* version but not to stand behind a signature.
|
|
32
|
+
*/
|
|
33
|
+
digest: string;
|
|
34
|
+
/** The signature over the digest, in base64url. */
|
|
35
|
+
signature: string;
|
|
36
|
+
/** ISO-8601 time it was signed. */
|
|
37
|
+
signedAt: string;
|
|
38
|
+
}
|
|
39
|
+
/** Signs bundle digests with a private key, under a key id. */
|
|
40
|
+
export interface ContextSigner {
|
|
41
|
+
/** The key's id, recorded on each signature so a keyring finds the public key. */
|
|
42
|
+
keyId: string;
|
|
43
|
+
/** Signs bytes. */
|
|
44
|
+
sign(data: Uint8Array): Promise<Uint8Array> | Uint8Array;
|
|
45
|
+
}
|
|
46
|
+
/** Verifies signatures by key id. A signature by a key the keyring does not hold never verifies. */
|
|
47
|
+
export interface ContextKeyring {
|
|
48
|
+
/** Whether `signature` is `keyId`'s signature over `data`. */
|
|
49
|
+
verify(keyId: string, data: Uint8Array, signature: Uint8Array): Promise<boolean> | boolean;
|
|
50
|
+
}
|
|
51
|
+
/** A new signing key, with both halves as JSON Web Keys to keep in a secret store. */
|
|
52
|
+
export interface ContextSigningKey {
|
|
53
|
+
/** The key's id. */
|
|
54
|
+
keyId: string;
|
|
55
|
+
/** Signs with the new key. */
|
|
56
|
+
signer: ContextSigner;
|
|
57
|
+
/** The public half, for every keyring that should trust it. */
|
|
58
|
+
publicKey: ContextJsonWebKey;
|
|
59
|
+
/** The private half. Keep it where the signer runs, and nowhere else. */
|
|
60
|
+
privateKey: ContextJsonWebKey;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Creates an Ed25519 signing key through Web Crypto, so on any runtime. The key id defaults to the
|
|
64
|
+
* first 16 hex digits of the public key's SHA-256, which is stable for the key and names nothing
|
|
65
|
+
* else.
|
|
66
|
+
*/
|
|
67
|
+
export declare function generateContextKey(keyId?: string): Promise<ContextSigningKey>;
|
|
68
|
+
/** A signer from an Ed25519 private key, as a JSON Web Key or a Web Crypto key. */
|
|
69
|
+
export declare function ed25519Signer(keyId: string, privateKey: ContextJsonWebKey | ContextCryptoKey): Promise<ContextSigner>;
|
|
70
|
+
/** A keyring of trusted Ed25519 public keys, by key id. */
|
|
71
|
+
export declare function ed25519Keyring(keys: Record<string, ContextJsonWebKey | ContextCryptoKey>): Promise<ContextKeyring>;
|
|
72
|
+
/** What a bundle signature covers: the bundle's versioned content, and its pinned prompts' content. */
|
|
73
|
+
export interface ContextSignedContent {
|
|
74
|
+
/** The bundle's name and the content its version covers. */
|
|
75
|
+
bundle: {
|
|
76
|
+
name: string;
|
|
77
|
+
description?: string;
|
|
78
|
+
prompts?: Record<string, {
|
|
79
|
+
name: string;
|
|
80
|
+
version: string;
|
|
81
|
+
}>;
|
|
82
|
+
instructions?: Record<string, string>;
|
|
83
|
+
tools?: unknown;
|
|
84
|
+
skills?: unknown;
|
|
85
|
+
config?: Record<string, unknown>;
|
|
86
|
+
};
|
|
87
|
+
/** Each pinned prompt's content, by role: what its own version covers. */
|
|
88
|
+
prompts: Record<string, Pick<PromptDefinition, 'name' | 'messages' | 'partials' | 'config' | 'defaults'>>;
|
|
89
|
+
}
|
|
90
|
+
/** The digest a signature covers, as `sha256:` and 64 hex digits. */
|
|
91
|
+
export declare function contextDigest(content: ContextSignedContent): Promise<string>;
|
|
92
|
+
/** Signs a digest. */
|
|
93
|
+
export declare function signDigest(signer: ContextSigner, digest: string, at: Date): Promise<ContextSignature>;
|
|
94
|
+
/** The first signature that is a trusted key's signature over exactly this digest, if any. */
|
|
95
|
+
export declare function verifiedBy(keyring: ContextKeyring, digest: string, signatures?: readonly ContextSignature[]): Promise<ContextSignature | undefined>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { canonicalJson } from '../prompts/version.js';
|
|
2
|
+
import { decodeBase64, encodeBase64 } from '../utils/base64.js';
|
|
3
|
+
const subtle = () => globalThis.crypto.subtle;
|
|
4
|
+
const ED25519 = { name: 'Ed25519' };
|
|
5
|
+
export async function generateContextKey(keyId) {
|
|
6
|
+
const pair = await subtle().generateKey(ED25519, true, ['sign', 'verify']);
|
|
7
|
+
const publicKey = await subtle().exportKey('jwk', pair.publicKey);
|
|
8
|
+
const privateKey = await subtle().exportKey('jwk', pair.privateKey);
|
|
9
|
+
const id = keyId ?? (await keyIdOf(publicKey));
|
|
10
|
+
return { keyId: id, signer: await ed25519Signer(id, pair.privateKey), publicKey, privateKey };
|
|
11
|
+
}
|
|
12
|
+
export async function ed25519Signer(keyId, privateKey) {
|
|
13
|
+
const key = isCryptoKey(privateKey)
|
|
14
|
+
? privateKey
|
|
15
|
+
: await subtle().importKey('jwk', privateKey, ED25519, false, ['sign']);
|
|
16
|
+
return { keyId, sign: async (data) => new Uint8Array(await subtle().sign(ED25519, key, data)) };
|
|
17
|
+
}
|
|
18
|
+
export async function ed25519Keyring(keys) {
|
|
19
|
+
const imported = new Map();
|
|
20
|
+
for (const [keyId, key] of Object.entries(keys)) {
|
|
21
|
+
imported.set(keyId, isCryptoKey(key) ? key : await subtle().importKey('jwk', key, ED25519, false, ['verify']));
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
async verify(keyId, data, signature) {
|
|
25
|
+
const key = imported.get(keyId);
|
|
26
|
+
if (!key)
|
|
27
|
+
return false;
|
|
28
|
+
try {
|
|
29
|
+
return await subtle().verify(ED25519, key, signature, data);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export async function contextDigest(content) {
|
|
38
|
+
const text = canonicalJson({ format: 'nexus-context-signature', formatVersion: 1, ...content });
|
|
39
|
+
const digest = await subtle().digest('SHA-256', new TextEncoder().encode(text));
|
|
40
|
+
return `sha256:${hexOf(new Uint8Array(digest))}`;
|
|
41
|
+
}
|
|
42
|
+
export async function signDigest(signer, digest, at) {
|
|
43
|
+
const signature = await signer.sign(new TextEncoder().encode(digest));
|
|
44
|
+
return {
|
|
45
|
+
keyId: signer.keyId,
|
|
46
|
+
algorithm: 'Ed25519',
|
|
47
|
+
digest,
|
|
48
|
+
signature: toBase64Url(signature),
|
|
49
|
+
signedAt: at.toISOString(),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export async function verifiedBy(keyring, digest, signatures = []) {
|
|
53
|
+
const data = new TextEncoder().encode(digest);
|
|
54
|
+
for (const signature of signatures) {
|
|
55
|
+
if (signature?.algorithm !== 'Ed25519' || signature.digest !== digest)
|
|
56
|
+
continue;
|
|
57
|
+
let bytes;
|
|
58
|
+
try {
|
|
59
|
+
bytes = fromBase64Url(signature.signature);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (await keyring.verify(signature.keyId, data, bytes))
|
|
65
|
+
return signature;
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
async function keyIdOf(publicKey) {
|
|
70
|
+
const digest = await subtle().digest('SHA-256', new TextEncoder().encode(String(publicKey.x)));
|
|
71
|
+
return hexOf(new Uint8Array(digest).subarray(0, 8));
|
|
72
|
+
}
|
|
73
|
+
function hexOf(bytes) {
|
|
74
|
+
let hex = '';
|
|
75
|
+
for (const byte of bytes)
|
|
76
|
+
hex += byte.toString(16).padStart(2, '0');
|
|
77
|
+
return hex;
|
|
78
|
+
}
|
|
79
|
+
function isCryptoKey(value) {
|
|
80
|
+
return typeof value.algorithm === 'object' && Array.isArray(value.usages);
|
|
81
|
+
}
|
|
82
|
+
function toBase64Url(bytes) {
|
|
83
|
+
return encodeBase64(bytes).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
84
|
+
}
|
|
85
|
+
function fromBase64Url(text) {
|
|
86
|
+
const padded = text.replace(/-/g, '+').replace(/_/g, '/');
|
|
87
|
+
return decodeBase64(padded + '='.repeat((4 - (padded.length % 4)) % 4));
|
|
88
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { utf8Length } from '../utils/ids.js';
|
|
1
2
|
import { redactSensitiveText } from '../security/output-guard.js';
|
|
2
3
|
const MAX_BUFFERED_STREAM_BYTES = 1_048_576;
|
|
3
4
|
const MAX_BUFFERED_STREAM_CHUNKS = 100_000;
|
|
@@ -21,13 +22,13 @@ export function protectStreamOutput(stream, security, requestSignal) {
|
|
|
21
22
|
}
|
|
22
23
|
if (chunk.type === 'text' && chunk.content) {
|
|
23
24
|
bufferedText += chunk.content;
|
|
24
|
-
bufferedBytes +=
|
|
25
|
+
bufferedBytes += utf8Length(chunk.content);
|
|
25
26
|
}
|
|
26
27
|
else if (chunk.type === 'error' && chunk.error) {
|
|
27
|
-
bufferedBytes +=
|
|
28
|
+
bufferedBytes += utf8Length(chunk.error);
|
|
28
29
|
}
|
|
29
30
|
else if (chunk.type === 'tool_call' && chunk.toolCall) {
|
|
30
|
-
bufferedBytes +=
|
|
31
|
+
bufferedBytes += utf8Length(chunk.toolCall.function.arguments);
|
|
31
32
|
bufferedToolCalls.push(chunk.toolCall);
|
|
32
33
|
}
|
|
33
34
|
if (bufferedBytes > MAX_BUFFERED_STREAM_BYTES) {
|
package/dist/doctor/index.d.ts
CHANGED
|
@@ -42,8 +42,10 @@ export interface DoctorDeployment {
|
|
|
42
42
|
replicas: number;
|
|
43
43
|
/**
|
|
44
44
|
* The stores and shared state it uses, by any name: a checkpointer, the operation store, the
|
|
45
|
-
* server's state, tenant usage, a rate-limit store, a circuit store, a cache. Each is
|
|
46
|
-
* it keeps its state in process memory, where each replica would see only its own
|
|
45
|
+
* server's state, tenant usage, a rate-limit store, a circuit store, a cache, a tracer. Each is
|
|
46
|
+
* reported when it keeps its state in process memory, where each replica would see only its own,
|
|
47
|
+
* and when it lacks an opt-in setting a deployment needs: a Redis operation store without its
|
|
48
|
+
* dispatch index, or a tracer writing to a persistent store without incremental tracing.
|
|
47
49
|
*/
|
|
48
50
|
stores?: Record<string, unknown>;
|
|
49
51
|
}
|
package/dist/doctor/index.js
CHANGED
|
@@ -54,6 +54,7 @@ export async function diagnose(options = {}) {
|
|
|
54
54
|
await run('queue', 'Operation queue and leases', () => queueCheck(options.operations, now(), options.maxQueueAgeMs));
|
|
55
55
|
await run('health', 'Provider health', () => healthCheck(options.ai));
|
|
56
56
|
await run('topology', 'Settings that only work in one process', () => topologyCheck(options.deployment));
|
|
57
|
+
await run('settings', 'Opt-in settings a deployment needs', () => settingsCheck(options.deployment));
|
|
57
58
|
const failures = checks.filter((check) => check.status === 'fail').length;
|
|
58
59
|
const warnings = checks.filter((check) => check.status === 'warn').length;
|
|
59
60
|
return { checks, ok: failures === 0, failures, warnings };
|
|
@@ -235,3 +236,31 @@ function topologyCheck(deployment) {
|
|
|
235
236
|
hint: 'Use the Redis, Postgres, or SQLite adapter for each, or run one replica.',
|
|
236
237
|
};
|
|
237
238
|
}
|
|
239
|
+
function settingsCheck(deployment) {
|
|
240
|
+
const stores = Object.entries(deployment?.stores ?? {});
|
|
241
|
+
if (stores.length === 0)
|
|
242
|
+
return { status: 'skip', detail: 'No stores described.' };
|
|
243
|
+
const findings = [];
|
|
244
|
+
const hints = new Set();
|
|
245
|
+
for (const [name, value] of stores) {
|
|
246
|
+
const kind = kindOf(value);
|
|
247
|
+
if (kind === 'RedisOperationStore' && value.indexed === false) {
|
|
248
|
+
findings.push(`${name} is a RedisOperationStore without index: true, so finding queued work reads every record and a long queue slows every claim`);
|
|
249
|
+
hints.add('Give the Redis operation store index: true once every worker sharing its prefix runs 2.2 or later, then call reindex() once.');
|
|
250
|
+
}
|
|
251
|
+
if (kind === 'Tracer') {
|
|
252
|
+
const tracer = value;
|
|
253
|
+
const store = kindOf(tracer.store);
|
|
254
|
+
if (tracer.incremental === false && store && store !== 'MemoryTraceStore') {
|
|
255
|
+
findings.push(`${name} writes to a ${store} without incremental, so a trace is written when its root finishes and a replica that dies mid-run loses it`);
|
|
256
|
+
hints.add('Create the Tracer with incremental: true, and close what a dead process left with closeAbandonedRuns().');
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (findings.length === 0)
|
|
261
|
+
return { status: 'ok', detail: 'No store lacks a setting a deployment needs.' };
|
|
262
|
+
return { status: 'warn', detail: `${findings.join('; ')}.`, hint: [...hints].join(' ') };
|
|
263
|
+
}
|
|
264
|
+
function kindOf(value) {
|
|
265
|
+
return value?.constructor?.name;
|
|
266
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { decodeBase64 } from '../utils/base64.js';
|
|
1
2
|
import { createProviderHttpError, toNexusProviderError } from '../providers/errors.js';
|
|
2
3
|
import { EmbeddingProviderResponseError } from './errors.js';
|
|
3
4
|
class HttpEmbeddingProvider {
|
|
@@ -31,7 +32,7 @@ class HttpEmbeddingProvider {
|
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
function decodeBase64Vector(value) {
|
|
34
|
-
const bytes =
|
|
35
|
+
const bytes = decodeBase64(value);
|
|
35
36
|
const floats = new Float32Array(bytes.buffer, bytes.byteOffset, Math.floor(bytes.byteLength / 4));
|
|
36
37
|
return Array.from(floats);
|
|
37
38
|
}
|
package/dist/evals/judge.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { sha256Hex } from '../utils/sha256.js';
|
|
2
2
|
import { withProvenance } from '../evaluate/provenance.js';
|
|
3
3
|
const DEFAULT_SYSTEM_PROMPT = [
|
|
4
4
|
'You are a careful evaluation judge.',
|
|
@@ -64,10 +64,7 @@ export class LLMJudge {
|
|
|
64
64
|
name: options.key ?? 'judge',
|
|
65
65
|
judge: {
|
|
66
66
|
model: this.options.model,
|
|
67
|
-
promptVersion:
|
|
68
|
-
.update(this.options.systemPrompt ?? 'default')
|
|
69
|
-
.digest('hex')
|
|
70
|
-
.slice(0, 12),
|
|
67
|
+
promptVersion: sha256Hex(this.options.systemPrompt ?? 'default').slice(0, 12),
|
|
71
68
|
temperature: this.options.temperature ?? 0,
|
|
72
69
|
},
|
|
73
70
|
...(this.options.rubric ? { rubric: this.options.rubric } : {}),
|
package/dist/evaluate/cache.d.ts
CHANGED
|
@@ -42,20 +42,6 @@ export declare class MemoryEvaluationCache implements EvaluationCache {
|
|
|
42
42
|
/** Drops every output. */
|
|
43
43
|
clear(): void;
|
|
44
44
|
}
|
|
45
|
-
/**
|
|
46
|
-
* Target outputs as JSON files in a directory, one per key, so a CI job keeps them between runs by
|
|
47
|
-
* caching the directory. Writes go through a temporary file and a rename, so a reader never sees half
|
|
48
|
-
* an entry. Outputs must survive `JSON.stringify()`.
|
|
49
|
-
*/
|
|
50
|
-
export declare class FileEvaluationCache implements EvaluationCache {
|
|
51
|
-
private readonly directory;
|
|
52
|
-
constructor(directory: string);
|
|
53
|
-
/** The output under a key, or `undefined` when there is none or its file cannot be read. */
|
|
54
|
-
get(key: string): Promise<CachedOutput | undefined>;
|
|
55
|
-
/** Writes an output under a key. */
|
|
56
|
-
set(key: string, value: CachedOutput): Promise<void>;
|
|
57
|
-
private fileOf;
|
|
58
|
-
}
|
|
59
45
|
/**
|
|
60
46
|
* A fingerprint for whatever decides a target's output — a model name, a prompt version, a
|
|
61
47
|
* temperature, a context bundle version — as `f` and 16 hex digits of a SHA-256 over its canonical
|
package/dist/evaluate/cache.js
CHANGED
|
@@ -28,36 +28,6 @@ export class MemoryEvaluationCache {
|
|
|
28
28
|
this.entries.clear();
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
export class FileEvaluationCache {
|
|
32
|
-
directory;
|
|
33
|
-
constructor(directory) {
|
|
34
|
-
this.directory = directory;
|
|
35
|
-
}
|
|
36
|
-
async get(key) {
|
|
37
|
-
const { readFile } = await import('node:fs/promises');
|
|
38
|
-
try {
|
|
39
|
-
return JSON.parse(await readFile(await this.fileOf(key), 'utf8'));
|
|
40
|
-
}
|
|
41
|
-
catch {
|
|
42
|
-
return undefined;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
async set(key, value) {
|
|
46
|
-
const { mkdir, rename, writeFile } = await import('node:fs/promises');
|
|
47
|
-
const path = await import('node:path');
|
|
48
|
-
const file = await this.fileOf(key);
|
|
49
|
-
await mkdir(path.dirname(file), { recursive: true });
|
|
50
|
-
const temporary = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
51
|
-
await writeFile(temporary, JSON.stringify(value), 'utf8');
|
|
52
|
-
await rename(temporary, file);
|
|
53
|
-
}
|
|
54
|
-
async fileOf(key) {
|
|
55
|
-
if (!/^[0-9a-f]{16,}$/.test(key))
|
|
56
|
-
throw new RangeError('An evaluation cache key is a hex string');
|
|
57
|
-
const path = await import('node:path');
|
|
58
|
-
return path.join(this.directory, key.slice(0, 2), `${key}.json`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
31
|
export async function fingerprintOf(value) {
|
|
62
32
|
return `f${(await sha256(canonicalJson(value))).slice(0, 16)}`;
|
|
63
33
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CachedOutput, EvaluationCache } from './cache.js';
|
|
2
|
+
/**
|
|
3
|
+
* Target outputs as JSON files in a directory, one per key, so a CI job keeps them between runs by
|
|
4
|
+
* caching the directory. Writes go through a temporary file and a rename, so a reader never sees half
|
|
5
|
+
* an entry. Outputs must survive `JSON.stringify()`.
|
|
6
|
+
*/
|
|
7
|
+
export declare class FileEvaluationCache implements EvaluationCache {
|
|
8
|
+
private readonly directory;
|
|
9
|
+
constructor(directory: string);
|
|
10
|
+
/** The output under a key, or `undefined` when there is none or its file cannot be read. */
|
|
11
|
+
get(key: string): Promise<CachedOutput | undefined>;
|
|
12
|
+
/** Writes an output under a key. */
|
|
13
|
+
set(key: string, value: CachedOutput): Promise<void>;
|
|
14
|
+
private fileOf;
|
|
15
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export class FileEvaluationCache {
|
|
2
|
+
directory;
|
|
3
|
+
constructor(directory) {
|
|
4
|
+
this.directory = directory;
|
|
5
|
+
}
|
|
6
|
+
async get(key) {
|
|
7
|
+
const { readFile } = await import('node:fs/promises');
|
|
8
|
+
try {
|
|
9
|
+
return JSON.parse(await readFile(await this.fileOf(key), 'utf8'));
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
async set(key, value) {
|
|
16
|
+
const { mkdir, rename, writeFile } = await import('node:fs/promises');
|
|
17
|
+
const path = await import('node:path');
|
|
18
|
+
const file = await this.fileOf(key);
|
|
19
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
20
|
+
const temporary = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
21
|
+
await writeFile(temporary, JSON.stringify(value), 'utf8');
|
|
22
|
+
await rename(temporary, file);
|
|
23
|
+
}
|
|
24
|
+
async fileOf(key) {
|
|
25
|
+
if (!/^[0-9a-f]{16,}$/.test(key))
|
|
26
|
+
throw new RangeError('An evaluation cache key is a hex string');
|
|
27
|
+
const path = await import('node:path');
|
|
28
|
+
return path.join(this.directory, key.slice(0, 2), `${key}.json`);
|
|
29
|
+
}
|
|
30
|
+
}
|
package/dist/evaluate/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export { contentVersion, createDataset, type CreateDatasetOptions, datasetFromTr
|
|
|
3
3
|
export { completed, contains, embeddingSimilarity, exactMatch, mustNotMatch, pairwise, passRate, recallAtK, reciprocalRank, type RetrievalEvaluatorOptions, totalCost, trajectory, type TrajectoryOptions, underCost, underLatency, } from './evaluators.js';
|
|
4
4
|
export { AnnotationQueue, type AnnotationQueueOptions, evaluateOnline, type OnlineEvaluationOptions, type OnlineEvaluationReport, type ReviewAnswer, type ReviewItem, type ReviewQuestion, } from './review.js';
|
|
5
5
|
export { evaluate, type EvaluateOptions, type EvaluationTarget, stats, summarize } from './run.js';
|
|
6
|
-
export { type CachedOutput, type EvaluationCache, evaluationCacheKey,
|
|
6
|
+
export { type CachedOutput, type EvaluationCache, evaluationCacheKey, fingerprintOf, MemoryEvaluationCache, type MemoryEvaluationCacheOptions, } from './cache.js';
|
|
7
|
+
export { FileEvaluationCache } from './file-cache.js';
|
|
7
8
|
export type { Dataset, DatasetExample, DatasetStore, EvaluationContext, EvaluationScore, Evaluator, ExampleResult, Experiment, ExperimentStore, MetricSummary, SummaryEvaluator, } from '../types/evaluate.js';
|
|
8
9
|
export { withProvenance } from './provenance.js';
|
package/dist/evaluate/index.js
CHANGED
|
@@ -3,5 +3,6 @@ export { contentVersion, createDataset, datasetFromTraces, FileDatasetStore, Fil
|
|
|
3
3
|
export { completed, contains, embeddingSimilarity, exactMatch, mustNotMatch, pairwise, passRate, recallAtK, reciprocalRank, totalCost, trajectory, underCost, underLatency, } from './evaluators.js';
|
|
4
4
|
export { AnnotationQueue, evaluateOnline, } from './review.js';
|
|
5
5
|
export { evaluate, stats, summarize } from './run.js';
|
|
6
|
-
export { evaluationCacheKey,
|
|
6
|
+
export { evaluationCacheKey, fingerprintOf, MemoryEvaluationCache, } from './cache.js';
|
|
7
|
+
export { FileEvaluationCache } from './file-cache.js';
|
|
7
8
|
export { withProvenance } from './provenance.js';
|
package/dist/evaluate/review.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomHex } from '../utils/ids.js';
|
|
2
2
|
export class AnnotationQueue {
|
|
3
3
|
options;
|
|
4
4
|
items = new Map();
|
|
@@ -9,7 +9,7 @@ export class AnnotationQueue {
|
|
|
9
9
|
}
|
|
10
10
|
enqueue(subject, metadata) {
|
|
11
11
|
const item = {
|
|
12
|
-
id: `review-${
|
|
12
|
+
id: `review-${randomHex(6)}`,
|
|
13
13
|
subject,
|
|
14
14
|
rubric: this.options.rubric,
|
|
15
15
|
status: 'pending',
|
package/dist/evaluate/run.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomHex } from '../utils/ids.js';
|
|
2
2
|
import { NEXUS_VERSION } from '../version.js';
|
|
3
3
|
import { evaluationCacheKey } from './cache.js';
|
|
4
4
|
import { declaredProvenance } from './provenance.js';
|
|
@@ -29,7 +29,7 @@ export async function evaluate(target, dataset, evaluators, options = {}) {
|
|
|
29
29
|
for (const evaluator of options.summary ?? [])
|
|
30
30
|
summary.push(...(await evaluator(results)));
|
|
31
31
|
const experiment = {
|
|
32
|
-
id: `exp-${
|
|
32
|
+
id: `exp-${randomHex(6)}`,
|
|
33
33
|
name: options.name ?? `${dataset.name} ${startedAt}`,
|
|
34
34
|
dataset: { name: dataset.name, version: dataset.version },
|
|
35
35
|
startedAt,
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Evaluation of whole conversations. A simulated user holds a conversation with an agent for each
|
|
3
|
+
* goal in a dataset, and thread evaluators score the conversation: whether the goal was met, each
|
|
4
|
+
* turn, and the turn where it went wrong. A multi-turn agent is then compared between versions as a
|
|
5
|
+
* single-turn one is, with `evaluate()` and `compareExperiments()`.
|
|
6
|
+
*/
|
|
7
|
+
import type { LLMJudge } from '../evals/judge.js';
|
|
8
|
+
import type { Evaluator } from '../types/evaluate.js';
|
|
9
|
+
import type { CompletionRequest, Message } from '../types/messages.js';
|
|
10
|
+
import type { EvaluationTarget } from './run.js';
|
|
11
|
+
/** One turn of a conversation. */
|
|
12
|
+
export interface ThreadTurn {
|
|
13
|
+
/** Who spoke. */
|
|
14
|
+
role: 'user' | 'assistant';
|
|
15
|
+
/** What they said. */
|
|
16
|
+
content: string;
|
|
17
|
+
/** For an assistant turn, how long the agent took, in milliseconds. */
|
|
18
|
+
latencyMs?: number;
|
|
19
|
+
/**
|
|
20
|
+
* For an assistant turn, every message the agent added: its tool calls, their results, and the
|
|
21
|
+
* answer. The agent is given them back on later turns, and an evaluator can read its trajectory.
|
|
22
|
+
*/
|
|
23
|
+
messages?: Message[];
|
|
24
|
+
}
|
|
25
|
+
/** Why a conversation ended. */
|
|
26
|
+
export type ThreadEnding = 'goal' | 'gave-up' | 'max-turns' | 'error';
|
|
27
|
+
/** A finished conversation: what a conversation target returns, and what thread evaluators read. */
|
|
28
|
+
export interface Thread {
|
|
29
|
+
/** Every turn, in order. */
|
|
30
|
+
turns: ThreadTurn[];
|
|
31
|
+
/**
|
|
32
|
+
* `goal` when the user said its goal was met, `gave-up` when it said it never would be, `max-turns`
|
|
33
|
+
* when the turn limit ended it, and `error` when the agent failed.
|
|
34
|
+
*/
|
|
35
|
+
ended: ThreadEnding;
|
|
36
|
+
/** The agent's error, when it failed. */
|
|
37
|
+
error?: string;
|
|
38
|
+
}
|
|
39
|
+
/** What a conversation is about: the user's goal, and optionally who they are and how they open. */
|
|
40
|
+
export interface ThreadInputs {
|
|
41
|
+
/** What the user wants, in their words or the dataset's. */
|
|
42
|
+
goal: string;
|
|
43
|
+
/** Who the user is and how they talk, such as "an impatient customer who writes in short lines". */
|
|
44
|
+
persona?: string;
|
|
45
|
+
/** The user's first message, instead of letting the simulated user write it. */
|
|
46
|
+
opening?: string;
|
|
47
|
+
}
|
|
48
|
+
/** What a simulated user sees when it writes its next message. */
|
|
49
|
+
export interface SimulatedUserContext {
|
|
50
|
+
/** The goal. */
|
|
51
|
+
goal: string;
|
|
52
|
+
/** The persona, when the example gives one. */
|
|
53
|
+
persona?: string;
|
|
54
|
+
/** The conversation so far. Empty for the first message. */
|
|
55
|
+
turns: readonly ThreadTurn[];
|
|
56
|
+
/** Aborted when the evaluation is cancelled or times out. */
|
|
57
|
+
signal?: AbortSignal;
|
|
58
|
+
}
|
|
59
|
+
/** A simulated user's move: its next message, or the end of the conversation and why. */
|
|
60
|
+
export type SimulatedUserReply = {
|
|
61
|
+
message: string;
|
|
62
|
+
} | {
|
|
63
|
+
done: 'goal' | 'gave-up';
|
|
64
|
+
};
|
|
65
|
+
/** Plays the user in a conversation. */
|
|
66
|
+
export interface SimulatedUser {
|
|
67
|
+
/** The next move, given the conversation so far. */
|
|
68
|
+
next(context: SimulatedUserContext): Promise<SimulatedUserReply> | SimulatedUserReply;
|
|
69
|
+
}
|
|
70
|
+
/** A model client, as the judges take one. */
|
|
71
|
+
export interface SimulatedUserClient {
|
|
72
|
+
/** Runs one completion. */
|
|
73
|
+
complete(request: CompletionRequest): Promise<{
|
|
74
|
+
content: string;
|
|
75
|
+
}>;
|
|
76
|
+
}
|
|
77
|
+
/** Options for `simulatedUser()`. */
|
|
78
|
+
export interface SimulatedUserOptions {
|
|
79
|
+
/** The client the user's model is called through. */
|
|
80
|
+
client: SimulatedUserClient;
|
|
81
|
+
/** The model that plays the user. */
|
|
82
|
+
model: string;
|
|
83
|
+
/** A persona for every conversation, when the examples give none. */
|
|
84
|
+
persona?: string;
|
|
85
|
+
/** Sampling temperature. Defaults to 0, so a conversation repeats. */
|
|
86
|
+
temperature?: number;
|
|
87
|
+
/** Output token limit for each message. Defaults to 300. */
|
|
88
|
+
maxTokens?: number;
|
|
89
|
+
/** Replaces the instructions the model is given. `{goal}` and `{persona}` are filled in. */
|
|
90
|
+
instructions?: string;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* A simulated user driven by a model: it is told the goal and the persona, sees the conversation
|
|
94
|
+
* with the roles turned around, and writes the next message, or says it is done and why.
|
|
95
|
+
*/
|
|
96
|
+
export declare function simulatedUser(options: SimulatedUserOptions): SimulatedUser;
|
|
97
|
+
/**
|
|
98
|
+
* A user that follows a script, for deterministic tests: its messages in order, then `done`. A
|
|
99
|
+
* function decides each move itself.
|
|
100
|
+
*/
|
|
101
|
+
export declare function scriptedUser(script: {
|
|
102
|
+
messages: readonly string[];
|
|
103
|
+
done?: 'goal' | 'gave-up';
|
|
104
|
+
} | SimulatedUser['next']): SimulatedUser;
|
|
105
|
+
/** What a thread agent is told besides the conversation. */
|
|
106
|
+
export interface ThreadAgentContext<I extends ThreadInputs = ThreadInputs> {
|
|
107
|
+
/** The example's inputs. */
|
|
108
|
+
inputs: I;
|
|
109
|
+
/** The turn, counted from 1. */
|
|
110
|
+
turn: number;
|
|
111
|
+
/** Names this conversation, the same on every turn and new for every conversation: a thread id. */
|
|
112
|
+
conversationId: string;
|
|
113
|
+
/** Aborted when the evaluation is cancelled or times out. */
|
|
114
|
+
signal?: AbortSignal;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* What a thread agent answers: the reply, or the reply and every message the agent added on the way,
|
|
118
|
+
* such as its tool calls and their results. Those are given back to it on later turns.
|
|
119
|
+
*/
|
|
120
|
+
export type ThreadAgentReply = string | {
|
|
121
|
+
content: string;
|
|
122
|
+
messages?: Message[];
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* The agent a conversation target talks to. It is given the conversation so far, with its own earlier
|
|
126
|
+
* turns as it recorded them, and answers the user's newest message.
|
|
127
|
+
*/
|
|
128
|
+
export type ThreadAgent<I extends ThreadInputs = ThreadInputs> = (messages: readonly Message[], context: ThreadAgentContext<I>) => Promise<ThreadAgentReply> | ThreadAgentReply;
|
|
129
|
+
/** Options for `conversationTarget()`. */
|
|
130
|
+
export interface ConversationTargetOptions<I extends ThreadInputs = ThreadInputs> {
|
|
131
|
+
/** The user, or a user per example, such as one with the example's own persona. */
|
|
132
|
+
user: SimulatedUser | ((inputs: I) => SimulatedUser);
|
|
133
|
+
/** The most user messages a conversation may have. Defaults to 10. */
|
|
134
|
+
maxTurns?: number;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* An `evaluate()` target that holds a conversation per example: the simulated user speaks, the agent
|
|
138
|
+
* answers, until the user says it is done or the turn limit is reached. It returns the `Thread`.
|
|
139
|
+
*/
|
|
140
|
+
export declare function conversationTarget<I extends ThreadInputs = ThreadInputs>(agent: ThreadAgent<I>, options: ConversationTargetOptions<I>): EvaluationTarget<I>;
|
|
141
|
+
/** A graph a thread agent can drive: one that takes `{ messages }`, as `createAgent()` builds. */
|
|
142
|
+
export interface ThreadGraph {
|
|
143
|
+
/** Runs one turn. */
|
|
144
|
+
invoke(input: {
|
|
145
|
+
messages: Message[];
|
|
146
|
+
}, options?: {
|
|
147
|
+
threadId?: string;
|
|
148
|
+
signal?: AbortSignal;
|
|
149
|
+
}): Promise<{
|
|
150
|
+
state: unknown;
|
|
151
|
+
}>;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* An agent from `createAgent()`, or any graph that takes `{ messages }` and ends with an `answer`, as
|
|
155
|
+
* a thread agent. Each turn runs on the conversation's thread with the transcript so far, and the
|
|
156
|
+
* messages the run added to its `messages` are recorded with the turn. The agent therefore sees its
|
|
157
|
+
* own tool calls on later turns, as it does with a real user, without needing a checkpointer.
|
|
158
|
+
*/
|
|
159
|
+
export declare function graphThreadAgent(graph: ThreadGraph): ThreadAgent;
|
|
160
|
+
/** The conversation as text, for a judge or a person. */
|
|
161
|
+
export declare function formatThread(thread: Thread): string;
|
|
162
|
+
/**
|
|
163
|
+
* Whether the user said its goal was met: 1 when the conversation ended on `goal`, otherwise 0, with
|
|
164
|
+
* how it ended as the comment. Key `goal_reached`.
|
|
165
|
+
*/
|
|
166
|
+
export declare function goalReached(): Evaluator<ThreadInputs>;
|
|
167
|
+
/**
|
|
168
|
+
* Whether the goal was met, in a judge's opinion of the whole conversation, not the user's. Key
|
|
169
|
+
* `goal_completion`, with the judge's rationale as the comment.
|
|
170
|
+
*/
|
|
171
|
+
export declare function goalCompletion(judge: Pick<LLMJudge, 'evaluate'>): Evaluator<ThreadInputs>;
|
|
172
|
+
/** How many times the assistant spoke: fewer is better. Key `turns`; compare with `lowerIsBetter: ['turns']`. */
|
|
173
|
+
export declare function turnCount(): Evaluator<ThreadInputs>;
|
|
174
|
+
/** Options for `turnScores()`. */
|
|
175
|
+
export interface TurnScoresOptions {
|
|
176
|
+
/** The score's key. Defaults to `turn_score`. */
|
|
177
|
+
key?: string;
|
|
178
|
+
/** A turn scoring below this went wrong. Defaults to 0.5. */
|
|
179
|
+
threshold?: number;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Scores every assistant turn with an evaluator of single answers, and the conversation by their
|
|
183
|
+
* mean. The evaluator sees each turn as an example of its own: the user's message as the input, the
|
|
184
|
+
* conversation before it in `metadata.history`, and the turn as the output. The score's metadata holds
|
|
185
|
+
* each turn's score, and `wentWrongAt`, the first turn below the threshold, counted from 1.
|
|
186
|
+
*/
|
|
187
|
+
export declare function turnScores<I = unknown>(evaluator: Evaluator<I>, options?: TurnScoresOptions): Evaluator<ThreadInputs>;
|