nexus-ai-pro 2.1.0 → 2.3.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 +77 -0
- package/CHANGELOG.md +286 -1
- package/README.md +19 -9
- package/dist/agent/capabilities.d.ts +39 -0
- package/dist/agent/capabilities.js +36 -0
- package/dist/agent/create-agent.d.ts +61 -11
- package/dist/agent/create-agent.js +136 -22
- package/dist/agent/index.d.ts +3 -2
- package/dist/agent/index.js +1 -1
- package/dist/agent/middleware/approval.d.ts +26 -0
- package/dist/agent/middleware/approval.js +23 -0
- package/dist/agent/{middleware.d.ts → middleware/basic.d.ts} +5 -4
- package/dist/agent/{middleware.js → middleware/basic.js} +3 -5
- package/dist/agent/middleware/context-editor.d.ts +52 -0
- package/dist/agent/middleware/context-editor.js +66 -0
- package/dist/agent/middleware/filesystem.d.ts +44 -0
- package/dist/agent/middleware/filesystem.js +83 -0
- package/dist/agent/middleware/index.d.ts +19 -0
- package/dist/agent/middleware/index.js +9 -0
- package/dist/agent/middleware/limits.d.ts +42 -0
- package/dist/agent/middleware/limits.js +52 -0
- package/dist/agent/middleware/models.d.ts +48 -0
- package/dist/agent/middleware/models.js +44 -0
- package/dist/agent/middleware/pii.d.ts +67 -0
- package/dist/agent/middleware/pii.js +173 -0
- package/dist/agent/middleware/retry.d.ts +66 -0
- package/dist/agent/middleware/retry.js +59 -0
- package/dist/agent/middleware/shared.d.ts +41 -0
- package/dist/agent/middleware/shared.js +73 -0
- package/dist/agent/middleware/tool-selector.d.ts +64 -0
- package/dist/agent/middleware/tool-selector.js +106 -0
- package/dist/agent/permissions.d.ts +113 -0
- package/dist/agent/permissions.js +180 -0
- package/dist/agent/sandbox.d.ts +151 -0
- package/dist/agent/sandbox.js +257 -0
- package/dist/agent/tool.d.ts +8 -3
- package/dist/agent/tool.js +14 -3
- package/dist/cache/memory-cache.d.ts +8 -0
- package/dist/cache/memory-cache.js +9 -0
- package/dist/cache/semantic-cache.d.ts +6 -3
- package/dist/cache/semantic-cache.js +5 -2
- package/dist/cli/args.js +3 -0
- package/dist/cli/db.d.ts +16 -4
- package/dist/cli/db.js +169 -22
- package/dist/cli/doctor.d.ts +17 -0
- package/dist/cli/doctor.js +53 -0
- package/dist/cli.js +12 -1
- package/dist/core/lifecycle.js +11 -6
- package/dist/core/nexus.d.ts +6 -0
- package/dist/core/nexus.js +103 -22
- package/dist/deep-agent/index.d.ts +109 -0
- package/dist/deep-agent/index.js +216 -0
- package/dist/doctor/index.d.ts +90 -0
- package/dist/doctor/index.js +237 -0
- package/dist/graph/functional.d.ts +89 -7
- package/dist/graph/functional.js +156 -36
- package/dist/graph/graph.d.ts +15 -5
- package/dist/graph/graph.js +95 -22
- package/dist/graph/lint.d.ts +4 -1
- package/dist/graph/lint.js +35 -0
- package/dist/images/evals.js +4 -2
- package/dist/operations/adapters.d.ts +52 -4
- package/dist/operations/adapters.js +194 -7
- package/dist/operations/runner.js +1 -0
- package/dist/operations/stats.js +8 -1
- package/dist/operations/store.d.ts +5 -1
- package/dist/operations/store.js +7 -0
- package/dist/ops/circuit-breaker.d.ts +52 -0
- package/dist/ops/circuit-breaker.js +4 -0
- package/dist/ops/circuit-store.d.ts +13 -2
- package/dist/ops/circuit-store.js +68 -0
- package/dist/ops/circuit-sync.d.ts +17 -0
- package/dist/ops/circuit-sync.js +86 -0
- package/dist/ops/rate-limiter.d.ts +2 -0
- package/dist/ops/rate-limiter.js +2 -0
- package/dist/postgres/circuits.d.ts +21 -4
- package/dist/postgres/circuits.js +65 -4
- package/dist/postgres/client.d.ts +0 -2
- package/dist/postgres/client.js +0 -4
- package/dist/postgres/evaluate.d.ts +16 -4
- package/dist/postgres/evaluate.js +17 -5
- package/dist/postgres/fulltext.d.ts +67 -0
- package/dist/postgres/fulltext.js +91 -0
- package/dist/postgres/index.d.ts +32 -11
- package/dist/postgres/index.js +65 -31
- package/dist/postgres/migrations.d.ts +60 -0
- package/dist/postgres/migrations.js +121 -0
- package/dist/postgres/operations.d.ts +14 -3
- package/dist/postgres/operations.js +45 -15
- package/dist/postgres/prompts.d.ts +11 -2
- package/dist/postgres/prompts.js +14 -3
- package/dist/postgres/rollups.d.ts +35 -0
- package/dist/postgres/rollups.js +129 -0
- package/dist/postgres/store.d.ts +11 -2
- package/dist/postgres/store.js +14 -3
- package/dist/postgres/traces.d.ts +13 -3
- package/dist/postgres/traces.js +25 -3
- package/dist/postgres/vectors.d.ts +11 -2
- package/dist/postgres/vectors.js +14 -3
- package/dist/rag/elasticsearch.d.ts +71 -0
- package/dist/rag/elasticsearch.js +130 -0
- package/dist/rag/pipeline.d.ts +140 -0
- package/dist/rag/pipeline.js +187 -0
- package/dist/rag/rerankers.d.ts +92 -0
- package/dist/rag/rerankers.js +97 -0
- package/dist/rag/retrievers.d.ts +12 -1
- package/dist/router/auto-router.d.ts +34 -7
- package/dist/router/auto-router.js +201 -68
- package/dist/server/assistant.d.ts +9 -1
- package/dist/server/assistant.js +10 -0
- package/dist/server/auth.d.ts +250 -0
- package/dist/server/auth.js +392 -0
- package/dist/server/errors.d.ts +12 -0
- package/dist/server/errors.js +7 -0
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/runs.d.ts +24 -1
- package/dist/server/runs.js +90 -14
- package/dist/sqlite/index.d.ts +16 -3
- package/dist/sqlite/index.js +16 -3
- package/dist/sqlite/migrations.d.ts +26 -0
- package/dist/sqlite/migrations.js +63 -0
- package/dist/sqlite/operations.d.ts +14 -3
- package/dist/sqlite/operations.js +42 -15
- package/dist/sqlite/store.d.ts +9 -2
- package/dist/sqlite/store.js +13 -3
- package/dist/sqlite/vectors.d.ts +11 -2
- package/dist/sqlite/vectors.js +13 -2
- package/dist/store/memory.d.ts +10 -0
- package/dist/store/memory.js +10 -0
- package/dist/store/tenant.d.ts +12 -0
- package/dist/store/tenant.js +29 -0
- package/dist/tenancy/index.d.ts +113 -0
- package/dist/tenancy/index.js +233 -0
- package/dist/tracing/instrument.js +2 -0
- package/dist/types/config.d.ts +24 -2
- package/dist/types/graph.d.ts +49 -1
- package/dist/types/messages.d.ts +41 -2
- package/dist/types/operations.d.ts +6 -0
- package/dist/types/principal.d.ts +21 -0
- package/dist/types/principal.js +1 -0
- package/dist/types/response.d.ts +20 -0
- package/dist/types/server.d.ts +12 -10
- package/dist/utils/schema-migrations.d.ts +97 -0
- package/dist/utils/schema-migrations.js +91 -0
- package/dist/utils/signals.d.ts +17 -0
- package/dist/utils/signals.js +26 -0
- package/dist/utils/tenant.d.ts +25 -0
- package/dist/utils/tenant.js +30 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist-cjs/agent/capabilities.d.ts +39 -0
- package/dist-cjs/agent/capabilities.js +41 -0
- package/dist-cjs/agent/create-agent.d.ts +61 -11
- package/dist-cjs/agent/create-agent.js +136 -22
- package/dist-cjs/agent/index.d.ts +3 -2
- package/dist-cjs/agent/index.js +4 -4
- package/dist-cjs/agent/middleware/approval.d.ts +26 -0
- package/dist-cjs/agent/middleware/approval.js +26 -0
- package/dist-cjs/agent/{middleware.d.ts → middleware/basic.d.ts} +5 -4
- package/dist-cjs/agent/{middleware.js → middleware/basic.js} +3 -5
- package/dist-cjs/agent/middleware/context-editor.d.ts +52 -0
- package/dist-cjs/agent/middleware/context-editor.js +70 -0
- package/dist-cjs/agent/middleware/filesystem.d.ts +44 -0
- package/dist-cjs/agent/middleware/filesystem.js +86 -0
- package/dist-cjs/agent/middleware/index.d.ts +19 -0
- package/dist-cjs/agent/middleware/index.js +28 -0
- package/dist-cjs/agent/middleware/limits.d.ts +42 -0
- package/dist-cjs/agent/middleware/limits.js +57 -0
- package/dist-cjs/agent/middleware/models.d.ts +48 -0
- package/dist-cjs/agent/middleware/models.js +48 -0
- package/dist-cjs/agent/middleware/pii.d.ts +67 -0
- package/dist-cjs/agent/middleware/pii.js +178 -0
- package/dist-cjs/agent/middleware/retry.d.ts +66 -0
- package/dist-cjs/agent/middleware/retry.js +63 -0
- package/dist-cjs/agent/middleware/shared.d.ts +41 -0
- package/dist-cjs/agent/middleware/shared.js +83 -0
- package/dist-cjs/agent/middleware/tool-selector.d.ts +64 -0
- package/dist-cjs/agent/middleware/tool-selector.js +109 -0
- package/dist-cjs/agent/permissions.d.ts +113 -0
- package/dist-cjs/agent/permissions.js +189 -0
- package/dist-cjs/agent/sandbox.d.ts +151 -0
- package/dist-cjs/agent/sandbox.js +297 -0
- package/dist-cjs/agent/tool.d.ts +8 -3
- package/dist-cjs/agent/tool.js +14 -3
- package/dist-cjs/cache/memory-cache.d.ts +8 -0
- package/dist-cjs/cache/memory-cache.js +9 -0
- package/dist-cjs/cache/semantic-cache.d.ts +6 -3
- package/dist-cjs/cache/semantic-cache.js +5 -2
- package/dist-cjs/cli/args.js +3 -0
- package/dist-cjs/cli/db.d.ts +16 -4
- package/dist-cjs/cli/db.js +200 -20
- package/dist-cjs/cli/doctor.d.ts +17 -0
- package/dist-cjs/cli/doctor.js +56 -0
- package/dist-cjs/core/lifecycle.js +11 -6
- package/dist-cjs/core/nexus.d.ts +6 -0
- package/dist-cjs/core/nexus.js +103 -22
- package/dist-cjs/deep-agent/index.d.ts +109 -0
- package/dist-cjs/deep-agent/index.js +219 -0
- package/dist-cjs/doctor/index.d.ts +90 -0
- package/dist-cjs/doctor/index.js +276 -0
- package/dist-cjs/graph/functional.d.ts +89 -7
- package/dist-cjs/graph/functional.js +155 -35
- package/dist-cjs/graph/graph.d.ts +15 -5
- package/dist-cjs/graph/graph.js +95 -22
- package/dist-cjs/graph/lint.d.ts +4 -1
- package/dist-cjs/graph/lint.js +35 -0
- package/dist-cjs/images/evals.js +4 -2
- package/dist-cjs/operations/adapters.d.ts +52 -4
- package/dist-cjs/operations/adapters.js +193 -6
- package/dist-cjs/operations/runner.js +1 -0
- package/dist-cjs/operations/stats.js +8 -1
- package/dist-cjs/operations/store.d.ts +5 -1
- package/dist-cjs/operations/store.js +7 -0
- package/dist-cjs/ops/circuit-breaker.d.ts +52 -0
- package/dist-cjs/ops/circuit-breaker.js +4 -0
- package/dist-cjs/ops/circuit-store.d.ts +13 -2
- package/dist-cjs/ops/circuit-store.js +68 -0
- package/dist-cjs/ops/circuit-sync.d.ts +17 -0
- package/dist-cjs/ops/circuit-sync.js +87 -0
- package/dist-cjs/ops/rate-limiter.d.ts +2 -0
- package/dist-cjs/ops/rate-limiter.js +2 -0
- package/dist-cjs/postgres/circuits.d.ts +21 -4
- package/dist-cjs/postgres/circuits.js +98 -3
- package/dist-cjs/postgres/client.d.ts +0 -2
- package/dist-cjs/postgres/client.js +0 -5
- package/dist-cjs/postgres/evaluate.d.ts +16 -4
- package/dist-cjs/postgres/evaluate.js +50 -4
- package/dist-cjs/postgres/fulltext.d.ts +67 -0
- package/dist-cjs/postgres/fulltext.js +129 -0
- package/dist-cjs/postgres/index.d.ts +32 -11
- package/dist-cjs/postgres/index.js +69 -20
- package/dist-cjs/postgres/migrations.d.ts +60 -0
- package/dist-cjs/postgres/migrations.js +129 -0
- package/dist-cjs/postgres/operations.d.ts +14 -3
- package/dist-cjs/postgres/operations.js +78 -14
- package/dist-cjs/postgres/prompts.d.ts +11 -2
- package/dist-cjs/postgres/prompts.js +47 -2
- package/dist-cjs/postgres/rollups.d.ts +35 -0
- package/dist-cjs/postgres/rollups.js +168 -0
- package/dist-cjs/postgres/store.d.ts +11 -2
- package/dist-cjs/postgres/store.js +47 -2
- package/dist-cjs/postgres/traces.d.ts +13 -3
- package/dist-cjs/postgres/traces.js +58 -2
- package/dist-cjs/postgres/vectors.d.ts +11 -2
- package/dist-cjs/postgres/vectors.js +47 -2
- package/dist-cjs/rag/elasticsearch.d.ts +71 -0
- package/dist-cjs/rag/elasticsearch.js +135 -0
- package/dist-cjs/rag/pipeline.d.ts +140 -0
- package/dist-cjs/rag/pipeline.js +191 -0
- package/dist-cjs/rag/rerankers.d.ts +92 -0
- package/dist-cjs/rag/rerankers.js +107 -0
- package/dist-cjs/rag/retrievers.d.ts +12 -1
- package/dist-cjs/router/auto-router.d.ts +34 -7
- package/dist-cjs/router/auto-router.js +201 -68
- package/dist-cjs/server/assistant.d.ts +9 -1
- package/dist-cjs/server/assistant.js +10 -0
- package/dist-cjs/server/auth.d.ts +250 -0
- package/dist-cjs/server/auth.js +402 -0
- package/dist-cjs/server/errors.d.ts +12 -0
- package/dist-cjs/server/errors.js +9 -1
- package/dist-cjs/server/index.d.ts +1 -1
- package/dist-cjs/server/index.js +2 -1
- package/dist-cjs/server/runs.d.ts +24 -1
- package/dist-cjs/server/runs.js +89 -13
- package/dist-cjs/sqlite/index.d.ts +16 -3
- package/dist-cjs/sqlite/index.js +31 -10
- package/dist-cjs/sqlite/migrations.d.ts +26 -0
- package/dist-cjs/sqlite/migrations.js +71 -0
- package/dist-cjs/sqlite/operations.d.ts +14 -3
- package/dist-cjs/sqlite/operations.js +76 -15
- package/dist-cjs/sqlite/store.d.ts +9 -2
- package/dist-cjs/sqlite/store.js +47 -3
- package/dist-cjs/sqlite/vectors.d.ts +11 -2
- package/dist-cjs/sqlite/vectors.js +47 -2
- package/dist-cjs/store/memory.d.ts +10 -0
- package/dist-cjs/store/memory.js +10 -0
- package/dist-cjs/store/tenant.d.ts +12 -0
- package/dist-cjs/store/tenant.js +33 -0
- package/dist-cjs/tenancy/index.d.ts +113 -0
- package/dist-cjs/tenancy/index.js +250 -0
- package/dist-cjs/tracing/instrument.js +2 -0
- package/dist-cjs/types/config.d.ts +24 -2
- package/dist-cjs/types/graph.d.ts +49 -1
- package/dist-cjs/types/messages.d.ts +41 -2
- package/dist-cjs/types/operations.d.ts +6 -0
- package/dist-cjs/types/principal.d.ts +21 -0
- package/dist-cjs/types/principal.js +2 -0
- package/dist-cjs/types/response.d.ts +20 -0
- package/dist-cjs/types/server.d.ts +12 -10
- package/dist-cjs/utils/schema-migrations.d.ts +97 -0
- package/dist-cjs/utils/schema-migrations.js +102 -0
- package/dist-cjs/utils/signals.d.ts +17 -0
- package/dist-cjs/utils/signals.js +29 -0
- package/dist-cjs/utils/tenant.d.ts +25 -0
- package/dist-cjs/utils/tenant.js +37 -0
- package/dist-cjs/version.d.ts +1 -1
- package/dist-cjs/version.js +1 -1
- package/llms.txt +6 -4
- package/package.json +188 -2
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { Reranker } from './retrievers.js';
|
|
2
|
+
/** Raised when a rerank API answers with an error. */
|
|
3
|
+
export declare class RerankerError extends Error {
|
|
4
|
+
/** The HTTP status the API answered with. */
|
|
5
|
+
readonly status: number;
|
|
6
|
+
/** The API's response body, as text. */
|
|
7
|
+
readonly body?: string | undefined;
|
|
8
|
+
constructor(message: string,
|
|
9
|
+
/** The HTTP status the API answered with. */
|
|
10
|
+
status: number,
|
|
11
|
+
/** The API's response body, as text. */
|
|
12
|
+
body?: string | undefined);
|
|
13
|
+
}
|
|
14
|
+
/** What every HTTP reranker takes. */
|
|
15
|
+
export interface RerankerHttpOptions {
|
|
16
|
+
/** Replaces the global `fetch`, for a proxy, retries, or tests. */
|
|
17
|
+
fetch?: typeof globalThis.fetch;
|
|
18
|
+
/** Headers added to every request. */
|
|
19
|
+
headers?: Record<string, string>;
|
|
20
|
+
/** Aborts a request that takes longer, in milliseconds. Defaults to 30 seconds. */
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
/** Characters of each chunk sent. Defaults to 4,000. */
|
|
23
|
+
maxChunkCharacters?: number;
|
|
24
|
+
}
|
|
25
|
+
/** Options for a hosted rerank API. */
|
|
26
|
+
export interface HostedRerankerOptions extends RerankerHttpOptions {
|
|
27
|
+
/** The API key. */
|
|
28
|
+
apiKey: string;
|
|
29
|
+
/** The rerank model, such as `rerank-v3.5`. */
|
|
30
|
+
model: string;
|
|
31
|
+
/** Replaces the API's endpoint, for a regional or proxied one. */
|
|
32
|
+
url?: string;
|
|
33
|
+
}
|
|
34
|
+
/** Options for `httpReranker()`: any rerank API, described by its request and its response. */
|
|
35
|
+
export interface HttpRerankerOptions extends RerankerHttpOptions {
|
|
36
|
+
/** The endpoint. */
|
|
37
|
+
url: string;
|
|
38
|
+
/** The request body for a query and its passages. */
|
|
39
|
+
body: (query: string, passages: string[]) => unknown;
|
|
40
|
+
/** Reads the response: each passage's index and score. Passages it leaves out score 0. */
|
|
41
|
+
scores: (response: unknown) => ReadonlyArray<{
|
|
42
|
+
index: number;
|
|
43
|
+
score: number;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A reranker over any HTTP rerank API: you describe the request body and how to read the scores,
|
|
48
|
+
* and it handles the rest — truncation, the timeout, cancellation, errors, and scores put back in
|
|
49
|
+
* the order of the chunks.
|
|
50
|
+
*/
|
|
51
|
+
export declare function httpReranker(options: HttpRerankerOptions): Reranker;
|
|
52
|
+
/** Cohere's rerank API (`/v2/rerank`), such as `rerank-v3.5`. */
|
|
53
|
+
export declare function cohereReranker(options: HostedRerankerOptions): Reranker;
|
|
54
|
+
/** Voyage AI's rerank API (`/v1/rerank`), such as `rerank-2`. */
|
|
55
|
+
export declare function voyageReranker(options: HostedRerankerOptions): Reranker;
|
|
56
|
+
/** Jina AI's rerank API (`/v1/rerank`), such as `jina-reranker-v2-base-multilingual`. */
|
|
57
|
+
export declare function jinaReranker(options: HostedRerankerOptions): Reranker;
|
|
58
|
+
/** Options for `teiReranker()`. */
|
|
59
|
+
export interface TeiRerankerOptions extends RerankerHttpOptions {
|
|
60
|
+
/** The server, such as `http://localhost:8080`. */
|
|
61
|
+
url: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* A cross-encoder served by Hugging Face's text-embeddings-inference, on your own machine or
|
|
65
|
+
* cluster: `docker run ... --model-id BAAI/bge-reranker-base`, then `teiReranker({ url })`. The
|
|
66
|
+
* scores are the model's own, from 0 to 1.
|
|
67
|
+
*/
|
|
68
|
+
export declare function teiReranker(options: TeiRerankerOptions): Reranker;
|
|
69
|
+
/**
|
|
70
|
+
* A cross-encoder run in process: one score per (query, passage) pair, higher meaning more relevant.
|
|
71
|
+
* A model from an ONNX runtime or a transformers library fits it in a few lines, so the package
|
|
72
|
+
* depends on neither.
|
|
73
|
+
*/
|
|
74
|
+
export interface CrossEncoder {
|
|
75
|
+
/** Scores each pair, in order. */
|
|
76
|
+
score(pairs: ReadonlyArray<readonly [query: string, passage: string]>): Promise<readonly number[]> | readonly number[];
|
|
77
|
+
}
|
|
78
|
+
/** Options for `crossEncoderReranker()`. */
|
|
79
|
+
export interface CrossEncoderRerankerOptions {
|
|
80
|
+
/** Pairs scored in one call. Defaults to 32. */
|
|
81
|
+
batchSize?: number;
|
|
82
|
+
/** Characters of each chunk scored. Defaults to 2,000. */
|
|
83
|
+
maxChunkCharacters?: number;
|
|
84
|
+
/** Maps raw logits to 0–1 with a sigmoid, for a model that returns logits. Defaults to false. */
|
|
85
|
+
sigmoid?: boolean;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* A reranker over a cross-encoder you run in process: no network call, no per-query price, and the
|
|
89
|
+
* passages never leave the machine. Pairs are scored in batches, and the run's signal is checked
|
|
90
|
+
* between batches.
|
|
91
|
+
*/
|
|
92
|
+
export declare function crossEncoderReranker(model: CrossEncoder, options?: CrossEncoderRerankerOptions): Reranker;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RerankerError = void 0;
|
|
4
|
+
exports.httpReranker = httpReranker;
|
|
5
|
+
exports.cohereReranker = cohereReranker;
|
|
6
|
+
exports.voyageReranker = voyageReranker;
|
|
7
|
+
exports.jinaReranker = jinaReranker;
|
|
8
|
+
exports.teiReranker = teiReranker;
|
|
9
|
+
exports.crossEncoderReranker = crossEncoderReranker;
|
|
10
|
+
const signals_js_1 = require("../utils/signals.js");
|
|
11
|
+
class RerankerError extends Error {
|
|
12
|
+
status;
|
|
13
|
+
body;
|
|
14
|
+
constructor(message, status, body) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.status = status;
|
|
17
|
+
this.body = body;
|
|
18
|
+
this.name = 'RerankerError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.RerankerError = RerankerError;
|
|
22
|
+
function httpReranker(options) {
|
|
23
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
24
|
+
const limit = options.maxChunkCharacters ?? 4_000;
|
|
25
|
+
return async (query, chunks, signal) => {
|
|
26
|
+
if (chunks.length === 0)
|
|
27
|
+
return [];
|
|
28
|
+
const passages = chunks.map((chunk) => chunk.content.slice(0, limit));
|
|
29
|
+
const linked = (0, signals_js_1.linkSignals)([AbortSignal.timeout(options.timeoutMs ?? 30_000), signal]);
|
|
30
|
+
try {
|
|
31
|
+
const response = await fetchImpl(options.url, {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
headers: { 'content-type': 'application/json', ...options.headers },
|
|
34
|
+
body: JSON.stringify(options.body(query, passages)),
|
|
35
|
+
signal: linked.signal,
|
|
36
|
+
});
|
|
37
|
+
const text = await response.text();
|
|
38
|
+
if (!response.ok)
|
|
39
|
+
throw new RerankerError(`The rerank API answered ${response.status}`, response.status, text);
|
|
40
|
+
const scores = new Array(chunks.length).fill(0);
|
|
41
|
+
for (const { index, score } of options.scores(JSON.parse(text))) {
|
|
42
|
+
if (Number.isInteger(index) && index >= 0 && index < scores.length)
|
|
43
|
+
scores[index] = Number(score);
|
|
44
|
+
}
|
|
45
|
+
return scores;
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
linked.dispose();
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const bearer = (options) => ({ authorization: `Bearer ${options.apiKey}`, ...options.headers });
|
|
53
|
+
const readScored = (list) => (list ?? []).map((item) => ({ index: item.index, score: item.relevance_score }));
|
|
54
|
+
function cohereReranker(options) {
|
|
55
|
+
return httpReranker({
|
|
56
|
+
...options,
|
|
57
|
+
url: options.url ?? 'https://api.cohere.com/v2/rerank',
|
|
58
|
+
headers: bearer(options),
|
|
59
|
+
body: (query, documents) => ({ model: options.model, query, documents, top_n: documents.length }),
|
|
60
|
+
scores: (response) => readScored(response.results),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function voyageReranker(options) {
|
|
64
|
+
return httpReranker({
|
|
65
|
+
...options,
|
|
66
|
+
url: options.url ?? 'https://api.voyageai.com/v1/rerank',
|
|
67
|
+
headers: bearer(options),
|
|
68
|
+
body: (query, documents) => ({ model: options.model, query, documents, top_k: documents.length }),
|
|
69
|
+
scores: (response) => readScored(response.data),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
function jinaReranker(options) {
|
|
73
|
+
return httpReranker({
|
|
74
|
+
...options,
|
|
75
|
+
url: options.url ?? 'https://api.jina.ai/v1/rerank',
|
|
76
|
+
headers: bearer(options),
|
|
77
|
+
body: (query, documents) => ({ model: options.model, query, documents, top_n: documents.length }),
|
|
78
|
+
scores: (response) => readScored(response.results),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function teiReranker(options) {
|
|
82
|
+
return httpReranker({
|
|
83
|
+
...options,
|
|
84
|
+
url: `${options.url.replace(/\/+$/, '')}/rerank`,
|
|
85
|
+
body: (query, texts) => ({ query, texts, raw_scores: false, truncate: true }),
|
|
86
|
+
scores: (response) => response,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
function crossEncoderReranker(model, options = {}) {
|
|
90
|
+
const batchSize = Math.max(1, options.batchSize ?? 32);
|
|
91
|
+
const limit = options.maxChunkCharacters ?? 2_000;
|
|
92
|
+
return async (query, chunks, signal) => {
|
|
93
|
+
const pairs = chunks.map((chunk) => [query, chunk.content.slice(0, limit)]);
|
|
94
|
+
const scores = [];
|
|
95
|
+
for (let start = 0; start < pairs.length; start += batchSize) {
|
|
96
|
+
signal?.throwIfAborted();
|
|
97
|
+
const batch = pairs.slice(start, start + batchSize);
|
|
98
|
+
const result = await model.score(batch);
|
|
99
|
+
if (result.length !== batch.length) {
|
|
100
|
+
throw new RangeError(`The cross-encoder returned ${result.length} scores for ${batch.length} pairs`);
|
|
101
|
+
}
|
|
102
|
+
for (const raw of result)
|
|
103
|
+
scores.push(options.sigmoid ? 1 / (1 + Math.exp(-raw)) : raw);
|
|
104
|
+
}
|
|
105
|
+
return scores;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
@@ -19,6 +19,17 @@ export interface Retriever {
|
|
|
19
19
|
/** The chunks that best answer a query, best first. */
|
|
20
20
|
retrieve(query: string, options?: RetrieveOptions): Promise<VectorSearchResult[]>;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Keyword search that holds its own chunks: an in-memory BM25 `KeywordIndex`, Postgres full-text
|
|
24
|
+
* search, a search engine. It is a `Retriever`, so `hybridRetriever()` fuses it with vector search,
|
|
25
|
+
* and it takes writes, so ingestion keeps it in step with a vector store.
|
|
26
|
+
*/
|
|
27
|
+
export interface SparseRetriever extends Retriever {
|
|
28
|
+
/** Adds chunks, or replaces those whose id exists. */
|
|
29
|
+
add(chunks: readonly RagChunk[]): Promise<void> | void;
|
|
30
|
+
/** Removes chunks by id. An id that is not held is ignored. */
|
|
31
|
+
delete(ids: readonly string[]): Promise<void> | void;
|
|
32
|
+
}
|
|
22
33
|
/** A client that runs one completion — a `NexusAI` instance, or anything with the same `complete()`. */
|
|
23
34
|
export interface RetrievalModelClient {
|
|
24
35
|
/** Runs one completion and returns its text. */
|
|
@@ -47,7 +58,7 @@ export declare function tokenize(text: string): string[];
|
|
|
47
58
|
* codes, error numbers, names — that keyword search finds; `hybridRetriever()` fuses the two.
|
|
48
59
|
* Adding a chunk whose id exists replaces it, as in a vector store.
|
|
49
60
|
*/
|
|
50
|
-
export declare class KeywordIndex implements
|
|
61
|
+
export declare class KeywordIndex implements SparseRetriever {
|
|
51
62
|
private readonly chunks;
|
|
52
63
|
private readonly postings;
|
|
53
64
|
private totalLength;
|
|
@@ -1,14 +1,37 @@
|
|
|
1
1
|
import type { RouterContext, RouteDecision } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Ranks configured providers' models for a request routed as `auto`.
|
|
4
|
+
*
|
|
5
|
+
* Everything that depends only on configuration — which models are candidates, which pass the
|
|
6
|
+
* allow and deny lists and the capability requirements, and how each scores for the strategy — is
|
|
7
|
+
* computed once into a plan and kept until the configuration changes. A request pays only for what
|
|
8
|
+
* can differ between requests: which circuits are open, and provider health.
|
|
9
|
+
*/
|
|
2
10
|
export declare class AutoRouter {
|
|
11
|
+
private readonly plans;
|
|
12
|
+
private readonly patterns;
|
|
13
|
+
private readonly registryIds;
|
|
14
|
+
/** Routes one request: the best candidate first, every other as a fallback in order. */
|
|
3
15
|
route(ctx: RouterContext): RouteDecision;
|
|
16
|
+
/** Plans cached by this router, for tests and diagnostics. */
|
|
17
|
+
get planCount(): number;
|
|
18
|
+
/** The plan for a request's configuration, built on first use and kept until the configuration changes. */
|
|
19
|
+
private plan;
|
|
4
20
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* open usually means a shared dependency is down rather than every provider individually, and
|
|
9
|
-
* attempting one call is strictly better than failing the request without trying anything.
|
|
21
|
+
* What a plan depends on, normalized into a string: the routing settings, the model settings, and
|
|
22
|
+
* the configured providers. A registry is identified by its object, so replacing
|
|
23
|
+
* `models.registry` builds a new plan; changing one in place does not.
|
|
10
24
|
*/
|
|
11
|
-
private
|
|
25
|
+
private planKey;
|
|
26
|
+
private registryId;
|
|
27
|
+
private buildPlan;
|
|
28
|
+
/**
|
|
29
|
+
* The plan's candidates in order for this request. Providers whose circuit is open are left out,
|
|
30
|
+
* unless that would leave nothing: every circuit open usually means a shared dependency is down,
|
|
31
|
+
* and one attempt is strictly better than failing without trying. Health moves a whole provider
|
|
32
|
+
* up or down, so the per-provider lists are merged by their adjusted scores rather than re-sorted.
|
|
33
|
+
*/
|
|
34
|
+
private rank;
|
|
12
35
|
private getCandidates;
|
|
13
36
|
/**
|
|
14
37
|
* Scores a model for a strategy. For privacy, a model counts as local when its name says so or when
|
|
@@ -16,15 +39,19 @@ export declare class AutoRouter {
|
|
|
16
39
|
* is preferred without renaming its models.
|
|
17
40
|
*/
|
|
18
41
|
private score;
|
|
42
|
+
private capabilitiesOf;
|
|
19
43
|
private localFallbackCapabilities;
|
|
20
44
|
private costScore;
|
|
21
45
|
private speedScore;
|
|
22
46
|
private qualityScore;
|
|
23
47
|
private preferencesForProvider;
|
|
24
48
|
private isAllowed;
|
|
49
|
+
/** Whether a model meets the routing requirements: the boolean ones as two bitmask tests, the rest by value. */
|
|
25
50
|
private matchesRequirements;
|
|
51
|
+
/** Whether a model name matches an allow or deny entry, with `*` as a wildcard. Patterns are compiled once. */
|
|
26
52
|
private matchesModelPattern;
|
|
27
53
|
private isProviderRunnable;
|
|
28
|
-
|
|
54
|
+
/** Each provider's health adjustment for this request, or nothing when health does not apply. */
|
|
55
|
+
private healthOffsets;
|
|
29
56
|
}
|
|
30
57
|
export declare function routeDirectModel(model: string, ctx: RouterContext): RouteDecision;
|
|
@@ -3,38 +3,140 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.AutoRouter = void 0;
|
|
4
4
|
exports.routeDirectModel = routeDirectModel;
|
|
5
5
|
const registry_js_1 = require("../models/registry.js");
|
|
6
|
+
const PLAN_LIMIT = 16;
|
|
7
|
+
const BOOLEAN_CAPABILITIES = ['streaming', 'toolCalling', 'structuredOutputs', 'jsonMode', 'reasoning'];
|
|
6
8
|
class AutoRouter {
|
|
9
|
+
plans = new Map();
|
|
10
|
+
patterns = new Map();
|
|
11
|
+
registryIds = new WeakMap();
|
|
7
12
|
route(ctx) {
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
score: this.score(candidate.model, strategy, ctx.config, ctx.providers.get(candidate.providerName)?.info?.isLocal === true) +
|
|
12
|
-
candidate.weight +
|
|
13
|
-
this.healthScore(candidate.providerName, ctx),
|
|
14
|
-
reason: `auto route by ${strategy} score`,
|
|
15
|
-
}));
|
|
16
|
-
candidates.sort((a, b) => b.score - a.score);
|
|
17
|
-
const winner = candidates[0];
|
|
13
|
+
const plan = this.plan(ctx);
|
|
14
|
+
const ranked = this.rank(plan, ctx);
|
|
15
|
+
const winner = ranked[0];
|
|
18
16
|
if (!winner) {
|
|
19
17
|
throw new Error('No configured providers are available for routing');
|
|
20
18
|
}
|
|
21
19
|
return {
|
|
22
20
|
providerName: winner.providerName,
|
|
23
21
|
model: winner.model,
|
|
24
|
-
reason:
|
|
25
|
-
fallbacks:
|
|
22
|
+
reason: `auto route by ${plan.strategy} score: ${winner.model}`,
|
|
23
|
+
fallbacks: ranked.slice(1).map((candidate) => candidate.attempt),
|
|
26
24
|
};
|
|
27
25
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
26
|
+
get planCount() {
|
|
27
|
+
return this.plans.size;
|
|
28
|
+
}
|
|
29
|
+
plan(ctx) {
|
|
30
|
+
const key = this.planKey(ctx);
|
|
31
|
+
const cached = this.plans.get(key);
|
|
32
|
+
if (cached) {
|
|
33
|
+
this.plans.delete(key);
|
|
34
|
+
this.plans.set(key, cached);
|
|
35
|
+
return cached;
|
|
36
|
+
}
|
|
37
|
+
const plan = this.buildPlan(ctx);
|
|
38
|
+
this.plans.set(key, plan);
|
|
39
|
+
if (this.plans.size > PLAN_LIMIT)
|
|
40
|
+
this.plans.delete(this.plans.keys().next().value);
|
|
41
|
+
return plan;
|
|
42
|
+
}
|
|
43
|
+
planKey(ctx) {
|
|
44
|
+
const routing = ctx.config.routing;
|
|
45
|
+
const models = ctx.config.models;
|
|
46
|
+
const providers = [];
|
|
47
|
+
for (const [name, provider] of ctx.providers)
|
|
48
|
+
providers.push(`${name}${provider.info?.isLocal === true ? '+local' : ''}`);
|
|
49
|
+
return JSON.stringify([
|
|
50
|
+
routing?.strategy ?? 'quality',
|
|
51
|
+
routing?.candidateModels ?? null,
|
|
52
|
+
routing?.modelPreferences ?? null,
|
|
53
|
+
routing?.allowModels ?? null,
|
|
54
|
+
routing?.denyModels ?? null,
|
|
55
|
+
normalizedRequirements(routing?.requiredCapabilities),
|
|
56
|
+
models?.includeDefaults ?? null,
|
|
57
|
+
models?.aliases ?? null,
|
|
58
|
+
models?.registry ? this.registryId(models.registry) : 0,
|
|
59
|
+
providers,
|
|
60
|
+
]);
|
|
34
61
|
}
|
|
35
|
-
|
|
62
|
+
registryId(registry) {
|
|
63
|
+
let id = this.registryIds.get(registry);
|
|
64
|
+
if (id === undefined) {
|
|
65
|
+
id = this.registryIds.get(this.registryIds) ?? 1;
|
|
66
|
+
this.registryIds.set(registry, id);
|
|
67
|
+
this.registryIds.set(this.registryIds, id + 1);
|
|
68
|
+
}
|
|
69
|
+
return id;
|
|
70
|
+
}
|
|
71
|
+
buildPlan(ctx) {
|
|
72
|
+
const strategy = ctx.config.routing?.strategy || 'quality';
|
|
73
|
+
const registry = (0, registry_js_1.getModelRegistry)(ctx.config);
|
|
74
|
+
const requirements = compileRequirements(ctx.config.routing?.requiredCapabilities);
|
|
75
|
+
const candidates = this.getCandidates(ctx, registry, requirements);
|
|
76
|
+
const planned = candidates.map((candidate, index) => ({
|
|
77
|
+
providerName: candidate.providerName,
|
|
78
|
+
model: candidate.model,
|
|
79
|
+
base: this.score(candidate.model, strategy, registry, ctx.providers.get(candidate.providerName)?.info?.isLocal === true) + candidate.weight,
|
|
80
|
+
index,
|
|
81
|
+
attempt: Object.freeze({ providerName: candidate.providerName, model: candidate.model }),
|
|
82
|
+
}));
|
|
83
|
+
const ranked = [...planned].sort(byScore);
|
|
84
|
+
const byProvider = new Map();
|
|
85
|
+
for (const candidate of ranked) {
|
|
86
|
+
const list = byProvider.get(candidate.providerName);
|
|
87
|
+
if (list)
|
|
88
|
+
list.push(candidate);
|
|
89
|
+
else
|
|
90
|
+
byProvider.set(candidate.providerName, [candidate]);
|
|
91
|
+
}
|
|
92
|
+
return { strategy, ranked, byProvider };
|
|
93
|
+
}
|
|
94
|
+
rank(plan, ctx) {
|
|
95
|
+
let excluded;
|
|
96
|
+
if (ctx.openCircuits?.length) {
|
|
97
|
+
const open = new Set(ctx.openCircuits);
|
|
98
|
+
if (plan.ranked.some((candidate) => !open.has(candidate.providerName)))
|
|
99
|
+
excluded = open;
|
|
100
|
+
}
|
|
101
|
+
const offsets = this.healthOffsets(ctx);
|
|
102
|
+
if (!offsets) {
|
|
103
|
+
return excluded ? plan.ranked.filter((candidate) => !excluded.has(candidate.providerName)) : plan.ranked;
|
|
104
|
+
}
|
|
105
|
+
const lists = [];
|
|
106
|
+
let total = 0;
|
|
107
|
+
for (const [providerName, list] of plan.byProvider) {
|
|
108
|
+
if (excluded?.has(providerName))
|
|
109
|
+
continue;
|
|
110
|
+
lists.push({ list, offset: offsets.get(providerName) ?? 0, at: 0 });
|
|
111
|
+
total += list.length;
|
|
112
|
+
}
|
|
113
|
+
const merged = new Array(total);
|
|
114
|
+
for (let position = 0; position < total; position += 1) {
|
|
115
|
+
let best = -1;
|
|
116
|
+
let bestScore = Number.NEGATIVE_INFINITY;
|
|
117
|
+
let bestIndex = Number.POSITIVE_INFINITY;
|
|
118
|
+
for (let which = 0; which < lists.length; which += 1) {
|
|
119
|
+
const head = lists[which];
|
|
120
|
+
const candidate = head.list[head.at];
|
|
121
|
+
if (!candidate)
|
|
122
|
+
continue;
|
|
123
|
+
const score = candidate.base + head.offset;
|
|
124
|
+
if (score > bestScore || (score === bestScore && candidate.index < bestIndex)) {
|
|
125
|
+
best = which;
|
|
126
|
+
bestScore = score;
|
|
127
|
+
bestIndex = candidate.index;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const chosen = lists[best];
|
|
131
|
+
merged[position] = chosen.list[chosen.at];
|
|
132
|
+
chosen.at += 1;
|
|
133
|
+
}
|
|
134
|
+
return merged;
|
|
135
|
+
}
|
|
136
|
+
getCandidates(ctx, registry, requirements) {
|
|
36
137
|
const candidates = [];
|
|
37
138
|
const configuredModels = ctx.config.routing?.candidateModels;
|
|
139
|
+
const passes = (model) => !requirements || this.matchesRequirements(model, registry, requirements);
|
|
38
140
|
if (configuredModels?.length) {
|
|
39
141
|
for (const model of configuredModels) {
|
|
40
142
|
const resolved = (0, registry_js_1.resolveModel)(model, ctx.config);
|
|
@@ -43,23 +145,19 @@ class AutoRouter {
|
|
|
43
145
|
candidates.push({ providerName, model: resolved.model, weight: 0 });
|
|
44
146
|
}
|
|
45
147
|
}
|
|
46
|
-
return candidates.filter((candidate) =>
|
|
148
|
+
return candidates.filter((candidate) => passes(candidate.model));
|
|
47
149
|
}
|
|
48
150
|
const preferences = ctx.config.routing?.modelPreferences?.[ctx.config.routing?.strategy || 'quality'];
|
|
49
151
|
for (const providerName of ctx.providers.keys()) {
|
|
50
|
-
const preferred = this.preferencesForProvider(providerName, preferences, ctx.config);
|
|
152
|
+
const preferred = this.preferencesForProvider(providerName, preferences, ctx.config, registry);
|
|
51
153
|
if (preferred.length) {
|
|
52
154
|
candidates.push(...preferred);
|
|
53
155
|
}
|
|
54
156
|
}
|
|
55
|
-
return candidates.filter((candidate) =>
|
|
56
|
-
return this.isAllowed(candidate.model, ctx.config) && this.matchesRequirements(candidate.model, ctx.config);
|
|
57
|
-
});
|
|
157
|
+
return candidates.filter((candidate) => this.isAllowed(candidate.model, ctx.config) && passes(candidate.model));
|
|
58
158
|
}
|
|
59
|
-
score(model, strategy,
|
|
60
|
-
const
|
|
61
|
-
const normalized = model.startsWith('ollama/') ? model.slice(7) : model;
|
|
62
|
-
const caps = registry[model] || registry[normalized] || this.localFallbackCapabilities(model);
|
|
159
|
+
score(model, strategy, registry, providerIsLocal = false) {
|
|
160
|
+
const caps = this.capabilitiesOf(model, registry);
|
|
63
161
|
if (strategy === 'privacy')
|
|
64
162
|
return providerIsLocal || isLocalModel(model) ? 100 : 30;
|
|
65
163
|
if (strategy === 'speed')
|
|
@@ -68,6 +166,10 @@ class AutoRouter {
|
|
|
68
166
|
return this.costScore(model, caps);
|
|
69
167
|
return this.qualityScore(model, caps);
|
|
70
168
|
}
|
|
169
|
+
capabilitiesOf(model, registry) {
|
|
170
|
+
const normalized = model.startsWith('ollama/') ? model.slice(7) : model;
|
|
171
|
+
return registry[model] || registry[normalized] || this.localFallbackCapabilities(model);
|
|
172
|
+
}
|
|
71
173
|
localFallbackCapabilities(model) {
|
|
72
174
|
return {
|
|
73
175
|
inputModalities: ['text'],
|
|
@@ -113,7 +215,7 @@ class AutoRouter {
|
|
|
113
215
|
return 65;
|
|
114
216
|
return Math.min(85, caps.maxContextTokens / 2000);
|
|
115
217
|
}
|
|
116
|
-
preferencesForProvider(providerName, preferences, config) {
|
|
218
|
+
preferencesForProvider(providerName, preferences, config, registry) {
|
|
117
219
|
if (preferences?.length) {
|
|
118
220
|
return preferences
|
|
119
221
|
.map((preference) => typeof preference === 'string'
|
|
@@ -132,7 +234,7 @@ class AutoRouter {
|
|
|
132
234
|
const registryModels = (0, registry_js_1.listModelsForProvider)(providerName, config);
|
|
133
235
|
if (registryModels.length) {
|
|
134
236
|
return registryModels
|
|
135
|
-
.filter((model) => this.isProviderRunnable(providerName, model,
|
|
237
|
+
.filter((model) => this.isProviderRunnable(providerName, model, registry))
|
|
136
238
|
.map((model) => ({ providerName, model, weight: 0 }));
|
|
137
239
|
}
|
|
138
240
|
if (providerName === 'ollama') {
|
|
@@ -173,52 +275,44 @@ class AutoRouter {
|
|
|
173
275
|
return false;
|
|
174
276
|
return !deny.some((entry) => this.matchesModelPattern(model, entry));
|
|
175
277
|
}
|
|
176
|
-
matchesRequirements(model,
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const registry = (0, registry_js_1.getModelRegistry)(config);
|
|
181
|
-
const normalized = model.startsWith('ollama/') ? model.slice(7) : model;
|
|
182
|
-
const caps = registry[model] || registry[normalized] || this.localFallbackCapabilities(model);
|
|
183
|
-
if (requirements.streaming !== undefined && caps.streaming !== requirements.streaming)
|
|
184
|
-
return false;
|
|
185
|
-
if (requirements.toolCalling !== undefined && caps.toolCalling !== requirements.toolCalling)
|
|
186
|
-
return false;
|
|
187
|
-
if (requirements.structuredOutputs !== undefined && caps.structuredOutputs !== requirements.structuredOutputs)
|
|
278
|
+
matchesRequirements(model, registry, requirements) {
|
|
279
|
+
const caps = this.capabilitiesOf(model, registry);
|
|
280
|
+
const { mustBe, mustNotBe } = capabilityMasks(caps);
|
|
281
|
+
if ((mustBe & requirements.mustBe) !== requirements.mustBe)
|
|
188
282
|
return false;
|
|
189
|
-
if (
|
|
283
|
+
if ((mustNotBe & requirements.mustNotBe) !== requirements.mustNotBe)
|
|
190
284
|
return false;
|
|
191
|
-
|
|
285
|
+
const source = requirements.source;
|
|
286
|
+
if (source.minContextTokens && caps.maxContextTokens < source.minContextTokens)
|
|
192
287
|
return false;
|
|
193
|
-
if (
|
|
288
|
+
if (source.maxInputCostPer1k !== undefined && caps.costPer1kInput > source.maxInputCostPer1k)
|
|
194
289
|
return false;
|
|
195
|
-
if (
|
|
290
|
+
if (source.maxOutputCostPer1k !== undefined && caps.costPer1kOutput > source.maxOutputCostPer1k)
|
|
196
291
|
return false;
|
|
197
|
-
if (
|
|
292
|
+
if (source.statuses?.length && caps.status && !source.statuses.includes(caps.status))
|
|
198
293
|
return false;
|
|
199
|
-
if (
|
|
294
|
+
if (source.inputModalities?.some((modality) => !caps.inputModalities.includes(modality)))
|
|
200
295
|
return false;
|
|
201
|
-
if (
|
|
202
|
-
return false;
|
|
203
|
-
if (requirements.outputModalities?.some((modality) => !caps.outputModalities.includes(modality)))
|
|
296
|
+
if (source.outputModalities?.some((modality) => !caps.outputModalities.includes(modality)))
|
|
204
297
|
return false;
|
|
205
298
|
return true;
|
|
206
299
|
}
|
|
207
300
|
matchesModelPattern(model, pattern) {
|
|
208
301
|
if (pattern === model)
|
|
209
302
|
return true;
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
303
|
+
let regex = this.patterns.get(pattern);
|
|
304
|
+
if (regex === undefined) {
|
|
305
|
+
regex = pattern.includes('*')
|
|
306
|
+
? new RegExp(`^${pattern
|
|
307
|
+
.split('*')
|
|
308
|
+
.map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
|
|
309
|
+
.join('.*')}$`)
|
|
310
|
+
: null;
|
|
311
|
+
this.patterns.set(pattern, regex);
|
|
312
|
+
}
|
|
313
|
+
return regex ? regex.test(model) : false;
|
|
219
314
|
}
|
|
220
|
-
isProviderRunnable(providerName, model,
|
|
221
|
-
const registry = (0, registry_js_1.getModelRegistry)(config);
|
|
315
|
+
isProviderRunnable(providerName, model, registry) {
|
|
222
316
|
const caps = registry[model];
|
|
223
317
|
if (!caps?.endpoints)
|
|
224
318
|
return true;
|
|
@@ -230,16 +324,55 @@ class AutoRouter {
|
|
|
230
324
|
return caps.endpoints.includes('generateContent');
|
|
231
325
|
return true;
|
|
232
326
|
}
|
|
233
|
-
|
|
327
|
+
healthOffsets(ctx) {
|
|
234
328
|
if (!ctx.config.health?.enabled || !ctx.health?.length)
|
|
235
|
-
return
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
329
|
+
return undefined;
|
|
330
|
+
const offsets = new Map();
|
|
331
|
+
for (const snapshot of ctx.health) {
|
|
332
|
+
if (offsets.has(snapshot.providerName))
|
|
333
|
+
continue;
|
|
334
|
+
offsets.set(snapshot.providerName, snapshot.healthy ? snapshot.score / 10 : -100);
|
|
335
|
+
}
|
|
336
|
+
return offsets;
|
|
240
337
|
}
|
|
241
338
|
}
|
|
242
339
|
exports.AutoRouter = AutoRouter;
|
|
340
|
+
function compileRequirements(requirements) {
|
|
341
|
+
if (!requirements)
|
|
342
|
+
return undefined;
|
|
343
|
+
let mustBe = 0;
|
|
344
|
+
let mustNotBe = 0;
|
|
345
|
+
BOOLEAN_CAPABILITIES.forEach((name, bit) => {
|
|
346
|
+
const wanted = requirements[name];
|
|
347
|
+
if (wanted === true)
|
|
348
|
+
mustBe |= 1 << bit;
|
|
349
|
+
else if (wanted === false)
|
|
350
|
+
mustNotBe |= 1 << bit;
|
|
351
|
+
});
|
|
352
|
+
return { mustBe, mustNotBe, source: requirements };
|
|
353
|
+
}
|
|
354
|
+
function capabilityMasks(caps) {
|
|
355
|
+
let mustBe = 0;
|
|
356
|
+
let mustNotBe = 0;
|
|
357
|
+
BOOLEAN_CAPABILITIES.forEach((name, bit) => {
|
|
358
|
+
const value = name === 'reasoning' ? Boolean(caps.reasoning) : caps[name];
|
|
359
|
+
if (value === true)
|
|
360
|
+
mustBe |= 1 << bit;
|
|
361
|
+
else if (value === false)
|
|
362
|
+
mustNotBe |= 1 << bit;
|
|
363
|
+
});
|
|
364
|
+
return { mustBe, mustNotBe };
|
|
365
|
+
}
|
|
366
|
+
function normalizedRequirements(requirements) {
|
|
367
|
+
if (!requirements)
|
|
368
|
+
return null;
|
|
369
|
+
return Object.keys(requirements)
|
|
370
|
+
.sort()
|
|
371
|
+
.map((key) => [key, requirements[key]]);
|
|
372
|
+
}
|
|
373
|
+
function byScore(a, b) {
|
|
374
|
+
return b.base - a.base || a.index - b.index;
|
|
375
|
+
}
|
|
243
376
|
function isLocalModel(model) {
|
|
244
377
|
return (model.startsWith('ollama/') ||
|
|
245
378
|
model.startsWith('lmstudio/') ||
|