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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { boxOverlap, } from './core.js';
|
|
1
2
|
export function decisionRoute(decisions, options) {
|
|
2
3
|
return async (graph) => {
|
|
3
4
|
const { answers } = await decisions.decide({
|
|
@@ -128,9 +129,23 @@ export function decisionEvaluator(decisions, options) {
|
|
|
128
129
|
const answer = answers.judged;
|
|
129
130
|
const metadata = {
|
|
130
131
|
confidence: answer.confidence,
|
|
131
|
-
|
|
132
|
+
...(answer.type === 'box'
|
|
133
|
+
? { box: answer.box }
|
|
134
|
+
: { probabilities: answer.type === 'yes-no' ? { yes: answer.probability } : answer.probabilities }),
|
|
132
135
|
model,
|
|
133
136
|
};
|
|
137
|
+
if (answer.type === 'box') {
|
|
138
|
+
const where = options.where?.(context);
|
|
139
|
+
if (!where)
|
|
140
|
+
return { key: options.key, score: answer.confidence, metadata };
|
|
141
|
+
const overlap = boxOverlap(answer.box, where);
|
|
142
|
+
return {
|
|
143
|
+
key: options.key,
|
|
144
|
+
score: overlap,
|
|
145
|
+
passed: overlap >= (options.overlap ?? 0.5),
|
|
146
|
+
metadata,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
134
149
|
if (answer.type === 'yes-no') {
|
|
135
150
|
const passWhen = options.passing !== false;
|
|
136
151
|
return {
|
package/dist/decisions/mock.d.ts
CHANGED
|
@@ -7,8 +7,9 @@ export interface MockDecisionProviderOptions {
|
|
|
7
7
|
/** The model it reports. Defaults to `mock-decision`. */
|
|
8
8
|
model?: string;
|
|
9
9
|
/**
|
|
10
|
-
* Answers each question with its probabilities, from the question's name, the question,
|
|
11
|
-
* state. Returning `undefined`, or leaving it out, answers with an even
|
|
10
|
+
* Answers each question with its probabilities, or a box, from the question's name, the question,
|
|
11
|
+
* and the state, pictures and all. Returning `undefined`, or leaving it out, answers with an even
|
|
12
|
+
* spread, and a box with the whole picture: confidence 0.
|
|
12
13
|
*/
|
|
13
14
|
answer?: (name: string, question: DecisionProviderQuestion, state: DecisionState) => DecisionDistribution | undefined;
|
|
14
15
|
/** Fails every call with this error, or the error the function returns. */
|
|
@@ -18,7 +19,8 @@ export interface MockDecisionProviderOptions {
|
|
|
18
19
|
}
|
|
19
20
|
/**
|
|
20
21
|
* Answers decisions as `answer` says, without a model, and keeps every request it was asked in
|
|
21
|
-
* `requests`. It takes every question type, 255 options, and 10 levels, as TypeSafe AI's Jev
|
|
22
|
+
* `requests`. It takes every question type, pictures, 255 options, and 10 levels, as TypeSafe AI's Jev
|
|
23
|
+
* does.
|
|
22
24
|
*/
|
|
23
25
|
export declare class MockDecisionProvider implements DecisionProvider {
|
|
24
26
|
private readonly options;
|
package/dist/decisions/mock.js
CHANGED
|
@@ -7,9 +7,10 @@ export class MockDecisionProvider {
|
|
|
7
7
|
this.info = {
|
|
8
8
|
name: options.name ?? 'mock',
|
|
9
9
|
capabilities: {
|
|
10
|
-
types: ['yes-no', 'choice', 'score'],
|
|
10
|
+
types: ['yes-no', 'choice', 'score', 'box'],
|
|
11
11
|
maxOptions: 255,
|
|
12
12
|
maxLevels: 10,
|
|
13
|
+
images: true,
|
|
13
14
|
defaultModel: options.model ?? 'mock-decision',
|
|
14
15
|
},
|
|
15
16
|
};
|
|
@@ -37,6 +38,8 @@ export class MockDecisionProvider {
|
|
|
37
38
|
function evenly(question) {
|
|
38
39
|
if (question.type === 'yes-no')
|
|
39
40
|
return { type: 'yes-no', yes: 0.5 };
|
|
41
|
+
if (question.type === 'box')
|
|
42
|
+
return { type: 'box', box: { top: 0, bottom: 1, left: 0, right: 1 }, confidence: 0 };
|
|
40
43
|
if (question.type === 'choice') {
|
|
41
44
|
return {
|
|
42
45
|
type: 'choice',
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pictures a decision's state holds: image parts, or image assets, anywhere in an object or a
|
|
3
|
+
* list, each named by its path, which a question writes in backticks, such as `photo`, `faces.happy`,
|
|
4
|
+
* or `frames[2].frame`.
|
|
5
|
+
*/
|
|
6
|
+
import { type MediaResolveOptions } from '../evaluate/media.js';
|
|
7
|
+
import type { MediaPart } from '../types/evaluate.js';
|
|
8
|
+
/** A picture in a decision's state, and where it is. */
|
|
9
|
+
export interface DecisionPicture {
|
|
10
|
+
/** Its path in the state, as a question names it in backticks: `photo`, `faces.happy`, or `frames[2].frame`. */
|
|
11
|
+
path: string;
|
|
12
|
+
/** The picture: an image part, `{ type: 'image', source }`, or an image asset. */
|
|
13
|
+
part: MediaPart;
|
|
14
|
+
}
|
|
15
|
+
/** A picture's bytes, read for a provider that sends them. */
|
|
16
|
+
export interface ReadDecisionPicture extends DecisionPicture {
|
|
17
|
+
/** Its bytes. */
|
|
18
|
+
bytes: Uint8Array;
|
|
19
|
+
/** Its MIME type, as its first bytes say, whatever it was declared as. */
|
|
20
|
+
mimeType: string;
|
|
21
|
+
/** Its width in pixels, from its header. */
|
|
22
|
+
width?: number;
|
|
23
|
+
/** Its height in pixels. */
|
|
24
|
+
height?: number;
|
|
25
|
+
}
|
|
26
|
+
/** Every picture in a state, in the order they appear, each with its path. A picture used twice is listed at both. */
|
|
27
|
+
export declare function statePictures(state: unknown): DecisionPicture[];
|
|
28
|
+
/**
|
|
29
|
+
* The state with each picture replaced by what `replace` returns for it, copying only the objects and
|
|
30
|
+
* lists on the way to one, so a state without pictures comes back as it was given.
|
|
31
|
+
*/
|
|
32
|
+
export declare function replacePictures(state: unknown, replace: (picture: DecisionPicture) => unknown): unknown;
|
|
33
|
+
/**
|
|
34
|
+
* Identifies a picture without its bytes, for a cache key or a trace: a digest of what it holds, or
|
|
35
|
+
* where it is when it is a file or at a URL, so a picture changed at the same path is keyed the same.
|
|
36
|
+
*/
|
|
37
|
+
export declare function pictureDigest(part: MediaPart): string;
|
|
38
|
+
/**
|
|
39
|
+
* Reads a picture's bytes, with the media family's readers: bytes and base64 as they are, a file with
|
|
40
|
+
* `readFile`, a URL with `fetch`, and a stored asset from `assets`. Throws `DecisionError`
|
|
41
|
+
* `DECISION_INVALID`, naming the picture's path, for one that cannot be read or is not a picture.
|
|
42
|
+
*/
|
|
43
|
+
export declare function readPicture(picture: DecisionPicture, options?: MediaResolveOptions): Promise<ReadDecisionPicture>;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { resolveMedia } from '../evaluate/media.js';
|
|
2
|
+
import { asMediaPart } from '../evaluate/trace.js';
|
|
3
|
+
import { sha256Hex } from '../utils/sha256.js';
|
|
4
|
+
import { DecisionError } from './errors.js';
|
|
5
|
+
const MAX_DEPTH = 256;
|
|
6
|
+
export function statePictures(state) {
|
|
7
|
+
const found = [];
|
|
8
|
+
replacePictures(state, (picture) => {
|
|
9
|
+
found.push(picture);
|
|
10
|
+
return picture.part;
|
|
11
|
+
});
|
|
12
|
+
return found;
|
|
13
|
+
}
|
|
14
|
+
export function replacePictures(state, replace) {
|
|
15
|
+
const above = new Set();
|
|
16
|
+
const visit = (item, path, depth) => {
|
|
17
|
+
if (item === null || typeof item !== 'object' || depth > MAX_DEPTH || above.has(item))
|
|
18
|
+
return item;
|
|
19
|
+
if (ArrayBuffer.isView(item) || item instanceof ArrayBuffer)
|
|
20
|
+
return item;
|
|
21
|
+
const part = pictureOf(item);
|
|
22
|
+
if (part)
|
|
23
|
+
return replace({ path, part });
|
|
24
|
+
above.add(item);
|
|
25
|
+
try {
|
|
26
|
+
if (Array.isArray(item)) {
|
|
27
|
+
let changed = false;
|
|
28
|
+
const copy = item.map((entry, index) => {
|
|
29
|
+
const next = visit(entry, `${path}[${index}]`, depth + 1);
|
|
30
|
+
if (next !== entry)
|
|
31
|
+
changed = true;
|
|
32
|
+
return next;
|
|
33
|
+
});
|
|
34
|
+
return changed ? copy : item;
|
|
35
|
+
}
|
|
36
|
+
const prototype = Object.getPrototypeOf(item);
|
|
37
|
+
if (prototype !== Object.prototype && prototype !== null)
|
|
38
|
+
return item;
|
|
39
|
+
let changed = false;
|
|
40
|
+
const copy = {};
|
|
41
|
+
for (const [key, entry] of Object.entries(item)) {
|
|
42
|
+
const next = visit(entry, path ? `${path}.${key}` : key, depth + 1);
|
|
43
|
+
if (next !== entry)
|
|
44
|
+
changed = true;
|
|
45
|
+
copy[key] = next;
|
|
46
|
+
}
|
|
47
|
+
return changed ? copy : item;
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
above.delete(item);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
return visit(state, '', 0);
|
|
54
|
+
}
|
|
55
|
+
export function pictureDigest(part) {
|
|
56
|
+
const source = (part.type === 'asset' ? part.asset.location : part.source);
|
|
57
|
+
if (typeof source.base64 === 'string')
|
|
58
|
+
return `sha256:${sha256Hex(source.base64)}`;
|
|
59
|
+
const bytes = source.buffer ?? source.data;
|
|
60
|
+
if (bytes instanceof Uint8Array)
|
|
61
|
+
return `sha256:${sha256Hex(bytes)}`;
|
|
62
|
+
if (typeof source.path === 'string')
|
|
63
|
+
return `path:${source.path}`;
|
|
64
|
+
if (typeof source.url === 'string')
|
|
65
|
+
return `url:${source.url}`;
|
|
66
|
+
if (typeof source.assetId === 'string')
|
|
67
|
+
return `asset:${source.assetId}`;
|
|
68
|
+
if (source.asset && typeof source.asset === 'object')
|
|
69
|
+
return pictureDigest({ type: 'asset', asset: source.asset });
|
|
70
|
+
return `sha256:${sha256Hex(JSON.stringify(source) ?? '')}`;
|
|
71
|
+
}
|
|
72
|
+
export async function readPicture(picture, options = {}) {
|
|
73
|
+
const media = await resolveMedia(picture.part, options);
|
|
74
|
+
if (!media.bytes) {
|
|
75
|
+
const why = media.unreadable ??
|
|
76
|
+
(media.path !== undefined
|
|
77
|
+
? `it is the file ${media.path}, and no readFile was given to read it`
|
|
78
|
+
: media.url !== undefined
|
|
79
|
+
? `it is at ${media.url}, and no fetch was given to fetch it`
|
|
80
|
+
: 'it holds no bytes');
|
|
81
|
+
throw new DecisionError(`The picture at \`${picture.path}\` could not be read: ${why}`, 'DECISION_INVALID');
|
|
82
|
+
}
|
|
83
|
+
if (media.kind !== 'image' || !media.mimeType?.startsWith('image/')) {
|
|
84
|
+
throw new DecisionError(`The picture at \`${picture.path}\` is not an image its bytes name${media.mimeType ? `, but ${media.mimeType}` : ''}`, 'DECISION_INVALID');
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
...picture,
|
|
88
|
+
bytes: media.bytes,
|
|
89
|
+
mimeType: media.mimeType,
|
|
90
|
+
...(media.width !== undefined ? { width: media.width } : {}),
|
|
91
|
+
...(media.height !== undefined ? { height: media.height } : {}),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function pictureOf(item) {
|
|
95
|
+
const part = asMediaPart(item);
|
|
96
|
+
if (!part)
|
|
97
|
+
return undefined;
|
|
98
|
+
if (part.type === 'image')
|
|
99
|
+
return part;
|
|
100
|
+
if (part.type === 'asset' && part.asset.mimeType.startsWith('image/'))
|
|
101
|
+
return part;
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* TypeSafe AI's decision models, Jev first, over its System One API: every question of a decision in
|
|
3
|
+
* one call, each answered with its probabilities, or with a box around something in a picture, and no
|
|
4
|
+
* text to parse.
|
|
5
|
+
*/
|
|
6
|
+
import type { MediaResolveOptions } from '../evaluate/media.js';
|
|
7
|
+
import type { DecisionCallContext, DecisionProvider, DecisionProviderInfo, DecisionProviderRequest, DecisionProviderResult } from './core.js';
|
|
2
8
|
/** Options for `TypeSafeDecisionProvider`. */
|
|
3
9
|
export interface TypeSafeDecisionProviderOptions {
|
|
4
10
|
/** A TypeSafe AI API key, from its console. A tenant's resolved credential replaces it per call. */
|
|
@@ -15,6 +21,18 @@ export interface TypeSafeDecisionProviderOptions {
|
|
|
15
21
|
* than its usual context, and marks as experimental.
|
|
16
22
|
*/
|
|
17
23
|
longContext?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Reads the pictures a state holds by reference, with the media family's readers: `readFile` for a
|
|
26
|
+
* file, `fetch` for a URL, and `assets` for a stored asset. A picture held as bytes or base64 needs
|
|
27
|
+
* none of them. Without them, a picture by reference fails the decision, naming it.
|
|
28
|
+
*/
|
|
29
|
+
media?: MediaResolveOptions;
|
|
30
|
+
/**
|
|
31
|
+
* How a decision's pictures travel: `multipart`, the default, each as a part of its own beside the
|
|
32
|
+
* request, as bytes; or `base64`, within the JSON request, a third larger. Either way a request
|
|
33
|
+
* holds at most 32 MiB, so multipart carries more.
|
|
34
|
+
*/
|
|
35
|
+
pictures?: 'multipart' | 'base64';
|
|
18
36
|
/** How long a call may take before it fails, in milliseconds. Defaults to 30 seconds. */
|
|
19
37
|
timeoutMs?: number;
|
|
20
38
|
/** Headers added to every request. */
|
|
@@ -24,9 +42,12 @@ export interface TypeSafeDecisionProviderOptions {
|
|
|
24
42
|
}
|
|
25
43
|
/**
|
|
26
44
|
* TypeSafe AI's System One API (`POST /v1/systemone`): Jev, a model that answers only typed
|
|
27
|
-
* questions, a yes or no ("noul"), a choice among up to 255 options,
|
|
28
|
-
*
|
|
29
|
-
* one call and is answered in isolation, against the same state.
|
|
45
|
+
* questions, a yes or no ("noul"), a choice among up to 255 options, a score on up to ten levels, or a
|
|
46
|
+
* box around something in a picture, and bills for input tokens alone. Every question of a decision
|
|
47
|
+
* goes in one call and is answered in isolation, against the same state. The pictures a state holds
|
|
48
|
+
* are sent as the API's assets, each as a part of a multipart request unless `pictures` says
|
|
49
|
+
* `base64`, with the `TypeSafe-Danger-Zone-Images` header it requires for them and for a box, both of
|
|
50
|
+
* which it marks as experimental; a box needs Jev 1.14 or later.
|
|
30
51
|
*
|
|
31
52
|
* ```ts
|
|
32
53
|
* const decisions = new DecisionManager({ providers: { jev: new TypeSafeDecisionProvider({ apiKey: process.env.TYPESAFE_API_KEY! }) } });
|
|
@@ -34,12 +55,19 @@ export interface TypeSafeDecisionProviderOptions {
|
|
|
34
55
|
*/
|
|
35
56
|
export declare class TypeSafeDecisionProvider implements DecisionProvider {
|
|
36
57
|
private readonly options;
|
|
37
|
-
/** Its name and what it takes: every question type, 255 options, and 10 levels. */
|
|
58
|
+
/** Its name and what it takes: every question type, pictures, 255 options, and 10 levels. */
|
|
38
59
|
readonly info: DecisionProviderInfo;
|
|
39
60
|
constructor(options: TypeSafeDecisionProviderOptions);
|
|
40
61
|
/** Answers every question of a decision in one call. */
|
|
41
62
|
decide(request: DecisionProviderRequest, context: DecisionCallContext): Promise<DecisionProviderResult>;
|
|
42
63
|
/** The models the key may use, from `GET /v1/models`. */
|
|
43
64
|
listModels(context?: Partial<DecisionCallContext>): Promise<string[]>;
|
|
65
|
+
/**
|
|
66
|
+
* The state with each picture replaced by a reference of its own, and the pictures as assets by those
|
|
67
|
+
* references; a state without pictures as it was. A new reference per picture per request, as the
|
|
68
|
+
* API advises, since it matches a reference wherever its text appears. Sent multipart, each picture's
|
|
69
|
+
* part is named by its reference too.
|
|
70
|
+
*/
|
|
71
|
+
private withAssets;
|
|
44
72
|
private call;
|
|
45
73
|
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
import { encodeBase64 } from '../utils/base64.js';
|
|
1
2
|
import { linkSignals } from '../utils/signals.js';
|
|
2
|
-
import { DecisionError
|
|
3
|
+
import { DecisionError } from './errors.js';
|
|
4
|
+
import { readPicture, replacePictures, statePictures } from './pictures.js';
|
|
5
|
+
const PICTURE_BYTES = 16 * 1024 * 1024;
|
|
6
|
+
const PICTURE_SIDE = { min: 280, max: 1568 };
|
|
7
|
+
const REQUEST_BYTES = 32 * 1024 * 1024;
|
|
8
|
+
const PART_OVERHEAD = 256;
|
|
3
9
|
export class TypeSafeDecisionProvider {
|
|
4
10
|
options;
|
|
5
11
|
info;
|
|
@@ -8,16 +14,40 @@ export class TypeSafeDecisionProvider {
|
|
|
8
14
|
this.info = {
|
|
9
15
|
name: 'typesafe',
|
|
10
16
|
capabilities: {
|
|
11
|
-
types: ['yes-no', 'choice', 'score'],
|
|
17
|
+
types: ['yes-no', 'choice', 'score', 'box'],
|
|
12
18
|
maxOptions: 255,
|
|
13
19
|
maxLevels: 10,
|
|
20
|
+
images: true,
|
|
14
21
|
defaultModel: options.model ?? 'jev-latest',
|
|
15
22
|
},
|
|
16
23
|
};
|
|
17
24
|
}
|
|
18
25
|
async decide(request, context) {
|
|
26
|
+
context.signal.throwIfAborted();
|
|
27
|
+
const boxes = Object.keys(request.questions).filter((name) => request.questions[name]?.type === 'box');
|
|
28
|
+
const before = boxes.length ? beforeBoxes(request.model) : undefined;
|
|
29
|
+
if (before) {
|
|
30
|
+
throw new DecisionError(`TypeSafe AI answers a box, such as "${boxes[0]}", on Jev 1.14 or later, not ${before}`, 'DECISION_UNSUPPORTED');
|
|
31
|
+
}
|
|
32
|
+
const multipart = (this.options.pictures ?? 'multipart') === 'multipart';
|
|
33
|
+
const { state, assets, parts } = await this.withAssets(request.state, context, multipart);
|
|
19
34
|
const questions = Object.fromEntries(Object.entries(request.questions).map(([name, question]) => [name, wireQuestion(question)]));
|
|
20
|
-
const
|
|
35
|
+
const json = JSON.stringify({ model: request.model, state, questions, ...(assets ? { assets } : {}) });
|
|
36
|
+
const size = new TextEncoder().encode(json).byteLength +
|
|
37
|
+
(parts.length ? parts.reduce((total, part) => total + part.bytes.byteLength + PART_OVERHEAD, PART_OVERHEAD) : 0);
|
|
38
|
+
if (assets && size > REQUEST_BYTES) {
|
|
39
|
+
throw new DecisionError(`The decision is ${size} bytes with its pictures, and TypeSafe AI takes at most ${REQUEST_BYTES}`, 'DECISION_INVALID');
|
|
40
|
+
}
|
|
41
|
+
let body = json;
|
|
42
|
+
if (parts.length) {
|
|
43
|
+
const form = new FormData();
|
|
44
|
+
form.append('request', json);
|
|
45
|
+
for (const part of parts) {
|
|
46
|
+
form.append(part.name, new Blob([part.bytes], { type: `image/${part.format}` }), `${part.name}.${part.format === 'png' ? 'png' : 'jpg'}`);
|
|
47
|
+
}
|
|
48
|
+
body = form;
|
|
49
|
+
}
|
|
50
|
+
const payload = await this.call('/v1/systemone', { method: 'POST', body }, context, assets || boxes.length ? { 'TypeSafe-Danger-Zone-Images': 'true' } : {});
|
|
21
51
|
const distributions = {};
|
|
22
52
|
for (const [name, question] of Object.entries(request.questions)) {
|
|
23
53
|
const answer = payload.answers?.[name];
|
|
@@ -51,7 +81,29 @@ export class TypeSafeDecisionProvider {
|
|
|
51
81
|
: String(item?.id ?? item?.name ?? ''))
|
|
52
82
|
.filter(Boolean);
|
|
53
83
|
}
|
|
54
|
-
async
|
|
84
|
+
async withAssets(state, context, multipart) {
|
|
85
|
+
const pictures = typeof state === 'string' ? [] : statePictures(state);
|
|
86
|
+
if (pictures.length === 0)
|
|
87
|
+
return { state, parts: [] };
|
|
88
|
+
const read = await Promise.all(pictures.map((picture) => readPicture(picture, this.options.media)));
|
|
89
|
+
context.signal.throwIfAborted();
|
|
90
|
+
const references = new Map();
|
|
91
|
+
const assets = {};
|
|
92
|
+
const parts = [];
|
|
93
|
+
for (const picture of read) {
|
|
94
|
+
const format = fitting(picture);
|
|
95
|
+
const reference = globalThis.crypto.randomUUID();
|
|
96
|
+
references.set(picture.path, reference);
|
|
97
|
+
if (multipart) {
|
|
98
|
+
assets[reference] = { type: 'image', format, part: reference };
|
|
99
|
+
parts.push({ name: reference, format, bytes: picture.bytes });
|
|
100
|
+
}
|
|
101
|
+
else
|
|
102
|
+
assets[reference] = { type: 'image', format, data_base64: encodeBase64(picture.bytes) };
|
|
103
|
+
}
|
|
104
|
+
return { state: replacePictures(state, (picture) => references.get(picture.path)), assets, parts };
|
|
105
|
+
}
|
|
106
|
+
async call(path, init, context, extra = {}) {
|
|
55
107
|
const credential = await context.credential?.(this.info.name);
|
|
56
108
|
const linked = linkSignals([context.signal, AbortSignal.timeout(this.options.timeoutMs ?? 30_000)]);
|
|
57
109
|
const signal = linked.signal;
|
|
@@ -61,9 +113,10 @@ export class TypeSafeDecisionProvider {
|
|
|
61
113
|
response = await (this.options.fetch ?? fetch)(`${(this.options.baseUrl ?? 'https://api.typesafe.ai').replace(/\/+$/, '')}${path}`, {
|
|
62
114
|
...init,
|
|
63
115
|
headers: {
|
|
64
|
-
...(init.body ? { 'content-type': 'application/json' } : {}),
|
|
116
|
+
...(typeof init.body === 'string' ? { 'content-type': 'application/json' } : {}),
|
|
65
117
|
authorization: `Bearer ${credential?.apiKey ?? this.options.apiKey}`,
|
|
66
118
|
...(this.options.longContext ? { 'TypeSafe-Danger-Zone-Long-Context': 'true' } : {}),
|
|
119
|
+
...extra,
|
|
67
120
|
...this.options.headers,
|
|
68
121
|
...credential?.headers,
|
|
69
122
|
},
|
|
@@ -89,7 +142,51 @@ export class TypeSafeDecisionProvider {
|
|
|
89
142
|
}
|
|
90
143
|
}
|
|
91
144
|
}
|
|
145
|
+
function beforeBoxes(model) {
|
|
146
|
+
const version = /^jev-(\d+)\.(\d+)/.exec(model);
|
|
147
|
+
if (!version)
|
|
148
|
+
return undefined;
|
|
149
|
+
const [major, minor] = [Number(version[1]), Number(version[2])];
|
|
150
|
+
return major < 1 || (major === 1 && minor < 14) ? model : undefined;
|
|
151
|
+
}
|
|
152
|
+
function fitting(picture) {
|
|
153
|
+
const at = `The picture at \`${picture.path}\``;
|
|
154
|
+
const refuse = (why) => new DecisionError(`${at} ${why}; TypeSafe AI refuses it rather than convert or resize it`, 'DECISION_INVALID');
|
|
155
|
+
if (picture.mimeType !== 'image/png' && picture.mimeType !== 'image/jpeg') {
|
|
156
|
+
throw refuse(`is ${picture.mimeType}, and TypeSafe AI takes PNG and JPEG`);
|
|
157
|
+
}
|
|
158
|
+
if (picture.mimeType === 'image/png' && animated(picture.bytes))
|
|
159
|
+
throw refuse('is an animated PNG');
|
|
160
|
+
if (picture.bytes.length > PICTURE_BYTES) {
|
|
161
|
+
throw refuse(`is ${picture.bytes.length} bytes, over TypeSafe AI's ${PICTURE_BYTES}`);
|
|
162
|
+
}
|
|
163
|
+
const { width, height } = picture;
|
|
164
|
+
if (width === undefined || height === undefined)
|
|
165
|
+
throw refuse('has a header its size cannot be read from');
|
|
166
|
+
if ([width, height].some((side) => side < PICTURE_SIDE.min || side > PICTURE_SIDE.max)) {
|
|
167
|
+
throw refuse(`is ${width} by ${height} pixels, and TypeSafe AI takes ${PICTURE_SIDE.min} to ${PICTURE_SIDE.max} a side`);
|
|
168
|
+
}
|
|
169
|
+
return picture.mimeType === 'image/png' ? 'png' : 'jpeg';
|
|
170
|
+
}
|
|
171
|
+
function animated(bytes) {
|
|
172
|
+
let offset = 8;
|
|
173
|
+
while (offset + 8 <= bytes.length) {
|
|
174
|
+
const length = ((bytes[offset] ?? 0) << 24) |
|
|
175
|
+
((bytes[offset + 1] ?? 0) << 16) |
|
|
176
|
+
((bytes[offset + 2] ?? 0) << 8) |
|
|
177
|
+
(bytes[offset + 3] ?? 0);
|
|
178
|
+
const type = String.fromCharCode(...bytes.subarray(offset + 4, offset + 8));
|
|
179
|
+
if (type === 'acTL')
|
|
180
|
+
return true;
|
|
181
|
+
if (type === 'IDAT' || type === 'IEND' || length < 0)
|
|
182
|
+
return false;
|
|
183
|
+
offset += 12 + length;
|
|
184
|
+
}
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
92
187
|
function wireQuestion(question) {
|
|
188
|
+
if (question.type === 'box')
|
|
189
|
+
return { type: 'box', instructions: question.question };
|
|
93
190
|
const instructions = question.question ? { instructions: question.question } : {};
|
|
94
191
|
if (question.type === 'yes-no') {
|
|
95
192
|
const criteria = {
|
|
@@ -118,21 +215,37 @@ function distributionOf(name, question, answer) {
|
|
|
118
215
|
throw invalidAnswer(name, 'choice', answer);
|
|
119
216
|
return { type: 'choice', probabilities: answer.probabilities };
|
|
120
217
|
}
|
|
218
|
+
if (question.type === 'box') {
|
|
219
|
+
const box = answer.box;
|
|
220
|
+
if (!box || typeof answer.confidence !== 'number')
|
|
221
|
+
throw invalidAnswer(name, 'box', answer);
|
|
222
|
+
const edges = box;
|
|
223
|
+
return {
|
|
224
|
+
type: 'box',
|
|
225
|
+
box: { top: edges.top, bottom: edges.bottom, left: edges.left, right: edges.right },
|
|
226
|
+
confidence: answer.confidence,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
121
229
|
if (!answer.probabilities)
|
|
122
230
|
throw invalidAnswer(name, 'score', answer);
|
|
123
231
|
const probabilities = answer.probabilities;
|
|
124
232
|
return { type: 'score', probabilities: question.levels.map((_, level) => probabilities[String(level)] ?? 0) };
|
|
125
233
|
}
|
|
126
234
|
function invalidAnswer(name, type, answer) {
|
|
127
|
-
return new DecisionError(`TypeSafe AI answered "${name}" without the ${type}
|
|
235
|
+
return new DecisionError(`TypeSafe AI answered "${name}" without the ${type} it asks for: ${JSON.stringify(answer).slice(0, 200)}`, 'DECISION_FAILED');
|
|
128
236
|
}
|
|
129
237
|
function failure(status, text) {
|
|
130
|
-
if (status === 422) {
|
|
238
|
+
if (status === 422 || status === 400 || status === 413) {
|
|
131
239
|
let detail = text.slice(0, 300);
|
|
132
240
|
try {
|
|
133
241
|
const parsed = JSON.parse(text);
|
|
134
242
|
if (Array.isArray(parsed.detail)) {
|
|
135
|
-
detail = parsed.detail
|
|
243
|
+
detail = parsed.detail
|
|
244
|
+
.map((item) => `${(item.loc ?? []).join('.')}: ${item.msg ?? 'invalid'}`)
|
|
245
|
+
.join('; ');
|
|
246
|
+
}
|
|
247
|
+
else if (typeof parsed.detail === 'string') {
|
|
248
|
+
detail = parsed.detail;
|
|
136
249
|
}
|
|
137
250
|
}
|
|
138
251
|
catch {
|
package/dist/doctor/index.d.ts
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Checks a deployment before it serves traffic: the runtime, credentials, the database and its
|
|
3
|
-
* migrations, Redis, the operation queue, the model registry, provider health, and settings that
|
|
4
|
-
* only work in one process. `nexus doctor` runs the same checks from a shell.
|
|
5
|
-
*
|
|
6
|
-
* Every check is cheap and read-only, and anything heavy — the model registry, the migration lists —
|
|
7
|
-
* is loaded only when its check runs, so an application can call `diagnose()` at boot or from a
|
|
8
|
-
* health endpoint.
|
|
9
|
-
*/
|
|
10
1
|
import type { NexusAIConfig } from '../types/config.js';
|
|
11
2
|
import type { SchemaMigration } from '../utils/schema-migrations.js';
|
|
12
3
|
/** How one check came out: `fail` breaks the deployment, `warn` needs a look, `skip` had nothing to check. */
|
|
@@ -97,6 +88,12 @@ export interface DoctorOptions {
|
|
|
97
88
|
models?: NexusAIConfig['models'];
|
|
98
89
|
/** How the application is deployed. */
|
|
99
90
|
deployment?: DoctorDeployment;
|
|
91
|
+
/**
|
|
92
|
+
* A release to check readiness for: `3.0` lists, for the stores `deployment` describes, what 3.0's
|
|
93
|
+
* defaults would change and what to do first, or confirms the preview of them is on. Without it, the
|
|
94
|
+
* check is skipped.
|
|
95
|
+
*/
|
|
96
|
+
upgrade?: '3.0';
|
|
100
97
|
/** Replaces the clock, for tests. */
|
|
101
98
|
now?: () => Date;
|
|
102
99
|
}
|
package/dist/doctor/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { nexusDefaults } from '../core/defaults.js';
|
|
1
2
|
import { createRequire } from 'node:module';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
const PEERS = {
|
|
@@ -55,6 +56,7 @@ export async function diagnose(options = {}) {
|
|
|
55
56
|
await run('health', 'Provider health', () => healthCheck(options.ai));
|
|
56
57
|
await run('topology', 'Settings that only work in one process', () => topologyCheck(options.deployment));
|
|
57
58
|
await run('settings', 'Opt-in settings a deployment needs', () => settingsCheck(options.deployment));
|
|
59
|
+
await run('upgrade', 'What 3.0 changes', () => upgradeCheck(options.upgrade, options.deployment));
|
|
58
60
|
const failures = checks.filter((check) => check.status === 'fail').length;
|
|
59
61
|
const warnings = checks.filter((check) => check.status === 'warn').length;
|
|
60
62
|
return { checks, ok: failures === 0, failures, warnings };
|
|
@@ -266,6 +268,45 @@ function settingsCheck(deployment) {
|
|
|
266
268
|
return { status: 'ok', detail: 'No store lacks a setting a deployment needs.' };
|
|
267
269
|
return { status: 'warn', detail: `${findings.join('; ')}.`, hint: [...hints].join(' ') };
|
|
268
270
|
}
|
|
271
|
+
async function upgradeCheck(upgrade, deployment) {
|
|
272
|
+
if (upgrade === undefined)
|
|
273
|
+
return { status: 'skip', detail: 'No release asked about: pass upgrade 3.0.' };
|
|
274
|
+
if (upgrade !== '3.0')
|
|
275
|
+
throw new Error(`Readiness is checked for 3.0, not ${JSON.stringify(upgrade)}`);
|
|
276
|
+
if (nexusDefaults() === '3.0') {
|
|
277
|
+
return { status: 'ok', detail: "3.0's defaults are on already: what runs here is what 3.0 runs." };
|
|
278
|
+
}
|
|
279
|
+
const stores = Object.entries(deployment?.stores ?? {});
|
|
280
|
+
if (stores.length === 0)
|
|
281
|
+
return { status: 'skip', detail: 'No stores described.' };
|
|
282
|
+
const findings = [];
|
|
283
|
+
for (const [name, value] of stores) {
|
|
284
|
+
const kind = kindOf(value);
|
|
285
|
+
if (kind === 'Tracer') {
|
|
286
|
+
const tracer = value;
|
|
287
|
+
if (tracer.incremental === false && tracer.store && tracer.store.durable !== false) {
|
|
288
|
+
findings.push(`${name} writes each trace when its root finishes; 3.0 writes each run as it starts and ends, unless it is created with incremental: false`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (kind === 'RedisOperationStore' && value.indexed === false) {
|
|
292
|
+
findings.push(`${name} reads every record to find queued work; 3.0 keeps an index, unless it is created with index: false: first run 2.2 or later on every worker sharing its prefix, and call reindex() once`);
|
|
293
|
+
}
|
|
294
|
+
if (kind === 'Deployments' && typeof value.list === 'function') {
|
|
295
|
+
const records = await value.list();
|
|
296
|
+
const unrecorded = records.filter((record) => record.version === 0 && record.bucketStrategy === undefined);
|
|
297
|
+
if (unrecorded.length > 0) {
|
|
298
|
+
findings.push(`${name} has not yet recorded ${unrecorded.map((record) => record.assistant).join(', ')}; 3.0 would first record ${unrecorded.length === 1 ? 'it' : 'them'} with hash-v2, unless registered with bucketStrategy: 'fnv1a-v1'`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (findings.length === 0)
|
|
303
|
+
return { status: 'ok', detail: "Nothing described here changes under 3.0's defaults." };
|
|
304
|
+
return {
|
|
305
|
+
status: 'warn',
|
|
306
|
+
detail: `${findings.join('; ')}.`,
|
|
307
|
+
hint: "Run with 3.0's defaults now: call useDefaults('3.0') before creating anything, or set NEXUS_DEFAULTS=3.0.",
|
|
308
|
+
};
|
|
309
|
+
}
|
|
269
310
|
function kindOf(value) {
|
|
270
311
|
return value?.constructor?.name;
|
|
271
312
|
}
|
|
@@ -52,7 +52,7 @@ export declare class GoogleEmbeddingProvider extends HttpEmbeddingProvider {
|
|
|
52
52
|
readonly info: EmbeddingProviderInfo;
|
|
53
53
|
constructor(options?: EmbeddingAdapterOptions);
|
|
54
54
|
protected authHeaders(key: string | undefined): Record<string, string>;
|
|
55
|
-
/** Embeds a batch of inputs: texts, or parts with images, audio, and documents. */
|
|
55
|
+
/** Embeds a batch of inputs: texts, or parts with images, audio, video, and documents. */
|
|
56
56
|
embed(request: EmbeddingProviderRequest, context: EmbeddingProviderCallContext): Promise<EmbeddingProviderResult>;
|
|
57
57
|
}
|
|
58
58
|
/** Cohere v2 `/embed`. */
|
|
@@ -100,7 +100,7 @@ export class GoogleEmbeddingProvider extends HttpEmbeddingProvider {
|
|
|
100
100
|
models: ['gemini-embedding-001', 'gemini-embedding-2', 'text-embedding-004'],
|
|
101
101
|
maxBatchSize: 100,
|
|
102
102
|
maxInputTokens: 8192,
|
|
103
|
-
inputModalities: ['text', 'image', 'audio', 'document'],
|
|
103
|
+
inputModalities: ['text', 'image', 'audio', 'video', 'document'],
|
|
104
104
|
dimensions: true,
|
|
105
105
|
inputTypes: ['document', 'query', 'classification', 'clustering'],
|
|
106
106
|
encodingFormats: ['float'],
|
|
@@ -149,10 +149,10 @@ export class CohereEmbeddingProvider extends HttpEmbeddingProvider {
|
|
|
149
149
|
name: 'cohere',
|
|
150
150
|
defaultModel: options.model || 'embed-v4.0',
|
|
151
151
|
capabilities: {
|
|
152
|
-
models: ['embed-v4.0', 'embed-english-v3.0', 'embed-multilingual-v3.0'],
|
|
152
|
+
models: ['embed-v5.0-pro', 'embed-v5.0-fast', 'embed-v4.0', 'embed-english-v3.0', 'embed-multilingual-v3.0'],
|
|
153
153
|
maxBatchSize: 96,
|
|
154
154
|
inputModalities: ['text', 'image'],
|
|
155
|
-
dimensions: [256, 512, 1024, 1536],
|
|
155
|
+
dimensions: [256, 512, 768, 1024, 1536, 2048],
|
|
156
156
|
inputTypes: ['document', 'query', 'classification', 'clustering'],
|
|
157
157
|
encodingFormats: ['float'],
|
|
158
158
|
truncate: true,
|