@hviana/sema 0.1.6 → 0.1.8
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/dist/example/train_base.js +42 -9
- package/dist/src/alu/src/parser.d.ts +9 -0
- package/dist/src/alu/src/parser.js +110 -2
- package/dist/src/canon.d.ts +26 -0
- package/dist/src/canon.js +57 -0
- package/dist/src/geometry.d.ts +13 -2
- package/dist/src/geometry.js +101 -2
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/mind/attention.js +11 -7
- package/dist/src/mind/learning.js +33 -1
- package/dist/src/mind/match.d.ts +4 -2
- package/dist/src/mind/match.js +30 -6
- package/dist/src/mind/mechanisms/cast.js +18 -4
- package/dist/src/mind/mechanisms/confluence.js +13 -1
- package/dist/src/mind/mechanisms/recall.js +115 -31
- package/dist/src/mind/mind.d.ts +138 -12
- package/dist/src/mind/mind.js +284 -22
- package/dist/src/mind/primitives.d.ts +22 -2
- package/dist/src/mind/primitives.js +95 -6
- package/dist/src/mind/recognition.js +31 -8
- package/dist/src/mind/traverse.d.ts +13 -0
- package/dist/src/mind/traverse.js +41 -0
- package/dist/src/mind/types.d.ts +33 -21
- package/dist/src/store-sqlite.d.ts +10 -0
- package/dist/src/store-sqlite.js +58 -0
- package/dist/src/store.d.ts +23 -0
- package/dist/src/store.js +13 -2
- package/example/train_base.ts +49 -9
- package/index.html +65 -0
- package/package.json +1 -1
- package/src/alu/src/parser.ts +105 -4
- package/src/canon.ts +65 -0
- package/src/geometry.ts +105 -1
- package/src/index.ts +1 -0
- package/src/mind/attention.ts +11 -6
- package/src/mind/learning.ts +39 -1
- package/src/mind/match.ts +29 -6
- package/src/mind/mechanisms/cast.ts +21 -6
- package/src/mind/mechanisms/confluence.ts +15 -1
- package/src/mind/mechanisms/recall.ts +132 -41
- package/src/mind/mind.ts +396 -22
- package/src/mind/primitives.ts +109 -7
- package/src/mind/recognition.ts +36 -8
- package/src/mind/traverse.ts +47 -0
- package/src/mind/types.ts +30 -21
- package/src/store-sqlite.ts +76 -0
- package/src/store.ts +46 -2
- package/test/13-conversation.test.mjs +240 -20
- package/test/35-prefix-edge.test.mjs +86 -0
|
@@ -13,8 +13,9 @@ import {
|
|
|
13
13
|
} from "../../geometry.js";
|
|
14
14
|
import type { MindContext } from "../types.js";
|
|
15
15
|
import { gistOf, read, resolve } from "../primitives.js";
|
|
16
|
+
import { bytesEqual, indexOf } from "../../bytes.js";
|
|
16
17
|
import { corpusN, hubBound } from "../traverse.js";
|
|
17
|
-
import { project, reverseContext } from "../match.js";
|
|
18
|
+
import { follow, project, reverseContext } from "../match.js";
|
|
18
19
|
import { CONCEPT, STEP } from "../graph-search.js";
|
|
19
20
|
import { unexplainedLabel } from "../rationale.js";
|
|
20
21
|
import type { PipelineMechanism, Precomputed } from "../pipeline-mechanism.js";
|
|
@@ -81,6 +82,51 @@ export async function recallByResonance(
|
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
84
|
|
|
85
|
+
// 0b. ARGUMENT BINDING (RC8): the query is not itself a stored form, but
|
|
86
|
+
// it CONTAINS a recognised constituent that is an edge SOURCE — a learnt
|
|
87
|
+
// pair's left side carried inside a wrapper ("How do you say 'thank you'
|
|
88
|
+
// in French?"). The wrapper is scaffolding; the argument is the span
|
|
89
|
+
// that LEADS somewhere, so its continuation — guided by the whole query's
|
|
90
|
+
// gist — is the answer. Matching the wrapper while ignoring the argument
|
|
91
|
+
// (the observed "good morning" template failure) is worse than silence,
|
|
92
|
+
// so anything short of ONE unambiguous binding falls through: the
|
|
93
|
+
// constituent bar is the same two-quanta (2W) reading confluence binds
|
|
94
|
+
// under, nested recognitions collapse to their MAXIMAL span, and two
|
|
95
|
+
// distinct maximal arguments mean the query asks about neither alone.
|
|
96
|
+
if (qId === null) {
|
|
97
|
+
const W2 = 2 * ctx.space.maxGroup;
|
|
98
|
+
const args = pre.rec.sites.filter((s) =>
|
|
99
|
+
s.end - s.start >= W2 &&
|
|
100
|
+
s.end - s.start < query.length &&
|
|
101
|
+
ctx.store.hasNext(s.payload)
|
|
102
|
+
);
|
|
103
|
+
// Maximal spans by one sorted sweep (starts ascending, ties longest
|
|
104
|
+
// first): every earlier span starts at or before s, so s is contained
|
|
105
|
+
// exactly when the running max end already covers it. O(m log m) — a
|
|
106
|
+
// long input recognises O(|input|) sites, and a pairwise scan here was
|
|
107
|
+
// quadratic in the input.
|
|
108
|
+
args.sort((a, b) => a.start - b.start || b.end - a.end);
|
|
109
|
+
const maximal: typeof args = [];
|
|
110
|
+
let maxEnd = -1;
|
|
111
|
+
for (const s of args) {
|
|
112
|
+
if (s.end <= maxEnd) continue;
|
|
113
|
+
maximal.push(s);
|
|
114
|
+
maxEnd = s.end;
|
|
115
|
+
}
|
|
116
|
+
if (maximal.length === 1) {
|
|
117
|
+
const arg = maximal[0];
|
|
118
|
+
const g = await follow(ctx, arg.payload, queryGist);
|
|
119
|
+
if (g !== null && g.length > 0) {
|
|
120
|
+
return ground(
|
|
121
|
+
g,
|
|
122
|
+
"argument binding — the query's sole edge-source constituent, continuation followed",
|
|
123
|
+
[[arg.start, arg.end]],
|
|
124
|
+
STEP,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
84
130
|
const whole = await ctx.store.resonate(queryGist, k);
|
|
85
131
|
if (whole.length === 0) {
|
|
86
132
|
return ground(null, "empty store — nothing to resonate with", [], 0);
|
|
@@ -99,11 +145,27 @@ export async function recallByResonance(
|
|
|
99
145
|
// into bytes — at most one river window (see {@link identityBar}). A
|
|
100
146
|
// fixed cosine bar let long queries claim "near-identical" while whole
|
|
101
147
|
// windows — an answer word — differed.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
148
|
+
// A hit RESTATES the query when its bytes are the query's own — exactly,
|
|
149
|
+
// or under the response's equivalence (a case/width twin). Restating
|
|
150
|
+
// hits may only conclude through disciplined reverse recall: voicing
|
|
151
|
+
// their bytes echoes the query back at itself (never an answer — the
|
|
152
|
+
// same principle that keeps cast from voicing stored questions), and
|
|
153
|
+
// projecting them forward is reverse recall's containment failure in the
|
|
154
|
+
// other direction — "whatever followed these bytes in some document".
|
|
155
|
+
const qKey = ctx.canon ? ctx.canon(query) : query;
|
|
156
|
+
const restates = (b: Uint8Array): boolean =>
|
|
157
|
+
bytesEqual(b, query) ||
|
|
158
|
+
(ctx.canon !== null && bytesEqual(ctx.canon(b), qKey));
|
|
159
|
+
const idBar = identityBar(ctx.store.D, ctx.space.maxGroup, query.length);
|
|
160
|
+
if (top.score >= idBar) {
|
|
105
161
|
for (const h of whole) {
|
|
106
|
-
|
|
162
|
+
// The identity claim is PER HIT, not per tier: hits are ranked
|
|
163
|
+
// nearest-first, and grounding one below the bar under this tier's
|
|
164
|
+
// "near-identical" label would launder byte-overlap noise (observed:
|
|
165
|
+
// "merci" projecting through the unrelated near hit "meraih").
|
|
166
|
+
if (h.score < idBar) break;
|
|
167
|
+
const own = read(ctx, h.id);
|
|
168
|
+
if (h.id === qId || restates(own)) {
|
|
107
169
|
const rev = ctx.store.prevFirst(h.id, hubBound(ctx));
|
|
108
170
|
const g = reverseContext(ctx, h.id, queryGist, rev);
|
|
109
171
|
if (g !== null) {
|
|
@@ -116,15 +178,7 @@ export async function recallByResonance(
|
|
|
116
178
|
STEP,
|
|
117
179
|
);
|
|
118
180
|
}
|
|
119
|
-
|
|
120
|
-
if (own.length > 0) {
|
|
121
|
-
return ground(
|
|
122
|
-
own,
|
|
123
|
-
"perfect self-match — the query IS this node",
|
|
124
|
-
nothing,
|
|
125
|
-
STEP,
|
|
126
|
-
);
|
|
127
|
-
}
|
|
181
|
+
continue;
|
|
128
182
|
}
|
|
129
183
|
const g = await project(ctx, h.id, queryGist);
|
|
130
184
|
if (g) {
|
|
@@ -138,8 +192,35 @@ export async function recallByResonance(
|
|
|
138
192
|
}
|
|
139
193
|
}
|
|
140
194
|
|
|
195
|
+
// The query-relative grounding fraction, shared by tiers 2–4 — gated on
|
|
196
|
+
// the FRACTION OF THE QUERY the grounding explains, not the raw cosine.
|
|
197
|
+
// Root gists are unit vectors, but their magnitudes are recoverable from
|
|
198
|
+
// the byte lengths (‖·‖ = √len under the linear fold):
|
|
199
|
+
// cos = shared/√(lenQ·lenG), so shared/lenQ = cos·√(lenG/lenQ).
|
|
200
|
+
// The raw cosine punished honest containment — a query fully inside a
|
|
201
|
+
// longer grounded answer scored √(lenQ/lenG) and was refused — and let a
|
|
202
|
+
// long answer sharing only scaffolding pass; the query-relative fraction
|
|
203
|
+
// measures exactly what the reach bar means: how much of THE QUERY the
|
|
204
|
+
// store accounts for.
|
|
205
|
+
// Chance similarity survives the length conversion AMPLIFIED: the same
|
|
206
|
+
// √(lenG/lenQ) factor that converts an honest shared fraction into a
|
|
207
|
+
// query-relative one multiplies the estimator/chance floor too, so a long
|
|
208
|
+
// stored form (√(lenG/lenQ) ≈ 10 at 100×) lifted a noise-level cosine past
|
|
209
|
+
// the reach bar and grounded pure gibberish (observed). Only the
|
|
210
|
+
// ABOVE-CHANCE part of the similarity is evidence of shared content —
|
|
211
|
+
// subtract the significance bar (3/√D, §8.3) before converting. Derived
|
|
212
|
+
// from the existing bars; never tuned.
|
|
213
|
+
const sig = significanceBar(ctx.store.D);
|
|
214
|
+
const reach = reachThreshold(ctx.space.maxGroup);
|
|
215
|
+
const fracOfQuery = (cos: number, otherLen: number): number =>
|
|
216
|
+
Math.min(
|
|
217
|
+
1,
|
|
218
|
+
Math.max(0, cos - sig) *
|
|
219
|
+
Math.sqrt(otherLen / Math.max(1, query.length)),
|
|
220
|
+
);
|
|
221
|
+
|
|
141
222
|
// 2. Scaffolding-dominated.
|
|
142
|
-
if (top.score >=
|
|
223
|
+
if (top.score >= sig) {
|
|
143
224
|
const N = corpusN(ctx);
|
|
144
225
|
const minVote = consensusFloor(N);
|
|
145
226
|
// The committed points of attention ARE the shared climb's roots (same
|
|
@@ -148,7 +229,15 @@ export async function recallByResonance(
|
|
|
148
229
|
const forest = (await pre.attention()).roots;
|
|
149
230
|
if (forest.length > 0 && forest[0].vote >= minVote) {
|
|
150
231
|
const g = await project(ctx, forest[0].anchor, queryGist);
|
|
151
|
-
|
|
232
|
+
// The anchor cleared the consensus floor, but the floor prices the
|
|
233
|
+
// ANCHOR's evidence, not the projection's: a junk attractor can clear
|
|
234
|
+
// it and project a PIECE OF THE QUERY back at it (the observed
|
|
235
|
+
// "buenos días in English" → "English" fragment). A projection that
|
|
236
|
+
// is a proper byte-subspan of the query restates part of the question
|
|
237
|
+
// — never an answer (the same principle as `restates` above, extended
|
|
238
|
+
// to fragments). Genuine anchor groundings — longer than the query,
|
|
239
|
+
// or disjoint from it — pass untouched.
|
|
240
|
+
if (g && !(g.length < query.length && indexOf(query, g, 0) >= 0)) {
|
|
152
241
|
return ground(
|
|
153
242
|
g,
|
|
154
243
|
"scaffolding-dominated query — ground the consensus-climb anchor",
|
|
@@ -159,23 +248,13 @@ export async function recallByResonance(
|
|
|
159
248
|
}
|
|
160
249
|
}
|
|
161
250
|
|
|
162
|
-
// 3. Last resort —
|
|
163
|
-
// explains, not the raw cosine. Root gists are unit vectors, but their
|
|
164
|
-
// magnitudes are recoverable from the byte lengths (‖·‖ = √len under the
|
|
165
|
-
// linear fold): cos = shared/√(lenQ·lenG), so shared/lenQ = cos·√(lenG/lenQ).
|
|
166
|
-
// The raw cosine punished honest containment — a query fully inside a
|
|
167
|
-
// longer grounded answer scored √(lenQ/lenG) and was refused — and let a
|
|
168
|
-
// long answer sharing only scaffolding pass; the query-relative fraction
|
|
169
|
-
// measures exactly what the reach bar means: how much of THE QUERY the
|
|
170
|
-
// store accounts for.
|
|
171
|
-
const fracOfQuery = (cos: number, otherLen: number): number =>
|
|
172
|
-
Math.min(1, cos * Math.sqrt(otherLen / Math.max(1, query.length)));
|
|
251
|
+
// 3. Last resort — the nearest grounded whole-query hit, same gate.
|
|
173
252
|
for (const h of whole) {
|
|
174
253
|
const g = await project(ctx, h.id, queryGist);
|
|
175
254
|
if (g) {
|
|
176
255
|
if (
|
|
177
256
|
fracOfQuery(cosine(queryGist, gistOf(ctx, g)), g.length) >=
|
|
178
|
-
|
|
257
|
+
reach
|
|
179
258
|
) {
|
|
180
259
|
return ground(
|
|
181
260
|
g,
|
|
@@ -186,18 +265,20 @@ export async function recallByResonance(
|
|
|
186
265
|
}
|
|
187
266
|
}
|
|
188
267
|
}
|
|
189
|
-
// The refusal/echo decision
|
|
190
|
-
//
|
|
191
|
-
//
|
|
192
|
-
//
|
|
193
|
-
// reach bar
|
|
194
|
-
//
|
|
195
|
-
//
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
268
|
+
// The refusal/echo decision. The echo returns a stored form's bytes AS
|
|
269
|
+
// the answer — a near-identity claim about the query — and identity-grade
|
|
270
|
+
// decisions are never made on an estimated score ("approximate scores may
|
|
271
|
+
// rank and propose; they may never decide", §6.2): the RaBitQ estimate
|
|
272
|
+
// overshooting the reach bar echoed a WRONG-entity neighbour ("capital of
|
|
273
|
+
// Zamunda?" echoed the Armenia fact, observed). The bytes are read
|
|
274
|
+
// anyway to be echoed, so the decision uses their EXACT fold: one river
|
|
275
|
+
// fold of the top hit, measured in the same query-relative,
|
|
276
|
+
// chance-corrected units as the tier above.
|
|
277
|
+
const topBytes = read(ctx, top.id);
|
|
278
|
+
const exact = topBytes.length > 0
|
|
279
|
+
? cosine(queryGist, gistOf(ctx, topBytes))
|
|
280
|
+
: 0;
|
|
281
|
+
if (fracOfQuery(exact, topBytes.length) < reach) {
|
|
201
282
|
return ground(
|
|
202
283
|
null,
|
|
203
284
|
"below reach threshold — nothing in the store relates to this query",
|
|
@@ -205,9 +286,19 @@ export async function recallByResonance(
|
|
|
205
286
|
0,
|
|
206
287
|
);
|
|
207
288
|
}
|
|
289
|
+
// Echoing the query's own bytes back at it is not an echo of a RELATED
|
|
290
|
+
// form — it is the query restated, which answers nothing.
|
|
291
|
+
if (restates(topBytes)) {
|
|
292
|
+
return ground(
|
|
293
|
+
null,
|
|
294
|
+
"the nearest form IS the query itself — restating it answers nothing",
|
|
295
|
+
[],
|
|
296
|
+
0,
|
|
297
|
+
);
|
|
298
|
+
}
|
|
208
299
|
// Honest echo.
|
|
209
300
|
return ground(
|
|
210
|
-
|
|
301
|
+
topBytes,
|
|
211
302
|
"last resort: the nearest resonant form's own bytes (echo, not grounded)",
|
|
212
303
|
[],
|
|
213
304
|
0,
|