nexus-ai-pro 2.20.2 → 2.22.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 +93 -0
- package/CHANGELOG.md +208 -1
- package/README.md +11 -6
- package/SECURITY.md +16 -2
- package/dist/adapter-kit/gather.d.ts +76 -0
- package/dist/adapter-kit/gather.js +147 -0
- package/dist/adapter-kit/index.d.ts +1 -0
- package/dist/adapter-kit/index.js +1 -0
- package/dist/adapter-kit/record.d.ts +3 -1
- package/dist/adapter-kit/record.js +20 -5
- package/dist/agent/openapi.d.ts +94 -8
- package/dist/agent/openapi.js +528 -32
- package/dist/cli/adapters.d.ts +4 -3
- package/dist/cli/adapters.js +71 -2
- package/dist/cli/doctor.js +3 -0
- package/dist/cli.js +3 -1
- package/dist/core/config-builder.d.ts +1 -0
- package/dist/core/config-builder.js +1 -0
- package/dist/core/defaults.d.ts +34 -0
- package/dist/core/defaults.js +39 -0
- package/dist/core/secure-stream.js +4 -1
- package/dist/decisions/completion.d.ts +10 -7
- package/dist/decisions/completion.js +82 -8
- package/dist/decisions/core.d.ts +88 -43
- package/dist/decisions/core.js +115 -28
- package/dist/decisions/errors.d.ts +25 -0
- package/dist/decisions/errors.js +12 -0
- package/dist/decisions/index.d.ts +6 -5
- package/dist/decisions/index.js +2 -1
- package/dist/decisions/integrations.d.ts +14 -5
- package/dist/decisions/integrations.js +16 -1
- package/dist/decisions/mock.d.ts +5 -3
- package/dist/decisions/mock.js +4 -1
- package/dist/decisions/pictures.d.ts +43 -0
- package/dist/decisions/pictures.js +103 -0
- package/dist/decisions/typesafe.d.ts +33 -5
- package/dist/decisions/typesafe.js +121 -8
- package/dist/doctor/index.d.ts +6 -9
- package/dist/doctor/index.js +41 -0
- package/dist/embeddings/adapters.d.ts +1 -1
- package/dist/embeddings/adapters.js +3 -3
- package/dist/embeddings/batch-google.d.ts +27 -8
- package/dist/embeddings/batch-google.js +112 -36
- package/dist/embeddings/batch-mock.js +1 -1
- package/dist/embeddings/batch.js +10 -4
- package/dist/embeddings/engine.js +14 -13
- package/dist/embeddings/index.d.ts +1 -1
- package/dist/embeddings/inputs.d.ts +26 -3
- package/dist/embeddings/inputs.js +84 -5
- package/dist/embeddings/mock.js +1 -1
- package/dist/embeddings/models.js +39 -4
- package/dist/loaders/csv.d.ts +23 -1
- package/dist/loaders/csv.js +125 -72
- package/dist/loaders/index.d.ts +7 -2
- package/dist/loaders/input.d.ts +6 -0
- package/dist/loaders/input.js +42 -1
- package/dist/loaders/json.d.ts +14 -3
- package/dist/loaders/json.js +171 -20
- package/dist/loaders/web.d.ts +55 -0
- package/dist/loaders/web.js +20 -1
- package/dist/mcp/client.d.ts +54 -5
- package/dist/mcp/client.js +171 -20
- package/dist/mcp/http.d.ts +45 -0
- package/dist/mcp/http.js +233 -0
- package/dist/mcp/index.d.ts +4 -2
- package/dist/mcp/index.js +3 -1
- package/dist/mcp/protocol.d.ts +107 -0
- package/dist/mcp/sampling.d.ts +41 -0
- package/dist/mcp/sampling.js +52 -0
- package/dist/mcp/server.d.ts +96 -14
- package/dist/mcp/server.js +274 -59
- package/dist/models/data.js +20 -1
- package/dist/operations/adapters.d.ts +1 -0
- package/dist/operations/adapters.js +3 -1
- package/dist/protocols/a2a-grpc.js +10 -3
- package/dist/protocols/a2a-signing.d.ts +65 -0
- package/dist/protocols/a2a-signing.js +133 -0
- package/dist/protocols/a2a.d.ts +14 -0
- package/dist/protocols/a2a.js +60 -5
- package/dist/providers/cohere.js +16 -2
- package/dist/providers/google.js +7 -1
- package/dist/providers/ollama.d.ts +1 -1
- package/dist/providers/ollama.js +35 -27
- package/dist/providers/openai.js +19 -2
- package/dist/rag/pinecone.d.ts +63 -2
- package/dist/rag/pinecone.js +80 -0
- package/dist/realtime/conversation.js +18 -0
- package/dist/realtime/gemini-live.transport.d.ts +28 -3
- package/dist/realtime/gemini-live.transport.js +170 -32
- package/dist/realtime/session.d.ts +2 -0
- package/dist/realtime/session.js +19 -4
- package/dist/realtime/types.d.ts +35 -3
- package/dist/security/output-guard.js +4 -1
- package/dist/server/auth.d.ts +263 -2
- package/dist/server/auth.js +432 -5
- package/dist/server/deployments.d.ts +3 -2
- package/dist/server/deployments.js +6 -1
- package/dist/telephony/index.d.ts +1 -1
- package/dist/telephony/manager.d.ts +8 -1
- package/dist/telephony/manager.js +13 -0
- package/dist/telephony/providers/telnyx.d.ts +18 -5
- package/dist/telephony/providers/telnyx.js +44 -7
- package/dist/telephony/providers/twilio.d.ts +3 -1
- package/dist/telephony/providers/twilio.js +16 -0
- package/dist/tracing/stores.d.ts +2 -0
- package/dist/tracing/stores.js +1 -0
- package/dist/tracing/tracer.d.ts +5 -2
- package/dist/tracing/tracer.js +6 -3
- package/dist/types/embeddings.d.ts +47 -5
- package/dist/types/messages.d.ts +7 -0
- package/dist/types/principal.d.ts +1 -1
- package/dist/types/response.d.ts +5 -0
- package/dist/types/telephony.d.ts +38 -0
- package/dist/types/tracing.d.ts +6 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/voice/providers/google.d.ts +44 -0
- package/dist/voice/providers/google.js +215 -0
- package/dist-cjs/adapter-kit/gather.d.ts +76 -0
- package/dist-cjs/adapter-kit/gather.js +152 -0
- package/dist-cjs/adapter-kit/index.d.ts +1 -0
- package/dist-cjs/adapter-kit/index.js +4 -1
- package/dist-cjs/adapter-kit/record.d.ts +3 -1
- package/dist-cjs/adapter-kit/record.js +20 -5
- package/dist-cjs/agent/openapi.d.ts +94 -8
- package/dist-cjs/agent/openapi.js +530 -32
- package/dist-cjs/cli/adapters.d.ts +4 -3
- package/dist-cjs/cli/adapters.js +70 -1
- package/dist-cjs/cli/doctor.js +3 -0
- package/dist-cjs/core/config-builder.d.ts +1 -0
- package/dist-cjs/core/config-builder.js +5 -1
- package/dist-cjs/core/defaults.d.ts +34 -0
- package/dist-cjs/core/defaults.js +44 -0
- package/dist-cjs/core/secure-stream.js +4 -1
- package/dist-cjs/decisions/completion.d.ts +10 -7
- package/dist-cjs/decisions/completion.js +81 -7
- package/dist-cjs/decisions/core.d.ts +88 -43
- package/dist-cjs/decisions/core.js +134 -46
- package/dist-cjs/decisions/errors.d.ts +25 -0
- package/dist-cjs/decisions/errors.js +16 -0
- package/dist-cjs/decisions/index.d.ts +6 -5
- package/dist-cjs/decisions/index.js +5 -1
- package/dist-cjs/decisions/integrations.d.ts +14 -5
- package/dist-cjs/decisions/integrations.js +16 -1
- package/dist-cjs/decisions/mock.d.ts +5 -3
- package/dist-cjs/decisions/mock.js +4 -1
- package/dist-cjs/decisions/pictures.d.ts +43 -0
- package/dist-cjs/decisions/pictures.js +109 -0
- package/dist-cjs/decisions/typesafe.d.ts +33 -5
- package/dist-cjs/decisions/typesafe.js +126 -13
- package/dist-cjs/doctor/index.d.ts +6 -9
- package/dist-cjs/doctor/index.js +41 -0
- package/dist-cjs/embeddings/adapters.d.ts +1 -1
- package/dist-cjs/embeddings/adapters.js +3 -3
- package/dist-cjs/embeddings/batch-google.d.ts +27 -8
- package/dist-cjs/embeddings/batch-google.js +112 -36
- package/dist-cjs/embeddings/batch-mock.js +1 -1
- package/dist-cjs/embeddings/batch.js +9 -3
- package/dist-cjs/embeddings/engine.js +13 -12
- package/dist-cjs/embeddings/index.d.ts +1 -1
- package/dist-cjs/embeddings/inputs.d.ts +26 -3
- package/dist-cjs/embeddings/inputs.js +89 -5
- package/dist-cjs/embeddings/mock.js +1 -1
- package/dist-cjs/embeddings/models.js +39 -4
- package/dist-cjs/loaders/csv.d.ts +23 -1
- package/dist-cjs/loaders/csv.js +126 -71
- package/dist-cjs/loaders/index.d.ts +7 -2
- package/dist-cjs/loaders/input.d.ts +6 -0
- package/dist-cjs/loaders/input.js +43 -1
- package/dist-cjs/loaders/json.d.ts +14 -3
- package/dist-cjs/loaders/json.js +170 -19
- package/dist-cjs/loaders/web.d.ts +55 -0
- package/dist-cjs/loaders/web.js +21 -1
- package/dist-cjs/mcp/client.d.ts +54 -5
- package/dist-cjs/mcp/client.js +171 -20
- package/dist-cjs/mcp/http.d.ts +45 -0
- package/dist-cjs/mcp/http.js +236 -0
- package/dist-cjs/mcp/index.d.ts +4 -2
- package/dist-cjs/mcp/index.js +5 -1
- package/dist-cjs/mcp/protocol.d.ts +107 -0
- package/dist-cjs/mcp/sampling.d.ts +41 -0
- package/dist-cjs/mcp/sampling.js +55 -0
- package/dist-cjs/mcp/server.d.ts +96 -14
- package/dist-cjs/mcp/server.js +273 -58
- package/dist-cjs/models/data.js +20 -1
- package/dist-cjs/operations/adapters.d.ts +1 -0
- package/dist-cjs/operations/adapters.js +3 -1
- package/dist-cjs/protocols/a2a-grpc.js +10 -3
- package/dist-cjs/protocols/a2a-signing.d.ts +65 -0
- package/dist-cjs/protocols/a2a-signing.js +140 -0
- package/dist-cjs/protocols/a2a.d.ts +14 -0
- package/dist-cjs/protocols/a2a.js +65 -6
- package/dist-cjs/providers/cohere.js +16 -2
- package/dist-cjs/providers/google.js +7 -1
- package/dist-cjs/providers/ollama.d.ts +1 -1
- package/dist-cjs/providers/ollama.js +35 -27
- package/dist-cjs/providers/openai.js +19 -2
- package/dist-cjs/rag/pinecone.d.ts +63 -2
- package/dist-cjs/rag/pinecone.js +81 -0
- package/dist-cjs/realtime/conversation.js +18 -0
- package/dist-cjs/realtime/gemini-live.transport.d.ts +28 -3
- package/dist-cjs/realtime/gemini-live.transport.js +170 -32
- package/dist-cjs/realtime/session.d.ts +2 -0
- package/dist-cjs/realtime/session.js +19 -4
- package/dist-cjs/realtime/types.d.ts +35 -3
- package/dist-cjs/security/output-guard.js +4 -1
- package/dist-cjs/server/auth.d.ts +263 -2
- package/dist-cjs/server/auth.js +437 -5
- package/dist-cjs/server/deployments.d.ts +3 -2
- package/dist-cjs/server/deployments.js +6 -1
- package/dist-cjs/telephony/index.d.ts +1 -1
- package/dist-cjs/telephony/manager.d.ts +8 -1
- package/dist-cjs/telephony/manager.js +13 -0
- package/dist-cjs/telephony/providers/telnyx.d.ts +18 -5
- package/dist-cjs/telephony/providers/telnyx.js +44 -7
- package/dist-cjs/telephony/providers/twilio.d.ts +3 -1
- package/dist-cjs/telephony/providers/twilio.js +16 -0
- package/dist-cjs/tracing/stores.d.ts +2 -0
- package/dist-cjs/tracing/stores.js +1 -0
- package/dist-cjs/tracing/tracer.d.ts +5 -2
- package/dist-cjs/tracing/tracer.js +6 -3
- package/dist-cjs/types/embeddings.d.ts +47 -5
- package/dist-cjs/types/messages.d.ts +7 -0
- package/dist-cjs/types/principal.d.ts +1 -1
- package/dist-cjs/types/response.d.ts +5 -0
- package/dist-cjs/types/telephony.d.ts +38 -0
- package/dist-cjs/types/tracing.d.ts +6 -0
- package/dist-cjs/version.d.ts +1 -1
- package/dist-cjs/version.js +1 -1
- package/dist-cjs/voice/providers/google.d.ts +44 -0
- package/dist-cjs/voice/providers/google.js +252 -0
- package/llms.txt +1 -1
- package/package.json +17 -1
package/dist/cli/adapters.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { type ParsedArgs } from './args.js';
|
|
2
2
|
/**
|
|
3
|
-
* `nexus adapters verify <module>[#export]`, `nexus adapters page <records...>`,
|
|
4
|
-
* `nexus adapters recheck <records
|
|
3
|
+
* `nexus adapters verify <module>[#export]`, `nexus adapters page <records...>`,
|
|
4
|
+
* `nexus adapters recheck <records...>`, and `nexus adapters gather --out <dir>`.
|
|
5
5
|
*
|
|
6
6
|
* `verify` runs an adapter's contract suite and benchmark from its own repository and writes its
|
|
7
7
|
* compatibility record; `page` lists records on one page; `recheck` verifies each listed adapter again
|
|
8
|
-
* against the installed release and fails when one that passed no longer does
|
|
8
|
+
* against the installed release and fails when one that passed no longer does; `gather` fetches the
|
|
9
|
+
* records adapters' own CI publishes, checked and never run, into a directory a page lists.
|
|
9
10
|
*/
|
|
10
11
|
export declare function runAdaptersCommand(subcommand: string | undefined, args: ParsedArgs): Promise<void>;
|
package/dist/cli/adapters.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
|
-
import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { mkdir, readdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { createRequire } from 'node:module';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { pathToFileURL } from 'node:url';
|
|
6
|
+
import { ADAPTER_KEYWORD, gatherRecords } from '../adapter-kit/gather.js';
|
|
6
7
|
import { compareRecords, compatibilityPage, readCompatibilityRecord, recordStatus, } from '../adapter-kit/record.js';
|
|
7
8
|
import { CliUsageError, flagBool, isRecord, printTable, stringFlag, writeJson } from './args.js';
|
|
8
9
|
export async function runAdaptersCommand(subcommand, args) {
|
|
@@ -12,7 +13,75 @@ export async function runAdaptersCommand(subcommand, args) {
|
|
|
12
13
|
return page(args);
|
|
13
14
|
if (subcommand === 'recheck')
|
|
14
15
|
return recheck(args);
|
|
15
|
-
|
|
16
|
+
if (subcommand === 'gather')
|
|
17
|
+
return gather(args);
|
|
18
|
+
throw new CliUsageError(`Unknown adapters command "${subcommand ?? ''}". Use verify, page, recheck, or gather.`);
|
|
19
|
+
}
|
|
20
|
+
async function gather(args) {
|
|
21
|
+
const out = stringFlag(args.flags, 'out');
|
|
22
|
+
if (!out)
|
|
23
|
+
throw new CliUsageError('nexus adapters gather needs --out <dir>: where the gathered records are kept.');
|
|
24
|
+
const sourcesFile = stringFlag(args.flags, 'sources');
|
|
25
|
+
const sources = sourcesFile ? sourcesFrom(await readFile(sourcesFile, 'utf8'), sourcesFile) : [];
|
|
26
|
+
const previous = [];
|
|
27
|
+
for (const name of existsSync(out) ? (await readdir(out)).sort() : []) {
|
|
28
|
+
if (!name.endsWith('.json'))
|
|
29
|
+
continue;
|
|
30
|
+
try {
|
|
31
|
+
previous.push({ file: name, record: readCompatibilityRecord(await readFile(path.join(out, name), 'utf8')) });
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const registry = stringFlag(args.flags, 'registry');
|
|
37
|
+
const report = await gatherRecords({
|
|
38
|
+
keyword: args.flags.keyword === false ? false : (stringFlag(args.flags, 'keyword') ?? ADAPTER_KEYWORD),
|
|
39
|
+
sources,
|
|
40
|
+
previous: previous.map((entry) => entry.record),
|
|
41
|
+
...(registry ? { registry } : {}),
|
|
42
|
+
});
|
|
43
|
+
await mkdir(out, { recursive: true });
|
|
44
|
+
const written = new Set();
|
|
45
|
+
for (const source of report.sources) {
|
|
46
|
+
if (!source.record)
|
|
47
|
+
continue;
|
|
48
|
+
const file = `${slug(source.package)}.json`;
|
|
49
|
+
written.add(file);
|
|
50
|
+
await writeFile(path.join(out, file), `${JSON.stringify(source.record, null, 2)}
|
|
51
|
+
`);
|
|
52
|
+
}
|
|
53
|
+
for (const { file } of previous)
|
|
54
|
+
if (!written.has(file))
|
|
55
|
+
await rm(path.join(out, file));
|
|
56
|
+
if (flagBool(args.flags, 'json')) {
|
|
57
|
+
writeJson(report);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
printTable(report.sources.map((source) => ({
|
|
61
|
+
package: source.package,
|
|
62
|
+
outcome: source.outcome,
|
|
63
|
+
state: source.record ? recordStatus(source.record).state : '',
|
|
64
|
+
detail: source.detail ?? '',
|
|
65
|
+
})), ['package', 'outcome', 'state', 'detail']);
|
|
66
|
+
console.log('');
|
|
67
|
+
console.log(`${report.records.length} records listed in ${out}, of ${report.sources.length} packages.`);
|
|
68
|
+
}
|
|
69
|
+
function sourcesFrom(text, file) {
|
|
70
|
+
let parsed;
|
|
71
|
+
try {
|
|
72
|
+
parsed = JSON.parse(text);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
throw new CliUsageError(`${file} is not JSON: ${error.message}`);
|
|
76
|
+
}
|
|
77
|
+
if (!Array.isArray(parsed))
|
|
78
|
+
throw new CliUsageError(`${file} must list sources: [{ "package": ..., "url": ... }]`);
|
|
79
|
+
return parsed.map((item, index) => {
|
|
80
|
+
if (!isRecord(item) || typeof item.package !== 'string' || typeof item.url !== 'string') {
|
|
81
|
+
throw new CliUsageError(`${file}, source ${index + 1}: each source names its "package" and its record's "url"`);
|
|
82
|
+
}
|
|
83
|
+
return { package: item.package, url: item.url };
|
|
84
|
+
});
|
|
16
85
|
}
|
|
17
86
|
async function verify(args) {
|
|
18
87
|
const [target] = args.positionals;
|
package/dist/cli/doctor.js
CHANGED
|
@@ -28,6 +28,9 @@ export async function runDoctorCommand({ flags }) {
|
|
|
28
28
|
const maxQueueAgeMs = numberFlag(flags, 'max-queue-age-ms');
|
|
29
29
|
if (maxQueueAgeMs !== undefined)
|
|
30
30
|
options.maxQueueAgeMs = maxQueueAgeMs;
|
|
31
|
+
const upgrade = stringFlag(flags, 'upgrade');
|
|
32
|
+
if (upgrade !== undefined)
|
|
33
|
+
options.upgrade = upgrade;
|
|
31
34
|
let report;
|
|
32
35
|
try {
|
|
33
36
|
report = await diagnose(options);
|
package/dist/cli.js
CHANGED
|
@@ -437,7 +437,7 @@ Usage:
|
|
|
437
437
|
nexus db status --client <db.mjs> [--adapters ...] [--dialect postgres|sqlite] [--table t] [--check] [--json]
|
|
438
438
|
nexus db migrate --client <db.mjs> [--dry-run] [--adapters ...] [--dialect postgres|sqlite] [--table t] [--json]
|
|
439
439
|
nexus migrate [paths...] [--write] [--check] [--json]
|
|
440
|
-
nexus doctor [--module doctor.mjs] [--replicas n] [--max-queue-age-ms n] [--strict] [--json]
|
|
440
|
+
nexus doctor [--module doctor.mjs] [--replicas n] [--max-queue-age-ms n] [--upgrade 3.0] [--strict] [--json]
|
|
441
441
|
nexus graph lint <module>[#export] [--deployed] [--strict] [--ignore CODE,CODE] [--json]
|
|
442
442
|
nexus deploy status [assistant] --url <server> [--token t] [--json]
|
|
443
443
|
nexus deploy canary <assistant> <revision> <percent> --url <server> [--reason r] [--version n]
|
|
@@ -446,6 +446,7 @@ Usage:
|
|
|
446
446
|
nexus adapters verify <module>[#export] [--out record.json] [--package name@version] [--repository url] [--verify spec] [--json]
|
|
447
447
|
nexus adapters page <records|dir...> [--out page.md] [--html] [--title t] [--intro text] [--expect page.md] [--check] [--strict]
|
|
448
448
|
nexus adapters recheck <records|dir...> [--out dir] [--update] [--json]
|
|
449
|
+
nexus adapters gather --out <dir> [--keyword nexus-ai-pro-adapter | --no-keyword] [--sources sources.json] [--registry url] [--json]
|
|
449
450
|
nexus policy check <policy.json> [--base <policy.json>|git:<ref>] [--accept path,path] [--json]
|
|
450
451
|
nexus optimize [request.json|prompt.txt] [--model gpt-5-mini] [--max-input-tokens 4000] [--densify] [--json]
|
|
451
452
|
|
|
@@ -461,6 +462,7 @@ Database modules for "db status" and "db migrate" export { client }, and optiona
|
|
|
461
462
|
"graph lint" reads a compiled graph's shape and exits 1 on an error finding, or on a warning with --strict.
|
|
462
463
|
"adapters verify" exits 1 when the adapter does not pass; its module exports { adapter, config, options, record, close }.
|
|
463
464
|
"adapters recheck" verifies each listed adapter again and exits 1 when one that passed no longer does; "adapters page --check" exits 1 on a failing or incompatible record, --strict on a stale one too.
|
|
465
|
+
"adapters gather" fetches the records adapters' own CI publishes, found on npm by keyword or listed in --sources, keeps only those sealed and naming their package, and never runs them.
|
|
464
466
|
"policy check" exits 1 when the policy is not valid, or, against --base, when a change that widens what a tenant may reach is not acknowledged.`);
|
|
465
467
|
}
|
|
466
468
|
main().catch((error) => {
|
|
@@ -95,3 +95,4 @@ export declare function createNexusConfig(seed?: Partial<NexusAIConfig>): NexusC
|
|
|
95
95
|
* Identity helper for users who prefer object literals but want type inference and validation in editors.
|
|
96
96
|
*/
|
|
97
97
|
export declare function defineNexusConfig(config: NexusAIConfig): NexusAIConfig;
|
|
98
|
+
export { DEFAULT_CHANGES, type DefaultChange, type NexusDefaults, nexusDefaults, useDefaults } from './defaults.js';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which release's defaults apply where an option is left out: 2.x's, or a preview of 3.0's. 3.0 turns on
|
|
3
|
+
* three settings that 2.x keeps opt-in, because turning them on changes what an upgrade does; the
|
|
4
|
+
* preview turns on all three at once, so an application can run as 3.0 will before it upgrades. An
|
|
5
|
+
* option set explicitly always wins, under either.
|
|
6
|
+
*/
|
|
7
|
+
/** A release whose defaults apply: `2.x`, the default, or `3.0`, a preview of the next major release's. */
|
|
8
|
+
export type NexusDefaults = '2.x' | '3.0';
|
|
9
|
+
/** One default that 3.0 changes: what it is, what 2.x does, what 3.0 does, and what to do first. */
|
|
10
|
+
export interface DefaultChange {
|
|
11
|
+
/** The setting, as its option is named. */
|
|
12
|
+
setting: string;
|
|
13
|
+
/** Where it is set. */
|
|
14
|
+
module: string;
|
|
15
|
+
/** What 2.x does when the option is left out. */
|
|
16
|
+
today: string;
|
|
17
|
+
/** What 3.0, and the preview, does when it is left out. */
|
|
18
|
+
next: string;
|
|
19
|
+
/** What to do before turning it on, if anything. */
|
|
20
|
+
before: string;
|
|
21
|
+
}
|
|
22
|
+
/** The defaults 3.0 changes, each turned on by the preview. */
|
|
23
|
+
export declare const DEFAULT_CHANGES: readonly DefaultChange[];
|
|
24
|
+
/**
|
|
25
|
+
* Chooses which release's defaults apply to everything created after it: `3.0` previews the next
|
|
26
|
+
* major release's, `2.x` returns to today's. Call it before creating a tracer, an operation store, or
|
|
27
|
+
* a deployment: each reads the defaults when it is created. Overrides `NEXUS_DEFAULTS`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function useDefaults(release: NexusDefaults): void;
|
|
30
|
+
/**
|
|
31
|
+
* The defaults in effect: as `useDefaults()` chose, else the `NEXUS_DEFAULTS` environment variable,
|
|
32
|
+
* `2.x` or `3.0`, else `2.x`. Throws for any other value of the variable, rather than ignore a typo.
|
|
33
|
+
*/
|
|
34
|
+
export declare function nexusDefaults(): NexusDefaults;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export const DEFAULT_CHANGES = [
|
|
2
|
+
{
|
|
3
|
+
setting: 'Tracer incremental',
|
|
4
|
+
module: 'nexus-ai-pro/tracing',
|
|
5
|
+
today: 'false: a trace is written when its root run finishes',
|
|
6
|
+
next: 'true on a durable trace store: each run is written as it starts and again as it ends, so a run in a process that dies is not lost',
|
|
7
|
+
before: 'Nothing; close what a process that died left open with closeAbandonedRuns().',
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
setting: 'RedisOperationStore index',
|
|
11
|
+
module: 'nexus-ai-pro/operations',
|
|
12
|
+
today: 'false: finding queued work reads every record',
|
|
13
|
+
next: 'true where the client has zadd, zrem, and zrangebyscore: queued and leased work is kept in sorted sets',
|
|
14
|
+
before: 'Run 2.2 or later on every worker sharing the prefix, and call reindex() once.',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
setting: 'Deployments bucketStrategy',
|
|
18
|
+
module: 'nexus-ai-pro/server',
|
|
19
|
+
today: 'fnv1a-v1 for a deployment first recorded',
|
|
20
|
+
next: 'hash-v2 for a deployment first recorded, which spreads sequential thread ids evenly; a recorded deployment keeps its own',
|
|
21
|
+
before: 'Nothing; a deployment already recorded keeps the strategy it records, and bucketing() changes one.',
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
let chosen;
|
|
25
|
+
export function useDefaults(release) {
|
|
26
|
+
chosen = checked(release, 'useDefaults()');
|
|
27
|
+
}
|
|
28
|
+
export function nexusDefaults() {
|
|
29
|
+
if (chosen)
|
|
30
|
+
return chosen;
|
|
31
|
+
const variable = globalThis.process?.env
|
|
32
|
+
?.NEXUS_DEFAULTS;
|
|
33
|
+
return variable === undefined || variable === '' ? '2.x' : checked(variable, 'NEXUS_DEFAULTS');
|
|
34
|
+
}
|
|
35
|
+
function checked(release, where) {
|
|
36
|
+
if (release === '2.x' || release === '3.0')
|
|
37
|
+
return release;
|
|
38
|
+
throw new Error(`${where} takes 2.x or 3.0, not ${JSON.stringify(release)}`);
|
|
39
|
+
}
|
|
@@ -50,6 +50,7 @@ export function protectStreamOutput(stream, security, requestSignal) {
|
|
|
50
50
|
const protectedOutput = security.protectOutput(toPseudoResponse(bufferedText, bufferedToolCalls, sourceMeta));
|
|
51
51
|
security.assertOutputSafe(protectedOutput);
|
|
52
52
|
const safeText = protectedOutput.value.content;
|
|
53
|
+
const textChanged = safeText !== bufferedText;
|
|
53
54
|
const safeToolCalls = protectedOutput.value.toolCalls || [];
|
|
54
55
|
const applied = protectedOutput.value.meta.guardrailsApplied;
|
|
55
56
|
const textIndexes = chunks
|
|
@@ -69,9 +70,11 @@ export function protectStreamOutput(stream, security, requestSignal) {
|
|
|
69
70
|
safeOffset += length;
|
|
70
71
|
if (!content)
|
|
71
72
|
continue;
|
|
73
|
+
const { logprobs, ...text } = chunk;
|
|
72
74
|
yield {
|
|
73
|
-
...
|
|
75
|
+
...text,
|
|
74
76
|
content,
|
|
77
|
+
...(logprobs && !textChanged ? { logprobs } : {}),
|
|
75
78
|
meta: withGuardrails(chunk.meta, applied),
|
|
76
79
|
};
|
|
77
80
|
continue;
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Decisions from any completion model: each question asked on its own, answered with a one-token code,
|
|
3
|
-
* and its probabilities read from the code token's log probabilities, or counted over samples where
|
|
4
|
-
* the model reports none.
|
|
5
|
-
*/
|
|
6
1
|
import type { CompletionRequest } from '../types/messages.js';
|
|
7
2
|
import type { NexusResponse } from '../types/response.js';
|
|
8
3
|
import { type DecisionProvider } from './core.js';
|
|
@@ -25,8 +20,14 @@ export interface CompletionDecisionsOptions {
|
|
|
25
20
|
* a model that reports none or refuses to, which it then asks by sampling from the start.
|
|
26
21
|
*/
|
|
27
22
|
method?: CompletionDecisionMethod;
|
|
28
|
-
/** Calls per question, when sampling. Defaults to 8. */
|
|
23
|
+
/** Calls per question, when sampling, and per box. Defaults to 8. */
|
|
29
24
|
samples?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Whether the model reads pictures, so a decision about the pictures a state holds, or a box, may
|
|
27
|
+
* be asked of it. Defaults to `true`; set `false` for a text-only model, which such a decision then
|
|
28
|
+
* passes over to a fallback.
|
|
29
|
+
*/
|
|
30
|
+
images?: boolean;
|
|
30
31
|
/** The temperature samples are drawn at. Defaults to 1. */
|
|
31
32
|
temperature?: number;
|
|
32
33
|
/** Questions asked at once. Defaults to 8. */
|
|
@@ -41,7 +42,9 @@ export interface CompletionDecisionsOptions {
|
|
|
41
42
|
* Its probabilities are read from the log probabilities of that token's alternatives where the model
|
|
42
43
|
* reports them, the way TypeSafe AI's Jev reads its own; elsewhere the question is asked `samples`
|
|
43
44
|
* times, and each answer counted. Sampled probabilities come in steps of one sample, and cost as many
|
|
44
|
-
* calls.
|
|
45
|
+
* calls. The pictures a state holds are shown to the model after the state, each labelled by its
|
|
46
|
+
* path; a box is asked `samples` times, and is the median of each edge, its confidence how far the
|
|
47
|
+
* samples overlap it.
|
|
45
48
|
*/
|
|
46
49
|
export declare function completionDecisions(client: DecisionCompletionClient, options: CompletionDecisionsOptions): DecisionProvider & {
|
|
47
50
|
readonly sampledModels: ReadonlySet<string>;
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import { DecisionError, } from './core.js';
|
|
1
|
+
import { boxOverlap, DecisionError, } from './core.js';
|
|
2
|
+
import { replacePictures, statePictures } from './pictures.js';
|
|
2
3
|
const LOGPROB_OPTIONS = 20;
|
|
3
4
|
export function completionDecisions(client, options) {
|
|
4
5
|
const method = options.method ?? 'auto';
|
|
6
|
+
const images = options.images !== false;
|
|
5
7
|
const sampled = new Set();
|
|
6
8
|
const info = {
|
|
7
9
|
name: options.name ?? options.model,
|
|
8
10
|
capabilities: {
|
|
9
|
-
types: ['yes-no', 'choice', 'score'],
|
|
11
|
+
types: images ? ['yes-no', 'choice', 'score', 'box'] : ['yes-no', 'choice', 'score'],
|
|
10
12
|
maxOptions: 255,
|
|
11
13
|
maxLevels: 10,
|
|
14
|
+
images,
|
|
12
15
|
defaultModel: options.model,
|
|
13
16
|
},
|
|
14
17
|
chargedByItsCalls: true,
|
|
@@ -21,7 +24,9 @@ export function completionDecisions(client, options) {
|
|
|
21
24
|
messages: [
|
|
22
25
|
{
|
|
23
26
|
role: 'system',
|
|
24
|
-
content:
|
|
27
|
+
content: question.type === 'box'
|
|
28
|
+
? 'You answer where something is in a picture with exactly four numbers, and nothing else.'
|
|
29
|
+
: 'You answer one question about a state with exactly one of the codes given, and nothing else.',
|
|
25
30
|
},
|
|
26
31
|
{ role: 'user', content: prompt },
|
|
27
32
|
],
|
|
@@ -29,6 +34,8 @@ export function completionDecisions(client, options) {
|
|
|
29
34
|
...(context.tenantId !== undefined ? { tenantId: context.tenantId } : {}),
|
|
30
35
|
...extra,
|
|
31
36
|
});
|
|
37
|
+
if (question.type === 'box')
|
|
38
|
+
return askBox(request, model, usage);
|
|
32
39
|
const readable = codes.length <= LOGPROB_OPTIONS && codes.every((code) => code.length === 1);
|
|
33
40
|
if (method !== 'sampling' && readable && !sampled.has(model)) {
|
|
34
41
|
let response;
|
|
@@ -64,6 +71,28 @@ export function completionDecisions(client, options) {
|
|
|
64
71
|
}
|
|
65
72
|
return distributionOf(question, codes, weights);
|
|
66
73
|
};
|
|
74
|
+
const askBox = async (request, model, usage) => {
|
|
75
|
+
if (method === 'logprobs') {
|
|
76
|
+
throw new DecisionError('A box is read from several samples of the answer, not from log probabilities', 'DECISION_UNSUPPORTED');
|
|
77
|
+
}
|
|
78
|
+
const count = Math.max(1, Math.floor(options.samples ?? 8));
|
|
79
|
+
const readings = await Promise.all(Array.from({ length: count }, () => client.complete(request({ temperature: options.temperature ?? 1, maxTokens: 32 })).then((response) => {
|
|
80
|
+
usage.add(response);
|
|
81
|
+
return boxIn(response.content);
|
|
82
|
+
})));
|
|
83
|
+
const boxes = readings.filter((box) => box !== undefined);
|
|
84
|
+
if (boxes.length === 0) {
|
|
85
|
+
throw new DecisionError(`"${model}" answered none of ${count} samples with a box`, 'DECISION_FAILED');
|
|
86
|
+
}
|
|
87
|
+
const box = {
|
|
88
|
+
top: median(boxes.map((item) => item.top)),
|
|
89
|
+
bottom: median(boxes.map((item) => item.bottom)),
|
|
90
|
+
left: median(boxes.map((item) => item.left)),
|
|
91
|
+
right: median(boxes.map((item) => item.right)),
|
|
92
|
+
};
|
|
93
|
+
const agreement = boxes.reduce((sum, item) => sum + boxOverlap(item, box), 0) / count;
|
|
94
|
+
return { type: 'box', box, confidence: agreement };
|
|
95
|
+
};
|
|
67
96
|
return {
|
|
68
97
|
info,
|
|
69
98
|
sampledModels: sampled,
|
|
@@ -112,6 +141,8 @@ class Totals {
|
|
|
112
141
|
}
|
|
113
142
|
}
|
|
114
143
|
function codesOf(question) {
|
|
144
|
+
if (question.type === 'box')
|
|
145
|
+
return [];
|
|
115
146
|
if (question.type === 'yes-no')
|
|
116
147
|
return ['Y', 'N'];
|
|
117
148
|
if (question.type === 'score')
|
|
@@ -121,10 +152,37 @@ function codesOf(question) {
|
|
|
121
152
|
: question.options.map((_, index) => String(index + 1));
|
|
122
153
|
}
|
|
123
154
|
function promptOf(state, question, codes, instructions) {
|
|
124
|
-
const
|
|
125
|
-
const
|
|
155
|
+
const pictures = typeof state === 'string' ? [] : statePictures(state);
|
|
156
|
+
const shown = pictures.length ? replacePictures(state, (picture) => `[picture \`${picture.path}\`]`) : state;
|
|
157
|
+
const text = typeof shown === 'string' ? shown : JSON.stringify(shown, null, 2);
|
|
158
|
+
const asked = questionLines(question, codes, instructions).join('\n');
|
|
159
|
+
if (pictures.length === 0)
|
|
160
|
+
return `State:\n${text}\n\n${asked}`;
|
|
161
|
+
return [
|
|
162
|
+
{ type: 'text', text: `State:\n${text}\n\nThe pictures it holds, by path:` },
|
|
163
|
+
...pictures.flatMap((picture) => [
|
|
164
|
+
{ type: 'text', text: `\`${picture.path}\`:` },
|
|
165
|
+
imagePart(picture.part),
|
|
166
|
+
]),
|
|
167
|
+
{ type: 'text', text: asked },
|
|
168
|
+
];
|
|
169
|
+
}
|
|
170
|
+
function imagePart(part) {
|
|
171
|
+
if (part.type === 'image')
|
|
172
|
+
return part;
|
|
173
|
+
if (part.type === 'asset')
|
|
174
|
+
return { type: 'image', source: { asset: part.asset } };
|
|
175
|
+
return part;
|
|
176
|
+
}
|
|
177
|
+
function questionLines(question, codes, instructions) {
|
|
178
|
+
const lines = [];
|
|
126
179
|
if (instructions)
|
|
127
180
|
lines.push(instructions, '');
|
|
181
|
+
if (question.type === 'box') {
|
|
182
|
+
lines.push(`Find: ${question.question}`);
|
|
183
|
+
lines.push('', "Answer with four numbers from 0 to 1, separated by spaces: the top, bottom, left, and right edges of a box around it, each a fraction of the picture's height or width from its top-left corner.");
|
|
184
|
+
return lines;
|
|
185
|
+
}
|
|
128
186
|
if (question.type === 'yes-no') {
|
|
129
187
|
lines.push(`Question: ${question.question ?? 'Is the statement true of the state?'}`);
|
|
130
188
|
lines.push(`Y: yes${question.criteria?.yes ? `, ${question.criteria.yes}` : ''}`);
|
|
@@ -143,16 +201,30 @@ function promptOf(state, question, codes, instructions) {
|
|
|
143
201
|
});
|
|
144
202
|
}
|
|
145
203
|
lines.push('', `Answer with one code: ${codes.join(', ')}.`);
|
|
146
|
-
return lines
|
|
204
|
+
return lines;
|
|
205
|
+
}
|
|
206
|
+
function boxIn(content) {
|
|
207
|
+
const numbers = (content.match(/-?\d*\.?\d+/g) ?? []).slice(0, 4).map(Number);
|
|
208
|
+
if (numbers.length < 4 || numbers.some((value) => !(value >= 0 && value <= 1)))
|
|
209
|
+
return undefined;
|
|
210
|
+
const [top, bottom, left, right] = numbers;
|
|
211
|
+
return top <= bottom && left <= right ? { top, bottom, left, right } : undefined;
|
|
212
|
+
}
|
|
213
|
+
function median(values) {
|
|
214
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
215
|
+
const middle = Math.floor(sorted.length / 2);
|
|
216
|
+
return sorted.length % 2
|
|
217
|
+
? sorted[middle]
|
|
218
|
+
: (sorted[middle - 1] + sorted[middle]) / 2;
|
|
147
219
|
}
|
|
148
220
|
function codeOf(token) {
|
|
149
221
|
return token.replace(/^[\s"'`*(]+|[\s"'`*).:,]+$/g, '').toUpperCase();
|
|
150
222
|
}
|
|
151
223
|
function weightsFrom(logprobs, codes) {
|
|
152
224
|
const first = logprobs?.find((item) => item.token.trim() !== '');
|
|
153
|
-
if (!first)
|
|
225
|
+
if (!first?.top?.length)
|
|
154
226
|
return undefined;
|
|
155
|
-
const candidates = first.top
|
|
227
|
+
const candidates = first.top;
|
|
156
228
|
const weights = codes.map(() => 0);
|
|
157
229
|
let found = false;
|
|
158
230
|
for (const candidate of candidates) {
|
|
@@ -184,6 +256,8 @@ function distributionOf(question, codes, weights) {
|
|
|
184
256
|
return { type: 'yes-no', yes: share(0) };
|
|
185
257
|
if (question.type === 'score')
|
|
186
258
|
return { type: 'score', probabilities: codes.map((_, index) => share(index)) };
|
|
259
|
+
if (question.type === 'box')
|
|
260
|
+
throw new DecisionError('A box has no codes to weigh', 'DECISION_INVALID');
|
|
187
261
|
return {
|
|
188
262
|
type: 'choice',
|
|
189
263
|
probabilities: Object.fromEntries(question.options.map((option, index) => [option.name, share(index)])),
|