@hviana/sema 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +6 -5
- package/CITATION.cff +49 -0
- package/HOW_IT_WORKS.md +11 -12
- package/README.md +7 -5
- package/dist/example/demo.js +25 -25
- package/dist/example/train_base.d.ts +19 -21
- package/dist/example/train_base.js +1700 -1952
- package/dist/src/alphabet.d.ts +3 -3
- package/dist/src/alphabet.js +24 -27
- package/dist/src/alu/src/alu.d.ts +169 -194
- package/dist/src/alu/src/alu.js +374 -414
- package/dist/src/alu/src/expr.d.ts +36 -43
- package/dist/src/alu/src/expr.js +252 -278
- package/dist/src/alu/src/index.d.ts +9 -98
- package/dist/src/alu/src/index.js +10 -64
- package/dist/src/alu/src/kernel-arith.d.ts +1 -5
- package/dist/src/alu/src/kernel-arith.js +195 -285
- package/dist/src/alu/src/kernel-bits.d.ts +1 -5
- package/dist/src/alu/src/kernel-bits.js +81 -115
- package/dist/src/alu/src/kernel-logic.js +33 -63
- package/dist/src/alu/src/kernel-nd.js +155 -195
- package/dist/src/alu/src/kernel-numeric.d.ts +16 -80
- package/dist/src/alu/src/kernel-numeric.js +259 -331
- package/dist/src/alu/src/operation.d.ts +112 -132
- package/dist/src/alu/src/operation.js +149 -163
- package/dist/src/alu/src/parser.d.ts +168 -175
- package/dist/src/alu/src/parser.js +382 -421
- package/dist/src/alu/src/resonance.d.ts +16 -25
- package/dist/src/alu/src/resonance.js +49 -56
- package/dist/src/alu/src/text.d.ts +3 -7
- package/dist/src/alu/src/text.js +31 -37
- package/dist/src/alu/src/value.d.ts +14 -14
- package/dist/src/alu/src/value.js +150 -160
- package/dist/src/alu/test/alu.test.js +543 -656
- package/dist/src/bytes.d.ts +1 -5
- package/dist/src/bytes.js +33 -40
- package/dist/src/config.d.ts +99 -102
- package/dist/src/config.js +78 -83
- package/dist/src/derive/src/deduction.d.ts +60 -62
- package/dist/src/derive/src/deduction.js +103 -109
- package/dist/src/derive/src/index.d.ts +1 -7
- package/dist/src/derive/src/priority-queue.d.ts +8 -8
- package/dist/src/derive/src/priority-queue.js +57 -61
- package/dist/src/derive/src/rewrite.d.ts +15 -18
- package/dist/src/derive/src/rewrite.js +59 -67
- package/dist/src/derive/src/trie.d.ts +56 -56
- package/dist/src/derive/src/trie.js +175 -179
- package/dist/src/derive/test/derive.test.js +96 -100
- package/dist/src/extension.d.ts +13 -16
- package/dist/src/geometry.d.ts +29 -43
- package/dist/src/geometry.js +324 -268
- package/dist/src/index.d.ts +2 -17
- package/dist/src/index.js +2 -12
- package/dist/src/ingest-cache.d.ts +25 -30
- package/dist/src/ingest-cache.js +108 -127
- package/dist/src/mind/articulation.d.ts +1 -5
- package/dist/src/mind/articulation.js +77 -112
- package/dist/src/mind/attention.d.ts +40 -108
- package/dist/src/mind/attention.js +754 -872
- package/dist/src/mind/canonical.d.ts +4 -19
- package/dist/src/mind/canonical.js +27 -31
- package/dist/src/mind/graph-search.d.ts +201 -225
- package/dist/src/mind/graph-search.js +742 -821
- package/dist/src/mind/index.d.ts +2 -10
- package/dist/src/mind/junction.d.ts +31 -58
- package/dist/src/mind/junction.js +172 -237
- package/dist/src/mind/learning.d.ts +16 -52
- package/dist/src/mind/learning.js +143 -165
- package/dist/src/mind/match.d.ts +20 -69
- package/dist/src/mind/match.js +259 -318
- package/dist/src/mind/mechanisms/alu.js +16 -16
- package/dist/src/mind/mechanisms/cast.d.ts +9 -13
- package/dist/src/mind/mechanisms/cast.js +363 -456
- package/dist/src/mind/mechanisms/confluence.d.ts +8 -12
- package/dist/src/mind/mechanisms/confluence.js +152 -183
- package/dist/src/mind/mechanisms/cover.d.ts +2 -8
- package/dist/src/mind/mechanisms/cover.js +148 -210
- package/dist/src/mind/mechanisms/extraction.d.ts +8 -34
- package/dist/src/mind/mechanisms/extraction.js +234 -288
- package/dist/src/mind/mechanisms/recall.d.ts +6 -10
- package/dist/src/mind/mechanisms/recall.js +141 -185
- package/dist/src/mind/mind.d.ts +194 -155
- package/dist/src/mind/mind.js +392 -289
- package/dist/src/mind/pipeline-mechanism.d.ts +112 -124
- package/dist/src/mind/pipeline-mechanism.js +161 -172
- package/dist/src/mind/pipeline.d.ts +4 -14
- package/dist/src/mind/pipeline.js +125 -189
- package/dist/src/mind/primitives.d.ts +20 -33
- package/dist/src/mind/primitives.js +124 -120
- package/dist/src/mind/rationale.d.ts +86 -98
- package/dist/src/mind/rationale.js +113 -121
- package/dist/src/mind/reasoning.d.ts +2 -13
- package/dist/src/mind/reasoning.js +129 -166
- package/dist/src/mind/recognition.d.ts +1 -4
- package/dist/src/mind/recognition.js +198 -210
- package/dist/src/mind/resonance.d.ts +5 -22
- package/dist/src/mind/resonance.js +0 -0
- package/dist/src/mind/trace.d.ts +6 -25
- package/dist/src/mind/trace.js +50 -58
- package/dist/src/mind/traverse.d.ts +29 -58
- package/dist/src/mind/traverse.js +344 -356
- package/dist/src/mind/types.d.ts +122 -127
- package/dist/src/mind/types.js +60 -69
- package/dist/src/rabitq-ivf/src/database.d.ts +113 -0
- package/dist/src/rabitq-ivf/src/database.js +201 -0
- package/dist/src/rabitq-ivf/src/index.d.ts +7 -0
- package/dist/src/{rabitq-hnsw → rabitq-ivf}/src/index.js +1 -3
- package/dist/src/rabitq-ivf/src/ivf.d.ts +200 -0
- package/dist/src/rabitq-ivf/src/ivf.js +1165 -0
- package/dist/src/rabitq-ivf/src/prng.d.ts +19 -0
- package/dist/src/rabitq-ivf/src/prng.js +36 -0
- package/dist/src/rabitq-ivf/src/rabitq.d.ts +95 -0
- package/dist/src/rabitq-ivf/src/rabitq.js +283 -0
- package/dist/src/sema.d.ts +9 -13
- package/dist/src/sema.js +26 -40
- package/dist/src/store-sqlite.d.ts +171 -166
- package/dist/src/store-sqlite.js +768 -662
- package/dist/src/store.d.ts +597 -630
- package/dist/src/store.js +1434 -1581
- package/dist/src/vec.d.ts +5 -9
- package/dist/src/vec.js +61 -73
- package/example/train_base.ts +33 -15
- package/index.html +65 -0
- package/package.json +1 -1
- package/src/alu/README.md +1 -1
- package/src/alu/src/index.ts +1 -1
- package/src/config.ts +19 -27
- package/src/geometry.ts +93 -1
- package/src/index.ts +6 -11
- package/src/mind/attention.ts +11 -6
- package/src/mind/mechanisms/recall.ts +29 -13
- package/src/mind/mind.ts +250 -19
- package/src/mind/primitives.ts +43 -6
- package/src/mind/recognition.ts +26 -8
- package/src/mind/traverse.ts +47 -0
- package/src/mind/types.ts +20 -21
- package/src/rabitq-ivf/README.md +56 -0
- package/src/rabitq-ivf/src/database.ts +276 -0
- package/src/{rabitq-hnsw → rabitq-ivf}/src/index.ts +2 -5
- package/src/rabitq-ivf/src/ivf.ts +1330 -0
- package/src/{rabitq-hnsw → rabitq-ivf}/src/prng.ts +1 -1
- package/src/store-sqlite.ts +204 -9
- package/src/store.ts +27 -34
- package/test/08-storage.test.mjs +3 -3
- package/test/13-conversation.test.mjs +190 -20
- package/test/14-scaling.test.mjs +2 -2
- package/test/35-ivf.test.mjs +263 -0
- package/test/36-bloom.test.mjs +123 -0
- package/dist/src/rabitq-hnsw/src/database.d.ts +0 -202
- package/dist/src/rabitq-hnsw/src/database.js +0 -405
- package/dist/src/rabitq-hnsw/src/heap.d.ts +0 -22
- package/dist/src/rabitq-hnsw/src/heap.js +0 -94
- package/dist/src/rabitq-hnsw/src/hnsw.d.ts +0 -125
- package/dist/src/rabitq-hnsw/src/hnsw.js +0 -500
- package/dist/src/rabitq-hnsw/src/index.d.ts +0 -15
- package/dist/src/rabitq-hnsw/src/prng.d.ts +0 -19
- package/dist/src/rabitq-hnsw/src/prng.js +0 -38
- package/dist/src/rabitq-hnsw/src/rabitq.d.ts +0 -95
- package/dist/src/rabitq-hnsw/src/rabitq.js +0 -299
- package/dist/src/rabitq-hnsw/src/store.d.ts +0 -162
- package/dist/src/rabitq-hnsw/src/store.js +0 -916
- package/dist/src/rabitq-hnsw/test/hnsw.test.d.ts +0 -1
- package/dist/src/rabitq-hnsw/test/hnsw.test.js +0 -1197
- package/src/rabitq-hnsw/README.md +0 -303
- package/src/rabitq-hnsw/src/database.ts +0 -492
- package/src/rabitq-hnsw/src/heap.ts +0 -90
- package/src/rabitq-hnsw/src/hnsw.ts +0 -514
- package/src/rabitq-hnsw/src/store.ts +0 -994
- package/src/rabitq-hnsw/test/hnsw.test.ts +0 -1213
- /package/src/{rabitq-hnsw → rabitq-ivf}/src/rabitq.ts +0 -0
|
@@ -31,174 +31,130 @@ import { rItem, rNode, traceFail } from "../trace.js";
|
|
|
31
31
|
* the answers' merits, decided the grounding.) Null when no skill
|
|
32
32
|
* applies. */
|
|
33
33
|
export async function extractBySkill(ctx, query, pre) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
let exemplar = null;
|
|
53
|
-
let chosenAnchor = null;
|
|
54
|
-
let skipped = 0;
|
|
55
|
-
for (const cand of ranked) {
|
|
56
|
-
const ex = await pre.spanShapedOf(cand.anchor);
|
|
57
|
-
if (ex) {
|
|
58
|
-
exemplar = ex;
|
|
59
|
-
chosenAnchor = cand.anchor;
|
|
60
|
-
break;
|
|
34
|
+
const t = ctx.trace?.enter("extractBySkill", [
|
|
35
|
+
rItem(query, "query"),
|
|
36
|
+
]);
|
|
37
|
+
const fail = traceFail(t);
|
|
38
|
+
// Use climbAttentionAll to get the FULL ranked list, not just the
|
|
39
|
+
// roots that cleared commitVotes' significance floor. The floor
|
|
40
|
+
// gates further points of attention for fusion, but extraction only
|
|
41
|
+
// needs ONE anchor that IS a span-shaped skill exemplar — and on
|
|
42
|
+
// some seeds the top-voted anchor is not one (e.g. a concept-merge
|
|
43
|
+
// nickname outvotes the painting exemplars on shared substrings,
|
|
44
|
+
// while the exemplars' votes fall below the floor). Iterating the
|
|
45
|
+
// ranked list instead of just the roots lets extraction reach the
|
|
46
|
+
// first painting-exemplar anchor regardless of its floor status.
|
|
47
|
+
//
|
|
48
|
+
const { ranked } = await pre.attention();
|
|
49
|
+
if (ranked.length === 0) {
|
|
50
|
+
return fail("no consensus anchor — no skill to apply");
|
|
61
51
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return fail("no consensus root is a span-shaped skill exemplar");
|
|
72
|
-
}
|
|
73
|
-
if (skipped > 0) {
|
|
74
|
-
ctx.trace?.step(
|
|
75
|
-
"trySkillAnchors",
|
|
76
|
-
[
|
|
77
|
-
rItem(query.subarray(0, 0), `skipped ${skipped}`),
|
|
78
|
-
rNode(ctx, chosenAnchor, "chosen"),
|
|
79
|
-
],
|
|
80
|
-
[],
|
|
81
|
-
`skipped ${skipped} anchor(s) that are not skill exemplars`,
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
const { contextBytes, answerBytes } = exemplar;
|
|
85
|
-
const ansCtxRuns = answerRunsInContext(ctx, contextBytes, answerBytes);
|
|
86
|
-
if (ansCtxRuns === null || ansCtxRuns.length === 0) {
|
|
87
|
-
return fail("answer is not a subsequence of the context");
|
|
88
|
-
}
|
|
89
|
-
if (ansCtxRuns.length > 1) {
|
|
90
|
-
ctx.trace?.step(
|
|
91
|
-
"decomposeAnswer",
|
|
92
|
-
[rItem(answerBytes, "multi-piece-answer")],
|
|
93
|
-
ansCtxRuns.map((r) =>
|
|
94
|
-
rItem(contextBytes.subarray(r.start, r.end), "piece", undefined, [
|
|
95
|
-
r.start,
|
|
96
|
-
r.end,
|
|
97
|
-
])
|
|
98
|
-
),
|
|
99
|
-
`answer splits into ${ansCtxRuns.length} piece(s) within the exemplar context`,
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
const pieces = [];
|
|
103
|
-
const accounted = [];
|
|
104
|
-
for (let ri = 0; ri < ansCtxRuns.length; ri++) {
|
|
105
|
-
const run = ansCtxRuns[ri];
|
|
106
|
-
const isLast = ri === ansCtxRuns.length - 1;
|
|
107
|
-
const framePreLen = Math.min(run.start, ctx.space.maxGroup);
|
|
108
|
-
const framePre = run.start > 0
|
|
109
|
-
? contextBytes.subarray(run.start - framePreLen, run.start)
|
|
110
|
-
: null;
|
|
111
|
-
const frames = [];
|
|
112
|
-
let start = 0;
|
|
113
|
-
if (framePre) {
|
|
114
|
-
const prePos = locate(ctx, query, framePre, 0, pre.rec.sites);
|
|
115
|
-
if (prePos < 0) {
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
start = prePos + framePre.length;
|
|
119
|
-
frames.push([prePos, start]); // the located frame IS matched evidence
|
|
120
|
-
}
|
|
121
|
-
let end;
|
|
122
|
-
if (isLast) {
|
|
123
|
-
if (run.end < contextBytes.length) {
|
|
124
|
-
const framePostLen = Math.min(
|
|
125
|
-
contextBytes.length - run.end,
|
|
126
|
-
ctx.space.maxGroup,
|
|
127
|
-
);
|
|
128
|
-
const framePost = contextBytes.subarray(
|
|
129
|
-
run.end,
|
|
130
|
-
run.end + framePostLen,
|
|
131
|
-
);
|
|
132
|
-
const postPos = locate(
|
|
133
|
-
ctx,
|
|
134
|
-
query.subarray(start),
|
|
135
|
-
framePost,
|
|
136
|
-
0,
|
|
137
|
-
pre.rec.sites,
|
|
138
|
-
);
|
|
139
|
-
if (postPos < 0) {
|
|
140
|
-
continue;
|
|
52
|
+
let exemplar = null;
|
|
53
|
+
let chosenAnchor = null;
|
|
54
|
+
let skipped = 0;
|
|
55
|
+
for (const cand of ranked) {
|
|
56
|
+
const ex = await pre.spanShapedOf(cand.anchor);
|
|
57
|
+
if (ex) {
|
|
58
|
+
exemplar = ex;
|
|
59
|
+
chosenAnchor = cand.anchor;
|
|
60
|
+
break;
|
|
141
61
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
);
|
|
154
|
-
const nextPos = locate(
|
|
155
|
-
ctx,
|
|
156
|
-
query.subarray(start),
|
|
157
|
-
nextPre,
|
|
158
|
-
0,
|
|
159
|
-
pre.rec.sites,
|
|
160
|
-
);
|
|
161
|
-
if (nextPos < 0) {
|
|
162
|
-
end = start + run.ansLen;
|
|
163
|
-
} else {
|
|
164
|
-
end = start + nextPos;
|
|
165
|
-
frames.push([end, end + nextPre.length]); // matched next-frame
|
|
166
|
-
}
|
|
62
|
+
skipped++;
|
|
63
|
+
}
|
|
64
|
+
if (exemplar === null) {
|
|
65
|
+
ctx.trace?.step("trySkillAnchors", [], [], `none of ${ranked.length} ranked anchor(s) is a span-shaped skill exemplar`);
|
|
66
|
+
return fail("no consensus root is a span-shaped skill exemplar");
|
|
67
|
+
}
|
|
68
|
+
if (skipped > 0) {
|
|
69
|
+
ctx.trace?.step("trySkillAnchors", [
|
|
70
|
+
rItem(query.subarray(0, 0), `skipped ${skipped}`),
|
|
71
|
+
rNode(ctx, chosenAnchor, "chosen"),
|
|
72
|
+
], [], `skipped ${skipped} anchor(s) that are not skill exemplars`);
|
|
167
73
|
}
|
|
168
|
-
|
|
169
|
-
|
|
74
|
+
const { contextBytes, answerBytes } = exemplar;
|
|
75
|
+
const ansCtxRuns = answerRunsInContext(ctx, contextBytes, answerBytes);
|
|
76
|
+
if (ansCtxRuns === null || ansCtxRuns.length === 0) {
|
|
77
|
+
return fail("answer is not a subsequence of the context");
|
|
170
78
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
// pre-frame); only when BOTH borders were located is the read bounded.
|
|
177
|
-
const preBounded = run.start === 0 || frames.some(([, e]) => e === start);
|
|
178
|
-
const postBounded = frames.some(([b]) => b === end);
|
|
179
|
-
if (preBounded && postBounded) {
|
|
180
|
-
accounted.push([start, end]);
|
|
79
|
+
if (ansCtxRuns.length > 1) {
|
|
80
|
+
ctx.trace?.step("decomposeAnswer", [rItem(answerBytes, "multi-piece-answer")], ansCtxRuns.map((r) => rItem(contextBytes.subarray(r.start, r.end), "piece", undefined, [
|
|
81
|
+
r.start,
|
|
82
|
+
r.end,
|
|
83
|
+
])), `answer splits into ${ansCtxRuns.length} piece(s) within the exemplar context`);
|
|
181
84
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
85
|
+
const pieces = [];
|
|
86
|
+
const accounted = [];
|
|
87
|
+
for (let ri = 0; ri < ansCtxRuns.length; ri++) {
|
|
88
|
+
const run = ansCtxRuns[ri];
|
|
89
|
+
const isLast = ri === ansCtxRuns.length - 1;
|
|
90
|
+
const framePreLen = Math.min(run.start, ctx.space.maxGroup);
|
|
91
|
+
const framePre = run.start > 0
|
|
92
|
+
? contextBytes.subarray(run.start - framePreLen, run.start)
|
|
93
|
+
: null;
|
|
94
|
+
const frames = [];
|
|
95
|
+
let start = 0;
|
|
96
|
+
if (framePre) {
|
|
97
|
+
const prePos = locate(ctx, query, framePre, 0, pre.rec.sites);
|
|
98
|
+
if (prePos < 0)
|
|
99
|
+
continue;
|
|
100
|
+
start = prePos + framePre.length;
|
|
101
|
+
frames.push([prePos, start]); // the located frame IS matched evidence
|
|
102
|
+
}
|
|
103
|
+
let end;
|
|
104
|
+
if (isLast) {
|
|
105
|
+
if (run.end < contextBytes.length) {
|
|
106
|
+
const framePostLen = Math.min(contextBytes.length - run.end, ctx.space.maxGroup);
|
|
107
|
+
const framePost = contextBytes.subarray(run.end, run.end + framePostLen);
|
|
108
|
+
const postPos = locate(ctx, query.subarray(start), framePost, 0, pre.rec.sites);
|
|
109
|
+
if (postPos < 0)
|
|
110
|
+
continue;
|
|
111
|
+
end = start + postPos;
|
|
112
|
+
frames.push([end, end + framePost.length]); // matched post-frame
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
end = query.length;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
const nextRun = ansCtxRuns[ri + 1];
|
|
120
|
+
const nextPreLen = Math.min(nextRun.start, ctx.space.maxGroup);
|
|
121
|
+
const nextPre = contextBytes.subarray(nextRun.start - nextPreLen, nextRun.start);
|
|
122
|
+
const nextPos = locate(ctx, query.subarray(start), nextPre, 0, pre.rec.sites);
|
|
123
|
+
if (nextPos < 0) {
|
|
124
|
+
end = start + run.ansLen;
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
end = start + nextPos;
|
|
128
|
+
frames.push([end, end + nextPre.length]); // matched next-frame
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (start >= end)
|
|
132
|
+
continue;
|
|
133
|
+
pieces.push(query.subarray(start, end));
|
|
134
|
+
accounted.push(...frames);
|
|
135
|
+
// Bounded on both sides ⇒ the read span itself is explained (see doc).
|
|
136
|
+
// frames carries the pre-border (when the answer is not at the context's
|
|
137
|
+
// start) and the located right border (post-frame or next piece's
|
|
138
|
+
// pre-frame); only when BOTH borders were located is the read bounded.
|
|
139
|
+
const preBounded = run.start === 0 || frames.some(([, e]) => e === start);
|
|
140
|
+
const postBounded = frames.some(([b]) => b === end);
|
|
141
|
+
if (preBounded && postBounded)
|
|
142
|
+
accounted.push([start, end]);
|
|
143
|
+
}
|
|
144
|
+
if (pieces.length === 0) {
|
|
145
|
+
return fail("no answer piece's frame located in the query");
|
|
146
|
+
}
|
|
147
|
+
const out = pieces.length === 1 ? pieces[0] : concatBytes(pieces);
|
|
148
|
+
t?.done([rItem(out, "extracted")], pieces.length === 1
|
|
149
|
+
? `apply a learnt extraction skill — read the analogous span of the query` +
|
|
150
|
+
` framed like "${decodeText(answerBytes)}" sits in its exemplar`
|
|
151
|
+
: `apply a learnt MULTI-PIECE skill — read ${pieces.length} analogous` +
|
|
152
|
+
` pieces of the query and synthesize them like "${decodeText(answerBytes)}"`);
|
|
153
|
+
return {
|
|
154
|
+
bytes: out,
|
|
155
|
+
accounted,
|
|
156
|
+
unexplained: unexplainedLabel(query, accounted),
|
|
157
|
+
};
|
|
202
158
|
}
|
|
203
159
|
// ── The two span-shape readings: OPEN acceptance vs. STRONG decomposition ──
|
|
204
160
|
//
|
|
@@ -225,55 +181,52 @@ export async function extractBySkill(ctx, query, pre) {
|
|
|
225
181
|
* when no greedy longest-run decomposition exists. Adjacent runs that
|
|
226
182
|
* connect contiguously are merged. */
|
|
227
183
|
export function answerRunsInContext(_ctx, context, answer) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}
|
|
232
|
-
const runs = [];
|
|
233
|
-
let ai = 0;
|
|
234
|
-
let ci = 0;
|
|
235
|
-
while (ai < answer.length) {
|
|
236
|
-
// Longest match of the remaining answer at any position of the remaining
|
|
237
|
-
// context: one direct extend per context position — O(|ctx|·match) per
|
|
238
|
-
// run, replacing the previous longest-first indexOf countdown whose
|
|
239
|
-
// repeated scans were cubic on long sparse-subsequence answers.
|
|
240
|
-
let bestLen = 0;
|
|
241
|
-
let bestPos = -1;
|
|
242
|
-
for (let p = ci; p < context.length; p++) {
|
|
243
|
-
let l = 0;
|
|
244
|
-
const maxL = Math.min(context.length - p, answer.length - ai);
|
|
245
|
-
if (maxL <= bestLen) {
|
|
246
|
-
break; // no later position can beat the best
|
|
247
|
-
}
|
|
248
|
-
while (l < maxL && context[p + l] === answer[ai + l]) {
|
|
249
|
-
l++;
|
|
250
|
-
}
|
|
251
|
-
if (l > bestLen) {
|
|
252
|
-
bestLen = l;
|
|
253
|
-
bestPos = p;
|
|
254
|
-
if (ai + l === answer.length) {
|
|
255
|
-
break; // the whole remainder matched
|
|
256
|
-
}
|
|
257
|
-
}
|
|
184
|
+
const pos = indexOf(context, answer, 0);
|
|
185
|
+
if (pos >= 0) {
|
|
186
|
+
return [{ start: pos, end: pos + answer.length, ansLen: answer.length }];
|
|
258
187
|
}
|
|
259
|
-
|
|
260
|
-
|
|
188
|
+
const runs = [];
|
|
189
|
+
let ai = 0;
|
|
190
|
+
let ci = 0;
|
|
191
|
+
while (ai < answer.length) {
|
|
192
|
+
// Longest match of the remaining answer at any position of the remaining
|
|
193
|
+
// context: one direct extend per context position — O(|ctx|·match) per
|
|
194
|
+
// run, replacing the previous longest-first indexOf countdown whose
|
|
195
|
+
// repeated scans were cubic on long sparse-subsequence answers.
|
|
196
|
+
let bestLen = 0;
|
|
197
|
+
let bestPos = -1;
|
|
198
|
+
for (let p = ci; p < context.length; p++) {
|
|
199
|
+
let l = 0;
|
|
200
|
+
const maxL = Math.min(context.length - p, answer.length - ai);
|
|
201
|
+
if (maxL <= bestLen)
|
|
202
|
+
break; // no later position can beat the best
|
|
203
|
+
while (l < maxL && context[p + l] === answer[ai + l])
|
|
204
|
+
l++;
|
|
205
|
+
if (l > bestLen) {
|
|
206
|
+
bestLen = l;
|
|
207
|
+
bestPos = p;
|
|
208
|
+
if (ai + l === answer.length)
|
|
209
|
+
break; // the whole remainder matched
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (bestLen === 0)
|
|
213
|
+
return null;
|
|
214
|
+
runs.push({ start: bestPos, end: bestPos + bestLen, ansLen: bestLen });
|
|
215
|
+
ai += bestLen;
|
|
216
|
+
ci = bestPos + bestLen;
|
|
261
217
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
} else {
|
|
273
|
-
merged.push({ ...r });
|
|
218
|
+
const merged = [];
|
|
219
|
+
for (const r of runs) {
|
|
220
|
+
const last = merged[merged.length - 1];
|
|
221
|
+
if (last && r.start === last.end) {
|
|
222
|
+
last.end = r.end;
|
|
223
|
+
last.ansLen += r.ansLen;
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
merged.push({ ...r });
|
|
227
|
+
}
|
|
274
228
|
}
|
|
275
|
-
|
|
276
|
-
return merged.length > 0 ? merged : null;
|
|
229
|
+
return merged.length > 0 ? merged : null;
|
|
277
230
|
}
|
|
278
231
|
/** Check whether an anchor is a span-shaped skill exemplar: it represents a
|
|
279
232
|
* fact whose context and answer together form a span-in-context pattern.
|
|
@@ -281,42 +234,41 @@ export function answerRunsInContext(_ctx, context, answer) {
|
|
|
281
234
|
* itself is the context. Otherwise the anchor's prevOf parents provide
|
|
282
235
|
* candidate contexts, and the longest one whose span is span-shaped wins. */
|
|
283
236
|
export async function skillExemplar(ctx, anchor, guide) {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
237
|
+
if (ctx.store.hasNext(anchor)) {
|
|
238
|
+
const contextBytes = read(ctx, anchor);
|
|
239
|
+
const answerBytes = await follow(ctx, anchor, guide);
|
|
240
|
+
if (answerBytes !== null && isSpanShaped(ctx, contextBytes, answerBytes)) {
|
|
241
|
+
return { contextBytes, answerBytes };
|
|
242
|
+
}
|
|
243
|
+
return null;
|
|
289
244
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
spanShaped.push({ id: p, bytes: ctxB });
|
|
245
|
+
const answerBytes = read(ctx, anchor);
|
|
246
|
+
// Candidate contexts, capped at the hub bound (a common answer's reverse
|
|
247
|
+
// fan-in is corpus-sized).
|
|
248
|
+
const capped = ctx.store.prevFirst(anchor, hubBound(ctx));
|
|
249
|
+
const spanShaped = [];
|
|
250
|
+
for (const p of capped) {
|
|
251
|
+
const ctxB = read(ctx, p);
|
|
252
|
+
if (ctxB.length > 0 && isSpanShaped(ctx, ctxB, answerBytes)) {
|
|
253
|
+
spanShaped.push({ id: p, bytes: ctxB });
|
|
254
|
+
}
|
|
301
255
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
}
|
|
319
|
-
return { contextBytes, answerBytes };
|
|
256
|
+
if (spanShaped.length === 0)
|
|
257
|
+
return null;
|
|
258
|
+
// Among span-shaped contexts, the longest wins (the smallest spanning frame
|
|
259
|
+
// heuristic's dual: more frame to locate in the query); the query gist,
|
|
260
|
+
// when given, breaks LENGTH TIES via chooseAmong — the same reverse-regime
|
|
261
|
+
// disambiguator every context pick uses, whose gist cache spares the
|
|
262
|
+
// re-fold this block once paid per tied candidate. Same strict first-seen
|
|
263
|
+
// tie-break as the hand loop it replaces.
|
|
264
|
+
const maxLen = Math.max(...spanShaped.map((s) => s.bytes.length));
|
|
265
|
+
const longest = spanShaped.filter((s) => s.bytes.length === maxLen);
|
|
266
|
+
let contextBytes = longest[0].bytes;
|
|
267
|
+
if (guide && longest.length > 1) {
|
|
268
|
+
const pick = chooseAmong(ctx, longest.map((s) => s.id), guide).id;
|
|
269
|
+
contextBytes = longest.find((s) => s.id === pick).bytes;
|
|
270
|
+
}
|
|
271
|
+
return { contextBytes, answerBytes };
|
|
320
272
|
}
|
|
321
273
|
/** Whether the answer is a SPARSE subsequence of the context (bytes in
|
|
322
274
|
* order, arbitrary gaps) — the OPEN span-shape reading (see the section
|
|
@@ -333,13 +285,12 @@ export async function skillExemplar(ctx, anchor, guide) {
|
|
|
333
285
|
* (a full river fold) per CANDIDATE in skillExemplar's √N-capped loop —
|
|
334
286
|
* pure cost, no discrimination. */
|
|
335
287
|
export function isSpanShaped(_ctx, context, answer) {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
288
|
+
let ai = 0;
|
|
289
|
+
for (let ci = 0; ci < context.length && ai < answer.length; ci++) {
|
|
290
|
+
if (context[ci] === answer[ai])
|
|
291
|
+
ai++;
|
|
340
292
|
}
|
|
341
|
-
|
|
342
|
-
return ai === answer.length;
|
|
293
|
+
return ai === answer.length;
|
|
343
294
|
}
|
|
344
295
|
/** STRICT containment: the answer's resolved node appears in the context's
|
|
345
296
|
* folded tree, or the answer occurs as one CONTIGUOUS byte run of the
|
|
@@ -350,47 +301,42 @@ export function isSpanShaped(_ctx, context, answer) {
|
|
|
350
301
|
* c…o…l…d in order), and gating fusion on it silently starved multi-topic
|
|
351
302
|
* queries of their further points of attention. */
|
|
352
303
|
export function containsSpan(ctx, context, answer) {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
return true;
|
|
304
|
+
const ansId = resolve(ctx, answer);
|
|
305
|
+
if (ansId !== null) {
|
|
306
|
+
let found = false;
|
|
307
|
+
foldTree(ctx, perceive(ctx, context), 0, (_n, _s, _e, node) => {
|
|
308
|
+
if (node === ansId)
|
|
309
|
+
found = true;
|
|
310
|
+
});
|
|
311
|
+
if (found)
|
|
312
|
+
return true;
|
|
363
313
|
}
|
|
364
|
-
|
|
365
|
-
return indexOf(context, answer, 0) >= 0;
|
|
314
|
+
return indexOf(context, answer, 0) >= 0;
|
|
366
315
|
}
|
|
367
316
|
// ── Pipeline mechanism ──────────────────────────────────────────────────────
|
|
368
317
|
export const extractionMechanism = {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
unexplained: ex.unexplained,
|
|
394
|
-
}];
|
|
395
|
-
},
|
|
318
|
+
name: "extraction",
|
|
319
|
+
provenance: "extract",
|
|
320
|
+
async floor(_ctx, _query, pre, worthRunning) {
|
|
321
|
+
// Extraction's floor is always exactly CONCEPT+STEP when it exists —
|
|
322
|
+
// same investment discipline as CAST's (see cast.ts): when the bound
|
|
323
|
+
// already cannot beat the incumbent, return it UNINVESTED (never
|
|
324
|
+
// first-touch the climb just to be pruned).
|
|
325
|
+
if (!worthRunning(CONCEPT + STEP))
|
|
326
|
+
return CONCEPT + STEP;
|
|
327
|
+
if ((await pre.attention()).ranked.length === 0)
|
|
328
|
+
return null;
|
|
329
|
+
return CONCEPT + STEP;
|
|
330
|
+
},
|
|
331
|
+
async run(ctx, query, pre) {
|
|
332
|
+
const ex = await extractBySkill(ctx, query, pre);
|
|
333
|
+
if (!ex)
|
|
334
|
+
return [];
|
|
335
|
+
return [{
|
|
336
|
+
bytes: ex.bytes,
|
|
337
|
+
accounted: ex.accounted,
|
|
338
|
+
moves: CONCEPT + STEP * ex.accounted.length,
|
|
339
|
+
unexplained: ex.unexplained,
|
|
340
|
+
}];
|
|
341
|
+
},
|
|
396
342
|
};
|
|
@@ -2,16 +2,12 @@ import type { MindContext } from "../types.js";
|
|
|
2
2
|
import type { PipelineMechanism, Precomputed } from "../pipeline-mechanism.js";
|
|
3
3
|
/** A recall result. */
|
|
4
4
|
export interface RecallResult {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
bytes: Uint8Array;
|
|
6
|
+
echoed: boolean;
|
|
7
|
+
accounted: Array<[number, number]>;
|
|
8
|
+
moves: number;
|
|
9
|
+
unexplained: string;
|
|
10
10
|
}
|
|
11
11
|
/** Recall the answer by resonating the whole query against the content index. */
|
|
12
|
-
export declare function recallByResonance(
|
|
13
|
-
ctx: MindContext,
|
|
14
|
-
query: Uint8Array,
|
|
15
|
-
pre: Precomputed,
|
|
16
|
-
): Promise<RecallResult | null>;
|
|
12
|
+
export declare function recallByResonance(ctx: MindContext, query: Uint8Array, pre: Precomputed): Promise<RecallResult | null>;
|
|
17
13
|
export declare const recallMechanism: PipelineMechanism;
|