@hviana/sema 0.2.5 → 0.2.6
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/package.json +1 -1
- package/src/mind/attention.ts +355 -19
- package/test/53-cross-region-probe-instrumentation.test.mjs +356 -0
- package/dist/example/demo.d.ts +0 -1
- package/dist/example/demo.js +0 -39
- package/dist/example/train_base.d.ts +0 -87
- package/dist/example/train_base.js +0 -2252
- 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 -26
- package/dist/src/canon.js +0 -57
- package/dist/src/config.d.ts +0 -111
- package/dist/src/config.js +0 -91
- package/dist/src/derive/src/deduction.d.ts +0 -125
- package/dist/src/derive/src/deduction.js +0 -155
- package/dist/src/derive/src/index.d.ts +0 -7
- 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 -202
- package/dist/src/geometry.js +0 -575
- package/dist/src/index.d.ts +0 -16
- package/dist/src/index.js +0 -18
- package/dist/src/ingest-cache.d.ts +0 -41
- package/dist/src/ingest-cache.js +0 -161
- 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 -315
- package/dist/src/mind/attention.js +0 -1549
- package/dist/src/mind/bridge.d.ts +0 -30
- package/dist/src/mind/bridge.js +0 -569
- package/dist/src/mind/canonical.d.ts +0 -29
- package/dist/src/mind/canonical.js +0 -88
- package/dist/src/mind/graph-search.d.ts +0 -285
- package/dist/src/mind/graph-search.js +0 -956
- package/dist/src/mind/index.d.ts +0 -7
- package/dist/src/mind/index.js +0 -5
- package/dist/src/mind/junction.d.ts +0 -124
- package/dist/src/mind/junction.js +0 -317
- package/dist/src/mind/learning.d.ts +0 -47
- package/dist/src/mind/learning.js +0 -248
- package/dist/src/mind/match.d.ts +0 -126
- package/dist/src/mind/match.js +0 -441
- 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 -716
- package/dist/src/mind/mechanisms/confluence.d.ts +0 -24
- package/dist/src/mind/mechanisms/confluence.js +0 -225
- package/dist/src/mind/mechanisms/cover.d.ts +0 -6
- package/dist/src/mind/mechanisms/cover.js +0 -203
- package/dist/src/mind/mechanisms/extraction.d.ts +0 -67
- package/dist/src/mind/mechanisms/extraction.js +0 -387
- package/dist/src/mind/mechanisms/recall.d.ts +0 -13
- package/dist/src/mind/mechanisms/recall.js +0 -325
- package/dist/src/mind/mind.d.ts +0 -276
- package/dist/src/mind/mind.js +0 -572
- package/dist/src/mind/pipeline-mechanism.d.ts +0 -142
- package/dist/src/mind/pipeline-mechanism.js +0 -213
- package/dist/src/mind/pipeline.d.ts +0 -20
- package/dist/src/mind/pipeline.js +0 -217
- package/dist/src/mind/primitives.d.ts +0 -70
- package/dist/src/mind/primitives.js +0 -287
- 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 -34
- package/dist/src/mind/reasoning.js +0 -240
- package/dist/src/mind/recognition.d.ts +0 -20
- package/dist/src/mind/recognition.js +0 -390
- package/dist/src/mind/resonance.d.ts +0 -23
- package/dist/src/mind/resonance.js +0 -0
- 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 -113
- package/dist/src/mind/traverse.js +0 -568
- package/dist/src/mind/types.d.ts +0 -289
- package/dist/src/mind/types.js +0 -130
- 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 -283
- package/dist/src/sema.d.ts +0 -31
- package/dist/src/sema.js +0 -63
- package/dist/src/store-sqlite.d.ts +0 -184
- package/dist/src/store-sqlite.js +0 -942
- package/dist/src/store.d.ts +0 -663
- package/dist/src/store.js +0 -1618
- package/dist/src/vec.d.ts +0 -31
- package/dist/src/vec.js +0 -109
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
// 53-cross-region-probe-instrumentation.test.mjs — extends the climbConsensus
|
|
2
|
+
// instrumentation (test/52) to cover the ordinary-region contrastive rival,
|
|
3
|
+
// the cross-region junction probe ladder, and structural-resonance. Purely
|
|
4
|
+
// additive: every assertion checks the STRUCTURE of the `data` payload's new
|
|
5
|
+
// fields — never that inference itself changed.
|
|
6
|
+
|
|
7
|
+
import { test } from "node:test";
|
|
8
|
+
import assert from "node:assert/strict";
|
|
9
|
+
import { Mind } from "../dist/src/index.js";
|
|
10
|
+
import { SQliteStore } from "../dist/src/store-sqlite.js";
|
|
11
|
+
|
|
12
|
+
const mk = (seed = 1) =>
|
|
13
|
+
new Mind({ seed, store: new SQliteStore({ path: ":memory:" }) });
|
|
14
|
+
|
|
15
|
+
async function trace(mind, q) {
|
|
16
|
+
const steps = [];
|
|
17
|
+
const ans = await mind.respondText(q, (s) => steps.push(s));
|
|
18
|
+
return { steps, ans };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function climbStep(steps) {
|
|
22
|
+
return steps.find((s) => s.mechanism.at(-1) === "climbConsensus");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Reused verbatim from test/51's proven fixtures.
|
|
26
|
+
const ATTR_CORPUS = [
|
|
27
|
+
["red", "is a color"],
|
|
28
|
+
["blue", "is a color"],
|
|
29
|
+
["circle", "is a shape"],
|
|
30
|
+
["square", "is a shape"],
|
|
31
|
+
["red circle", "answer alpha"],
|
|
32
|
+
["red square", "answer beta"],
|
|
33
|
+
["blue circle", "answer gamma"],
|
|
34
|
+
["blue square", "answer delta"],
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const SYN_ATTR_CORPUS = [
|
|
38
|
+
["red", "is a color"],
|
|
39
|
+
["crimson", "is a color"],
|
|
40
|
+
["blue", "is a color"],
|
|
41
|
+
["circle", "is a shape"],
|
|
42
|
+
["square", "is a shape"],
|
|
43
|
+
["red circle", "answer alpha"],
|
|
44
|
+
["crimson circle", "answer alpha2"],
|
|
45
|
+
["red square", "answer beta"],
|
|
46
|
+
["blue circle", "answer gamma"],
|
|
47
|
+
["blue square", "answer delta"],
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
51
|
+
// 1. ordinary-region contrastive rival.
|
|
52
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
53
|
+
|
|
54
|
+
test("1. an approximate region with a genuine rival reports contrastiveRival, and the margin matches score - rival.score", async () => {
|
|
55
|
+
const m = mk(1);
|
|
56
|
+
await m.ingest(ATTR_CORPUS);
|
|
57
|
+
const { steps } = await trace(m, "blue then circle then square");
|
|
58
|
+
await m.store.close();
|
|
59
|
+
|
|
60
|
+
const step = climbStep(steps);
|
|
61
|
+
assert.ok(step?.data, "expected a traced climbConsensus step");
|
|
62
|
+
const regions = step.data.regions ?? [];
|
|
63
|
+
const withRival = regions.filter((r) => r.contrastiveRival);
|
|
64
|
+
assert.ok(withRival.length > 0, "expected at least one region with a rival");
|
|
65
|
+
for (const r of withRival) {
|
|
66
|
+
assert.equal(typeof r.contrastiveRival.node, "number");
|
|
67
|
+
assert.equal(typeof r.contrastiveRival.rank, "number");
|
|
68
|
+
assert.equal(typeof r.contrastiveRival.score, "number");
|
|
69
|
+
assert.ok(
|
|
70
|
+
Math.abs(
|
|
71
|
+
r.contrastiveMargin - (r.selected.score - r.contrastiveRival.score),
|
|
72
|
+
) < 1e-9,
|
|
73
|
+
"contrastiveMargin must equal selected.score - contrastiveRival.score",
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("1b. a known region never carries a contrastiveRival (the contrast only applies to approximate regions)", async () => {
|
|
79
|
+
const m = mk(1);
|
|
80
|
+
await m.ingest(ATTR_CORPUS);
|
|
81
|
+
const { steps } = await trace(m, "red then circle");
|
|
82
|
+
await m.store.close();
|
|
83
|
+
|
|
84
|
+
const step = climbStep(steps);
|
|
85
|
+
const regions = step.data.regions ?? [];
|
|
86
|
+
for (const r of regions) {
|
|
87
|
+
if (r.known) {
|
|
88
|
+
assert.equal(r.contrastiveRival, undefined);
|
|
89
|
+
assert.equal(r.contrastiveMargin, undefined);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// And when a voted approximate region has NO rival at all, contrastiveMargin
|
|
93
|
+
// stays exactly selected.score, with no contrastiveRival field.
|
|
94
|
+
for (const r of regions) {
|
|
95
|
+
if (r.outcome === "voted" && !r.known && r.contrastiveRival === undefined) {
|
|
96
|
+
assert.equal(r.contrastiveMargin, r.selected.score);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
102
|
+
// 2. probe accounting invariant.
|
|
103
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
104
|
+
|
|
105
|
+
test("2. crossRegion.probes.length === crossRegion.probesAttempted", async () => {
|
|
106
|
+
const m = mk(1);
|
|
107
|
+
await m.ingest(SYN_ATTR_CORPUS);
|
|
108
|
+
const { steps } = await trace(m, "the crimson near the square");
|
|
109
|
+
await m.store.close();
|
|
110
|
+
|
|
111
|
+
const step = climbStep(steps);
|
|
112
|
+
const cr = step.data.crossRegion;
|
|
113
|
+
assert.ok(cr, "expected a crossRegion summary");
|
|
114
|
+
assert.equal(cr.probes.length, cr.probesAttempted);
|
|
115
|
+
assert.ok(
|
|
116
|
+
["insufficient-regions", "probe-limit", "pairs-exhausted"].includes(
|
|
117
|
+
cr.stopReason,
|
|
118
|
+
),
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
123
|
+
// 3. exact junction: attempt count, accepted structural outcome, vote-probe
|
|
124
|
+
// link.
|
|
125
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
126
|
+
|
|
127
|
+
test("3. exact junction: probe.exact attempted with candidates, accepted structural outcome, and vote links back to its probe", async () => {
|
|
128
|
+
const m = mk(3);
|
|
129
|
+
await m.ingest(ATTR_CORPUS);
|
|
130
|
+
const { steps } = await trace(m, "red then circle");
|
|
131
|
+
await m.store.close();
|
|
132
|
+
|
|
133
|
+
const step = climbStep(steps);
|
|
134
|
+
const cr = step.data.crossRegion;
|
|
135
|
+
assert.ok(cr.junctionVotes.length > 0);
|
|
136
|
+
const jv = cr.junctionVotes[0];
|
|
137
|
+
assert.equal(jv.tier, "exact");
|
|
138
|
+
assert.equal(typeof jv.probe, "number");
|
|
139
|
+
const probe = cr.probes[jv.probe];
|
|
140
|
+
assert.ok(probe, "vote's probe index must resolve into crossRegion.probes");
|
|
141
|
+
assert.equal(probe.exact.attempted, true);
|
|
142
|
+
assert.ok(probe.exact.candidatesReturned > 0);
|
|
143
|
+
assert.ok(probe.structural, "expected a structural trace on this probe");
|
|
144
|
+
assert.equal(probe.structural.outcome, "accepted");
|
|
145
|
+
assert.equal(probe.outcome, "accepted");
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
149
|
+
// 4. single-synonym junction.
|
|
150
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
151
|
+
|
|
152
|
+
test("4. single-synonym junction: exact returns zero, single attempted and returns candidates, selected single-synonym tier", async () => {
|
|
153
|
+
const m = mk(1);
|
|
154
|
+
await m.ingest(SYN_ATTR_CORPUS);
|
|
155
|
+
// "square then crimson" — order-free single-sibling substitution recovers
|
|
156
|
+
// 'red square' via crimson's sibling 'red' (mirrors test/51's synRight).
|
|
157
|
+
const { steps } = await trace(m, "square then crimson");
|
|
158
|
+
await m.store.close();
|
|
159
|
+
|
|
160
|
+
const step = climbStep(steps);
|
|
161
|
+
const cr = step.data.crossRegion;
|
|
162
|
+
assert.ok(cr.junctionVotes.length > 0);
|
|
163
|
+
const jv = cr.junctionVotes.find((v) => v.tier === "single-synonym");
|
|
164
|
+
assert.ok(jv, "expected a single-synonym junction vote");
|
|
165
|
+
const probe = cr.probes[jv.probe];
|
|
166
|
+
assert.equal(probe.exact.candidatesReturned, 0);
|
|
167
|
+
assert.equal(probe.singleSynonym.attempted, true);
|
|
168
|
+
assert.ok(probe.singleSynonym.candidatesReturned > 0);
|
|
169
|
+
assert.equal(probe.structural.tier, "single-synonym");
|
|
170
|
+
assert.equal(probe.structural.outcome, "accepted");
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
174
|
+
// 5. double-synonym junction: verify the ladder position and fields.
|
|
175
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
176
|
+
|
|
177
|
+
test("5. double-synonym tier: probe fields report single returned zero and double attempted when only the double tier can fire", async () => {
|
|
178
|
+
const m = mk(1);
|
|
179
|
+
await m.ingest(SYN_ATTR_CORPUS);
|
|
180
|
+
const { steps } = await trace(m, "crimson then crimson");
|
|
181
|
+
await m.store.close();
|
|
182
|
+
|
|
183
|
+
const step = climbStep(steps);
|
|
184
|
+
const cr = step.data.crossRegion ?? { probes: [] };
|
|
185
|
+
// Whether or not a double-synonym container was actually found (fixture-
|
|
186
|
+
// sensitive per test/51's own test 4), every probe that attempted the
|
|
187
|
+
// synonym ladder must report single/double consistently: double is only
|
|
188
|
+
// ever attempted when single returned zero.
|
|
189
|
+
for (const p of cr.probes) {
|
|
190
|
+
if (p.doubleSynonym.attempted) {
|
|
191
|
+
assert.equal(p.singleSynonym.candidatesReturned, 0);
|
|
192
|
+
assert.equal(p.singleSynonym.attempted, true);
|
|
193
|
+
}
|
|
194
|
+
if (p.singleSynonym.candidatesReturned > 0) {
|
|
195
|
+
assert.equal(p.doubleSynonym.attempted, false);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
201
|
+
// 6. structural resonance: variants, budget, hits, merged proposals,
|
|
202
|
+
// examined sequence, margin, noise floor, outcome.
|
|
203
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
204
|
+
|
|
205
|
+
test("6. structural-resonance: eligible probes report variants, merged proposals and an examined sequence with effectiveScore = annScore * semanticConfidence", async () => {
|
|
206
|
+
const m = mk(1);
|
|
207
|
+
await m.ingest(SYN_ATTR_CORPUS);
|
|
208
|
+
const { steps } = await trace(m, "crimson then square");
|
|
209
|
+
await m.store.close();
|
|
210
|
+
|
|
211
|
+
const step = climbStep(steps);
|
|
212
|
+
const cr = step.data.crossRegion;
|
|
213
|
+
const eligible = cr.probes.filter((p) =>
|
|
214
|
+
p.resonance && p.resonance.outcome !== "ineligible"
|
|
215
|
+
);
|
|
216
|
+
assert.ok(
|
|
217
|
+
eligible.length > 0,
|
|
218
|
+
"expected at least one eligible resonance probe",
|
|
219
|
+
);
|
|
220
|
+
for (const p of eligible) {
|
|
221
|
+
const res = p.resonance;
|
|
222
|
+
assert.equal(res.variantBudget, m.cfg.haloQueryK);
|
|
223
|
+
assert.ok(Array.isArray(res.variants));
|
|
224
|
+
assert.ok(res.variants.length > 0);
|
|
225
|
+
for (const v of res.variants) {
|
|
226
|
+
assert.equal(typeof v.semanticConfidence, "number");
|
|
227
|
+
assert.equal(typeof v.annHitsReturned, "number");
|
|
228
|
+
}
|
|
229
|
+
assert.equal(typeof res.mergedProposals, "number");
|
|
230
|
+
assert.ok(Array.isArray(res.examined));
|
|
231
|
+
for (const c of res.examined) {
|
|
232
|
+
assert.ok(
|
|
233
|
+
Math.abs(c.effectiveScore - c.annScore * c.semanticConfidence) < 1e-9,
|
|
234
|
+
);
|
|
235
|
+
assert.ok(
|
|
236
|
+
[
|
|
237
|
+
"saturated",
|
|
238
|
+
"no-roots",
|
|
239
|
+
"nonpositive-idf",
|
|
240
|
+
"same-as-endpoint",
|
|
241
|
+
"same-as-selected",
|
|
242
|
+
"selected",
|
|
243
|
+
"contrastive-rival",
|
|
244
|
+
].includes(c.outcome),
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
assert.equal(typeof res.noiseFloor, "number");
|
|
248
|
+
assert.ok(
|
|
249
|
+
["empty", "no-valid-proposal", "margin-rejected", "accepted"].includes(
|
|
250
|
+
res.outcome,
|
|
251
|
+
),
|
|
252
|
+
);
|
|
253
|
+
if (res.outcome === "accepted" || res.outcome === "margin-rejected") {
|
|
254
|
+
assert.equal(typeof res.contrastiveMargin, "number");
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
260
|
+
// 7. resonance rejection outcomes.
|
|
261
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
262
|
+
|
|
263
|
+
test("7. resonance outcomes: ineligible reasons are named and a margin-rejected probe reports a sub-noise-floor margin", async () => {
|
|
264
|
+
const mIneligible = mk(1);
|
|
265
|
+
await mIneligible.ingest(SYN_ATTR_CORPUS);
|
|
266
|
+
const { steps: ineligibleSteps } = await trace(
|
|
267
|
+
mIneligible,
|
|
268
|
+
"crimson beside square",
|
|
269
|
+
);
|
|
270
|
+
await mIneligible.store.close();
|
|
271
|
+
|
|
272
|
+
const ineligibleStep = climbStep(ineligibleSteps);
|
|
273
|
+
const ineligible = (ineligibleStep.data.crossRegion?.probes ?? []).filter(
|
|
274
|
+
(p) => p.resonance?.outcome === "ineligible",
|
|
275
|
+
);
|
|
276
|
+
assert.ok(ineligible.length > 0, "expected at least one ineligible probe");
|
|
277
|
+
for (const p of ineligible) {
|
|
278
|
+
assert.ok(Array.isArray(p.resonance.ineligibleReasons));
|
|
279
|
+
assert.ok(p.resonance.ineligibleReasons.length > 0);
|
|
280
|
+
for (const r of p.resonance.ineligibleReasons) {
|
|
281
|
+
assert.ok(
|
|
282
|
+
["between-region", "not-both-strong", "not-both-known", "gap-too-large"]
|
|
283
|
+
.includes(r),
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
assert.equal(p.outcome, "resonance-ineligible");
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const mMargin = mk(1);
|
|
290
|
+
await mMargin.ingest(SYN_ATTR_CORPUS);
|
|
291
|
+
const { steps: marginSteps } = await trace(mMargin, "crimson then square");
|
|
292
|
+
await mMargin.store.close();
|
|
293
|
+
const marginStep = climbStep(marginSteps);
|
|
294
|
+
const marginRejected = (marginStep.data.crossRegion?.probes ?? []).filter(
|
|
295
|
+
(p) => p.resonance?.outcome === "margin-rejected",
|
|
296
|
+
);
|
|
297
|
+
assert.ok(marginRejected.length > 0, "expected a margin-rejected probe");
|
|
298
|
+
for (const p of marginRejected) {
|
|
299
|
+
assert.ok(p.resonance.contrastiveMargin <= p.resonance.noiseFloor);
|
|
300
|
+
assert.equal(p.outcome, "resonance-rejected");
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
305
|
+
// 8. cross-region vote weight fields.
|
|
306
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
307
|
+
|
|
308
|
+
test("8. a linked cross-region vote reports confidence, evidenceBytes, mutualWeight, voteWeightPerRoot as numbers", async () => {
|
|
309
|
+
const m = mk(3);
|
|
310
|
+
await m.ingest(ATTR_CORPUS);
|
|
311
|
+
const { steps } = await trace(m, "red then circle");
|
|
312
|
+
await m.store.close();
|
|
313
|
+
|
|
314
|
+
const step = climbStep(steps);
|
|
315
|
+
const jv = step.data.crossRegion.junctionVotes[0];
|
|
316
|
+
assert.equal(typeof jv.confidence, "number");
|
|
317
|
+
assert.equal(typeof jv.evidenceBytes, "number");
|
|
318
|
+
assert.equal(typeof jv.mutualWeight, "number");
|
|
319
|
+
assert.equal(typeof jv.voteWeightPerRoot, "number");
|
|
320
|
+
assert.equal(jv.confidence, 1); // exact tier: confidence collapses to 1
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
324
|
+
// 9. additive behavior — the new fields never change roots/ranked/answer.
|
|
325
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
326
|
+
|
|
327
|
+
test("9. enabling the extended instrumentation never changes roots, ranked, or the answer", async () => {
|
|
328
|
+
const queries = [
|
|
329
|
+
"red then circle",
|
|
330
|
+
"square then crimson",
|
|
331
|
+
"crimson then square",
|
|
332
|
+
"crimson then crimson",
|
|
333
|
+
"the crimson near the square",
|
|
334
|
+
];
|
|
335
|
+
for (const q of queries) {
|
|
336
|
+
const plain = mk(9);
|
|
337
|
+
await plain.ingest(SYN_ATTR_CORPUS);
|
|
338
|
+
const plainAns = await plain.respondText(q);
|
|
339
|
+
await plain.store.close();
|
|
340
|
+
|
|
341
|
+
const traced = mk(9);
|
|
342
|
+
await traced.ingest(SYN_ATTR_CORPUS);
|
|
343
|
+
const { ans: tracedAns, steps } = await trace(traced, q);
|
|
344
|
+
await traced.store.close();
|
|
345
|
+
|
|
346
|
+
assert.equal(tracedAns, plainAns, `answer differs for "${q}"`);
|
|
347
|
+
|
|
348
|
+
const step = climbStep(steps);
|
|
349
|
+
if (step?.data?.crossRegion) {
|
|
350
|
+
// The probe/resonance instrumentation is present but does not affect
|
|
351
|
+
// the pooled result: roots/ranked come from the SAME vote objects the
|
|
352
|
+
// probes were recorded from.
|
|
353
|
+
assert.ok(Array.isArray(step.data.crossRegion.probes));
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
});
|
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,87 +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 byte-for-byte the pattern proven in test/13-conversation.test.mjs
|
|
66
|
-
* ("teachConversation"): each turn is the continuation of all prior turns joined
|
|
67
|
-
* by "\n", with BARE turn text — NO "User:/Assistant:" labels. Roles already
|
|
68
|
-
* alternate by position in an oasst2 best-path (the root is a prompter), so a
|
|
69
|
-
* label adds nothing the position does not, while a clean continuation matches
|
|
70
|
-
* the test's recall (predictNext queries bare prior turns) and lets a turn share
|
|
71
|
-
* its gist with the same text elsewhere (e.g. an Aya question stored bare).
|
|
72
|
-
*
|
|
73
|
-
* Returns [] for a conversation below the multi-turn threshold, so callers can
|
|
74
|
-
* simply skip empties. */
|
|
75
|
-
export declare function oasstConversationToItems(turns: OasstTurn[]): TrainingItem[];
|
|
76
|
-
/** One normalized General-Knowledge row. */
|
|
77
|
-
export interface GenKnowRow {
|
|
78
|
-
question: string;
|
|
79
|
-
answer: string;
|
|
80
|
-
}
|
|
81
|
-
/** Normalize a raw datasets-server row into a GenKnowRow, or null when it lacks
|
|
82
|
-
* a usable question/answer or a side is implausibly large (corruption). */
|
|
83
|
-
export declare function toGenKnowRow(row: unknown): GenKnowRow | null;
|
|
84
|
-
/** Translate ONE General-Knowledge row into SEMA items: exactly one
|
|
85
|
-
* (question → answer) FACT. refineItems drops a degenerate question === answer. */
|
|
86
|
-
export declare function genKnowRowToItems(row: GenKnowRow): TrainingItem[];
|
|
87
|
-
export {};
|