@hviana/sema 0.5.3 → 0.5.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/AGENTS.md +6 -6
- package/HOW_IT_WORKS.md +16 -0
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/mind/match.ts +81 -0
- package/src/mind/mechanisms/recall.ts +29 -3
- package/test/76-reference-binding.test.mjs +72 -0
- package/dist/example/demo.d.ts +0 -1
- package/dist/example/demo.js +0 -39
- package/dist/example/train_base.d.ts +0 -93
- package/dist/example/train_base.js +0 -2269
- package/dist/src/alphabet.d.ts +0 -7
- package/dist/src/alphabet.js +0 -33
- package/dist/src/alu/src/alu.d.ts +0 -185
- package/dist/src/alu/src/alu.js +0 -440
- package/dist/src/alu/src/expr.d.ts +0 -61
- package/dist/src/alu/src/expr.js +0 -318
- package/dist/src/alu/src/index.d.ts +0 -11
- package/dist/src/alu/src/index.js +0 -19
- package/dist/src/alu/src/kernel-arith.d.ts +0 -16
- package/dist/src/alu/src/kernel-arith.js +0 -264
- package/dist/src/alu/src/kernel-bits.d.ts +0 -19
- package/dist/src/alu/src/kernel-bits.js +0 -152
- package/dist/src/alu/src/kernel-logic.d.ts +0 -4
- package/dist/src/alu/src/kernel-logic.js +0 -60
- package/dist/src/alu/src/kernel-nd.d.ts +0 -3
- package/dist/src/alu/src/kernel-nd.js +0 -208
- package/dist/src/alu/src/kernel-numeric.d.ts +0 -54
- package/dist/src/alu/src/kernel-numeric.js +0 -366
- package/dist/src/alu/src/operation.d.ts +0 -168
- package/dist/src/alu/src/operation.js +0 -189
- package/dist/src/alu/src/parser.d.ts +0 -221
- package/dist/src/alu/src/parser.js +0 -577
- package/dist/src/alu/src/resonance.d.ts +0 -55
- package/dist/src/alu/src/resonance.js +0 -126
- package/dist/src/alu/src/text.d.ts +0 -31
- package/dist/src/alu/src/text.js +0 -73
- package/dist/src/alu/src/value.d.ts +0 -109
- package/dist/src/alu/src/value.js +0 -300
- package/dist/src/alu/test/alu.test.d.ts +0 -1
- package/dist/src/alu/test/alu.test.js +0 -764
- package/dist/src/bytes.d.ts +0 -14
- package/dist/src/bytes.js +0 -59
- package/dist/src/canon.d.ts +0 -45
- package/dist/src/canon.js +0 -85
- package/dist/src/config.d.ts +0 -111
- package/dist/src/config.js +0 -91
- package/dist/src/derive/src/deduction.d.ts +0 -136
- package/dist/src/derive/src/deduction.js +0 -159
- package/dist/src/derive/src/index.d.ts +0 -8
- package/dist/src/derive/src/index.js +0 -11
- package/dist/src/derive/src/priority-queue.d.ts +0 -20
- package/dist/src/derive/src/priority-queue.js +0 -73
- package/dist/src/derive/src/rewrite.d.ts +0 -56
- package/dist/src/derive/src/rewrite.js +0 -100
- package/dist/src/derive/src/trie.d.ts +0 -90
- package/dist/src/derive/src/trie.js +0 -217
- package/dist/src/derive/test/derive.test.d.ts +0 -1
- package/dist/src/derive/test/derive.test.js +0 -122
- package/dist/src/extension.d.ts +0 -37
- package/dist/src/extension.js +0 -7
- package/dist/src/geometry.d.ts +0 -233
- package/dist/src/geometry.js +0 -1089
- package/dist/src/index.d.ts +0 -17
- package/dist/src/index.js +0 -19
- package/dist/src/ingest-cache.d.ts +0 -41
- package/dist/src/ingest-cache.js +0 -165
- package/dist/src/meter.d.ts +0 -176
- package/dist/src/meter.js +0 -274
- package/dist/src/mind/articulation.d.ts +0 -6
- package/dist/src/mind/articulation.js +0 -99
- package/dist/src/mind/attention.d.ts +0 -430
- package/dist/src/mind/attention.js +0 -2418
- package/dist/src/mind/bridge.d.ts +0 -37
- package/dist/src/mind/bridge.js +0 -907
- package/dist/src/mind/canonical.d.ts +0 -34
- package/dist/src/mind/canonical.js +0 -93
- package/dist/src/mind/graph-search.d.ts +0 -294
- package/dist/src/mind/graph-search.js +0 -996
- package/dist/src/mind/index.d.ts +0 -9
- package/dist/src/mind/index.js +0 -5
- package/dist/src/mind/junction.d.ts +0 -137
- package/dist/src/mind/junction.js +0 -358
- package/dist/src/mind/learning.d.ts +0 -75
- package/dist/src/mind/learning.js +0 -265
- package/dist/src/mind/match.d.ts +0 -351
- package/dist/src/mind/match.js +0 -959
- package/dist/src/mind/mechanisms/alu.d.ts +0 -4
- package/dist/src/mind/mechanisms/alu.js +0 -36
- package/dist/src/mind/mechanisms/cast.d.ts +0 -89
- package/dist/src/mind/mechanisms/cast.js +0 -1186
- package/dist/src/mind/mechanisms/confluence.d.ts +0 -24
- package/dist/src/mind/mechanisms/confluence.js +0 -271
- package/dist/src/mind/mechanisms/cover.d.ts +0 -6
- package/dist/src/mind/mechanisms/cover.js +0 -232
- package/dist/src/mind/mechanisms/extraction.d.ts +0 -33
- package/dist/src/mind/mechanisms/extraction.js +0 -327
- package/dist/src/mind/mechanisms/prefix-completion.d.ts +0 -22
- package/dist/src/mind/mechanisms/prefix-completion.js +0 -243
- package/dist/src/mind/mechanisms/recall.d.ts +0 -16
- package/dist/src/mind/mechanisms/recall.js +0 -460
- package/dist/src/mind/mechanisms/reference.d.ts +0 -6
- package/dist/src/mind/mechanisms/reference.js +0 -296
- package/dist/src/mind/mind.d.ts +0 -386
- package/dist/src/mind/mind.js +0 -754
- package/dist/src/mind/pipeline-mechanism.d.ts +0 -238
- package/dist/src/mind/pipeline-mechanism.js +0 -622
- package/dist/src/mind/pipeline.d.ts +0 -49
- package/dist/src/mind/pipeline.js +0 -343
- package/dist/src/mind/primitives.d.ts +0 -85
- package/dist/src/mind/primitives.js +0 -333
- package/dist/src/mind/rationale.d.ts +0 -139
- package/dist/src/mind/rationale.js +0 -163
- package/dist/src/mind/reasoning.d.ts +0 -42
- package/dist/src/mind/reasoning.js +0 -305
- package/dist/src/mind/recognition.d.ts +0 -20
- package/dist/src/mind/recognition.js +0 -696
- package/dist/src/mind/resonance.d.ts +0 -35
- package/dist/src/mind/resonance.js +0 -365
- package/dist/src/mind/trace.d.ts +0 -15
- package/dist/src/mind/trace.js +0 -73
- package/dist/src/mind/traverse.d.ts +0 -196
- package/dist/src/mind/traverse.js +0 -806
- package/dist/src/mind/types.d.ts +0 -418
- package/dist/src/mind/types.js +0 -179
- package/dist/src/rabitq-ivf/src/database.d.ts +0 -113
- package/dist/src/rabitq-ivf/src/database.js +0 -201
- package/dist/src/rabitq-ivf/src/index.d.ts +0 -7
- package/dist/src/rabitq-ivf/src/index.js +0 -4
- package/dist/src/rabitq-ivf/src/ivf.d.ts +0 -200
- package/dist/src/rabitq-ivf/src/ivf.js +0 -1165
- package/dist/src/rabitq-ivf/src/prng.d.ts +0 -19
- package/dist/src/rabitq-ivf/src/prng.js +0 -36
- package/dist/src/rabitq-ivf/src/rabitq.d.ts +0 -95
- package/dist/src/rabitq-ivf/src/rabitq.js +0 -313
- package/dist/src/sema.d.ts +0 -41
- package/dist/src/sema.js +0 -77
- package/dist/src/store-sqlite.d.ts +0 -184
- package/dist/src/store-sqlite.js +0 -942
- package/dist/src/store.d.ts +0 -762
- package/dist/src/store.js +0 -1823
- package/dist/src/vec.d.ts +0 -31
- package/dist/src/vec.js +0 -109
package/AGENTS.md
CHANGED
|
@@ -179,12 +179,12 @@ matchers (`locate`, `alignRuns`, `alignGraded`, `alignAround`/`frameSlots`,
|
|
|
179
179
|
`bestHaloMate`, `haloSiblings`, `analogyStrength` and its structural tier
|
|
180
180
|
`sharedFrameStrength`, `spanHalo`, `spanSynonymStrength`), projections
|
|
181
181
|
(`follow`, `reverseContext`, `project`, `conceptHop`), the span-shape family
|
|
182
|
-
(`skillExemplar`, `isSpanShaped`, `containsSpan`), and the
|
|
183
|
-
|
|
184
|
-
`carriesFillers`). `mind/traverse.ts` owns the graph
|
|
185
|
-
`reachOf`, `chooseNext`/`chooseAmong`, `guidedFirst`,
|
|
186
|
-
`allWindowsAreScaffolding`, `formsOpenedBy`) and the corpus
|
|
187
|
-
`hubBound`, `hubCap`, `atomReach`).
|
|
182
|
+
(`skillExemplar`, `isSpanShaped`, `containsSpan`), and the STRUCTURAL gates that
|
|
183
|
+
are byte predicates rather than derived thresholds (`isSpanShaped`,
|
|
184
|
+
`carriesFillers`, `voicesDisplacedFiller`). `mind/traverse.ts` owns the graph
|
|
185
|
+
readings (`edgeAncestors`, `reachOf`, `chooseNext`/`chooseAmong`, `guidedFirst`,
|
|
186
|
+
`leadsSomewhere`, `allWindowsAreScaffolding`, `formsOpenedBy`) and the corpus
|
|
187
|
+
scale (`corpusN`, `hubBound`, `hubCap`, `atomReach`).
|
|
188
188
|
|
|
189
189
|
_Follow it:_ before writing a new generalising mechanism, express it as a
|
|
190
190
|
(matcher, direction, gate) triple. If those already exist, the mechanism is a
|
package/HOW_IT_WORKS.md
CHANGED
|
@@ -4314,6 +4314,22 @@ The tier accounts for exactly the query span whose evidence carried the winning
|
|
|
4314
4314
|
point of attention (§17.6's `start`–`end`), not the whole query — a consensus
|
|
4315
4315
|
vote for "ice" among scaffolding does not explain the word "steel". One CONCEPT.
|
|
4316
4316
|
|
|
4317
|
+
One further refusal is specific to grounding _through an anchor_. This tier
|
|
4318
|
+
voices the anchor's continuation, so when the query is the same structure as the
|
|
4319
|
+
anchor with one position filled differently, that continuation speaks the
|
|
4320
|
+
**anchor's** occupant of the position — fluent, specific, and about the wrong
|
|
4321
|
+
thing (`How do I compile main.c?` answered `Run gcc hello.c`). A displaced slot
|
|
4322
|
+
alone does not license the refusal, because it cannot separate a different
|
|
4323
|
+
_thing_ from a different _word_: `symbol` ← `formula` has exactly the shape
|
|
4324
|
+
`main` ← `hello` does, and refusing it silences a correct answer phrased in the
|
|
4325
|
+
corpus's own words. The discriminator is whether the projection still shares
|
|
4326
|
+
perceivable content with the query outside that position — one river window is
|
|
4327
|
+
enough. `The chemical formula for water is H2O.` shares `for water`;
|
|
4328
|
+
`Run gcc hello.c` shares only `.c`, two bytes, below the scale at which overlap
|
|
4329
|
+
stops being chance. The gate refuses only; putting the asker's own occupant in
|
|
4330
|
+
its place is a stronger claim needing the corpus's carriage evidence, which is
|
|
4331
|
+
§20.5's business.
|
|
4332
|
+
|
|
4317
4333
|
### 21.4 Tier 3 — the nearest grounded hit, at the query-relative fraction
|
|
4318
4334
|
|
|
4319
4335
|
Walk the hits nearest-first and ground the first whose grounding explains enough
|
package/jsr.json
CHANGED
package/package.json
CHANGED
package/src/mind/match.ts
CHANGED
|
@@ -612,6 +612,87 @@ export function frameSlots(
|
|
|
612
612
|
return { id, slots, matched: spans, covered };
|
|
613
613
|
}
|
|
614
614
|
|
|
615
|
+
/** THE DISPLACED-FILLER GATE: does `projection` speak the ANCHOR's occupant of
|
|
616
|
+
* a position the query fills differently?
|
|
617
|
+
*
|
|
618
|
+
* A mechanism grounding through an anchor voices that anchor's continuation.
|
|
619
|
+
* When the query is the same structure as the anchor with one position filled
|
|
620
|
+
* differently — a different filename, a different word — the anchor's
|
|
621
|
+
* continuation is ABOUT THE ANCHOR'S occupant, and voicing it answers a
|
|
622
|
+
* question the asker did not ask. It is worse than silence, because it is
|
|
623
|
+
* fluent and specific and wrong:
|
|
624
|
+
*
|
|
625
|
+
* trained `How do I compile hello.c?` -> `Run gcc hello.c`
|
|
626
|
+
* asked `How do I compile main.c?`
|
|
627
|
+
* voiced `Run gcc hello.c` <- the corpus's file, not the asker's
|
|
628
|
+
*
|
|
629
|
+
* The same shape on the trained 15.7M-node store: `How do you say 'flurbish'
|
|
630
|
+
* in French?` answers "the way to say hello is \"Bonjour\"".
|
|
631
|
+
*
|
|
632
|
+
* THIS IS NOT THE RESTATED-FRAGMENT GUARD. That one asks whether the
|
|
633
|
+
* projection is a piece of the QUERY; this asks whether it is a piece of the
|
|
634
|
+
* ANCHOR that the query displaced. Neither implies the other, and the
|
|
635
|
+
* observed failures pass the restatement guard cleanly.
|
|
636
|
+
*
|
|
637
|
+
* Three conditions, all byte-exact and all necessary:
|
|
638
|
+
*
|
|
639
|
+
* 1. the query and the anchor must be ONE STRUCTURE — what they share has to
|
|
640
|
+
* dominate the query, or the query is not a variant of the anchor at all
|
|
641
|
+
* and the anchor's occupant of anything is beside the point;
|
|
642
|
+
* 2. both sides of the position must reach one river window — below it byte
|
|
643
|
+
* overlap is chance, not evidence (the floor identityBar and the bridge's
|
|
644
|
+
* attestedQ both draw);
|
|
645
|
+
* 3. the projection must voice the anchor's filler and NOT the query's
|
|
646
|
+
* referent. Voicing both is a projection that carried the asker's own
|
|
647
|
+
* occupant through, which is exactly what a licensed reference does and
|
|
648
|
+
* must stay allowed;
|
|
649
|
+
* 4. and the projection must share NO perceivable content with the query
|
|
650
|
+
* outside that position — no run of one river window.
|
|
651
|
+
*
|
|
652
|
+
* GATE 4 IS WHAT SEPARATES A DIFFERENT THING FROM A DIFFERENT WORD, and
|
|
653
|
+
* without it this refuses correct answers. A displaced slot alone cannot
|
|
654
|
+
* tell them apart: `symbol` <- `formula` and `main` <- `hello` are the same
|
|
655
|
+
* shape to the matcher — one substitution slot, frame dominating. Measured
|
|
656
|
+
* on the trained store, gates 1-3 alone silenced
|
|
657
|
+
*
|
|
658
|
+
* Q `What is the chemical symbol for water?`
|
|
659
|
+
* A `The chemical formula for water is H2O.`
|
|
660
|
+
*
|
|
661
|
+
* which is right, and merely phrased in the corpus's own words. The answer
|
|
662
|
+
* shares `the chemical ` and ` for water` with the question, so it is plainly
|
|
663
|
+
* about what was asked. `Run gcc hello.c` against `How do I compile main.c?`
|
|
664
|
+
* shares nothing but `.c` — two bytes, below the window where overlap stops
|
|
665
|
+
* being chance — so it is not about what was asked at all. No new constant:
|
|
666
|
+
* W is the same floor identityBar, attestedQ and the site test already draw. */
|
|
667
|
+
export function voicesDisplacedFiller(
|
|
668
|
+
ctx: MindContext,
|
|
669
|
+
query: Uint8Array,
|
|
670
|
+
anchor: Uint8Array,
|
|
671
|
+
projection: Uint8Array,
|
|
672
|
+
): boolean {
|
|
673
|
+
const W = ctx.space.maxGroup;
|
|
674
|
+
const inst = frameSlots(ctx, query, anchor, 0);
|
|
675
|
+
if (inst === null) return false;
|
|
676
|
+
if (!dominates(inst.covered, query.length)) return false;
|
|
677
|
+
for (const slot of inst.slots) {
|
|
678
|
+
if (slot.kind !== "substitution") continue;
|
|
679
|
+
if (slot.qe - slot.qs < W || slot.filler.length < W) continue;
|
|
680
|
+
if (indexOf(projection, slot.filler, 0) < 0) continue;
|
|
681
|
+
const referent = query.subarray(slot.qs, slot.qe);
|
|
682
|
+
if (indexOf(projection, referent, 0) >= 0) continue;
|
|
683
|
+
// Gate 4: does the projection still speak about the query's FRAME? A run
|
|
684
|
+
// of one window anywhere outside the displaced position is enough — the
|
|
685
|
+
// answer is then about the thing that was asked, in the corpus's own
|
|
686
|
+
// wording. Sharing nothing means it is about something else.
|
|
687
|
+
const shared = alignRuns(ctx, query, projection).some((r) =>
|
|
688
|
+
r.qe - r.qs >= W && (r.qe <= slot.qs || r.qs >= slot.qe)
|
|
689
|
+
);
|
|
690
|
+
if (shared) continue;
|
|
691
|
+
return true;
|
|
692
|
+
}
|
|
693
|
+
return false;
|
|
694
|
+
}
|
|
695
|
+
|
|
615
696
|
/** Whether every member is byte-distinct from the others. */
|
|
616
697
|
export function distinct(items: readonly Uint8Array[]): boolean {
|
|
617
698
|
for (let i = 0; i < items.length; i++) {
|
|
@@ -17,7 +17,12 @@ import type { MindContext } from "../types.js";
|
|
|
17
17
|
import { gistOf, read, resolve } from "../primitives.js";
|
|
18
18
|
import { bytesEqual, indexOf } from "../../bytes.js";
|
|
19
19
|
import { allWindowsAreScaffolding, corpusN, hubBound } from "../traverse.js";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
follow,
|
|
22
|
+
project,
|
|
23
|
+
reverseContext,
|
|
24
|
+
voicesDisplacedFiller,
|
|
25
|
+
} from "../match.js";
|
|
21
26
|
import { CONCEPT, STEP } from "../graph-search.js";
|
|
22
27
|
import { unexplainedLabel } from "../rationale.js";
|
|
23
28
|
import type { PipelineMechanism, Precomputed } from "../pipeline-mechanism.js";
|
|
@@ -349,7 +354,28 @@ export async function recallByResonance(
|
|
|
349
354
|
(dominates(forest[0].breadth, 1) && forest[0].peak > Math.LN2))
|
|
350
355
|
) {
|
|
351
356
|
const g = await project(ctx, forest[0].anchor, queryGist);
|
|
352
|
-
//
|
|
357
|
+
// THE ANCHOR'S OCCUPANT IS NOT THE ASKER'S. This tier grounds an anchor
|
|
358
|
+
// the climb elected, and voices that anchor's continuation. When the
|
|
359
|
+
// query is the same structure with one position filled differently, the
|
|
360
|
+
// continuation speaks the ANCHOR's occupant of that position — a fluent,
|
|
361
|
+
// specific, wrong answer (see voicesDisplacedFiller). Refusing here is
|
|
362
|
+
// silence, not a redirection: putting the asker's referent in its place
|
|
363
|
+
// is a stronger claim that needs the corpus's own carriage evidence, and
|
|
364
|
+
// that is `reference`'s job.
|
|
365
|
+
const anchorBytes = read(
|
|
366
|
+
ctx,
|
|
367
|
+
forest[0].anchor,
|
|
368
|
+
query.length * ctx.space.maxGroup + 1,
|
|
369
|
+
);
|
|
370
|
+
if (g !== null && voicesDisplacedFiller(ctx, query, anchorBytes, g)) {
|
|
371
|
+
ctx.trace?.step(
|
|
372
|
+
"displacedFiller",
|
|
373
|
+
[rItem(query, "query"), rNode(ctx, forest[0].anchor, "anchor")],
|
|
374
|
+
[rItem(g, "withheld")],
|
|
375
|
+
"refused — the anchor's continuation speaks the anchor's own " +
|
|
376
|
+
"occupant of a position this query fills differently",
|
|
377
|
+
);
|
|
378
|
+
} // The anchor cleared the consensus floor, but the floor prices the
|
|
353
379
|
// ANCHOR's evidence, not the projection's: a junk attractor can clear
|
|
354
380
|
// it and project a PIECE OF THE QUERY back at it (the observed
|
|
355
381
|
// "buenos días in English" → "English" fragment). A projection that
|
|
@@ -357,7 +383,7 @@ export async function recallByResonance(
|
|
|
357
383
|
// — never an answer (the same principle as `restates` above, extended
|
|
358
384
|
// to fragments). Genuine anchor groundings — longer than the query,
|
|
359
385
|
// or disjoint from it — pass untouched.
|
|
360
|
-
if (g && !(g.length < query.length && indexOf(query, g, 0) >= 0)) {
|
|
386
|
+
else if (g && !(g.length < query.length && indexOf(query, g, 0) >= 0)) {
|
|
361
387
|
return ground(
|
|
362
388
|
g,
|
|
363
389
|
"scaffolding-dominated query — ground the consensus-climb anchor",
|
|
@@ -403,6 +403,78 @@ test("a referent may not be lifted out of the engine's OWN reply", async () => {
|
|
|
403
403
|
await m.store.close();
|
|
404
404
|
});
|
|
405
405
|
|
|
406
|
+
// ── THE DISPLACED-FILLER GATE ──────────────────────────────────────────────
|
|
407
|
+
//
|
|
408
|
+
// Reference needs two instances before it will voice a binding: one instance
|
|
409
|
+
// agrees with nothing, so nothing distinguishes "Run gcc hello.c" (a carriage
|
|
410
|
+
// that follows the filename) from "hello.c needs -lm" (a fact about that one
|
|
411
|
+
// file). Abstaining there is right.
|
|
412
|
+
//
|
|
413
|
+
// What is NOT right is what used to happen next. Recall's
|
|
414
|
+
// scaffolding-dominated tier grounds the anchor the climb elected and voices
|
|
415
|
+
// that anchor's continuation — which speaks the ANCHOR's occupant of the very
|
|
416
|
+
// position the query fills differently. Measured on a one-example corpus,
|
|
417
|
+
// `How do I compile main.c?` answered `Run gcc hello.c`; on the trained
|
|
418
|
+
// 15.7M-node store, `How do you say 'flurbish' in French?` answers "the way to
|
|
419
|
+
// say hello is \"Bonjour\"". Fluent, specific, and about the wrong thing.
|
|
420
|
+
//
|
|
421
|
+
// The gate REFUSES ONLY. It never puts the asker's referent in the corpus's
|
|
422
|
+
// place — that is a stronger claim needing the corpus's own carriage evidence,
|
|
423
|
+
// which is what the licence above supplies. Where the corpus cannot license a
|
|
424
|
+
// binding, silence is the honest degradation.
|
|
425
|
+
|
|
426
|
+
test("a projection may not voice the ANCHOR's occupant of a displaced slot", async () => {
|
|
427
|
+
const m = await mindWith([
|
|
428
|
+
["How do I compile hello.c?", "Run gcc hello.c"],
|
|
429
|
+
["What is the capital of France?", "Paris"],
|
|
430
|
+
]);
|
|
431
|
+
const out = clean((await m.respond("How do I compile main.c?")).bytes).trim();
|
|
432
|
+
assert.ok(
|
|
433
|
+
!out.includes("hello"),
|
|
434
|
+
`voiced the corpus's filename for the asker's file: ${JSON.stringify(out)}`,
|
|
435
|
+
);
|
|
436
|
+
assert.equal(out, "", "the honest degradation here is silence");
|
|
437
|
+
await m.store.close();
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
test("the gate is not the restated-fragment guard", async () => {
|
|
441
|
+
// The refused projection is not a piece of the QUERY — it passes the
|
|
442
|
+
// restatement guard cleanly — it is a piece of the ANCHOR the query
|
|
443
|
+
// displaced. Neither guard implies the other.
|
|
444
|
+
const m = await mindWith([
|
|
445
|
+
["How do I compile hello.c?", "Run gcc hello.c"],
|
|
446
|
+
["What is the capital of France?", "Paris"],
|
|
447
|
+
]);
|
|
448
|
+
const q = "How do I compile main.c?";
|
|
449
|
+
assert.ok(
|
|
450
|
+
!q.includes("Run gcc hello.c"),
|
|
451
|
+
"fixture broken: the projection must NOT be a query fragment",
|
|
452
|
+
);
|
|
453
|
+
assert.equal(clean((await m.respond(q)).bytes).trim(), "");
|
|
454
|
+
await m.store.close();
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
test("a projection that carries the ASKER's own occupant is untouched", async () => {
|
|
458
|
+
// Voicing the anchor's filler AND the query's referent is what a licensed
|
|
459
|
+
// reference does; the gate must not touch it.
|
|
460
|
+
const m = await mindWith(CARRIED);
|
|
461
|
+
const r = await m.respond("How do I compile main.c?");
|
|
462
|
+
assert.equal(clean(r.bytes).trim(), "Run gcc main.c");
|
|
463
|
+
assert.equal(r.provenance, "reference");
|
|
464
|
+
await m.store.close();
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
test("a sub-quantum difference does not trip the gate", async () => {
|
|
468
|
+
// Below one river window byte overlap is chance, not a displaced slot.
|
|
469
|
+
const m = await mindWith([
|
|
470
|
+
["What is the capital of France?", "Paris"],
|
|
471
|
+
["Who wrote Hamlet?", "Shakespeare"],
|
|
472
|
+
]);
|
|
473
|
+
const out = clean((await m.respond("What is the capital of Franc?")).bytes);
|
|
474
|
+
assert.match(out, /Paris/, `a near-identical query lost its answer: ${out}`);
|
|
475
|
+
await m.store.close();
|
|
476
|
+
});
|
|
477
|
+
|
|
406
478
|
// ── NO HIJACK ──────────────────────────────────────────────────────────────
|
|
407
479
|
|
|
408
480
|
test("a trained query is answered by its own edge, not by reference", async () => {
|
package/dist/example/demo.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/example/demo.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
// demo.ts — one short session that drives the WHOLE pipeline from one memory.
|
|
2
|
-
//
|
|
3
|
-
// We give Sema a handful of plain notes, then ask things that no single note
|
|
4
|
-
// answers. The headline query is the third one: from three worked examples Sema
|
|
5
|
-
// learns the shape of "X was painted by Y", lifts the painter out of a sentence
|
|
6
|
-
// it has NEVER seen, and then — in the same pass — reasons forward to a separate
|
|
7
|
-
// fact about that painter. The reply contains no word from the question. That is
|
|
8
|
-
// retrieval, generalization, and reasoning composing as a single act, with every
|
|
9
|
-
// step traceable back to the notes behind it.
|
|
10
|
-
import { Mind } from "../src/index.js";
|
|
11
|
-
import { SQliteStore } from "../src/store-sqlite.js";
|
|
12
|
-
async function main() {
|
|
13
|
-
const mind = new Mind({ store: new SQliteStore({ path: ":memory:" }) });
|
|
14
|
-
const ask = async (q) => (await mind.respondText(q)).trim();
|
|
15
|
-
// ── Jot down what we know. Each line is just (context → what follows). ──
|
|
16
|
-
await mind.ingest([
|
|
17
|
-
// One relation, shown three times — a pattern taught purely by example:
|
|
18
|
-
["The Mona Lisa was painted by Leonardo da Vinci.", "Leonardo da Vinci"],
|
|
19
|
-
["The Starry Night was painted by Vincent van Gogh.", "Vincent van Gogh"],
|
|
20
|
-
[
|
|
21
|
-
"The Night Watch was painted by Rembrandt van Rijn.",
|
|
22
|
-
"Rembrandt van Rijn",
|
|
23
|
-
],
|
|
24
|
-
// One stray fact, keyed on a name none of the examples mention:
|
|
25
|
-
["Pablo Picasso", "Pablo Picasso co-founded the Cubist movement"],
|
|
26
|
-
]);
|
|
27
|
-
// 1) GENERALIZE — apply the learned pattern to an unseen sentence and read out
|
|
28
|
-
// the painter. "Pablo Picasso" was never given as an answer; Sema locates it
|
|
29
|
-
// by analogy to the three examples.
|
|
30
|
-
console.log(await ask("The Weeping Woman was painted by Pablo Picasso."));
|
|
31
|
-
// → "Pablo Picasso co-founded the Cubist movement"
|
|
32
|
-
// …and, having found the painter, it KEEPS GOING: the name bridges into the
|
|
33
|
-
// one fact it holds about him. The answer appears in no word of the question.
|
|
34
|
-
// 2) COMPUTE — exact arithmetic, grounded right where the notes go silent.
|
|
35
|
-
console.log(await ask("a museum charges 12*4 for a family ticket"));
|
|
36
|
-
// → "48"
|
|
37
|
-
await mind.store.close();
|
|
38
|
-
}
|
|
39
|
-
main();
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
export interface Episode {
|
|
2
|
-
context: string;
|
|
3
|
-
continuation: string;
|
|
4
|
-
}
|
|
5
|
-
export type TrainingItem = string | Episode;
|
|
6
|
-
/** Dedup + trim a concept's items: drop empty/degenerate pairs and exact
|
|
7
|
-
* repeats so a concept never deposits the same form twice. */
|
|
8
|
-
export declare function refineItems(items: TrainingItem[]): TrainingItem[];
|
|
9
|
-
/** One normalized SmolSent row. */
|
|
10
|
-
export interface SmolSentRow {
|
|
11
|
-
src: string;
|
|
12
|
-
trg: string;
|
|
13
|
-
sl: string;
|
|
14
|
-
tl: string;
|
|
15
|
-
}
|
|
16
|
-
/** Normalize a raw datasets-server row into a SmolSentRow, or null when it lacks
|
|
17
|
-
* both sides or a side is implausibly large (a dump, not a sentence). */
|
|
18
|
-
export declare function toSmolSentRow(row: unknown): SmolSentRow | null;
|
|
19
|
-
/** Translate ONE SmolSent pair into SEMA facts: the two sentences are one
|
|
20
|
-
* meaning in two languages, so bind them BOTH ways. refineItems drops the
|
|
21
|
-
* degenerate case where src === trg. */
|
|
22
|
-
export declare function smolSentRowToItems(row: SmolSentRow): TrainingItem[];
|
|
23
|
-
/** One normalized Aya row. */
|
|
24
|
-
export interface AyaRow {
|
|
25
|
-
inputs: string;
|
|
26
|
-
targets: string;
|
|
27
|
-
language: string;
|
|
28
|
-
}
|
|
29
|
-
/** Normalize a raw datasets-server row object into an AyaRow, or null when it
|
|
30
|
-
* lacks a usable prompt/answer or a field is implausibly large (a dump, not a
|
|
31
|
-
* cognitive example). Trims surrounding whitespace; keeps inner text verbatim
|
|
32
|
-
* (human prose, possibly multi-paragraph). */
|
|
33
|
-
export declare function toAyaRow(row: unknown): AyaRow | null;
|
|
34
|
-
/** Translate ONE Aya row into SEMA training items. A row is a single human
|
|
35
|
-
* (question → answer) exchange — exactly one FACT, the (inputs → targets) edge.
|
|
36
|
-
* No standalone-answer experience and no one-exchange "cumulative" walk: a lone
|
|
37
|
-
* Q→A is not multi-turn, and both would only replicate the same edge. */
|
|
38
|
-
export declare function ayaRowToItems(row: AyaRow): TrainingItem[];
|
|
39
|
-
/** A single oasst2 message node (the fields we use; the tree nests via replies). */
|
|
40
|
-
interface OasstNode {
|
|
41
|
-
role?: string;
|
|
42
|
-
text?: string;
|
|
43
|
-
rank?: number | null;
|
|
44
|
-
deleted?: boolean;
|
|
45
|
-
replies?: OasstNode[];
|
|
46
|
-
}
|
|
47
|
-
/** One conversational turn extracted from a tree. */
|
|
48
|
-
export interface OasstTurn {
|
|
49
|
-
role: string;
|
|
50
|
-
text: string;
|
|
51
|
-
}
|
|
52
|
-
/** Collapse a conversation tree to ONE linear path: at each node, descend into
|
|
53
|
-
* its best-ranked, non-deleted reply (rank 0 preferred; unranked sorts last).
|
|
54
|
-
* Returns the ordered turns (already strictly alternating in this corpus). */
|
|
55
|
-
export declare function bestOasstPath(root: OasstNode): OasstTurn[];
|
|
56
|
-
/** Translate ONE multi-turn oasst2 conversation into SEMA training items.
|
|
57
|
-
*
|
|
58
|
-
* This is the ONE stage where cumulative continuous context is truly necessary:
|
|
59
|
-
* the data is a real multi-turn dialogue, and what must be learned is how each
|
|
60
|
-
* turn follows from the WHOLE conversation so far — not from the previous turn
|
|
61
|
-
* alone. The conversation is emitted ONLY as the accumulated walk; standalone
|
|
62
|
-
* turn experiences and local adjacent-pair facts are NOT emitted (they are
|
|
63
|
-
* subsumed by it and would merely replicate the content).
|
|
64
|
-
*
|
|
65
|
-
* The walk is the pattern proven in test/13-conversation.test.mjs
|
|
66
|
-
* ("teachConversation"): each turn is the continuation of all prior turns,
|
|
67
|
-
* with BARE turn text — NO "User:/Assistant:" labels. The SHAPE is identical
|
|
68
|
-
* (cumulative context → next turn); the join string is not, and does not need
|
|
69
|
-
* to be — that file joins with nothing and this corpus joins with "\n" (see
|
|
70
|
-
* `accumulate`). Saying "byte-for-byte", as this comment used to, invites the
|
|
71
|
-
* reading that the two must agree on a separator. They must not agree,
|
|
72
|
-
* because there is nothing to agree about: turn boundaries are offsets, and
|
|
73
|
-
* the join string is just corpus text. Roles already
|
|
74
|
-
* alternate by position in an oasst2 best-path (the root is a prompter), so a
|
|
75
|
-
* label adds nothing the position does not, while a clean continuation matches
|
|
76
|
-
* the test's recall (predictNext queries bare prior turns) and lets a turn share
|
|
77
|
-
* its gist with the same text elsewhere (e.g. an Aya question stored bare).
|
|
78
|
-
*
|
|
79
|
-
* Returns [] for a conversation below the multi-turn threshold, so callers can
|
|
80
|
-
* simply skip empties. */
|
|
81
|
-
export declare function oasstConversationToItems(turns: OasstTurn[]): TrainingItem[];
|
|
82
|
-
/** One normalized General-Knowledge row. */
|
|
83
|
-
export interface GenKnowRow {
|
|
84
|
-
question: string;
|
|
85
|
-
answer: string;
|
|
86
|
-
}
|
|
87
|
-
/** Normalize a raw datasets-server row into a GenKnowRow, or null when it lacks
|
|
88
|
-
* a usable question/answer or a side is implausibly large (corruption). */
|
|
89
|
-
export declare function toGenKnowRow(row: unknown): GenKnowRow | null;
|
|
90
|
-
/** Translate ONE General-Knowledge row into SEMA items: exactly one
|
|
91
|
-
* (question → answer) FACT. refineItems drops a degenerate question === answer. */
|
|
92
|
-
export declare function genKnowRowToItems(row: GenKnowRow): TrainingItem[];
|
|
93
|
-
export {};
|