@hviana/sema 0.2.2 → 0.2.4
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/src/mind/articulation.js +1 -1
- package/dist/src/mind/attention.d.ts +18 -2
- package/dist/src/mind/attention.js +88 -18
- package/dist/src/mind/bridge.d.ts +30 -0
- package/dist/src/mind/bridge.js +569 -0
- package/dist/src/mind/graph-search.d.ts +16 -1
- package/dist/src/mind/graph-search.js +34 -5
- package/dist/src/mind/match.d.ts +15 -2
- package/dist/src/mind/match.js +3 -8
- package/dist/src/mind/mechanisms/alu.js +8 -1
- package/dist/src/mind/mechanisms/cast.d.ts +54 -0
- package/dist/src/mind/mechanisms/cast.js +303 -48
- package/dist/src/mind/mechanisms/cover.js +24 -32
- package/dist/src/mind/mechanisms/extraction.js +75 -30
- package/dist/src/mind/mechanisms/recall.js +66 -0
- package/dist/src/mind/mind.d.ts +1 -0
- package/dist/src/mind/mind.js +6 -1
- package/dist/src/mind/pipeline.js +34 -2
- package/dist/src/mind/reasoning.d.ts +20 -1
- package/dist/src/mind/reasoning.js +84 -6
- package/dist/src/mind/recognition.js +157 -13
- package/dist/src/mind/traverse.js +16 -0
- package/dist/src/mind/types.d.ts +65 -2
- package/dist/src/mind/types.js +53 -7
- package/dist/src/store.d.ts +12 -3
- package/dist/src/store.js +9 -3
- package/package.json +1 -1
- package/src/mind/articulation.ts +1 -0
- package/src/mind/attention.ts +105 -17
- package/src/mind/bridge.ts +596 -0
- package/src/mind/graph-search.ts +59 -2
- package/src/mind/match.ts +19 -5
- package/src/mind/mechanisms/alu.ts +8 -1
- package/src/mind/mechanisms/cast.ts +336 -46
- package/src/mind/mechanisms/cover.ts +31 -36
- package/src/mind/mechanisms/extraction.ts +101 -40
- package/src/mind/mechanisms/recall.ts +79 -0
- package/src/mind/mind.ts +7 -1
- package/src/mind/pipeline.ts +37 -2
- package/src/mind/reasoning.ts +97 -5
- package/src/mind/recognition.ts +160 -12
- package/src/mind/traverse.ts +17 -0
- package/src/mind/types.ts +110 -6
- package/src/store.ts +19 -5
- package/test/35-attention-confidence.test.mjs +139 -0
- package/test/36-already-answered-fusion.test.mjs +128 -0
- package/test/37-cluster-dispersion-fusion.test.mjs +190 -0
- package/test/38-reason-restate-guard.test.mjs +94 -0
- package/test/39-cast-restate-guard.test.mjs +102 -0
- package/test/40-choosenext-scale-guard.test.mjs +75 -0
- package/test/41-seatofnode-direction.test.mjs +85 -0
- package/test/42-recognise-trace-idempotence.test.mjs +106 -0
- package/test/43-cast-analog-seat.test.mjs +244 -0
- package/test/44-recognise-edge-whitespace.test.mjs +63 -0
- package/test/45-liftanswer-restated-trim.test.mjs +60 -0
- package/test/46-recognise-multibyte-edge.test.mjs +85 -0
- package/test/47-cast-comparison-coverage.test.mjs +134 -0
- package/test/48-recognise-turn-connective.test.mjs +125 -0
- package/test/49-natural-units-synonym-bridge.test.mjs +175 -0
- package/test/50-cast-analog-consensus-floor.test.mjs +179 -0
|
@@ -70,43 +70,118 @@ export async function extractBySkill(
|
|
|
70
70
|
if (ranked.length === 0) {
|
|
71
71
|
return fail("no consensus anchor — no skill to apply");
|
|
72
72
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
73
|
+
|
|
74
|
+
// Try ranked anchors IN ORDER until one yields a USABLE extraction — not
|
|
75
|
+
// merely a span-shaped exemplar, but one whose extracted span clears the
|
|
76
|
+
// same one-river-fold quantum (W) cover.ts's restatedSpan gate already
|
|
77
|
+
// treats as the floor below which byte overlap is chance, not evidence.
|
|
78
|
+
// isSpanShaped (spanShapedOf) is a deliberately permissive sparse-
|
|
79
|
+
// subsequence check — see the section note below — so it accepts exemplars
|
|
80
|
+
// whose relation to the query is coincidental gap-matching, not genuine
|
|
81
|
+
// structure. Stopping at the FIRST such exemplar let a coincidental match
|
|
82
|
+
// early in the ranked list win outright and read out a sub-quantum
|
|
83
|
+
// fragment (observed: a 3-byte "Hel" pulled from an unrelated exemplar,
|
|
84
|
+
// while a later ranked anchor would have read the query's own "Hello…"
|
|
85
|
+
// correctly). Trying further anchors when one produces nothing usable is
|
|
86
|
+
// the same idiom this loop already uses for non-exemplars — extended to
|
|
87
|
+
// cover a bad extraction, not just a structural non-match.
|
|
88
|
+
//
|
|
89
|
+
// The retry is bounded at pre.k — the SAME evidence-breadth constant every
|
|
90
|
+
// other consumer of a ranked list already self-limits to (resonance, the
|
|
91
|
+
// weave, the climb itself; see Precomputed.k's own doc comment) — not the
|
|
92
|
+
// full ranked list. locate()'s frame match has an EXACT-byte tier with no
|
|
93
|
+
// significance correction of its own (short W-byte frames are cheap to
|
|
94
|
+
// match by pure chance), so trying every ranked anchor turns that per-
|
|
95
|
+
// anchor chance into a near-certainty over enough attempts: on a pure-
|
|
96
|
+
// gibberish query, 170 anchors deep found an unrelated Zulu exemplar whose
|
|
97
|
+
// short frame happened to byte-match, producing "xyzzy pl" — a coincidence
|
|
98
|
+
// no different in kind from the "RaBitQ estimate overshot the reach bar
|
|
99
|
+
// and grounded pure gibberish" failure recall.ts's own significance
|
|
100
|
+
// correction exists to prevent (see recallByResonance's reach-threshold
|
|
101
|
+
// comment). Bounding the search to the ranked list's own top-k restores
|
|
102
|
+
// the "genuinely relevant but not root-significant" exemplars this loop
|
|
103
|
+
// was built for, without the unbounded tail's chance collisions.
|
|
104
|
+
const W = ctx.space.maxGroup;
|
|
105
|
+
const searched = ranked.slice(0, pre.k);
|
|
106
|
+
let shapeMisses = 0;
|
|
107
|
+
let subQuantum = 0;
|
|
108
|
+
for (const cand of searched) {
|
|
109
|
+
const exemplar = await pre.spanShapedOf(cand.anchor);
|
|
110
|
+
if (!exemplar) {
|
|
111
|
+
shapeMisses++;
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const built = buildFromExemplar(ctx, query, pre, exemplar);
|
|
115
|
+
if (built === null || built.bytes.length < W) {
|
|
116
|
+
subQuantum++;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (shapeMisses > 0 || subQuantum > 0) {
|
|
120
|
+
ctx.trace?.step(
|
|
121
|
+
"trySkillAnchors",
|
|
122
|
+
[
|
|
123
|
+
rItem(
|
|
124
|
+
query.subarray(0, 0),
|
|
125
|
+
`skipped ${shapeMisses + subQuantum}`,
|
|
126
|
+
),
|
|
127
|
+
rNode(ctx, cand.anchor, "chosen"),
|
|
128
|
+
],
|
|
129
|
+
[],
|
|
130
|
+
`skipped ${shapeMisses} non-exemplar and ${subQuantum} sub-quantum ` +
|
|
131
|
+
`anchor(s) before one yielded a usable extraction`,
|
|
132
|
+
);
|
|
82
133
|
}
|
|
83
|
-
|
|
134
|
+
t?.done(
|
|
135
|
+
[rItem(built.bytes, "extracted")],
|
|
136
|
+
built.pieces === 1
|
|
137
|
+
? `apply a learnt extraction skill — read the analogous span of the query` +
|
|
138
|
+
` framed like "${
|
|
139
|
+
decodeText(exemplar.answerBytes)
|
|
140
|
+
}" sits in its exemplar`
|
|
141
|
+
: `apply a learnt MULTI-PIECE skill — read ${built.pieces} analogous` +
|
|
142
|
+
` pieces of the query and synthesize them like "${
|
|
143
|
+
decodeText(exemplar.answerBytes)
|
|
144
|
+
}"`,
|
|
145
|
+
);
|
|
146
|
+
return {
|
|
147
|
+
bytes: built.bytes,
|
|
148
|
+
accounted: built.accounted,
|
|
149
|
+
unexplained: unexplainedLabel(query, built.accounted),
|
|
150
|
+
};
|
|
84
151
|
}
|
|
85
|
-
if (
|
|
152
|
+
if (shapeMisses === searched.length) {
|
|
86
153
|
ctx.trace?.step(
|
|
87
154
|
"trySkillAnchors",
|
|
88
155
|
[],
|
|
89
156
|
[],
|
|
90
|
-
`none of ${
|
|
157
|
+
`none of the top ${searched.length} ranked anchor(s) (of ${ranked.length} total) ` +
|
|
158
|
+
`is a span-shaped skill exemplar`,
|
|
91
159
|
);
|
|
92
160
|
return fail("no consensus root is a span-shaped skill exemplar");
|
|
93
161
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
162
|
+
return fail(
|
|
163
|
+
"no ranked anchor yielded an extraction at or above the quantum floor",
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Build the extracted bytes for ONE already-accepted span-shaped exemplar —
|
|
168
|
+
* factored out of {@link extractBySkill} so its anchor loop can try
|
|
169
|
+
* successive ranked candidates instead of committing to the first
|
|
170
|
+
* structural match. Null when the exemplar's answer does not decompose
|
|
171
|
+
* against its context, or no piece's frame locates in the query. */
|
|
172
|
+
function buildFromExemplar(
|
|
173
|
+
ctx: MindContext,
|
|
174
|
+
query: Uint8Array,
|
|
175
|
+
pre: Precomputed,
|
|
176
|
+
exemplar: SkillInfo,
|
|
177
|
+
):
|
|
178
|
+
| { bytes: Uint8Array; accounted: Array<[number, number]>; pieces: number }
|
|
179
|
+
| null {
|
|
105
180
|
const { contextBytes, answerBytes } = exemplar;
|
|
106
181
|
|
|
107
182
|
const ansCtxRuns = answerRunsInContext(ctx, contextBytes, answerBytes);
|
|
108
183
|
if (ansCtxRuns === null || ansCtxRuns.length === 0) {
|
|
109
|
-
return
|
|
184
|
+
return null;
|
|
110
185
|
}
|
|
111
186
|
|
|
112
187
|
if (ansCtxRuns.length > 1) {
|
|
@@ -200,25 +275,11 @@ export async function extractBySkill(
|
|
|
200
275
|
if (preBounded && postBounded) accounted.push([start, end]);
|
|
201
276
|
}
|
|
202
277
|
if (pieces.length === 0) {
|
|
203
|
-
return
|
|
278
|
+
return null;
|
|
204
279
|
}
|
|
205
280
|
|
|
206
281
|
const out = pieces.length === 1 ? pieces[0] : concatBytes(pieces);
|
|
207
|
-
|
|
208
|
-
[rItem(out, "extracted")],
|
|
209
|
-
pieces.length === 1
|
|
210
|
-
? `apply a learnt extraction skill — read the analogous span of the query` +
|
|
211
|
-
` framed like "${decodeText(answerBytes)}" sits in its exemplar`
|
|
212
|
-
: `apply a learnt MULTI-PIECE skill — read ${pieces.length} analogous` +
|
|
213
|
-
` pieces of the query and synthesize them like "${
|
|
214
|
-
decodeText(answerBytes)
|
|
215
|
-
}"`,
|
|
216
|
-
);
|
|
217
|
-
return {
|
|
218
|
-
bytes: out,
|
|
219
|
-
accounted,
|
|
220
|
-
unexplained: unexplainedLabel(query, accounted),
|
|
221
|
-
};
|
|
282
|
+
return { bytes: out, accounted, pieces: pieces.length };
|
|
222
283
|
}
|
|
223
284
|
|
|
224
285
|
// ── The two span-shape readings: OPEN acceptance vs. STRONG decomposition ──
|
|
@@ -20,6 +20,7 @@ import { CONCEPT, STEP } from "../graph-search.js";
|
|
|
20
20
|
import { unexplainedLabel } from "../rationale.js";
|
|
21
21
|
import type { PipelineMechanism, Precomputed } from "../pipeline-mechanism.js";
|
|
22
22
|
import { rItem, rNode } from "../trace.js";
|
|
23
|
+
import { substitutionBridge } from "../bridge.js";
|
|
23
24
|
|
|
24
25
|
/** A recall result. */
|
|
25
26
|
export interface RecallResult {
|
|
@@ -293,6 +294,84 @@ export async function recallByResonance(
|
|
|
293
294
|
}
|
|
294
295
|
}
|
|
295
296
|
}
|
|
297
|
+
// 3b. Corroborated-substitution bridge — refusal-path only (bridge.ts).
|
|
298
|
+
// Every gist-based tier has failed; before refusing, align the query
|
|
299
|
+
// byte-for-byte against the trained contexts its own stored windows
|
|
300
|
+
// anchor, accepting mismatches only as corpus-attested, concept-bar
|
|
301
|
+
// substitutions. A bridged context grounds exactly like any hit —
|
|
302
|
+
// projected through its learnt edges — under the same restated-fragment
|
|
303
|
+
// guard tiers 0b/2 apply. Costs nothing on any answering path.
|
|
304
|
+
{
|
|
305
|
+
// The resonance hits already ranked above are handed to the bridge as
|
|
306
|
+
// PROPOSED candidates alongside its own anchor climbs: on a corpus this
|
|
307
|
+
// size a W-byte window is far too common for the clamped climb to
|
|
308
|
+
// single out the right trained context, while the whole-query gist
|
|
309
|
+
// already ranked it nearest (observed live: "what is the capital of
|
|
310
|
+
// france" resonating straight to "What is the capital of France?" yet
|
|
311
|
+
// refusing on the reach bar). Approximate scores propose; the bridge's
|
|
312
|
+
// byte-exact alignment and attestation gates decide.
|
|
313
|
+
//
|
|
314
|
+
// The proposal breadth here is widened PAST `k` — first by requesting
|
|
315
|
+
// hubBound(ctx) candidates instead of `k` (recall's own tiers above
|
|
316
|
+
// stay at `k`; this re-resonates only on the refusal path, exactly
|
|
317
|
+
// where the bridge itself already runs), AND by asking the index to
|
|
318
|
+
// search EXHAUSTIVELY. Both matter: the IVF only ever probes
|
|
319
|
+
// ⌈√clusters⌉ of them (store.ts's efFor) REGARDLESS of k — widening k
|
|
320
|
+
// alone just returns more hits from the SAME already-probed clusters,
|
|
321
|
+
// never a hit whose vector lives in an unprobed one. Measured live:
|
|
322
|
+
// "What is the chemical symbol for water?" needs "What is the
|
|
323
|
+
// chemical formula for water?", scoring only 0.58 against the
|
|
324
|
+
// query's gist (a MIDDLE-of-string word swap perturbs the river-fold
|
|
325
|
+
// tree hash far more than a same-length TAIL swap like the "carbon"/
|
|
326
|
+
// "oxygen" neighbours that outrank it at 0.87+) — absent from the
|
|
327
|
+
// resonance list even at k=5000, present and byte-exact-verified the
|
|
328
|
+
// moment it's force-fed to the bridge directly. `exhaustive` is the
|
|
329
|
+
// natural, tuning-free ceiling (probe every cluster) for a call that
|
|
330
|
+
// is ALREADY refusal-path-only and must not miss a candidate hiding
|
|
331
|
+
// behind an unlucky structural distance.
|
|
332
|
+
const wide = k < hubBound(ctx)
|
|
333
|
+
? await ctx.store.resonate(queryGist, hubBound(ctx), true)
|
|
334
|
+
: whole;
|
|
335
|
+
const bridged = await substitutionBridge(
|
|
336
|
+
ctx,
|
|
337
|
+
query,
|
|
338
|
+
wide.map((h) => h.id),
|
|
339
|
+
);
|
|
340
|
+
if (bridged !== null) {
|
|
341
|
+
const g = await project(ctx, bridged.id, queryGist);
|
|
342
|
+
// A projection contained in a substituted candidate-side span is the
|
|
343
|
+
// substitution RESTATED as if it were knowledge — the exact failure
|
|
344
|
+
// observed live: "Darwin was born in England." bridged to the
|
|
345
|
+
// Einstein fact through " England." → " Germany." and would have
|
|
346
|
+
// voiced "Germany", an answer the substitution itself manufactured.
|
|
347
|
+
// The same principle as the restated-fragment guards above, extended
|
|
348
|
+
// to the bridge's own substitutions.
|
|
349
|
+
const cBytes = ctx.store.bytes(bridged.id);
|
|
350
|
+
const manufactured = g !== null &&
|
|
351
|
+
bridged.subs.some((s) =>
|
|
352
|
+
indexOf(cBytes.subarray(s.cs, s.ce), g!, 0) >= 0
|
|
353
|
+
);
|
|
354
|
+
if (
|
|
355
|
+
g !== null && g.length > 0 && !restates(g) && !manufactured &&
|
|
356
|
+
!(g.length < query.length && indexOf(query, g, 0) >= 0)
|
|
357
|
+
) {
|
|
358
|
+
return ground(
|
|
359
|
+
g,
|
|
360
|
+
`substitution bridge — a trained context accounts for the query ` +
|
|
361
|
+
`up to ${bridged.subs.length} corroborated substitution(s)`,
|
|
362
|
+
// Accounted NOTHING — the same epistemic humility as the echo
|
|
363
|
+
// tier below: a substitution-bridged grounding is a last resort
|
|
364
|
+
// that must lose to ANY mechanism that actually explained the
|
|
365
|
+
// query (observed: pricing the aligned spans here outweighed
|
|
366
|
+
// extraction's correct answer in the grounding decider), while
|
|
367
|
+
// still beating silence when everything else refused.
|
|
368
|
+
[],
|
|
369
|
+
CONCEPT * bridged.subs.length + STEP,
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
296
375
|
// The refusal/echo decision. The echo returns a stored form's bytes AS
|
|
297
376
|
// the answer — a near-identity claim about the query — and identity-grade
|
|
298
377
|
// decisions are never made on an estimated score ("approximate scores may
|
package/src/mind/mind.ts
CHANGED
|
@@ -266,9 +266,15 @@ export class Mind implements MindContext {
|
|
|
266
266
|
sites: ReadonlyArray<Site>;
|
|
267
267
|
leaves: ReadonlyArray<Leaf>;
|
|
268
268
|
splits: ReadonlySet<number>;
|
|
269
|
+
starts: ReadonlySet<number>;
|
|
269
270
|
} {
|
|
270
271
|
const r = recognise(this, bytes);
|
|
271
|
-
return {
|
|
272
|
+
return {
|
|
273
|
+
sites: r.sites,
|
|
274
|
+
leaves: r.leaves,
|
|
275
|
+
splits: r.splits,
|
|
276
|
+
starts: r.starts,
|
|
277
|
+
};
|
|
272
278
|
}
|
|
273
279
|
|
|
274
280
|
/** Disambiguate among multiple learnt continuations of the same context node.
|
package/src/mind/pipeline.ts
CHANGED
|
@@ -288,8 +288,43 @@ export async function think(
|
|
|
288
288
|
? new Set<number>()
|
|
289
289
|
: new Set(recognise(ctx, answer).sites.map((s) => s.payload));
|
|
290
290
|
const reasoned = await reason(ctx, query, answer, preConsumed, pre);
|
|
291
|
-
|
|
292
|
-
|
|
291
|
+
|
|
292
|
+
// Fuse only when the query has a genuine REMAINDER no mechanism's
|
|
293
|
+
// structural evidence touched at all. `decided.accounted` alone
|
|
294
|
+
// undercounts this: it is a COST-LADDER quantity (cover.ts prices its
|
|
295
|
+
// masked/computed spans at near-zero and deliberately leaves them out of
|
|
296
|
+
// `accounted` so PASS-bridged bytes are still charged), not a coverage
|
|
297
|
+
// one — a query fully explained by one computed span plus bridged
|
|
298
|
+
// connectors can report `accounted: []` while nothing is actually left
|
|
299
|
+
// unexplained. The genuine remainder is what NEITHER the winning
|
|
300
|
+
// candidate's accounted spans NOR any recognised extension's computed
|
|
301
|
+
// span (`pre.computed` — every mechanism's parse() output, ALU included)
|
|
302
|
+
// ever touched. A remainder under one river-fold quantum (W, the same
|
|
303
|
+
// floor cover.ts's restatedSpan and the honesty-density bar above both
|
|
304
|
+
// use) is bridging punctuation/whitespace, never a second topic —
|
|
305
|
+
// observed: a single space between two fully-computed arithmetic spans
|
|
306
|
+
// ("2+2 3+3") registered as "unaccounted" and pulled in an unrelated
|
|
307
|
+
// corpus fact, corrupting "4 6" into "4 63".
|
|
308
|
+
const explained: Array<[number, number]> = [
|
|
309
|
+
...decided.accounted,
|
|
310
|
+
...pre.computed.map((u): [number, number] => [u.i, u.j]),
|
|
311
|
+
];
|
|
312
|
+
const remainder = unaccounted(explained);
|
|
313
|
+
// Whether the winning candidate's entire recognised substance is
|
|
314
|
+
// COMPUTED — every accounted span exactly a pre.computed span, nothing
|
|
315
|
+
// from a genuinely recognised/climbed site. fuseAttention's lone-root
|
|
316
|
+
// shortcut assumes a single point of attention already IS primary's own
|
|
317
|
+
// source; that assumption is exactly backwards for a pure computation
|
|
318
|
+
// (an ALU result has no anchor of its own) — see fuseAttention's
|
|
319
|
+
// `unclimbed` parameter, gated there by Attention.breadth so a
|
|
320
|
+
// coincidental echo (which this flag alone cannot distinguish) is still
|
|
321
|
+
// rejected.
|
|
322
|
+
const unclimbed = decided.accounted.length > 0 &&
|
|
323
|
+
decided.accounted.every(([i, j]) =>
|
|
324
|
+
pre.computed.some((u) => u.i === i && u.j === j)
|
|
325
|
+
);
|
|
326
|
+
const fused = remainder >= ctx.space.maxGroup
|
|
327
|
+
? await fuseAttention(ctx, query, reasoned, pre, unclimbed)
|
|
293
328
|
: reasoned;
|
|
294
329
|
|
|
295
330
|
done(
|
package/src/mind/reasoning.ts
CHANGED
|
@@ -13,6 +13,20 @@ import { joinWithBridge, pivotInto } from "./resonance.js";
|
|
|
13
13
|
import type { Precomputed } from "./pipeline-mechanism.js";
|
|
14
14
|
import type { Rationale } from "./rationale.js";
|
|
15
15
|
|
|
16
|
+
/** Whether `bytes` is a proper byte-subspan of `query` — already present in
|
|
17
|
+
* the question, so voicing it back only restates part of what was asked,
|
|
18
|
+
* never answers it. The exact guard recallByResonance already applies to
|
|
19
|
+
* its OWN grounding candidates (tier 1's `restates`, tier 2's subspan
|
|
20
|
+
* check, tier 0b's argument-binding subspan check) — every mechanism that
|
|
21
|
+
* walks a LEARNT CONTINUATION EDGE past an already-vetted grounding
|
|
22
|
+
* (reason()'s own hops below, and CAST's `projectCounterfactual` seat
|
|
23
|
+
* substitution — see cast.ts) needs the same guard applied to what the
|
|
24
|
+
* walk turns up, since `follow()`/`chooseNext`/`pivotInto` know nothing of
|
|
25
|
+
* the query at all — only of what structurally continues what. */
|
|
26
|
+
export function restatesQuery(query: Uint8Array, bytes: Uint8Array): boolean {
|
|
27
|
+
return bytes.length < query.length && indexOf(query, bytes, 0) >= 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
16
30
|
/** Extend a grounded answer forward across facts (multi-hop reasoning).
|
|
17
31
|
* Pivots on the longest unconsumed learnt context each answer contains,
|
|
18
32
|
* then follows the pivot's continuation to the next fact. Repeats up
|
|
@@ -91,7 +105,8 @@ export async function reason(
|
|
|
91
105
|
const fwdId = fwd !== null ? resolve(ctx, fwd) : null;
|
|
92
106
|
if (
|
|
93
107
|
fwd !== null && !bytesEqual(fwd, cur) &&
|
|
94
|
-
(fwdId === null || !consumed.has(fwdId))
|
|
108
|
+
(fwdId === null || !consumed.has(fwdId)) &&
|
|
109
|
+
!restatesQuery(query, fwd)
|
|
95
110
|
) {
|
|
96
111
|
consumeAll(curId);
|
|
97
112
|
t ??= ctx.trace?.enter("reason", [
|
|
@@ -115,7 +130,7 @@ export async function reason(
|
|
|
115
130
|
|
|
116
131
|
const fc = await follow(ctx, pivot, qv);
|
|
117
132
|
consumeAll(pivot);
|
|
118
|
-
if (fc === null || bytesEqual(fc, cur)) break;
|
|
133
|
+
if (fc === null || bytesEqual(fc, cur) || restatesQuery(query, fc)) break;
|
|
119
134
|
t ??= ctx.trace?.enter("reason", [rItem(startedFrom, "grounded")]);
|
|
120
135
|
ctx.trace?.step(
|
|
121
136
|
"pivotStep",
|
|
@@ -141,6 +156,14 @@ export async function fuseAttention(
|
|
|
141
156
|
query: Uint8Array,
|
|
142
157
|
primary: Uint8Array,
|
|
143
158
|
pre: Precomputed,
|
|
159
|
+
/** True when `primary` never touched the consensus climb at all — e.g. a
|
|
160
|
+
* pure ALU computation, which has no anchor of its own. commitVotes
|
|
161
|
+
* ALWAYS admits the dominant root regardless of its vote (attention.ts:
|
|
162
|
+
* "roots.length === 0 || …") on the assumption a lone root already IS
|
|
163
|
+
* primary's own source; that assumption is exactly backwards when
|
|
164
|
+
* primary is unclimbed. Absent or false preserves the original
|
|
165
|
+
* behaviour exactly. */
|
|
166
|
+
unclimbed = false,
|
|
144
167
|
): Promise<Uint8Array> {
|
|
145
168
|
// When the answer is structurally drawn from the query itself
|
|
146
169
|
// (extraction), it already spans all the query's pieces — fusion
|
|
@@ -155,17 +178,86 @@ export async function fuseAttention(
|
|
|
155
178
|
// query, same k, same DF mode) — read them from Precomputed instead of
|
|
156
179
|
// re-climbing, so even a traced response pays for the climb once.
|
|
157
180
|
const forest = (await pre.attention()).roots;
|
|
158
|
-
|
|
181
|
+
// A LONE root is ordinarily primary's own source — nothing to fuse. But
|
|
182
|
+
// when primary is unclimbed, the lone root was never checked against
|
|
183
|
+
// anything: it is admitted by commitVotes unconditionally, so it may be
|
|
184
|
+
// genuine consensus (Attention.breadth dominates — most of the query's
|
|
185
|
+
// OWN regions corroborate it) or a coincidental echo (breadth does not
|
|
186
|
+
// dominate — see test/35-attention-confidence). breadth is the SCALE-
|
|
187
|
+
// INVARIANT read of exactly this question: the raw IDF vote cannot serve
|
|
188
|
+
// here, since it is an absolute ln(N)-scaled quantity (a genuine root on
|
|
189
|
+
// a large store can score BELOW its own floor while a coincidental echo
|
|
190
|
+
// on a small one scores comfortably above its own, smaller, floor).
|
|
191
|
+
const lonePromotes = unclimbed && forest.length === 1 &&
|
|
192
|
+
forest[0].breadth > 0.5;
|
|
193
|
+
if (forest.length === 0 || (forest.length <= 1 && !lonePromotes)) {
|
|
194
|
+
return primary;
|
|
195
|
+
}
|
|
159
196
|
|
|
160
197
|
const pieces: Array<{ start: number; bytes: Uint8Array }> = [
|
|
161
198
|
{ start: forest[0].start, bytes: primary },
|
|
162
199
|
];
|
|
163
200
|
const qv = pre.guide; // once, not per root
|
|
201
|
+
const rest = lonePromotes ? forest : forest.slice(1);
|
|
164
202
|
const t = ctx.trace?.enter("fuseAttention", [
|
|
165
203
|
rItem(primary, "primary"),
|
|
166
|
-
...
|
|
204
|
+
...rest.map((r) => rNode(ctx, r.anchor, "point", r.vote)),
|
|
167
205
|
]);
|
|
168
|
-
for (const root of
|
|
206
|
+
for (const root of rest) {
|
|
207
|
+
// DISPERSION: this root's contributing regions are confined to a
|
|
208
|
+
// single cluster (see Attention.clusters) — one local neighbourhood of
|
|
209
|
+
// the query, not several separate places. Raw region count already
|
|
210
|
+
// failed to discriminate a coincidental match from a genuine further
|
|
211
|
+
// topic (test/24 gap 3.1 vs test/35's echo); dispersion is a different
|
|
212
|
+
// question — not how MUCH evidence, but how many separate PLACES in the
|
|
213
|
+
// query corroborate it — and a coincidental match is structurally
|
|
214
|
+
// confined to one cluster no matter how strongly it resonates.
|
|
215
|
+
//
|
|
216
|
+
// EXCEPTION: crossRegionVotes' own joint conclusions (a query naming
|
|
217
|
+
// two attributes that were only ever learnt TOGETHER — test/34's own
|
|
218
|
+
// binding corpus) are inherently ONE fused context and are pooled from
|
|
219
|
+
// a single synthetic region, so they always read as one cluster even
|
|
220
|
+
// though they already, by construction, account for both original
|
|
221
|
+
// mentions. `breadth` (dominates — the same > half-the-query bar used
|
|
222
|
+
// everywhere else) still correctly recognises these: a genuine joint
|
|
223
|
+
// binding explains the MAJORITY of the query's regions on its own,
|
|
224
|
+
// which a coincidental echo never does (verified: test/35's echo tops
|
|
225
|
+
// out at 0.40). So a root is trusted when EITHER measure alone
|
|
226
|
+
// indicates real signal — excluded only when BOTH are weak. Cheap and
|
|
227
|
+
// synchronous — checked before the async already-answered walk below.
|
|
228
|
+
if (root.clusters < 2 && root.breadth <= 0.5) {
|
|
229
|
+
ctx.trace?.step(
|
|
230
|
+
"singleCluster",
|
|
231
|
+
[rNode(ctx, root.anchor, "point", root.vote)],
|
|
232
|
+
[],
|
|
233
|
+
"this point's evidence is confined to one local neighbourhood of the query — not trusted as an independent topic",
|
|
234
|
+
);
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
// ALREADY ANSWERED: this root's own learnt continuation — the same
|
|
238
|
+
// content-addressed walk reason()'s echo guard already trusts
|
|
239
|
+
// (`ctx.store.prevCount(qId) > 0`), here applied per-candidate instead
|
|
240
|
+
// of to the whole query — is VERBATIM present later in the query. A
|
|
241
|
+
// query that embeds both an exchange's ask and its own already-given
|
|
242
|
+
// reply (a conversation's turn plus its own prior answer, concatenated
|
|
243
|
+
// raw by addTurn — or any caller pasting a transcript into one
|
|
244
|
+
// respond() call; the check is Mind-bookkeeping-free, so it treats both
|
|
245
|
+
// identically) has already spoken this root's answer — fusing it in
|
|
246
|
+
// again would only restate it. Deliberately NOT a magnitude measure:
|
|
247
|
+
// it fires on exact content-addressed recurrence, not on how strongly
|
|
248
|
+
// the root resonates.
|
|
249
|
+
const cont = await follow(ctx, root.anchor, qv);
|
|
250
|
+
if (
|
|
251
|
+
cont !== null && cont.length > 0 && indexOf(query, cont, root.end) >= 0
|
|
252
|
+
) {
|
|
253
|
+
ctx.trace?.step(
|
|
254
|
+
"alreadyAnswered",
|
|
255
|
+
[rNode(ctx, root.anchor, "point", root.vote)],
|
|
256
|
+
[rItem(cont, "continuation")],
|
|
257
|
+
"this point's own learnt continuation already appears later in the query — already answered, not fused",
|
|
258
|
+
);
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
169
261
|
const g = await project(ctx, root.anchor, qv);
|
|
170
262
|
if (g === null || g.length === 0) continue;
|
|
171
263
|
if (pieces.some((p) => indexOf(p.bytes, g, 0) >= 0)) continue;
|