@avantf/dsh-mem 0.4.1 → 0.6.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 +19 -1
- package/lib/client.js +40 -9
- package/lib/engine/mem/config/config_files.d.ts +1 -1
- package/lib/engine/mem/db/dao/chunks.d.ts +20 -1
- package/lib/engine/mem/db/dao/entities.d.ts +35 -11
- package/lib/engine/mem/db/dao/fact_sources.d.ts +53 -0
- package/lib/engine/mem/db/dao/facts.d.ts +127 -12
- package/lib/engine/mem/db/invariants.d.ts +26 -0
- package/lib/engine/mem/db/tokenizer.d.ts +23 -0
- package/lib/engine/mem/db/vectors.d.ts +40 -8
- package/lib/engine/mem/entities/extract.d.ts +1 -1
- package/lib/engine/mem/eval/derive.d.ts +120 -0
- package/lib/engine/mem/eval/derived_runner.d.ts +88 -0
- package/lib/engine/mem/eval/metrics.d.ts +24 -0
- package/lib/engine/mem/eval/runner.d.ts +8 -1
- package/lib/engine/mem/index.d.ts +1 -1
- package/lib/engine/mem/modelBootstrap.d.ts +2 -2
- package/lib/engine/mem/runtime.d.ts +1 -1
- package/lib/engine/mem/store/common.d.ts +33 -11
- package/lib/engine/mem/store/entity_leg.d.ts +95 -0
- package/lib/engine/mem/store/floors.d.ts +63 -31
- package/lib/engine/mem/store/hybrid.d.ts +21 -26
- package/lib/engine/mem/store/knowledge.d.ts +18 -9
- package/lib/engine/mem/store/lexical.d.ts +44 -0
- package/lib/engine/mem/store/memory.d.ts +151 -24
- package/lib/engine/mem/store/secret_guard.d.ts +34 -0
- package/lib/engine/mem/store/time_window.d.ts +74 -0
- package/lib/engine/mem-contract/config.d.ts +9 -16
- package/lib/engine/mem-contract/family.d.ts +1 -1
- package/lib/engine/mem-contract/tools.d.ts +10 -0
- package/lib/engine/mem-contract/types.d.ts +164 -9
- package/lib/engine/mem-retrieval/adapters/local_bge.d.ts +37 -7
- package/lib/engine/mem-retrieval/fusion.d.ts +14 -1
- package/lib/engine/mem-retrieval/index.d.ts +1 -3
- package/lib/engine/mem-retrieval/interfaces.d.ts +38 -14
- package/lib/engine/mem-retrieval/registry.d.ts +2 -4
- package/lib/engine/mem-retrieval/representation.d.ts +50 -0
- package/lib/engine/mem-retrieval/stats.d.ts +1 -3
- package/lib/index.js +2003 -565
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/index.d.ts +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/prompt.d.ts +7 -0
- package/lib/types/prompt.d.ts.map +1 -1
- package/lib/types/remote.d.ts +12 -6
- package/lib/types/remote.d.ts.map +1 -1
- package/lib/types/vectorMigration.d.ts +44 -0
- package/lib/types/vectorMigration.d.ts.map +1 -0
- package/package.json +5 -5
- package/lib/engine/mem-retrieval/adapters/local_reranker.d.ts +0 -52
- package/lib/engine/mem-retrieval/adapters/none_reranker.d.ts +0 -10
- package/lib/engine/mem-retrieval/rerank.d.ts +0 -13
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* MAXIMUM, so every leg's head is 1.0 by construction and the fused number is only comparable
|
|
6
6
|
* WITHIN one query. A cutoff on the fused score would therefore mean a different thing per query
|
|
7
7
|
* (and would move whenever a leg's head changed). The legs' raw numbers do have an absolute scale —
|
|
8
|
-
* cosine for the semantic leg,
|
|
9
|
-
* FTS — so that is where the floors live. The side benefit is that
|
|
10
|
-
* move its maximum, so the cap-invariance §20.17 relies on is untouched.
|
|
8
|
+
* cosine for the semantic leg, anchored Jaccard for the entity leg (`store/entity_leg.ts`),
|
|
9
|
+
* "distinct query terms hit" for FTS — so that is where the floors live. The side benefit is that
|
|
10
|
+
* flooring a leg's tail cannot move its maximum, so the cap-invariance §20.17 relies on is untouched.
|
|
11
11
|
*
|
|
12
12
|
* THREE FLOORS, ONE SHAPE. `min_semantic_similarity` / `min_fts_terms` / `min_jaccard`; each `0`
|
|
13
13
|
* means "this leg is not gated", and a candidate EQUAL to its floor is KEPT (only strictly-lower
|
|
@@ -49,10 +49,25 @@
|
|
|
49
49
|
* of its own is bounded instead: it costs one extra query, only when the strict answer was empty,
|
|
50
50
|
* and it can still report "nothing relevant" for a genuinely unrelated question.
|
|
51
51
|
*
|
|
52
|
-
* FTS REACHABILITY. `min_fts_terms` counts distinct query terms a row
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
52
|
+
* FTS REACHABILITY, AND THE SHORT-QUERY FALLBACK. `min_fts_terms` counts distinct query terms a row
|
|
53
|
+
* hits, but a query can only produce so many terms — a 3-character CJK query yields exactly ONE
|
|
54
|
+
* trigram, so a configured bar of 2 is unreachable and that leg is empty for it by construction. The
|
|
55
|
+
* effective bar is therefore `min(configured, termCount)`, and `termCount` is that of the text being
|
|
56
|
+
* GRADED — with 方案 A's augmentation the original and the rewrite are different texts with
|
|
57
|
+
* different counts, so each resolves its own bar (`store/hybrid.ts`). Resolving the rewrite from the
|
|
58
|
+
* original's count is the defect that let a single incidental trigram pass a bar it could not reach
|
|
59
|
+
* (2026-10-04).
|
|
60
|
+
*
|
|
61
|
+
* That clamp still leaves the shape with NO term at all: a 2-char CJK query (`buildFtsQuery` returns
|
|
62
|
+
* `null` — the trigram index holds no term under three characters, measured on the live store). The
|
|
63
|
+
* term set is therefore {@link gradedTerms}: when the query yields no index-expressible term, the
|
|
64
|
+
* 2-char CJK runs themselves become the terms, and the legs search/grade them as SUBSTRINGS
|
|
65
|
+
* (`content LIKE '%…%'`, the finer predicate an FTS5 trigram table still exposes; see
|
|
66
|
+
* `store/lexical.ts`). The clamp stays the same rule and stays honest — one substring term is
|
|
67
|
+
* `min(configured, 1)`, and {@link applyTermFloor} re-derives it from the query it is grading, so
|
|
68
|
+
* the reported number is the applied number. The guard against the noise a 2-char term invites is
|
|
69
|
+
* the term itself: the row must CONTAIN the run contiguously (not merely share a trigram), and a
|
|
70
|
+
* multi-run query must contain `min(configured, runs)` of them.
|
|
56
71
|
*
|
|
57
72
|
* @module store/floors
|
|
58
73
|
*/
|
|
@@ -82,16 +97,20 @@ export type FloorLeg = 'semantic' | 'fts' | 'jaccard';
|
|
|
82
97
|
* - fts: NOT a constant — {@link looseTermFloor}, half the query's distinct terms with a floor of
|
|
83
98
|
* 1. `1` here is the "no terms to scale by" baseline (the previously constant value); a query
|
|
84
99
|
* that cannot produce terms is not graded at all, and one that produces a single term (every
|
|
85
|
-
* 3-char CJK query
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
100
|
+
* 3-char CJK query, and every 2-char one through the substring fallback) is already graded
|
|
101
|
+
* against that one term by the reachability clamp.
|
|
102
|
+
* - jaccard **0.15** (positive — the previous `0` was the ungated hole this closes). The metric was
|
|
103
|
+
* recalibrated on 2026-10-04 (`store/entity_leg.ts`): the leg still scores a Jaccard ratio, but
|
|
104
|
+
* over the query's ANCHOR entities and with the fact's extra-entity term SATURATED at
|
|
105
|
+
* `ENTITY_UNION_CAP`. 0.15 on that unit means "shares at least one discriminative entity" for any
|
|
106
|
+
* query with up to six anchors (`1/6 = 0.167 > 0.15`), i.e. the bottom line that cannot be lowered
|
|
107
|
+
* to "any incidental overlap" without admitting a length-dependent fraction. The old Jaccard
|
|
108
|
+
* calibration, kept for the record: (a) on the live store the top non-answering candidate for
|
|
109
|
+
* common-entity questions sat at 0.0909 and most at 0.02–0.07; (b) the lowest Jaccard any
|
|
110
|
+
* answering pair on the gold 35-query set reached was 0.25. The entity leg cannot separate the
|
|
111
|
+
* classes on short queries by itself, so this is deliberately a "no incidental small overlap"
|
|
112
|
+
* line, not a relevance classifier — see `store/entity_leg.ts` for why the fact's own width was
|
|
113
|
+
* saturated rather than used as the separator.
|
|
95
114
|
*/
|
|
96
115
|
export declare const LOOSE_FLOORS: RetrievalFloors;
|
|
97
116
|
/**
|
|
@@ -112,21 +131,29 @@ export declare function emptyFloorDrops(): RetrievalFloorDrops;
|
|
|
112
131
|
* The FTS bar actually applied to a query carrying `termCount` distinct terms.
|
|
113
132
|
*
|
|
114
133
|
* A query cannot be graded against more terms than it has (see the module comment: a 3-char CJK
|
|
115
|
-
* query has one trigram), so the configured bar is CLAMPED
|
|
116
|
-
* `0`/omitted leaves the value alone — such a query is not
|
|
117
|
-
* and changing the REPORTED value there would misdescribe
|
|
118
|
-
* (the operator's off switch).
|
|
119
|
-
*
|
|
120
|
-
* This is the ONE place the clamp lives; `resolveFloors` (what the result reports)
|
|
121
|
-
* `applyTermFloor` (what the leg does) both call it, so the number
|
|
122
|
-
*
|
|
134
|
+
* query has one trigram, and a 2-char one has one substring term), so the configured bar is CLAMPED
|
|
135
|
+
* to the reachable maximum. `termCount` `0`/omitted leaves the value alone — such a query is not
|
|
136
|
+
* graded at all by {@link applyTermFloor}, and changing the REPORTED value there would misdescribe
|
|
137
|
+
* what the caller is reading. `0` stays `0` (the operator's off switch).
|
|
138
|
+
*
|
|
139
|
+
* This is the ONE place the clamp lives; `resolveFloors` (what the result reports / a run is handed)
|
|
140
|
+
* and `applyTermFloor` (what the leg does) both call it, so the number a run reads is the number its
|
|
141
|
+
* leg used. It is a `min`, i.e. one-directional: it can LOWER an unreachable configured bar but never
|
|
142
|
+
* RAISE a bar inherited from another text, which is why the caller must resolve per graded text
|
|
143
|
+
* rather than reusing one text's resolution for another.
|
|
123
144
|
*/
|
|
124
145
|
export declare function effectiveTermFloor(floor: number, termCount?: number): number;
|
|
125
146
|
/** The per-query floor resolution inputs beyond the configuration. */
|
|
126
147
|
export interface FloorResolution {
|
|
127
148
|
/** `loose` lowers each configured floor (see {@link LOOSE_FLOORS}); absent means `strict`. */
|
|
128
149
|
profile?: FloorProfile;
|
|
129
|
-
/**
|
|
150
|
+
/**
|
|
151
|
+
* The distinct term count of the text being GRADED (`gradedTerms(text).length`), for the FTS
|
|
152
|
+
* clamp. It must describe the query the legs will actually run on: under 方案 A's augmentation the
|
|
153
|
+
* rewrite is a DIFFERENT text with its own term count, and resolving both runs from the original's
|
|
154
|
+
* count is the defect that let one incidental trigram through (see `store/hybrid.ts`). Callers with
|
|
155
|
+
* several graded texts resolve once per text.
|
|
156
|
+
*/
|
|
130
157
|
termCount?: number;
|
|
131
158
|
/**
|
|
132
159
|
* Which legs a `'loose'` profile may lower, from {@link droppedLegs} of the pass that made the
|
|
@@ -170,16 +197,21 @@ export declare function applyScoreFloor(scores: Map<number, number>, floor: numb
|
|
|
170
197
|
*
|
|
171
198
|
* Substring containment, case-insensitively: FTS5's trigram tokenizer matches a quoted phrase by
|
|
172
199
|
* consecutive trigrams and is ASCII-case-insensitive, which is the same predicate as "the term
|
|
173
|
-
* occurs in the text"
|
|
200
|
+
* occurs in the text" — and the 2-char fallback terms ARE substrings by construction
|
|
201
|
+
* (`store/lexical.ts`), so one predicate covers both. `relevanceTerms` lowercases its latin words,
|
|
202
|
+
* so the comparison has to.
|
|
174
203
|
*/
|
|
175
204
|
export declare function countMatchedTerms(text: string, terms: readonly string[]): number;
|
|
176
205
|
/**
|
|
177
206
|
* Apply the per-row FTS floor to one FTS leg's scores.
|
|
178
207
|
*
|
|
179
|
-
* The terms come from `
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
208
|
+
* The terms come from `gradedTerms(query)` — latin words ≥ 5 chars + every CJK 3-gram, and (only
|
|
209
|
+
* when the query produces no such term) the 2-char CJK runs as substrings. The indexed terms are
|
|
210
|
+
* the same tokenisation the conditional-hint probe uses, so "relevant enough" means one thing in
|
|
211
|
+
* this repo; the fallback exists because the hint path may stay silent while the LEG must not
|
|
212
|
+
* (`store/lexical.ts`). A query with NO such term is not graded at all: there is nothing to count,
|
|
213
|
+
* and dropping every row on a technicality would turn a 3–4 char latin query (`buildFtsQuery` still
|
|
214
|
+
* emits it) into silence.
|
|
183
215
|
*
|
|
184
216
|
* The bar itself is {@link effectiveTermFloor}: a query that can only produce one term (a 3-char CJK
|
|
185
217
|
* query is one trigram) is measured against that one term, not against a configured 2 it could never
|
|
@@ -3,18 +3,16 @@
|
|
|
3
3
|
*
|
|
4
4
|
* `MemoryStore.search` and `KnowledgeStore.search` used to carry parallel copies of this flow
|
|
5
5
|
* (~80 lines each: limit normalization → weights → over-fetch → leg cap → fuse → live filter →
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* slice → output budget → health event). The legs themselves are legitimately store-specific —
|
|
7
|
+
* different tables, different filters (`category` vs `domain`/`source`), and only memory has the
|
|
8
|
+
* HRR probe — but the ORCHESTRATION is one algorithm, and two copies of it drifted:
|
|
9
9
|
*
|
|
10
10
|
* - the `NaN` limit guard existed only in memory, so a non-finite limit reached knowledge's SQL
|
|
11
11
|
* `LIMIT` and took the whole cross-store query down with it (`Promise.all` has no per-leg catch);
|
|
12
12
|
* - `retriever.over_fetch_factor` was read only by memory, while knowledge hardcoded `limit * 3`,
|
|
13
13
|
* so one config knob silently meant two different things;
|
|
14
14
|
* - `recordLegCapped()` was called only by memory, so a capped knowledge leg was unobservable —
|
|
15
|
-
* exactly the blindness DESIGN §20.17 says the counter exists to remove
|
|
16
|
-
* - the reranker's used/fallback flags were a helper in memory and an inline expression in
|
|
17
|
-
* knowledge.
|
|
15
|
+
* exactly the blindness DESIGN §20.17 says the counter exists to remove.
|
|
18
16
|
*
|
|
19
17
|
* Fixing those one at a time would leave the next drift a matter of when. The stores now supply
|
|
20
18
|
* their legs and their hit mapping; everything that can drift lives here once.
|
|
@@ -26,7 +24,7 @@
|
|
|
26
24
|
* @module store/hybrid
|
|
27
25
|
*/
|
|
28
26
|
import { type Config, type FloorProfile, type RecallResult, type RetrievalFloorDrops, type RetrievalFloors } from '../../mem-contract/index.js';
|
|
29
|
-
import { type BudgetInput, type Hit, type
|
|
27
|
+
import { type BudgetInput, type Hit, type SemanticBackend } from '../../mem-retrieval/index.js';
|
|
30
28
|
import { type FloorLeg } from './floors.js';
|
|
31
29
|
/** The `limit` a caller gets when it passes nothing usable. */
|
|
32
30
|
export declare const DEFAULT_SEARCH_LIMIT = 10;
|
|
@@ -63,8 +61,12 @@ export interface HybridLeg {
|
|
|
63
61
|
capped?: boolean;
|
|
64
62
|
/**
|
|
65
63
|
* Which floor governs this leg, for {@link HybridResult.dropped_by_floor}'s per-leg breakdown.
|
|
66
|
-
* The HRR probe shares the
|
|
67
|
-
* attributable without double-counting the
|
|
64
|
+
* The HRR probe shares the entity (`jaccard`) floor; naming it separately keeps a narrowed
|
|
65
|
+
* candidate set attributable without double-counting the entity leg's own drops.
|
|
66
|
+
*
|
|
67
|
+
* The `jaccard` KEY is historical: the entity leg's metric is now anchored Jaccard with a
|
|
68
|
+
* saturating union, whose unit (and the `min_jaccard` floor on it) is defined in
|
|
69
|
+
* `store/entity_leg.ts` — read that before re-calibrating the knob.
|
|
68
70
|
*/
|
|
69
71
|
leg?: keyof RetrievalFloorDrops;
|
|
70
72
|
/** Candidates this leg removed because they fell below its floor. */
|
|
@@ -113,7 +115,6 @@ export interface HybridDeps<H> {
|
|
|
113
115
|
kind: 'memory' | 'knowledge';
|
|
114
116
|
config: Config;
|
|
115
117
|
semantic: SemanticBackend;
|
|
116
|
-
reranker: Reranker;
|
|
117
118
|
/**
|
|
118
119
|
* The legs to fuse, as promises (or values). Awaited concurrently; a rejection costs that leg
|
|
119
120
|
* only. The async setup a store needs before it can build its legs — memory extracts the query's
|
|
@@ -121,7 +122,7 @@ export interface HybridDeps<H> {
|
|
|
121
122
|
* body, before the array is returned.
|
|
122
123
|
*/
|
|
123
124
|
legs(ctx: HybridContext): Promise<readonly (HybridLeg | Promise<HybridLeg>)[]>;
|
|
124
|
-
/** Text for the fused ids: the live filter
|
|
125
|
+
/** Text for the fused ids: the live filter and the hit bodies. */
|
|
125
126
|
texts(ids: number[]): Map<number, string>;
|
|
126
127
|
/** Build the caller-facing hits from the ranked, live, already-sliced ids. */
|
|
127
128
|
hits(ranked: readonly Hit[], texts: Map<number, string>): H[];
|
|
@@ -177,6 +178,12 @@ export interface HybridPlan {
|
|
|
177
178
|
* `queries` counts questions instead of legs (DESIGN §20.5).
|
|
178
179
|
*/
|
|
179
180
|
recordStats?: boolean;
|
|
181
|
+
/**
|
|
182
|
+
* P-01: attach per-leg evidence to the returned hits (`RecallHit.scores` / `final`). Default
|
|
183
|
+
* false, and that default is load-bearing: the evidence is explanation only, so a caller that did
|
|
184
|
+
* not ask must not pay for it in tokens (`fuse` skips building the arrays entirely).
|
|
185
|
+
*/
|
|
186
|
+
includeScores?: boolean;
|
|
180
187
|
}
|
|
181
188
|
/** A hit after the output budget: `text` may have been shortened, and `truncated` says so. */
|
|
182
189
|
export type Budgeted<T> = T & {
|
|
@@ -207,17 +214,6 @@ export interface HybridResult<H> {
|
|
|
207
214
|
* rather than per store, because the health counter compares against whatever this returns.
|
|
208
215
|
*/
|
|
209
216
|
export declare function legCapFor(config: Config, overFetch: number): number;
|
|
210
|
-
/**
|
|
211
|
-
* Whether the configured reranker is actually in use — the two flags a retrieval event carries.
|
|
212
|
-
*
|
|
213
|
-
* "Configured" is not "used": a reranker that is selected but unavailable falls back to the fused
|
|
214
|
-
* order, and counting that as a rerank would make the counter lie in exactly the case it exists to
|
|
215
|
-
* expose (the reference implementation leaves `rerank_fallback` unfeedable).
|
|
216
|
-
*/
|
|
217
|
-
export declare function rerankState(reranker: Reranker): {
|
|
218
|
-
used: boolean;
|
|
219
|
-
fallback: boolean;
|
|
220
|
-
};
|
|
221
217
|
/**
|
|
222
218
|
* Bound a ranked hit list to a token budget, marking what was shortened.
|
|
223
219
|
*
|
|
@@ -233,9 +229,8 @@ export declare function applyOutputBudget<H extends BudgetInput>(config: Config,
|
|
|
233
229
|
/**
|
|
234
230
|
* Run one hybrid search.
|
|
235
231
|
*
|
|
236
|
-
* The order of the last
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
* budget on hits that are about to be dropped.
|
|
232
|
+
* The order of the last steps is load-bearing: slice the fused (over-fetched) pool to `limit`,
|
|
233
|
+
* THEN apply the output budget, and only then tell the store what was returned. Budgeting before
|
|
234
|
+
* the slice would spend the budget on hits that are about to be dropped.
|
|
240
235
|
*/
|
|
241
236
|
export declare function hybridSearch<H extends BudgetInput>(deps: HybridDeps<H>, plan: HybridPlan): Promise<HybridResult<H>>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AvantfLogger, type Config, type DocumentDetail, type DocumentSummary, type DomainCatalog, type FloorProfile, type ImportResult, type IngestResult, type KbConflictReport, type KbIngestConflict, type KbSyncReport, type KnowledgeConfig, type RecallHit, type ReindexReport } from '../../mem-contract/index.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { SemanticBackend, VectorStore } from '../../mem-retrieval/index.js';
|
|
3
3
|
import { type LexicalProbe } from './lexical.js';
|
|
4
4
|
import { type FloorLeg } from './floors.js';
|
|
5
5
|
import { type HybridResult } from './hybrid.js';
|
|
@@ -43,6 +43,8 @@ export interface KnowledgeSearchOptions {
|
|
|
43
43
|
* store-level "last result" field would race two concurrent queries.
|
|
44
44
|
*/
|
|
45
45
|
onResult?: (result: HybridResult<RecallHit>) => void;
|
|
46
|
+
/** P-01: attach per-leg evidence (`scores` / `final`) to every hit. Default false. */
|
|
47
|
+
includeScores?: boolean;
|
|
46
48
|
}
|
|
47
49
|
/**
|
|
48
50
|
* One identity a write will land on: the title it will use, the resolved local path it would read
|
|
@@ -87,7 +89,6 @@ export type IngestMode = 'add' | 'replace';
|
|
|
87
89
|
export declare class KnowledgeStore {
|
|
88
90
|
private readonly semantic;
|
|
89
91
|
private readonly vstore;
|
|
90
|
-
private readonly reranker;
|
|
91
92
|
/** The store config file `addDomain` writes the allowlist back to (`knowledgeConfigPath(home)`). */
|
|
92
93
|
private readonly domainConfigPath;
|
|
93
94
|
private readonly logger?;
|
|
@@ -127,7 +128,7 @@ export declare class KnowledgeStore {
|
|
|
127
128
|
private readonly allowedDomains;
|
|
128
129
|
/** Whether the config allowlist is active (non-empty); `false` = `domains: []` = no restriction. */
|
|
129
130
|
private readonly domainsRestricted;
|
|
130
|
-
constructor(dbPath: string, config: Config, kbConfig: KnowledgeConfig, semantic: SemanticBackend, vstore: VectorStore,
|
|
131
|
+
constructor(dbPath: string, config: Config, kbConfig: KnowledgeConfig, semantic: SemanticBackend, vstore: VectorStore,
|
|
131
132
|
/** The store config file `addDomain` writes the allowlist back to (`knowledgeConfigPath(home)`). */
|
|
132
133
|
domainConfigPath: string, logger?: AvantfLogger | undefined);
|
|
133
134
|
/**
|
|
@@ -302,11 +303,12 @@ export declare class KnowledgeStore {
|
|
|
302
303
|
*/
|
|
303
304
|
private encodeAndStore;
|
|
304
305
|
/**
|
|
305
|
-
* Identity of the vector space this store currently writes into (DESIGN §20):
|
|
306
|
-
* backend, model
|
|
307
|
-
*
|
|
306
|
+
* Identity of the vector space this store currently writes into (DESIGN §20): the representation
|
|
307
|
+
* fingerprint — backend, model, width AND the backend's declared pooling/normalization/input
|
|
308
|
+
* window/model revision — so any representation change makes stored chunk vectors stale instead
|
|
309
|
+
* of silently mixing two spaces. Public so diagnostics and tests can name the space.
|
|
308
310
|
*/
|
|
309
|
-
|
|
311
|
+
vectorSpace(): string;
|
|
310
312
|
/**
|
|
311
313
|
* A stored vector is reusable when it exists, matches the space, and its text is unchanged.
|
|
312
314
|
*
|
|
@@ -434,7 +436,7 @@ export declare class KnowledgeStore {
|
|
|
434
436
|
* and the chunk→hit mapping.
|
|
435
437
|
*
|
|
436
438
|
* The limit normalization, the over-fetch factor, the leg cap, the capped-leg counter, the
|
|
437
|
-
* degraded weights
|
|
439
|
+
* degraded weights and the output budget all live there now. They used to be copied
|
|
438
440
|
* here, and every fix landed on the memory side only — a `NaN` limit reached this store's SQL and
|
|
439
441
|
* took the whole cross-store query down with it, `retriever.over_fetch_factor` meant something
|
|
440
442
|
* different per store, and a capped knowledge leg was invisible to the health counters.
|
|
@@ -463,7 +465,14 @@ export declare class KnowledgeStore {
|
|
|
463
465
|
*/
|
|
464
466
|
lexicalProbe(text: string, stopAt?: number): LexicalProbe;
|
|
465
467
|
private ftsPath;
|
|
466
|
-
/**
|
|
468
|
+
/**
|
|
469
|
+
* Entity overlap against entities extracted AT INGEST time (chunk_entities) — no corpus scan.
|
|
470
|
+
*
|
|
471
|
+
* Same metric as the memory store's sibling (`store/entity_leg.ts` carries the measurement and
|
|
472
|
+
* the reasoning): anchored Jaccard with a saturating union over the query's rare entities, so a
|
|
473
|
+
* wide chunk cannot dilute the denominator and a corpus-wide word cannot drag the whole knowledge
|
|
474
|
+
* base in.
|
|
475
|
+
*/
|
|
467
476
|
private jaccardPath;
|
|
468
477
|
}
|
|
469
478
|
export interface Chunk {
|
|
@@ -25,6 +25,20 @@
|
|
|
25
25
|
* `matched >= 2`, with the margin between 2 and 1 rather than a tuned threshold inside a
|
|
26
26
|
* continuum. A single-word query (`cgroup`) scores 1 and stays silent on purpose: one term is not
|
|
27
27
|
* evidence.
|
|
28
|
+
*
|
|
29
|
+
* THE SHORT-CJK FALLBACK (and why the hint probe does NOT use it). The trigram index has no term
|
|
30
|
+
* shorter than three characters — measured on the live store: `fts5vocab` over `facts_fts` holds
|
|
31
|
+
* 27906 distinct terms and EVERY one is 3 characters (and a 2-char `MATCH` returns nothing, even as
|
|
32
|
+
* a prefix query). So a 2-char CJK query produces no FTS term at all and that leg is empty by
|
|
33
|
+
* construction. The FTS LEG therefore falls back to the one finer predicate the FTS5 trigram TABLE
|
|
34
|
+
* does expose: `content LIKE '%…%'`, which matches a 2-char run by scanning (no index can serve a
|
|
35
|
+
* 2-char pattern). See {@link substringTerms} / {@link gradedTerms}.
|
|
36
|
+
*
|
|
37
|
+
* The conditional-hint probe deliberately keeps the index-expressible terms only: it is
|
|
38
|
+
* SYNCHRONOUS and runs on every step's prompt assembly, so it must stay a bounded number of
|
|
39
|
+
* `LIMIT 1` index lookups — an O(corpus) scan per 2-char term would put a full scan on the prompt
|
|
40
|
+
* path. Behaviour is unchanged there: a 2-char query scored 0 matched terms before and still does
|
|
41
|
+
* (`{terms: 0, matched: 0}`), so the hint stays silent exactly as it did.
|
|
28
42
|
*/
|
|
29
43
|
/** How many of an input's terms a store holds. `terms === 0` means the input had nothing to test. */
|
|
30
44
|
export interface LexicalProbe {
|
|
@@ -46,6 +60,36 @@ export type RelevanceHit = boolean;
|
|
|
46
60
|
* caller runs one `LIMIT 1` MATCH per term and counts the hits.
|
|
47
61
|
*/
|
|
48
62
|
export declare function relevanceTerms(text: string, cap?: number): string[];
|
|
63
|
+
/**
|
|
64
|
+
* The terms the FTS leg falls back to when the trigram index can express NONE of the query.
|
|
65
|
+
*
|
|
66
|
+
* A 2-char CJK run IS the finer granularity the FTS5 trigram TABLE can still answer — not as an
|
|
67
|
+
* indexed token (the vocabulary holds 3-character terms only, measured; a 2-char `MATCH`, quoted or
|
|
68
|
+
* prefixed, returns zero rows) but as the substring predicate FTS5 exposes on a trigram table
|
|
69
|
+
* (`content LIKE '%…%'`, which falls back to a scan for patterns under three characters). One-char
|
|
70
|
+
* runs are deliberately NOT included: a single character is not evidence (the same reason
|
|
71
|
+
* {@link looksRelevant} needs two terms), and it would make the leg match a large share of any
|
|
72
|
+
* corpus. Latin runs are not included either — `buildFtsQuery` already admits latin tokens from
|
|
73
|
+
* three characters, and the module's own floor (5, `MIN_LATIN`) is a separate, measured decision.
|
|
74
|
+
*
|
|
75
|
+
* PRECISION GUARD. The fallback is graded by the SAME per-row floor as the indexed path and by the
|
|
76
|
+
* reachability clamp: for a single 2-char term the effective bar is 1, and the term is a substring,
|
|
77
|
+
* so "the row contains the query's characters CONTIGUOUSLY" is what admits it — not "shares one
|
|
78
|
+
* incidental trigram". A two-run query ('李娜 张伟') produces two substring terms and is judged
|
|
79
|
+
* against `min(configured, 2)`, i.e. both runs must occur in the row.
|
|
80
|
+
*/
|
|
81
|
+
export declare function substringTerms(text: string, cap?: number): string[];
|
|
82
|
+
/**
|
|
83
|
+
* The term set the FTS leg is SEARCHED and GRADED with — the one definition of "the query's terms"
|
|
84
|
+
* for that leg (`store/floors.ts` grades with it, `store/hybrid.ts` resolves the reachability clamp
|
|
85
|
+
* from it, and the stores build their MATCH / substring probe from it).
|
|
86
|
+
*
|
|
87
|
+
* The fallback is whole-query, not per run: any text that yields even one index-expressible term is
|
|
88
|
+
* returned UNCHANGED from {@link relevanceTerms}. That is what keeps every long query bit-identical
|
|
89
|
+
* (a mixed text like `缓存失效 李娜` keeps only its trigrams, exactly as before) and confines the
|
|
90
|
+
* new behaviour to the shape that had NO leg at all.
|
|
91
|
+
*/
|
|
92
|
+
export declare function gradedTerms(text: string, cap?: number): string[];
|
|
49
93
|
/**
|
|
50
94
|
* The gate itself: is this store worth telling the model about?
|
|
51
95
|
*
|