@hviana/sema 0.1.5 → 0.1.7
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/AGENTS.md +6 -5
- package/CITATION.cff +49 -0
- package/HOW_IT_WORKS.md +11 -12
- package/README.md +7 -5
- package/dist/example/demo.js +25 -25
- package/dist/example/train_base.d.ts +19 -21
- package/dist/example/train_base.js +1700 -1952
- package/dist/src/alphabet.d.ts +3 -3
- package/dist/src/alphabet.js +24 -27
- package/dist/src/alu/src/alu.d.ts +169 -194
- package/dist/src/alu/src/alu.js +374 -414
- package/dist/src/alu/src/expr.d.ts +36 -43
- package/dist/src/alu/src/expr.js +252 -278
- package/dist/src/alu/src/index.d.ts +9 -98
- package/dist/src/alu/src/index.js +10 -64
- package/dist/src/alu/src/kernel-arith.d.ts +1 -5
- package/dist/src/alu/src/kernel-arith.js +195 -285
- package/dist/src/alu/src/kernel-bits.d.ts +1 -5
- package/dist/src/alu/src/kernel-bits.js +81 -115
- package/dist/src/alu/src/kernel-logic.js +33 -63
- package/dist/src/alu/src/kernel-nd.js +155 -195
- package/dist/src/alu/src/kernel-numeric.d.ts +16 -80
- package/dist/src/alu/src/kernel-numeric.js +259 -331
- package/dist/src/alu/src/operation.d.ts +112 -132
- package/dist/src/alu/src/operation.js +149 -163
- package/dist/src/alu/src/parser.d.ts +168 -175
- package/dist/src/alu/src/parser.js +382 -421
- package/dist/src/alu/src/resonance.d.ts +16 -25
- package/dist/src/alu/src/resonance.js +49 -56
- package/dist/src/alu/src/text.d.ts +3 -7
- package/dist/src/alu/src/text.js +31 -37
- package/dist/src/alu/src/value.d.ts +14 -14
- package/dist/src/alu/src/value.js +150 -160
- package/dist/src/alu/test/alu.test.js +543 -656
- package/dist/src/bytes.d.ts +1 -5
- package/dist/src/bytes.js +33 -40
- package/dist/src/config.d.ts +99 -102
- package/dist/src/config.js +78 -83
- package/dist/src/derive/src/deduction.d.ts +60 -62
- package/dist/src/derive/src/deduction.js +103 -109
- package/dist/src/derive/src/index.d.ts +1 -7
- package/dist/src/derive/src/priority-queue.d.ts +8 -8
- package/dist/src/derive/src/priority-queue.js +57 -61
- package/dist/src/derive/src/rewrite.d.ts +15 -18
- package/dist/src/derive/src/rewrite.js +59 -67
- package/dist/src/derive/src/trie.d.ts +56 -56
- package/dist/src/derive/src/trie.js +175 -179
- package/dist/src/derive/test/derive.test.js +96 -100
- package/dist/src/extension.d.ts +13 -16
- package/dist/src/geometry.d.ts +29 -43
- package/dist/src/geometry.js +324 -268
- package/dist/src/index.d.ts +2 -17
- package/dist/src/index.js +2 -12
- package/dist/src/ingest-cache.d.ts +25 -30
- package/dist/src/ingest-cache.js +108 -127
- package/dist/src/mind/articulation.d.ts +1 -5
- package/dist/src/mind/articulation.js +77 -112
- package/dist/src/mind/attention.d.ts +40 -108
- package/dist/src/mind/attention.js +754 -872
- package/dist/src/mind/canonical.d.ts +4 -19
- package/dist/src/mind/canonical.js +27 -31
- package/dist/src/mind/graph-search.d.ts +201 -225
- package/dist/src/mind/graph-search.js +742 -821
- package/dist/src/mind/index.d.ts +2 -10
- package/dist/src/mind/junction.d.ts +31 -58
- package/dist/src/mind/junction.js +172 -237
- package/dist/src/mind/learning.d.ts +16 -52
- package/dist/src/mind/learning.js +143 -165
- package/dist/src/mind/match.d.ts +20 -69
- package/dist/src/mind/match.js +259 -318
- package/dist/src/mind/mechanisms/alu.js +16 -16
- package/dist/src/mind/mechanisms/cast.d.ts +9 -13
- package/dist/src/mind/mechanisms/cast.js +363 -456
- package/dist/src/mind/mechanisms/confluence.d.ts +8 -12
- package/dist/src/mind/mechanisms/confluence.js +152 -183
- package/dist/src/mind/mechanisms/cover.d.ts +2 -8
- package/dist/src/mind/mechanisms/cover.js +148 -210
- package/dist/src/mind/mechanisms/extraction.d.ts +8 -34
- package/dist/src/mind/mechanisms/extraction.js +234 -288
- package/dist/src/mind/mechanisms/recall.d.ts +6 -10
- package/dist/src/mind/mechanisms/recall.js +141 -185
- package/dist/src/mind/mind.d.ts +194 -155
- package/dist/src/mind/mind.js +392 -289
- package/dist/src/mind/pipeline-mechanism.d.ts +112 -124
- package/dist/src/mind/pipeline-mechanism.js +161 -172
- package/dist/src/mind/pipeline.d.ts +4 -14
- package/dist/src/mind/pipeline.js +125 -189
- package/dist/src/mind/primitives.d.ts +20 -33
- package/dist/src/mind/primitives.js +124 -120
- package/dist/src/mind/rationale.d.ts +86 -98
- package/dist/src/mind/rationale.js +113 -121
- package/dist/src/mind/reasoning.d.ts +2 -13
- package/dist/src/mind/reasoning.js +129 -166
- package/dist/src/mind/recognition.d.ts +1 -4
- package/dist/src/mind/recognition.js +198 -210
- package/dist/src/mind/resonance.d.ts +5 -22
- package/dist/src/mind/resonance.js +0 -0
- package/dist/src/mind/trace.d.ts +6 -25
- package/dist/src/mind/trace.js +50 -58
- package/dist/src/mind/traverse.d.ts +29 -58
- package/dist/src/mind/traverse.js +344 -356
- package/dist/src/mind/types.d.ts +122 -127
- package/dist/src/mind/types.js +60 -69
- package/dist/src/rabitq-ivf/src/database.d.ts +113 -0
- package/dist/src/rabitq-ivf/src/database.js +201 -0
- package/dist/src/rabitq-ivf/src/index.d.ts +7 -0
- package/dist/src/{rabitq-hnsw → rabitq-ivf}/src/index.js +1 -3
- package/dist/src/rabitq-ivf/src/ivf.d.ts +200 -0
- package/dist/src/rabitq-ivf/src/ivf.js +1165 -0
- package/dist/src/rabitq-ivf/src/prng.d.ts +19 -0
- package/dist/src/rabitq-ivf/src/prng.js +36 -0
- package/dist/src/rabitq-ivf/src/rabitq.d.ts +95 -0
- package/dist/src/rabitq-ivf/src/rabitq.js +283 -0
- package/dist/src/sema.d.ts +9 -13
- package/dist/src/sema.js +26 -40
- package/dist/src/store-sqlite.d.ts +171 -166
- package/dist/src/store-sqlite.js +768 -662
- package/dist/src/store.d.ts +597 -630
- package/dist/src/store.js +1434 -1581
- package/dist/src/vec.d.ts +5 -9
- package/dist/src/vec.js +61 -73
- package/example/train_base.ts +33 -15
- package/index.html +65 -0
- package/package.json +1 -1
- package/src/alu/README.md +1 -1
- package/src/alu/src/index.ts +1 -1
- package/src/config.ts +19 -27
- package/src/geometry.ts +93 -1
- package/src/index.ts +6 -11
- package/src/mind/attention.ts +11 -6
- package/src/mind/mechanisms/recall.ts +29 -13
- package/src/mind/mind.ts +250 -19
- package/src/mind/primitives.ts +43 -6
- package/src/mind/recognition.ts +26 -8
- package/src/mind/traverse.ts +47 -0
- package/src/mind/types.ts +20 -21
- package/src/rabitq-ivf/README.md +56 -0
- package/src/rabitq-ivf/src/database.ts +276 -0
- package/src/{rabitq-hnsw → rabitq-ivf}/src/index.ts +2 -5
- package/src/rabitq-ivf/src/ivf.ts +1330 -0
- package/src/{rabitq-hnsw → rabitq-ivf}/src/prng.ts +1 -1
- package/src/store-sqlite.ts +204 -9
- package/src/store.ts +27 -34
- package/test/08-storage.test.mjs +3 -3
- package/test/13-conversation.test.mjs +190 -20
- package/test/14-scaling.test.mjs +2 -2
- package/test/35-ivf.test.mjs +263 -0
- package/test/36-bloom.test.mjs +123 -0
- package/dist/src/rabitq-hnsw/src/database.d.ts +0 -202
- package/dist/src/rabitq-hnsw/src/database.js +0 -405
- package/dist/src/rabitq-hnsw/src/heap.d.ts +0 -22
- package/dist/src/rabitq-hnsw/src/heap.js +0 -94
- package/dist/src/rabitq-hnsw/src/hnsw.d.ts +0 -125
- package/dist/src/rabitq-hnsw/src/hnsw.js +0 -500
- package/dist/src/rabitq-hnsw/src/index.d.ts +0 -15
- package/dist/src/rabitq-hnsw/src/prng.d.ts +0 -19
- package/dist/src/rabitq-hnsw/src/prng.js +0 -38
- package/dist/src/rabitq-hnsw/src/rabitq.d.ts +0 -95
- package/dist/src/rabitq-hnsw/src/rabitq.js +0 -299
- package/dist/src/rabitq-hnsw/src/store.d.ts +0 -162
- package/dist/src/rabitq-hnsw/src/store.js +0 -916
- package/dist/src/rabitq-hnsw/test/hnsw.test.d.ts +0 -1
- package/dist/src/rabitq-hnsw/test/hnsw.test.js +0 -1197
- package/src/rabitq-hnsw/README.md +0 -303
- package/src/rabitq-hnsw/src/database.ts +0 -492
- package/src/rabitq-hnsw/src/heap.ts +0 -90
- package/src/rabitq-hnsw/src/hnsw.ts +0 -514
- package/src/rabitq-hnsw/src/store.ts +0 -994
- package/src/rabitq-hnsw/test/hnsw.test.ts +0 -1213
- /package/src/{rabitq-hnsw → rabitq-ivf}/src/rabitq.ts +0 -0
package/dist/src/mind/types.d.ts
CHANGED
|
@@ -11,156 +11,158 @@ export type Input = string | Uint8Array | Grid | Grid[];
|
|
|
11
11
|
/** The host capabilities GraphSearch consults during a cover. MindContext
|
|
12
12
|
* extends this so the Mind can pass itself as the host. */
|
|
13
13
|
export interface GraphSearchHost {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
resolve(bytes: Uint8Array): number | null;
|
|
15
|
+
recogniseSpan?(bytes: Uint8Array): {
|
|
16
|
+
sites: ReadonlyArray<Site>;
|
|
17
|
+
leaves: ReadonlyArray<Leaf>;
|
|
18
|
+
splits: ReadonlySet<number>;
|
|
19
|
+
};
|
|
20
|
+
chooseNext?(node: number): number | undefined;
|
|
21
21
|
}
|
|
22
22
|
export interface Recognition {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
/** Forms that can lead somewhere — they have an edge or a halo. */
|
|
24
|
+
sites: Site[];
|
|
25
|
+
/** The query's perceived leaves (the search's covering axioms). */
|
|
26
|
+
leaves: Leaf[];
|
|
27
|
+
/** Sub-leaf positions where a form boundary falls between leaf edges. */
|
|
28
|
+
splits: Set<number>;
|
|
29
29
|
}
|
|
30
30
|
/** How the consensus climb weights a region's Document-Frequency reach. */
|
|
31
31
|
export type DFMode = "inverse" | "direct" | "combined";
|
|
32
32
|
/** One POINT OF ATTENTION the consensus climb resolved. */
|
|
33
33
|
export interface Attention {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
/** The learnt context this point resolves to. */
|
|
35
|
+
anchor: number;
|
|
36
|
+
/** IDF-weighted consensus vote — the strength that orders points. */
|
|
37
|
+
vote: number;
|
|
38
|
+
/** The union of the query byte-spans whose evidence supports this point. */
|
|
39
|
+
start: number;
|
|
40
|
+
end: number;
|
|
41
41
|
}
|
|
42
42
|
/** Both read-outs of one consensus climb. */
|
|
43
43
|
export interface AttentionRead {
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
roots: Attention[];
|
|
45
|
+
ranked: Attention[];
|
|
46
46
|
}
|
|
47
47
|
/** A positioned region of a byte stream paired with its gist. */
|
|
48
48
|
export interface Segment {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
start: number;
|
|
50
|
+
end: number;
|
|
51
|
+
v: Vec;
|
|
52
52
|
}
|
|
53
53
|
/** A region of the query's perceived tree for the consensus climb. */
|
|
54
54
|
export interface Region {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
55
|
+
v: Vec;
|
|
56
|
+
start: number;
|
|
57
|
+
end: number;
|
|
58
|
+
chunk: boolean;
|
|
59
|
+
/** Whether the region's bytes resolve to a KNOWN node (content-addressed,
|
|
60
|
+
* exact). Exact regions vote with full weight; approximate ones pay the
|
|
61
|
+
* contrastive margin (see voteRegions) — under the linear fold a raw
|
|
62
|
+
* resonance score is byte-overlap, evidence only in excess of its best
|
|
63
|
+
* rival conclusion. */
|
|
64
|
+
known: boolean;
|
|
65
65
|
}
|
|
66
66
|
/** Per-region vote data from the consensus climb's resonance pass. */
|
|
67
67
|
export interface RegionVote {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
start: number;
|
|
69
|
+
end: number;
|
|
70
|
+
canonicalFailed: boolean;
|
|
71
|
+
roots: readonly number[];
|
|
72
|
+
w: number;
|
|
73
|
+
wFocus: number;
|
|
74
74
|
}
|
|
75
75
|
/** The edge-bearing contexts reached by climbing from a node, plus saturation info. */
|
|
76
76
|
export interface AncestorReach {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
roots: number[];
|
|
78
|
+
contextsReached: number;
|
|
79
|
+
saturated: boolean;
|
|
80
80
|
}
|
|
81
81
|
/** Saturated-interval information for the noise-drop gate. */
|
|
82
82
|
export interface SaturationInfo {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
83
|
+
leadingEnd: number;
|
|
84
|
+
hasLeading: boolean;
|
|
85
|
+
intervals: Array<{
|
|
86
|
+
start: number;
|
|
87
|
+
end: number;
|
|
88
|
+
}>;
|
|
89
89
|
}
|
|
90
90
|
/** The items of poolVotes' deduction system. */
|
|
91
91
|
export type AItem = {
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
kind: "region";
|
|
93
|
+
ri: number;
|
|
94
94
|
} | {
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
kind: "anchor";
|
|
96
|
+
id: number;
|
|
97
97
|
} | {
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
kind: "anchorFocus";
|
|
99
|
+
id: number;
|
|
100
100
|
};
|
|
101
101
|
export interface MindContext extends GraphSearchHost {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
102
|
+
store: Store;
|
|
103
|
+
space: Space;
|
|
104
|
+
alphabet: Alphabet;
|
|
105
|
+
cfg: MindConfig;
|
|
106
|
+
search: GraphSearch;
|
|
107
|
+
trace: Rationale | null;
|
|
108
|
+
/** Memo of the consensus climb — content-keyed (latin1) so results
|
|
109
|
+
* persist across conversation turns where the same byte spans recur.
|
|
110
|
+
* Null outside respond(); during respondTurn() the conversation's
|
|
111
|
+
* persistent map is swapped in. */
|
|
112
|
+
climbMemo: Map<string, Map<string, AttentionRead>> | null;
|
|
113
|
+
/** Memo of {@link recognise} — content-keyed (latin1) so recognised
|
|
114
|
+
* forms carry forward across conversation turns. Bypassed while a
|
|
115
|
+
* trace is attached. Null outside respond(). */
|
|
116
|
+
recogniseMemo: Map<string, Recognition> | null;
|
|
117
|
+
/** Memo of {@link perceive} — content-keyed (latin1). The general
|
|
118
|
+
* cache the result-level memos each partially compensate for. NOT
|
|
119
|
+
* bypassed under trace — perception emits no rationale steps.
|
|
120
|
+
* Null outside respond(). */
|
|
121
|
+
perceiveMemo: Map<string, Sema> | null;
|
|
122
|
+
/** Subtree-resolution cache: Sema node → its store id and byte length.
|
|
123
|
+
* Populated by {@link foldTree} during inference; checked before
|
|
124
|
+
* walking children. When a conversation's pyramid reuses prefix
|
|
125
|
+
* subtrees, this cache lets {@link recognise} skip them entirely —
|
|
126
|
+
* O(suffix) instead of O(context). Mind-lifetime (WeakMap keys are
|
|
127
|
+
* the Sema objects the pyramid keeps alive). */
|
|
128
|
+
_resolvedSubtrees: WeakMap<Sema, {
|
|
129
|
+
id: number;
|
|
130
|
+
len: number;
|
|
131
|
+
}> | null;
|
|
132
|
+
_edgeGuide: Vec | null;
|
|
133
|
+
_edgeChoice: Map<number, number>;
|
|
134
|
+
_prevSeen: Set<number> | null;
|
|
135
|
+
/** Session cache of node-id → perceived gist, for candidate scoring
|
|
136
|
+
* ({@link chooseAmong} in the reverse projection's recall path re-gists up to
|
|
137
|
+
* √N contexts per pick — the measured bottleneck there). `chooseNext` does
|
|
138
|
+
* NOT use this cache; forward-edge disambiguation uses prevOf counts
|
|
139
|
+
* (distributional evidence) instead of gist comparison, because for short
|
|
140
|
+
* answer candidates the gist is dominated by accidental byte-pattern
|
|
141
|
+
* correlations. A node's bytes are immutable and perception is a pure
|
|
142
|
+
* function of bytes, so an entry stays valid for the store's lifetime —
|
|
143
|
+
* never invalidated. Bounded LRU (byte-sized); a miss only re-perceives,
|
|
144
|
+
* never a correctness risk. */
|
|
145
|
+
_gistCache: BoundedMap<number, Vec>;
|
|
146
|
+
/** DEPOSIT-path stable-prefix tree cache: content key (latin1) of a
|
|
147
|
+
* deposited input → its plain-fold level PYRAMID, so the NEXT deposit
|
|
148
|
+
* whose prefix it is (a conversation's accumulated context) reuses every
|
|
149
|
+
* full aligned block and refolds only the right edge of each level —
|
|
150
|
+
* O(turn) per deposit instead of O(context), with the tree BIT-IDENTICAL
|
|
151
|
+
* to a from-scratch fold (see FoldPyramid). Purely a performance cache.
|
|
152
|
+
* Entry-count bounded (a pyramid is ~KB/byte of content; only the few
|
|
153
|
+
* live conversation chains matter). */
|
|
154
|
+
_depositTrees: BoundedMap<string, FoldPyramid>;
|
|
155
|
+
/** The byte lengths present in {@link _depositTrees} — the candidate
|
|
156
|
+
* prefix lengths probed (longest first). Drifts on eviction (a stale
|
|
157
|
+
* length only costs a miss); cleared with the map when it outgrows the
|
|
158
|
+
* probe budget. */
|
|
159
|
+
_depositLens: Set<number>;
|
|
160
|
+
/** Mind-lifetime intern memo by NODE IDENTITY: perceived-tree node → its
|
|
161
|
+
* content-addressed id. Valid forever (ids are permanent, Sema nodes
|
|
162
|
+
* immutable); WeakMap, so entries live exactly as long as the pyramid
|
|
163
|
+
* cache keeps the shared subtrees alive. Lets internTreeIds skip whole
|
|
164
|
+
* shared subtrees and indexSubSpans keep its seenBefore window skip. */
|
|
165
|
+
_internIds: WeakMap<Sema, number>;
|
|
164
166
|
}
|
|
165
167
|
/** Read a whole node's bytes. */
|
|
166
168
|
export declare const ALL = 2147483647;
|
|
@@ -168,14 +170,7 @@ export declare const ALL = 2147483647;
|
|
|
168
170
|
export declare function spliceAll(segs: Seg[]): Uint8Array | null;
|
|
169
171
|
/** Lift the answer out of the cover for think: the recognised region, free of
|
|
170
172
|
* the asker's surrounding (unrecognised) framing. */
|
|
171
|
-
export declare function liftAnswer(
|
|
172
|
-
segs: Seg[],
|
|
173
|
-
queryLen: number,
|
|
174
|
-
): Uint8Array | null;
|
|
173
|
+
export declare function liftAnswer(segs: Seg[], queryLen: number): Uint8Array | null;
|
|
175
174
|
/** The CHANGED NODES of a freshly-perceived `tree` against the node ids a previous
|
|
176
175
|
* tracked deposit interned (`prevSeen`). */
|
|
177
|
-
export declare function changedNodes(
|
|
178
|
-
tree: Sema,
|
|
179
|
-
ids: Map<Sema, number>,
|
|
180
|
-
prevSeen: Set<number>,
|
|
181
|
-
): Sema[];
|
|
176
|
+
export declare function changedNodes(tree: Sema, ids: Map<Sema, number>, prevSeen: Set<number>): Sema[];
|
package/dist/src/mind/types.js
CHANGED
|
@@ -11,83 +11,74 @@ import { dominates } from "../geometry.js";
|
|
|
11
11
|
export const ALL = 0x7fffffff;
|
|
12
12
|
/** Splice every chosen span in order — the whole cover as one byte string. */
|
|
13
13
|
export function spliceAll(segs) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return concatBytes(segs.map((s) => s.bytes));
|
|
14
|
+
if (!segs.some((s) => s.rec))
|
|
15
|
+
return null;
|
|
16
|
+
return concatBytes(segs.map((s) => s.bytes));
|
|
18
17
|
}
|
|
19
18
|
/** Lift the answer out of the cover for think: the recognised region, free of
|
|
20
19
|
* the asker's surrounding (unrecognised) framing. */
|
|
21
20
|
export function liftAnswer(segs, queryLen) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
const recognised = [];
|
|
22
|
+
for (let k = 0; k < segs.length; k++)
|
|
23
|
+
if (segs[k].rec)
|
|
24
|
+
recognised.push(k);
|
|
25
|
+
if (recognised.length === 0)
|
|
26
|
+
return null;
|
|
27
|
+
if (recognised.length === 1) {
|
|
28
|
+
const s = segs[recognised[0]];
|
|
29
|
+
if (dominates(s.j - s.i, queryLen)) {
|
|
30
|
+
return concatBytes(segs.map((x) => x.bytes));
|
|
31
|
+
}
|
|
32
|
+
return s.bytes;
|
|
26
33
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
if (recognised.length === 1) {
|
|
32
|
-
const s = segs[recognised[0]];
|
|
33
|
-
if (dominates(s.j - s.i, queryLen)) {
|
|
34
|
-
return concatBytes(segs.map((x) => x.bytes));
|
|
35
|
-
}
|
|
36
|
-
return s.bytes;
|
|
37
|
-
}
|
|
38
|
-
const lo = recognised[0];
|
|
39
|
-
const hi = recognised[recognised.length - 1];
|
|
40
|
-
return concatBytes(segs.slice(lo, hi + 1).map((x) => x.bytes));
|
|
34
|
+
const lo = recognised[0];
|
|
35
|
+
const hi = recognised[recognised.length - 1];
|
|
36
|
+
return concatBytes(segs.slice(lo, hi + 1).map((x) => x.bytes));
|
|
41
37
|
}
|
|
42
38
|
/** The CHANGED NODES of a freshly-perceived `tree` against the node ids a previous
|
|
43
39
|
* tracked deposit interned (`prevSeen`). */
|
|
44
40
|
export function changedNodes(tree, ids, prevSeen) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
holder
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
if (holder === null) {
|
|
89
|
-
return [n];
|
|
41
|
+
const newCount = new Map();
|
|
42
|
+
const count = (n) => {
|
|
43
|
+
const memo = newCount.get(n);
|
|
44
|
+
if (memo !== undefined)
|
|
45
|
+
return memo;
|
|
46
|
+
const id = ids.get(n);
|
|
47
|
+
// PRUNE: a node whose id the previous deposit already interned is old,
|
|
48
|
+
// and content addressing makes that transitive — the same id names the
|
|
49
|
+
// same content, so every descendant was interned then too. The whole
|
|
50
|
+
// subtree counts 0 without walking it; with the pyramid fold sharing a
|
|
51
|
+
// conversation's prefix subtree, this is what keeps the changed-nodes
|
|
52
|
+
// read O(new nodes) instead of O(context). (A node internTreeIds
|
|
53
|
+
// memo-skipped has an id here exactly when it is such a shared root.)
|
|
54
|
+
if (id !== undefined && prevSeen.has(id)) {
|
|
55
|
+
newCount.set(n, 0);
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
let c = 1; // reachable only when NOT pruned above ⇒ this node is new
|
|
59
|
+
if (n.kids) {
|
|
60
|
+
for (const k of n.kids)
|
|
61
|
+
c += count(k);
|
|
62
|
+
}
|
|
63
|
+
newCount.set(n, c);
|
|
64
|
+
return c;
|
|
65
|
+
};
|
|
66
|
+
const total = count(tree);
|
|
67
|
+
if (total === 0)
|
|
68
|
+
return [tree];
|
|
69
|
+
let n = tree;
|
|
70
|
+
for (;;) {
|
|
71
|
+
if (n.kids === null)
|
|
72
|
+
return [n];
|
|
73
|
+
let holder = null;
|
|
74
|
+
for (const k of n.kids) {
|
|
75
|
+
if (newCount.get(k) === total) {
|
|
76
|
+
holder = k;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (holder === null)
|
|
81
|
+
return [n];
|
|
82
|
+
n = holder;
|
|
90
83
|
}
|
|
91
|
-
n = holder;
|
|
92
|
-
}
|
|
93
84
|
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/** External ids are integers only. */
|
|
2
|
+
export type ExternalId = number;
|
|
3
|
+
export interface DatabaseOptions {
|
|
4
|
+
/** Path to the SQLite database file (use ":memory:" for a transient store). */
|
|
5
|
+
dbPath: string;
|
|
6
|
+
/** Vector dimensionality. Required for a new database; ignored when reopening. */
|
|
7
|
+
dim?: number;
|
|
8
|
+
/** Query breadth: clusters probed per query = ceil(efSearch / 4). Default
|
|
9
|
+
* 64 (16 probes). Tunable at runtime and at reopen. */
|
|
10
|
+
efSearch?: number;
|
|
11
|
+
/** Bits used to scalar-quantise the query for the RaBitQ estimator.
|
|
12
|
+
* Query-side only (stored codes are independent of it), so it is tunable
|
|
13
|
+
* at reopen, like efSearch. Default 8. */
|
|
14
|
+
queryBits?: number;
|
|
15
|
+
/** Number of sign-flip + Hadamard rounds in the random rotation. Default 3. */
|
|
16
|
+
rotationRounds?: number;
|
|
17
|
+
/** Seed for the rotation. Default fixed. */
|
|
18
|
+
seed?: number;
|
|
19
|
+
/** Optional centroid the vectors are centered by before quantisation. */
|
|
20
|
+
centroid?: ArrayLike<number>;
|
|
21
|
+
/** SQLite page-cache budget in MiB. Purely a latency knob: correctness and
|
|
22
|
+
* the per-operation storage-read count are identical with it at 0. */
|
|
23
|
+
cacheSizeMb?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface QueryResult {
|
|
26
|
+
id: ExternalId;
|
|
27
|
+
/** Estimated cosine distance (1 - cosine). */
|
|
28
|
+
distance: number;
|
|
29
|
+
}
|
|
30
|
+
export interface StorageStats {
|
|
31
|
+
float32BytesPerVector: number;
|
|
32
|
+
codeBytesPerVector: number;
|
|
33
|
+
bytesPerVector: number;
|
|
34
|
+
compressionRatio: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A persistent vector database: an adaptive PARTITIONED (IVF) index over
|
|
38
|
+
* 1-bit RaBitQ codes (cosine), stored entirely in SQLite at `dbPath`. See
|
|
39
|
+
* ivf.ts for the index design. The original float vectors are never
|
|
40
|
+
* retained — only the sign codes — so a 1024-d vector costs 128 bytes.
|
|
41
|
+
*
|
|
42
|
+
* Inserting is route-and-append: cost is essentially FLAT in collection size
|
|
43
|
+
* (one RAM scan of the pivot table + one chunk append — no per-insert graph
|
|
44
|
+
* walk). Query cost is bounded by nprobe × cluster size — constant in N
|
|
45
|
+
* once the collection is past its first splits.
|
|
46
|
+
*/
|
|
47
|
+
export declare class VectorDatabase {
|
|
48
|
+
private readonly index;
|
|
49
|
+
constructor(options: DatabaseOptions);
|
|
50
|
+
get dim(): number;
|
|
51
|
+
/** Number of live (non-deleted) vectors. */
|
|
52
|
+
get size(): number;
|
|
53
|
+
/** Physical slot count including tombstones from deletes/updates. */
|
|
54
|
+
get physicalSize(): number;
|
|
55
|
+
/** Number of clusters (partitions) currently in the index. */
|
|
56
|
+
get clusterCount(): number;
|
|
57
|
+
get efSearch(): number;
|
|
58
|
+
set efSearch(value: number);
|
|
59
|
+
/** Distance computations performed during the most recent query. */
|
|
60
|
+
get lastQueryDistanceComputations(): number;
|
|
61
|
+
/** Storage row reads issued by the most recent query — the honest,
|
|
62
|
+
* cache-independent scalability metric. */
|
|
63
|
+
get lastQueryStorageReads(): number;
|
|
64
|
+
get storage(): StorageStats;
|
|
65
|
+
private checkId;
|
|
66
|
+
/** Convert a value to code bytes, selecting by length: `codeWords`
|
|
67
|
+
* elements → an existing 1-bit code; otherwise a raw `dim`-vector. */
|
|
68
|
+
private toCodeBytes;
|
|
69
|
+
has(id: ExternalId): boolean;
|
|
70
|
+
/** Stream every live external id (bounded memory). */
|
|
71
|
+
keys(): IterableIterator<ExternalId>;
|
|
72
|
+
/** Stream live entries whose INTERNAL id is > `after` — a durable
|
|
73
|
+
* incremental watermark (internal ids are monotone at insert and preserved
|
|
74
|
+
* by update and compact). */
|
|
75
|
+
keysSince(after: number): IterableIterator<{
|
|
76
|
+
ext: ExternalId;
|
|
77
|
+
internal: number;
|
|
78
|
+
}>;
|
|
79
|
+
/** Read the stored 1-bit code for an id (a copy as a Uint32Array), or null. */
|
|
80
|
+
get(id: ExternalId): Uint32Array | null;
|
|
81
|
+
insert(id: ExternalId, value: ArrayLike<number>): void;
|
|
82
|
+
update(id: ExternalId, value: ArrayLike<number>): void;
|
|
83
|
+
upsert(id: ExternalId, value: ArrayLike<number>): void;
|
|
84
|
+
/**
|
|
85
|
+
* Upsert many vectors under ONE transaction — one WAL commit for the whole
|
|
86
|
+
* batch instead of one per vector, with chunk appends to the same cluster
|
|
87
|
+
* coalesced in the index's write-back buffer.
|
|
88
|
+
*/
|
|
89
|
+
upsertMany(entries: Array<{
|
|
90
|
+
id: ExternalId;
|
|
91
|
+
vector: ArrayLike<number>;
|
|
92
|
+
}>): void;
|
|
93
|
+
/** Delete many ids under ONE transaction. Absent ids are skipped.
|
|
94
|
+
* Returns the number of vectors actually removed. */
|
|
95
|
+
deleteMany(ids: ExternalId[]): number;
|
|
96
|
+
/** Delete the vector bound to an id. Returns false if absent. */
|
|
97
|
+
delete(id: ExternalId): boolean;
|
|
98
|
+
/** Heat the SQLite page cache with sequential scans (latency only). */
|
|
99
|
+
warmCache(): number;
|
|
100
|
+
/** Reclaim tombstoned slots by rewriting the clusters that carry any, then
|
|
101
|
+
* VACUUM. Internal ids and cluster assignment are preserved. */
|
|
102
|
+
compact(): void;
|
|
103
|
+
/**
|
|
104
|
+
* k-NN search. The argument's length selects the mode:
|
|
105
|
+
* - `dim` elements → a raw vector (accurate estimator)
|
|
106
|
+
* - `codeWords` elements → a 1-bit code, by sign-bit Hamming
|
|
107
|
+
*/
|
|
108
|
+
query(query: ArrayLike<number>, k?: number, opts?: {
|
|
109
|
+
ef?: number;
|
|
110
|
+
}): QueryResult[];
|
|
111
|
+
/** Close the underlying database. The instance must not be used afterwards. */
|
|
112
|
+
close(): void;
|
|
113
|
+
}
|