@avantf/dsh-mem 0.3.0 → 0.4.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/README.md +89 -28
- package/cordis.patch.yml +17 -0
- package/lib/client.js +155 -14
- package/lib/dsh-build.json +5 -5
- package/lib/engine/mem/config/config_files.d.ts +26 -0
- package/lib/engine/mem/config/domains.d.ts +20 -0
- package/lib/engine/mem/config/loader.d.ts +35 -0
- package/lib/engine/mem/config/paths.d.ts +53 -0
- package/lib/engine/mem/db/chunk.d.ts +26 -0
- package/lib/engine/mem/db/conn.d.ts +13 -0
- package/lib/engine/mem/db/dao/chunks.d.ts +150 -0
- package/lib/engine/mem/db/dao/contradictions.d.ts +98 -0
- package/lib/engine/mem/db/dao/documents.d.ts +51 -0
- package/lib/engine/mem/db/dao/entities.d.ts +67 -0
- package/lib/engine/mem/db/dao/facts.d.ts +472 -0
- package/lib/engine/mem/db/dao/stats.d.ts +14 -0
- package/lib/engine/mem/db/dao/triples.d.ts +80 -0
- package/lib/engine/mem/db/hash.d.ts +2 -0
- package/lib/engine/mem/db/knowledge.d.ts +17 -0
- package/lib/engine/mem/db/migrations.d.ts +71 -0
- package/lib/engine/mem/db/port.d.ts +57 -0
- package/lib/engine/mem/db/schema.d.ts +26 -0
- package/lib/engine/mem/db/sqlite.d.ts +89 -0
- package/lib/engine/mem/db/store.d.ts +35 -0
- package/lib/engine/mem/db/tokenizer.d.ts +61 -0
- package/lib/engine/mem/db/vectors.d.ts +50 -0
- package/lib/engine/mem/dispatch.d.ts +51 -0
- package/lib/engine/mem/entities/extract.d.ts +84 -0
- package/lib/engine/mem/eval/loader.d.ts +19 -0
- package/lib/engine/mem/eval/metrics.d.ts +25 -0
- package/lib/engine/mem/eval/runner.d.ts +10 -0
- package/lib/engine/mem/eval/write_metrics.d.ts +88 -0
- package/lib/engine/mem/git.d.ts +73 -0
- package/lib/engine/mem/hrr/algebra.d.ts +30 -0
- package/lib/engine/mem/hrr/atoms.d.ts +22 -0
- package/lib/engine/mem/hrr/encode.d.ts +32 -0
- package/lib/engine/mem/hrr/index.d.ts +3 -0
- package/lib/engine/mem/index.d.ts +27 -0
- package/lib/engine/mem/lifecycle/contradiction.d.ts +156 -0
- package/lib/engine/mem/lifecycle/index.d.ts +5 -0
- package/lib/engine/mem/lifecycle/maintenance.d.ts +48 -0
- package/lib/engine/mem/lifecycle/presence.d.ts +29 -0
- package/lib/engine/mem/lifecycle/tick.d.ts +43 -0
- package/lib/engine/mem/lifecycle/trust.d.ts +90 -0
- package/lib/engine/mem/modelBootstrap.d.ts +207 -0
- package/lib/engine/mem/router.d.ts +51 -0
- package/lib/engine/mem/runtime.d.ts +232 -0
- package/lib/engine/mem/store/common.d.ts +82 -0
- package/lib/engine/mem/store/doc_files.d.ts +125 -0
- package/lib/engine/mem/store/document_text.d.ts +47 -0
- package/lib/engine/mem/store/floors.d.ts +191 -0
- package/lib/engine/mem/store/hybrid.d.ts +232 -0
- package/lib/engine/mem/store/ingest_guard.d.ts +25 -0
- package/lib/engine/mem/store/knowledge.d.ts +483 -0
- package/lib/engine/mem/store/lexical.d.ts +67 -0
- package/lib/engine/mem/store/memory.d.ts +543 -0
- package/lib/engine/mem/store/source_picker.d.ts +43 -0
- package/lib/engine/mem-contract/config.d.ts +129 -0
- package/lib/engine/mem-contract/env.d.ts +31 -0
- package/lib/engine/mem-contract/family.d.ts +25 -0
- package/lib/engine/mem-contract/index.d.ts +8 -0
- package/lib/engine/mem-contract/log.d.ts +29 -0
- package/lib/engine/mem-contract/remote.d.ts +80 -0
- package/lib/engine/mem-contract/text.d.ts +92 -0
- package/lib/engine/mem-contract/tools.d.ts +284 -0
- package/lib/engine/mem-contract/types.d.ts +615 -0
- package/lib/engine/mem-retrieval/adapters/auto_vstore.d.ts +47 -0
- package/lib/engine/mem-retrieval/adapters/hnswlib.d.ts +174 -0
- package/lib/engine/mem-retrieval/adapters/local_bge.d.ts +121 -0
- package/lib/engine/mem-retrieval/adapters/local_numpy.d.ts +28 -0
- package/lib/engine/mem-retrieval/adapters/local_reranker.d.ts +52 -0
- package/lib/engine/mem-retrieval/adapters/none_reranker.d.ts +10 -0
- package/lib/engine/mem-retrieval/adapters/warm_gate.d.ts +46 -0
- package/lib/engine/mem-retrieval/budget.d.ts +30 -0
- package/lib/engine/mem-retrieval/fusion.d.ts +53 -0
- package/lib/engine/mem-retrieval/index.d.ts +14 -0
- package/lib/engine/mem-retrieval/interfaces.d.ts +82 -0
- package/lib/engine/mem-retrieval/log.d.ts +11 -0
- package/lib/engine/mem-retrieval/registry.d.ts +23 -0
- package/lib/engine/mem-retrieval/rerank.d.ts +13 -0
- package/lib/engine/mem-retrieval/stats.d.ts +64 -0
- package/lib/engine/mem-retrieval/text_budget.d.ts +52 -0
- package/lib/index.js +1773 -473
- package/lib/interface-version.json +2 -2
- package/lib/types/client/domains.d.ts +5 -4
- package/lib/types/client/domains.d.ts.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/ingest.d.ts +1 -1
- package/lib/types/client/wire.d.ts +36 -0
- package/lib/types/client/wire.d.ts.map +1 -0
- package/lib/types/envinit-bootstrap.d.ts +3 -2
- package/lib/types/envinit.d.ts +2 -19
- package/lib/types/envinit.d.ts.map +1 -1
- package/lib/types/hints.d.ts +1 -1
- package/lib/types/index.d.ts +8 -3
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/open.d.ts +61 -11
- package/lib/types/open.d.ts.map +1 -1
- package/lib/types/prompt.d.ts +19 -1
- package/lib/types/prompt.d.ts.map +1 -1
- package/lib/types/provision.d.ts +1 -1
- package/lib/types/provision.d.ts.map +1 -1
- package/lib/types/reconcile.d.ts +64 -0
- package/lib/types/reconcile.d.ts.map +1 -0
- package/lib/types/remote.d.ts +53 -0
- package/lib/types/remote.d.ts.map +1 -1
- package/lib/types/render.d.ts +35 -0
- package/lib/types/render.d.ts.map +1 -0
- package/lib/types/tool_schema.d.ts +3 -13
- package/lib/types/tool_schema.d.ts.map +1 -1
- package/package.json +27 -18
- package/lib/client.js.map +0 -1
- package/lib/types/client/domains.js +0 -25
- package/lib/types/client/domains.js.map +0 -1
- package/lib/types/client/index.js +0 -1371
- package/lib/types/client/index.js.map +0 -1
- package/lib/types/client/ingest.js +0 -30
- package/lib/types/client/ingest.js.map +0 -1
- package/lib/types/client/paging.js +0 -36
- package/lib/types/client/paging.js.map +0 -1
- package/lib/types/client/summarize.js +0 -79
- package/lib/types/client/summarize.js.map +0 -1
- package/lib/types/data_home.js +0 -26
- package/lib/types/data_home.js.map +0 -1
- package/lib/types/envinit-bootstrap.js +0 -333
- package/lib/types/envinit.js +0 -402
- package/lib/types/envinit.js.map +0 -1
- package/lib/types/hints.js +0 -53
- package/lib/types/hints.js.map +0 -1
- package/lib/types/index.js +0 -755
- package/lib/types/index.js.map +0 -1
- package/lib/types/inject.js +0 -14
- package/lib/types/inject.js.map +0 -1
- package/lib/types/interface_gate.js +0 -84
- package/lib/types/interface_gate.js.map +0 -1
- package/lib/types/open.js +0 -133
- package/lib/types/open.js.map +0 -1
- package/lib/types/prompt.js +0 -148
- package/lib/types/prompt.js.map +0 -1
- package/lib/types/provision.js +0 -177
- package/lib/types/provision.js.map +0 -1
- package/lib/types/remote.js +0 -247
- package/lib/types/remote.js.map +0 -1
- package/lib/types/tool_schema.js +0 -149
- package/lib/types/tool_schema.js.map +0 -1
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import type { VectorStore } from '../interfaces.js';
|
|
2
|
+
/**
|
|
3
|
+
* Search-time beam width, when the config does not say otherwise.
|
|
4
|
+
*
|
|
5
|
+
* hnswlib defaults `ef` to the value passed to `initIndex` (10 here), and that default is a
|
|
6
|
+
* QUALITY CLIFF rather than a neutral choice. Measured through this adapter (2000 random unit
|
|
7
|
+
* vectors, dim 512, true top-10 as ground truth):
|
|
8
|
+
*
|
|
9
|
+
* ef 10 64 128 256 400
|
|
10
|
+
* R@10 0.45 0.78 0.93 1.00 1.00
|
|
11
|
+
* ms 0.10 0.36 0.53 0.74 0.90
|
|
12
|
+
*
|
|
13
|
+
* Recall falls as the corpus grows (at n=8000, R@50 was 0.89 even at `ef = 400`), and random
|
|
14
|
+
* vectors are the WORST case — real embeddings cluster, so production recall is higher than
|
|
15
|
+
* these numbers. 256 keeps the small/medium corpora exact without giving up the speedup; the
|
|
16
|
+
* knob is `vectorStore.hnswlib_ef_search`.
|
|
17
|
+
*/
|
|
18
|
+
export declare const DEFAULT_EF_SEARCH = 256;
|
|
19
|
+
/**
|
|
20
|
+
* `hnswlib` — real ANN backend via `hnswlib-node` (cosine space, HierarchicalNSW).
|
|
21
|
+
* Uses the stable v2 string-space + initIndex + Array API. Wraps every native call
|
|
22
|
+
* in a try/catch and falls back to the in-memory brute-force store so the plugin
|
|
23
|
+
* never crashes on a pathological native binding — and a fallback that is TAKEN is
|
|
24
|
+
* always logged, because "never crashes" must not become "silently returns nothing".
|
|
25
|
+
*/
|
|
26
|
+
export declare class HnswlibVectorStore implements VectorStore {
|
|
27
|
+
readonly name = "hnswlib";
|
|
28
|
+
readonly dim: number;
|
|
29
|
+
private lib;
|
|
30
|
+
private fallback;
|
|
31
|
+
private readonly vectors;
|
|
32
|
+
/**
|
|
33
|
+
* The beam width in effect, and the index it was applied to: `ef` is in force on `efOn`.
|
|
34
|
+
*
|
|
35
|
+
* The index is part of the key, not just the number. `reindex()` and `rebuild()` REPLACE the
|
|
36
|
+
* native index, and a replacement starts at hnswlib's own default `ef` (10), so a bare number
|
|
37
|
+
* would compare equal and skip the re-apply — silently dropping ANN recall to ~0.44 of the
|
|
38
|
+
* true top-k after any batch eviction, which is the cliff this whole knob exists to avoid.
|
|
39
|
+
*/
|
|
40
|
+
private efOn;
|
|
41
|
+
private ef;
|
|
42
|
+
private readonly efSearch;
|
|
43
|
+
/**
|
|
44
|
+
* Elements in the native graph, INCLUDING the ones `markDelete` has tombstoned.
|
|
45
|
+
*
|
|
46
|
+
* The live set is `vectors` (authoritative for `count`/`fetch`), so the number of dead points is
|
|
47
|
+
* `graphElements - vectors.size` — a derived quantity rather than a set of ids, because the graph
|
|
48
|
+
* OUTLIVES the process: a snapshot restored from disk already contains every dead point earlier
|
|
49
|
+
* sessions created, and a per-session `Set` could not see them (measured across 4 sessions of
|
|
50
|
+
* 100 evictions: live 1200→800 while the graph stayed at 1200, with the counter reading 100).
|
|
51
|
+
*
|
|
52
|
+
* Bookkeeping: a snapshot restore reads `getIdsList().length`; a build from rows sets it to the
|
|
53
|
+
* row count; `add` increments it only when the id was NOT already in the graph (an update in
|
|
54
|
+
* place, which also un-deletes a tombstone, does not grow the graph).
|
|
55
|
+
*/
|
|
56
|
+
private graphElements;
|
|
57
|
+
/** Snapshot path PREFIX (set by the owning store via {@link attachPersistence}); see `snapshotPath`. */
|
|
58
|
+
private cachePath;
|
|
59
|
+
/** Something changed since the snapshot was written. */
|
|
60
|
+
private dirty;
|
|
61
|
+
private restores;
|
|
62
|
+
private rebuilds;
|
|
63
|
+
constructor(dim: number, efSearch?: number);
|
|
64
|
+
add(id: number, vec: Float32Array): void;
|
|
65
|
+
/** Whether the real hnswlib native index is active (false = brute-force fallback). */
|
|
66
|
+
get native(): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Counters a caller (test, benchmark, diagnostics) can assert on without guessing from timing:
|
|
69
|
+
* `restores` = snapshots loaded, `rebuilds` = full native builds, `tombstones` = pending deletes.
|
|
70
|
+
*/
|
|
71
|
+
stats(): {
|
|
72
|
+
live: number;
|
|
73
|
+
tombstones: number;
|
|
74
|
+
graphElements: number;
|
|
75
|
+
restores: number;
|
|
76
|
+
rebuilds: number;
|
|
77
|
+
native: boolean;
|
|
78
|
+
};
|
|
79
|
+
/** Point the native index at an on-disk snapshot (see {@link VectorStore.attachPersistence}). */
|
|
80
|
+
attachPersistence(path: string): void;
|
|
81
|
+
/**
|
|
82
|
+
* The snapshot file for a row set: `prefix.<fingerprint>.hnsw`.
|
|
83
|
+
*
|
|
84
|
+
* Putting the fingerprint in the NAME is what makes the commit atomic. The previous shape wrote
|
|
85
|
+
* two files (index, then a JSON sidecar with the fingerprint) and validated the sidecar against
|
|
86
|
+
* the CURRENT rows — so an interleaved pair `(index_B, meta_A)` could describe two different
|
|
87
|
+
* corpora and still pass the check (the metadata was never bound to the graph it accompanied).
|
|
88
|
+
* With one file there is nothing to desynchronize: a snapshot either exists under the name of the
|
|
89
|
+
* row set it was built from, or it does not exist at all.
|
|
90
|
+
*/
|
|
91
|
+
private snapshotPath;
|
|
92
|
+
/**
|
|
93
|
+
* Widen the search beam, skipping the native call only when this exact width is already in
|
|
94
|
+
* force on THIS index (see {@link efOn}).
|
|
95
|
+
*/
|
|
96
|
+
private setEf;
|
|
97
|
+
private rebuildFallback;
|
|
98
|
+
topk(vec: Float32Array, k: number): {
|
|
99
|
+
id: number;
|
|
100
|
+
score: number;
|
|
101
|
+
}[];
|
|
102
|
+
fetch(ids: number[]): Map<number, Float32Array>;
|
|
103
|
+
remove(id: number): void;
|
|
104
|
+
/**
|
|
105
|
+
* Batch removal via TOMBSTONES — O(k), not O(N).
|
|
106
|
+
*
|
|
107
|
+
* `markDelete` excludes a label from `searchKnn` without touching the graph (measured: 1000
|
|
108
|
+
* tombstones = 0.19 ms on an 8000-vector index, against 1.3 s for the rebuild this used to
|
|
109
|
+
* do). Deleted points are still traversed and still occupy space, so {@link maybeCompact}
|
|
110
|
+
* rebuilds once tombstones become a meaningful fraction of the corpus.
|
|
111
|
+
*/
|
|
112
|
+
removeMany(ids: number[]): void;
|
|
113
|
+
/**
|
|
114
|
+
* Rebuild once the dead points are worth a full pass (see {@link COMPACT_RATIO}).
|
|
115
|
+
*
|
|
116
|
+
* The count is DERIVED from the graph, so it includes what earlier sessions left behind: that is
|
|
117
|
+
* what makes accumulation across CLI invocations self-correcting instead of monotonic.
|
|
118
|
+
*/
|
|
119
|
+
private maybeCompact;
|
|
120
|
+
/**
|
|
121
|
+
* Recreate the serving index (native when possible) from the tracked vectors.
|
|
122
|
+
*
|
|
123
|
+
* Public because it is the "compact now" operation: it drops every tombstone and leaves a
|
|
124
|
+
* clean graph. Called by `maybeCompact()` and by `rebuild()`'s failure paths.
|
|
125
|
+
*/
|
|
126
|
+
compact(): void;
|
|
127
|
+
/** Recreate the serving index (native when possible) from the tracked vectors. */
|
|
128
|
+
private reindex;
|
|
129
|
+
count(): number;
|
|
130
|
+
/**
|
|
131
|
+
* Build from the store's rows, reusing the on-disk snapshot when it provably describes them.
|
|
132
|
+
*
|
|
133
|
+
* The snapshot check is a FINGERPRINT over the live ids plus the count, not just a count: a
|
|
134
|
+
* session that evicted three facts and added three others has the same count with different
|
|
135
|
+
* contents, and loading that would silently serve vectors that no longer exist. The path
|
|
136
|
+
* carries the vector space (see `attachPersistence`), so a model/dim swap cannot load at all.
|
|
137
|
+
*/
|
|
138
|
+
rebuild(rows: Iterable<{
|
|
139
|
+
id: number;
|
|
140
|
+
vec: Float32Array;
|
|
141
|
+
}>): void;
|
|
142
|
+
/** Persist pending state (called on shutdown); a no-op without a cache path or change. */
|
|
143
|
+
flush(): void;
|
|
144
|
+
/**
|
|
145
|
+
* Load the snapshot that describes EXACTLY these rows, if it exists.
|
|
146
|
+
*
|
|
147
|
+
* There is no validity check to get wrong: the file is named by the fingerprint of the row set
|
|
148
|
+
* it was built from, so a snapshot for a different corpus is a different file (and simply will
|
|
149
|
+
* not be found). That is the whole reason the fingerprint moved into the name.
|
|
150
|
+
*
|
|
151
|
+
* `allowReplaceDeleted` is deliberately FALSE: with it enabled, `addPoint` on a label that the
|
|
152
|
+
* snapshot recorded as deleted throws ("Can't use addPoint to update deleted elements if
|
|
153
|
+
* replacement of deleted elements is enabled"), which would break the very first re-add of an
|
|
154
|
+
* archived-then-restored fact. Verified against the binding; deleted entries stay excluded from
|
|
155
|
+
* `searchKnn` either way.
|
|
156
|
+
*/
|
|
157
|
+
private restore;
|
|
158
|
+
/** Live rows, as `fingerprintOf` wants them. */
|
|
159
|
+
private liveRows;
|
|
160
|
+
/**
|
|
161
|
+
* Write the native graph, atomically: a temp file plus `rename`, so a reader (the other
|
|
162
|
+
* process sharing this data home) sees either the old snapshot or the complete new one.
|
|
163
|
+
* Best-effort by design — a failing cache must never fail a write that already hit the DB.
|
|
164
|
+
*/
|
|
165
|
+
private persist;
|
|
166
|
+
/**
|
|
167
|
+
* Delete snapshots for OTHER row sets of this store (best-effort).
|
|
168
|
+
*
|
|
169
|
+
* They can never be loaded again — the name IS the fingerprint — so keeping them only leaks disk.
|
|
170
|
+
* A concurrent process may have just written one; unlinking an open file is safe on POSIX, and
|
|
171
|
+
* the worst case is that the other process rebuilds next start.
|
|
172
|
+
*/
|
|
173
|
+
private dropStaleSnapshots;
|
|
174
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { SemanticBackend } from '../interfaces.js';
|
|
2
|
+
/**
|
|
3
|
+
* The injected pipeline surface: one text OR an array of texts in, a tensor out.
|
|
4
|
+
*
|
|
5
|
+
* The array form matters: transformers.js runs a batch through the model in one call (its own
|
|
6
|
+
* per-call overhead — session dispatch, tokenizer batching — is amortized), while a serial loop of
|
|
7
|
+
* single-text calls pays it per chunk. `dims` is the tensor shape (`[n, dim]`); the single-text
|
|
8
|
+
* form may omit it.
|
|
9
|
+
*/
|
|
10
|
+
export type PipelineFn = (text: string | string[], opts?: Record<string, unknown>) => Promise<{
|
|
11
|
+
data: Float32Array | number[];
|
|
12
|
+
dims?: number[];
|
|
13
|
+
}>;
|
|
14
|
+
/** The slice of the transformers.js module this project reads and configures. */
|
|
15
|
+
export type TransformersModule = {
|
|
16
|
+
env?: Record<string, unknown>;
|
|
17
|
+
pipeline?: (task: string, model: string) => Promise<unknown>;
|
|
18
|
+
};
|
|
19
|
+
/** Injectable seam: tests supply a fake pipeline instead of the transformers.js import. */
|
|
20
|
+
export type PipeFactory = (model: string, env: ModelEnv) => Promise<PipelineFn>;
|
|
21
|
+
export interface ModelEnv {
|
|
22
|
+
/** Mirror host for model downloads (default domestic). */
|
|
23
|
+
mirror: string;
|
|
24
|
+
/** Local cache dir for downloaded weights. */
|
|
25
|
+
cacheDir: string;
|
|
26
|
+
/** Whether to auto-download on warmup. */
|
|
27
|
+
autoDownload: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Upper bound on tokens handed to the model; `0`/unset = auto (the loaded model's declared
|
|
30
|
+
* window). Past the window transformers.js truncates SILENTLY, so the adapter bounds the
|
|
31
|
+
* text first and logs it once (`text_budget.ts`).
|
|
32
|
+
*/
|
|
33
|
+
maxInputTokens?: number;
|
|
34
|
+
/**
|
|
35
|
+
* Do NOT warm in the constructor: the first `encode()` / `encodeBatch()` warms lazily.
|
|
36
|
+
*
|
|
37
|
+
* Set when something ELSE owns the model root and will put the files there asynchronously — the
|
|
38
|
+
* DSH plugin does, because the family framework fetches the model as a background item. Warming
|
|
39
|
+
* in the constructor would race that install and let this process fetch its own copy; the caller
|
|
40
|
+
* warms explicitly once the item settles. Unset (the CLI / MCP / every direct construction) keeps
|
|
41
|
+
* the eager constructor warm.
|
|
42
|
+
*/
|
|
43
|
+
deferWarm?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Local BGE embedder via `@huggingface/transformers` (transformers.js, ONNX).
|
|
47
|
+
* Lazily imports the package; `warmUp()` (async, idempotent) sets the mirror +
|
|
48
|
+
* cache dir, then triggers a model load/download. Until warmup completes,
|
|
49
|
+
* `isAvailable()` is false and the retrieval pipeline stays degraded.
|
|
50
|
+
*
|
|
51
|
+
* A warmup that fails is not final: `ensureWarm()` lets observing call sites
|
|
52
|
+
* retry it cheaply (see `warm_gate.ts`), so a transient mirror outage does not
|
|
53
|
+
* degrade a long-lived host until the next restart.
|
|
54
|
+
*/
|
|
55
|
+
export declare class LocalBgeBackend implements SemanticBackend {
|
|
56
|
+
readonly name = "local_bge";
|
|
57
|
+
readonly dim: number;
|
|
58
|
+
private _available;
|
|
59
|
+
private _pipe;
|
|
60
|
+
private _warming;
|
|
61
|
+
private _dimWarned;
|
|
62
|
+
private _lengthWarned;
|
|
63
|
+
private _window;
|
|
64
|
+
private readonly configuredMax;
|
|
65
|
+
private readonly model;
|
|
66
|
+
private readonly env;
|
|
67
|
+
private readonly pipeFactory;
|
|
68
|
+
private readonly gate;
|
|
69
|
+
constructor(model: string, dim: number, env?: Partial<ModelEnv>, pipeFactory?: PipeFactory);
|
|
70
|
+
isAvailable(): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Fire-and-forget retry for call sites that only observe availability. No-op
|
|
73
|
+
* while the model is ready or an attempt is already in flight; otherwise
|
|
74
|
+
* throttled by {@link WarmGate}.
|
|
75
|
+
*/
|
|
76
|
+
ensureWarm(): void;
|
|
77
|
+
/** Idempotent warmup: configure transformers.js env and load/download the model. */
|
|
78
|
+
warmUp(): Promise<void>;
|
|
79
|
+
private _doWarmup;
|
|
80
|
+
/** Tell the operator whether the failure is recoverable in-process. */
|
|
81
|
+
private retryHint;
|
|
82
|
+
/**
|
|
83
|
+
* The model's usable input window, resolved once per process: an explicit
|
|
84
|
+
* `semantic.max_input_tokens` cap clamped to the loaded tokenizer's declared
|
|
85
|
+
* `model_max_length`, or the declared window itself when the config says "auto" (0).
|
|
86
|
+
*/
|
|
87
|
+
private window;
|
|
88
|
+
/**
|
|
89
|
+
* Bound `text` to the window BEFORE it reaches transformers.js.
|
|
90
|
+
*
|
|
91
|
+
* This is the only place the guard can live: the feature-extraction pipeline's `_call`
|
|
92
|
+
* accepts `{pooling, normalize, quantize, precision}` and nothing else, so `max_length`
|
|
93
|
+
* cannot be raised or lowered from the call site — past `model_max_length` the tokenizer
|
|
94
|
+
* truncates and says nothing.
|
|
95
|
+
*/
|
|
96
|
+
private bound;
|
|
97
|
+
encode(text: string): Promise<Float32Array>;
|
|
98
|
+
/**
|
|
99
|
+
* Encode several texts in ONE model call.
|
|
100
|
+
*
|
|
101
|
+
* This used to be a serial loop over `encode()`, and production code never called it — so
|
|
102
|
+
* ingest paid the per-call overhead once per chunk (measured: the dominant cost of `kb ingest`,
|
|
103
|
+
* 33.7 ms/chunk at 450 characters). Every text is bounded individually first (the window
|
|
104
|
+
* belongs to the model, not to the batch), and a failed batch falls back to per-text calls so
|
|
105
|
+
* an injected single-string pipeline (tests) and a memory-bound real batch both keep working.
|
|
106
|
+
*/
|
|
107
|
+
encodeBatch(texts: string[]): Promise<Float32Array[]>;
|
|
108
|
+
/** One model call for a length-homogeneous group; falls back to per-text calls on failure. */
|
|
109
|
+
private encodeGroup;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Apply this project's cache/mirror policy to transformers.js.
|
|
113
|
+
*
|
|
114
|
+
* `mod.env` is PROCESS-GLOBAL and last-writer-wins: the embedder and the reranker are two adapters
|
|
115
|
+
* over ONE module, so whichever warms second decides what both see. Setting the keys from a single
|
|
116
|
+
* place is what keeps the two from disagreeing — the reranker used to set `allowLocalModels` on its
|
|
117
|
+
* own, which reads as a policy difference between the adapters and was really an accident of which
|
|
118
|
+
* file got edited last. `allowLocalModels` is already the library default; it is stated here so the
|
|
119
|
+
* policy is explicit rather than inherited, and identical for both adapters.
|
|
120
|
+
*/
|
|
121
|
+
export declare function applyModelEnv(mod: TransformersModule, env: ModelEnv): void;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { VectorStore } from '../interfaces.js';
|
|
2
|
+
/**
|
|
3
|
+
* `local_numpy` — in-memory brute-force vector store.
|
|
4
|
+
* Default / dev backend; persists via the caller (the store DB owns the vectors).
|
|
5
|
+
* The fallback every backend without a real adapter resolves to; `hnswlib` is the implemented ANN
|
|
6
|
+
* store, while `faiss`/`pgvector`/`qdrant` warn and land here.
|
|
7
|
+
*/
|
|
8
|
+
export declare class LocalNumpyVectorStore implements VectorStore {
|
|
9
|
+
readonly name = "local_numpy";
|
|
10
|
+
readonly dim: number;
|
|
11
|
+
private readonly vectors;
|
|
12
|
+
constructor(dim: number);
|
|
13
|
+
add(id: number, vec: Float32Array): void;
|
|
14
|
+
topk(vec: Float32Array, k: number): {
|
|
15
|
+
id: number;
|
|
16
|
+
score: number;
|
|
17
|
+
}[];
|
|
18
|
+
fetch(ids: number[]): Map<number, Float32Array>;
|
|
19
|
+
remove(id: number): void;
|
|
20
|
+
removeMany(ids: number[]): void;
|
|
21
|
+
count(): number;
|
|
22
|
+
/** Iterate all stored (id, vector) pairs — used by `auto` to migrate to an ANN backend. */
|
|
23
|
+
entries(): IterableIterator<[number, Float32Array]>;
|
|
24
|
+
rebuild(rows: Iterable<{
|
|
25
|
+
id: number;
|
|
26
|
+
vec: Float32Array;
|
|
27
|
+
}>): void;
|
|
28
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Reranker } from '../interfaces.js';
|
|
2
|
+
import { type ModelEnv } from './local_bge.js';
|
|
3
|
+
/** One text-classification result: transformers.js yields `{label, score}`; tolerate a raw `{data}` tensor too. */
|
|
4
|
+
type ClassifyResult = {
|
|
5
|
+
label?: string;
|
|
6
|
+
score?: number;
|
|
7
|
+
data?: number[];
|
|
8
|
+
};
|
|
9
|
+
/** The injected pipeline surface: a batch of (query, document) pairs in, scores out. */
|
|
10
|
+
export type ClassifyFn = (inputs: [string, string][], opts?: Record<string, unknown>) => Promise<ClassifyResult[]>;
|
|
11
|
+
/** Injectable seam: tests supply a fake classifier instead of the transformers.js import. */
|
|
12
|
+
export type RerankerPipeFactory = (model: string, env: ModelEnv) => Promise<ClassifyFn>;
|
|
13
|
+
/** Never hand the classifier a degenerate document; below this the score is meaningless. */
|
|
14
|
+
export declare const MIN_DOC_TOKENS = 16;
|
|
15
|
+
/**
|
|
16
|
+
* Local cross-encoder reranker via `@huggingface/transformers` (`bge-reranker`).
|
|
17
|
+
* Lazily imported; `warmUp()` configures the mirror/cache and triggers a load.
|
|
18
|
+
* Degrades to identity order when the package/model is not available.
|
|
19
|
+
*
|
|
20
|
+
* Like {@link LocalBgeBackend}, a failed warmup is retryable through
|
|
21
|
+
* `ensureWarm()` rather than being final until the next restart.
|
|
22
|
+
*/
|
|
23
|
+
export declare class LocalReranker implements Reranker {
|
|
24
|
+
readonly name = "bge_reranker";
|
|
25
|
+
private _available;
|
|
26
|
+
private _pipe;
|
|
27
|
+
private _warming;
|
|
28
|
+
private _lengthWarned;
|
|
29
|
+
private _window;
|
|
30
|
+
private readonly configuredMax;
|
|
31
|
+
private readonly model;
|
|
32
|
+
private readonly env;
|
|
33
|
+
private readonly pipeFactory;
|
|
34
|
+
private readonly gate;
|
|
35
|
+
constructor(model: string, env?: Partial<ModelEnv>, pipeFactory?: RerankerPipeFactory);
|
|
36
|
+
isAvailable(): boolean;
|
|
37
|
+
/** Fire-and-forget retry for call sites that only observe availability (see `warm_gate.ts`). */
|
|
38
|
+
ensureWarm(): void;
|
|
39
|
+
warmUp(): Promise<void>;
|
|
40
|
+
private _doWarmup;
|
|
41
|
+
private window;
|
|
42
|
+
/**
|
|
43
|
+
* Bound one document for a pair, given how much of the window the query already spends.
|
|
44
|
+
* Returns the document text unchanged when it fits.
|
|
45
|
+
*/
|
|
46
|
+
private boundDocument;
|
|
47
|
+
rerank(query: string, candidates: {
|
|
48
|
+
id: number;
|
|
49
|
+
text: string;
|
|
50
|
+
}[]): Promise<number[]>;
|
|
51
|
+
}
|
|
52
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Reranker } from '../interfaces.js';
|
|
2
|
+
/** `none` — reranking disabled (the default). Reports unavailable so callers skip the rerank pass entirely. */
|
|
3
|
+
export declare class NoneReranker implements Reranker {
|
|
4
|
+
readonly name = "none";
|
|
5
|
+
isAvailable(): boolean;
|
|
6
|
+
rerank(_query: string, candidates: {
|
|
7
|
+
id: number;
|
|
8
|
+
text: string;
|
|
9
|
+
}[]): Promise<number[]>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry gate for the local model adapters.
|
|
3
|
+
*
|
|
4
|
+
* The model bootstrap runs exactly once, at startup (the model artifact in
|
|
5
|
+
* `provisionToolchainAsync`). When that single attempt fails — an unreachable
|
|
6
|
+
* mirror, a poisoned DNS entry, a
|
|
7
|
+
* proxy that is not up yet — nothing used to try again, so a long-lived host
|
|
8
|
+
* stayed on the FTS+entity fallback forever, even after the network recovered
|
|
9
|
+
* or the weights landed in the cache (`vectors_fix` kept reporting
|
|
10
|
+
* `semantic_available: false` on a running plugin host).
|
|
11
|
+
*
|
|
12
|
+
* Call sites that only *observe* availability (hybrid search, best-effort
|
|
13
|
+
* indexing) now ask for a retry through {@link WarmGate.nudge}. The gate is
|
|
14
|
+
* deliberately timer-free: there is no background poll, no handle to dispose,
|
|
15
|
+
* and no traffic while nothing is asking for semantic retrieval — recovery
|
|
16
|
+
* happens on the next retrieval that needs it.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Minimum gap between two retry attempts triggered by observing call sites.
|
|
20
|
+
* Comfortably above undici's 10 s connect timeout so a dead mirror is not
|
|
21
|
+
* re-probed on every query, and short enough that a recovered network is picked
|
|
22
|
+
* up without a restart.
|
|
23
|
+
*/
|
|
24
|
+
export declare const ENSURE_WARM_FLOOR_MS = 30000;
|
|
25
|
+
/** Throttles observing-call-site retries of a failed model warmup. */
|
|
26
|
+
export declare class WarmGate {
|
|
27
|
+
private readonly canAttempt;
|
|
28
|
+
private lastAttemptAt;
|
|
29
|
+
/**
|
|
30
|
+
* @param canAttempt whether a retry can still help. `false` makes the gate
|
|
31
|
+
* inert: with downloads disabled a miss is deterministic (only a local cache
|
|
32
|
+
* load is possible), so retrying would only add noise.
|
|
33
|
+
*/
|
|
34
|
+
constructor(canAttempt: () => boolean);
|
|
35
|
+
/**
|
|
36
|
+
* Record that an attempt actually started. Called by the adapter's warmup so
|
|
37
|
+
* the startup attempt also resets the floor — otherwise the first query after
|
|
38
|
+
* a failed boot would immediately re-attempt.
|
|
39
|
+
*/
|
|
40
|
+
markAttempt(now?: number): void;
|
|
41
|
+
/**
|
|
42
|
+
* Run `attempt` unless the floor has not elapsed. Non-blocking: the caller
|
|
43
|
+
* passes a fire-and-forget trigger. Returns whether an attempt was started.
|
|
44
|
+
*/
|
|
45
|
+
nudge(attempt: () => void, now?: number): boolean;
|
|
46
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** What the budget needs from a candidate: ranked text. */
|
|
2
|
+
export interface BudgetInput {
|
|
3
|
+
text: string;
|
|
4
|
+
score: number;
|
|
5
|
+
}
|
|
6
|
+
export interface BudgetOptions {
|
|
7
|
+
/** Total tokens to spend; `0` (or negative) means "no budget" and returns the input untouched. */
|
|
8
|
+
maxTokens: number;
|
|
9
|
+
/** Ceiling for one entry; defaults to twice the average share. */
|
|
10
|
+
perEntryCap?: number;
|
|
11
|
+
/** Token estimator override (tests, or a real tokenizer). */
|
|
12
|
+
estimate?: (text: string) => number;
|
|
13
|
+
}
|
|
14
|
+
export interface Budgeted<T> {
|
|
15
|
+
kept: T[];
|
|
16
|
+
used_tokens: number;
|
|
17
|
+
/** Entries whose text was shortened (including those left with no text at all). */
|
|
18
|
+
truncated: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Fit `items` into `maxTokens`, preserving their (ranked) order.
|
|
22
|
+
*
|
|
23
|
+
* Pass 1 gives every entry `min(perEntryCap, remaining)`. Pass 2 spends what is left growing the
|
|
24
|
+
* entries that were cut, best first. An entry that receives no budget keeps an empty `text` and
|
|
25
|
+
* `truncated: true` — dropping it would hide its existence, which is worse than showing a
|
|
26
|
+
* reference to it.
|
|
27
|
+
*/
|
|
28
|
+
export declare function fitToTokenBudget<T extends BudgetInput>(items: readonly T[], opts: BudgetOptions): Budgeted<T & {
|
|
29
|
+
truncated?: boolean;
|
|
30
|
+
}>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hybrid fusion: each path's raw scores are scaled by that path's OWN maximum, then summed with
|
|
3
|
+
* the configured weights and cut to the top-k.
|
|
4
|
+
*
|
|
5
|
+
* Scaling by the max (rather than min-max over the path's returned set) is what makes the result
|
|
6
|
+
* INVARIANT to a capped path — FOR A PATH THAT HANDS ITS ENTRIES OVER IN ITS OWN SCORE ORDER.
|
|
7
|
+
* The store bounds every non-semantic leg (`memory.ts` `legCap`) for cost, and a cap then removes
|
|
8
|
+
* that leg's weakest entries, which is exactly the minimum a min-max normalization would have
|
|
9
|
+
* measured against: trimming used to rescale every survivor and could reorder them (a documented,
|
|
10
|
+
* previously-accepted effect). With `v/max` the number is the same with or without the cap,
|
|
11
|
+
* because the maximum belongs to an entry a cap cannot remove.
|
|
12
|
+
*
|
|
13
|
+
* That "cannot remove" is the assumption, and the store has ONE leg that breaks it: the HRR probe.
|
|
14
|
+
* Its candidates arrive in another leg's order — the entity-Jaccard ratio (`candidateFactsForAnyEntity`,
|
|
15
|
+
* which is the JACCARD leg's score, not the phase similarity this leg scores by), or recency in the
|
|
16
|
+
* no-candidate fallback (see `MemoryStore.hrrPath`). Either way the cap may drop this leg's highest
|
|
17
|
+
* scorer and the scaling does move. Stated rather than implied, because the invariance is the whole
|
|
18
|
+
* reason this normalization was chosen; `docs/PROVENANCE_REVIEW.md` N5 records the missing
|
|
19
|
+
* differential, and `recordLegCapped()` is what makes the cap observable at all.
|
|
20
|
+
*
|
|
21
|
+
* Magnitudes are kept: a path whose scores cluster high still contributes more per entry than one
|
|
22
|
+
* whose scores are near zero, which is the information ranks alone would throw away.
|
|
23
|
+
*/
|
|
24
|
+
export interface FusionPath {
|
|
25
|
+
weight: number;
|
|
26
|
+
/** candidate id → raw score (un-normalized). */
|
|
27
|
+
scores: Map<number, number>;
|
|
28
|
+
}
|
|
29
|
+
export interface Hit {
|
|
30
|
+
id: number;
|
|
31
|
+
score: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Scale raw scores by the set's maximum, into [0,1].
|
|
35
|
+
*
|
|
36
|
+
* A set whose maximum is not positive carries no ranking information, so every entry maps to 0 —
|
|
37
|
+
* it still participates in the pool (with no contribution) instead of being dropped, which is what
|
|
38
|
+
* keeps a degenerate path from emptying the result.
|
|
39
|
+
*
|
|
40
|
+
* A NON-FINITE score is treated the same way (mapped to 0, kept in the pool). It cannot come from a
|
|
41
|
+
* healthy leg, but a corrupt HRR blob decodes to NaN without a length check failing, and one NaN
|
|
42
|
+
* would otherwise do two kinds of damage: poison that candidate's merged total, and make `fuse`'s
|
|
43
|
+
* sort comparator return NaN — an undefined order, so the whole ranking (not just one entry)
|
|
44
|
+
* becomes arbitrary.
|
|
45
|
+
*/
|
|
46
|
+
export declare function scaleByMax(scores: Map<number, number>): Map<number, number>;
|
|
47
|
+
/**
|
|
48
|
+
* Fuse paths by scaling each path's scores by its own maximum, summing weighted values, and
|
|
49
|
+
* returning the top-k descending. Ties break on ascending id so the ranking is deterministic —
|
|
50
|
+
* scaled scores collide far more often than min-max ones did (both legs of a pair can rank two
|
|
51
|
+
* candidates identically), and the previous order was Map-insertion order.
|
|
52
|
+
*/
|
|
53
|
+
export declare function fuse(paths: FusionPath[], topK: number): Hit[];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export * from './interfaces.js';
|
|
2
|
+
export * from './log.js';
|
|
3
|
+
export * from './stats.js';
|
|
4
|
+
export * from './text_budget.js';
|
|
5
|
+
export * from './budget.js';
|
|
6
|
+
export * from './registry.js';
|
|
7
|
+
export * from './fusion.js';
|
|
8
|
+
export * from './rerank.js';
|
|
9
|
+
export * from './adapters/local_numpy.js';
|
|
10
|
+
export * from './adapters/hnswlib.js';
|
|
11
|
+
export * from './adapters/auto_vstore.js';
|
|
12
|
+
export * from './adapters/none_reranker.js';
|
|
13
|
+
export * from './adapters/local_bge.js';
|
|
14
|
+
export * from './adapters/local_reranker.js';
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The three stable contracts the business flow depends on.
|
|
3
|
+
* The business flow (fusion pipeline, stores, tools, router, UI) must depend only on
|
|
4
|
+
* these interfaces — never on a concrete backend. See DESIGN §5.
|
|
5
|
+
*/
|
|
6
|
+
/** Encodes text into a normalized embedding vector. */
|
|
7
|
+
export interface SemanticBackend {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly dim: number;
|
|
10
|
+
encode(text: string): Promise<Float32Array>;
|
|
11
|
+
encodeBatch(texts: string[]): Promise<Float32Array[]>;
|
|
12
|
+
isAvailable(): boolean;
|
|
13
|
+
/** Optional async bootstrap: configure env + download/load the model. */
|
|
14
|
+
warmUp?(): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Optional non-blocking retry for call sites that only *observe* availability
|
|
17
|
+
* (hybrid search, best-effort indexing): re-attempts a failed bootstrap,
|
|
18
|
+
* coalescing with any in-flight attempt and throttling to one attempt per
|
|
19
|
+
* floor. Implementations that load lazily may omit it.
|
|
20
|
+
*/
|
|
21
|
+
ensureWarm?(): void;
|
|
22
|
+
}
|
|
23
|
+
/** Re-ranks candidate facts/chunks by query relevance. */
|
|
24
|
+
export interface Reranker {
|
|
25
|
+
readonly name: string;
|
|
26
|
+
rerank(query: string, candidates: {
|
|
27
|
+
id: number;
|
|
28
|
+
text: string;
|
|
29
|
+
}[]): Promise<number[]>;
|
|
30
|
+
isAvailable(): boolean;
|
|
31
|
+
/** Optional async bootstrap: configure env + download/load the model. */
|
|
32
|
+
warmUp?(): Promise<void>;
|
|
33
|
+
/** Optional non-blocking retry — see {@link SemanticBackend.ensureWarm}. */
|
|
34
|
+
ensureWarm?(): void;
|
|
35
|
+
}
|
|
36
|
+
/** Distance/ann index over entry vectors (per-store instance). */
|
|
37
|
+
export interface VectorStore {
|
|
38
|
+
readonly name: string;
|
|
39
|
+
readonly dim: number;
|
|
40
|
+
add(id: number, vec: Float32Array): void;
|
|
41
|
+
/** Cosine-similarity top-k over all stored vectors (brute force by default). */
|
|
42
|
+
topk(vec: Float32Array, k: number): {
|
|
43
|
+
id: number;
|
|
44
|
+
score: number;
|
|
45
|
+
}[];
|
|
46
|
+
fetch(ids: number[]): Map<number, Float32Array>;
|
|
47
|
+
remove(id: number): void;
|
|
48
|
+
/**
|
|
49
|
+
* Optional batch removal. A backend that cannot delete incrementally MUST implement this so
|
|
50
|
+
* eviction loops stay O(N) instead of O(N²); callers prefer it whenever they evict more than one
|
|
51
|
+
* id. (hnswlib deletes by tombstone, so its `removeMany` is O(k) — it implements this to share
|
|
52
|
+
* the "compact once for the whole batch" path, not because a single delete would rebuild.)
|
|
53
|
+
*/
|
|
54
|
+
removeMany?(ids: number[]): void;
|
|
55
|
+
/**
|
|
56
|
+
* Optional: drop any dead/tombstoned entries the backend is carrying, now. An explicit
|
|
57
|
+
* "reclaim it" hook for maintenance; implementations that cannot accumulate dead state omit it.
|
|
58
|
+
*/
|
|
59
|
+
compact?(): void;
|
|
60
|
+
count(): number;
|
|
61
|
+
rebuild(rows: Iterable<{
|
|
62
|
+
id: number;
|
|
63
|
+
vec: Float32Array;
|
|
64
|
+
}>): void;
|
|
65
|
+
/**
|
|
66
|
+
* Optional: materialize any structure the backend built LAZILY. `topk` must always be correct
|
|
67
|
+
* without it (it builds on demand); this exists so callers that need deterministic first-query
|
|
68
|
+
* latency — benchmarks, and tests that assert on build cost — can force the mission.
|
|
69
|
+
*/
|
|
70
|
+
prepare?(): void;
|
|
71
|
+
/**
|
|
72
|
+
* Optional: point the backend at an on-disk cache of its native index. The path is the
|
|
73
|
+
* STORE's choice and must embed the vector space (model/dim), so a model swap cannot load a
|
|
74
|
+
* snapshot built from another space. Best-effort: a missing/corrupt/stale file is rebuilt.
|
|
75
|
+
*/
|
|
76
|
+
attachPersistence?(path: string): void;
|
|
77
|
+
/**
|
|
78
|
+
* Optional: persist any state that is only in memory. Called on shutdown; implementations that
|
|
79
|
+
* have nothing pending may omit it.
|
|
80
|
+
*/
|
|
81
|
+
flush?(): void;
|
|
82
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-wide logger seam for the retrieval core. The DSH plugin installs the
|
|
3
|
+
* host logger through {@link setRetrievalLogger}; standalone runs (CLI / MCP /
|
|
4
|
+
* tests) keep the prefixed console default. The core deliberately has no Cordis
|
|
5
|
+
* dependency, so a module seam is the only way to reach every adapter.
|
|
6
|
+
*/
|
|
7
|
+
import { type AvantfLogger } from '../mem-contract/index.js';
|
|
8
|
+
/** Route retrieval-core logs (model warm / degrade) to a host logger. */
|
|
9
|
+
export declare function setRetrievalLogger(logger?: AvantfLogger): void;
|
|
10
|
+
/** The logger every adapter reports through. */
|
|
11
|
+
export declare function retrievalLogger(): AvantfLogger;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Config } from '../mem-contract/index.js';
|
|
2
|
+
import type { Reranker, SemanticBackend, VectorStore } from './interfaces.js';
|
|
3
|
+
type Factory<T> = (cfg: Config, opts?: ResolveOptions) => T;
|
|
4
|
+
/**
|
|
5
|
+
* How a backend is resolved.
|
|
6
|
+
*
|
|
7
|
+
* Only `deferWarm` exists, and only because the DSH plugin cannot let a backend warm itself: the
|
|
8
|
+
* family framework installs the model into the managed root ASYNCHRONOUSLY, so a constructor-time
|
|
9
|
+
* warm would race that install and fetch a second copy. The plugin passes `deferWarm: true` and
|
|
10
|
+
* warms from the model item's `onSettled`; every other caller (CLI, MCP, tests) keeps the eager
|
|
11
|
+
* constructor warm. `encode()` / `rerank()` still warm lazily, so deferring never loses the model.
|
|
12
|
+
*/
|
|
13
|
+
export interface ResolveOptions {
|
|
14
|
+
/** Skip the constructor warm; the caller warms explicitly (see `ModelEnv.deferWarm`). */
|
|
15
|
+
readonly deferWarm?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare function registerSemanticBackend(name: string, factory: Factory<SemanticBackend>): void;
|
|
18
|
+
export declare function registerReranker(name: string, factory: Factory<Reranker>): void;
|
|
19
|
+
export declare function registerVectorStore(name: string, factory: Factory<VectorStore>): void;
|
|
20
|
+
export declare function resolveSemantic(cfg: Config, opts?: ResolveOptions): SemanticBackend;
|
|
21
|
+
export declare function resolveReranker(cfg: Config, opts?: ResolveOptions): Reranker;
|
|
22
|
+
export declare function resolveVStore(cfg: Config): VectorStore;
|
|
23
|
+
export {};
|