pi-mega-compact 0.8.25 → 0.8.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/raptor-inject-summaries.test.js +10 -3
- package/dist/src/recall.js +24 -9
- package/dist/src/store/sqlite/turns.js +1 -3
- package/package.json +1 -1
- package/src/raptor-inject-summaries.test.ts +22 -6
- package/src/recall.ts +436 -368
- package/src/store/sqlite/turns.ts +177 -167
|
@@ -49,11 +49,17 @@ function msg(text, toolName) {
|
|
|
49
49
|
: { role: "user", text };
|
|
50
50
|
}
|
|
51
51
|
function seedTwoTopics(store, sid, per) {
|
|
52
|
-
const topics = [
|
|
52
|
+
const topics = [
|
|
53
|
+
"database connection pool postgres",
|
|
54
|
+
"user interface button react",
|
|
55
|
+
];
|
|
53
56
|
for (let i = 1; i <= per * 2; i++) {
|
|
54
57
|
compactSession({
|
|
55
58
|
sessionId: sid,
|
|
56
|
-
messages: [
|
|
59
|
+
messages: [
|
|
60
|
+
msg(`${topics[i % 2]} checkpoint ${i} alpha beta`),
|
|
61
|
+
msg(`ack ${i}`, "Edit"),
|
|
62
|
+
],
|
|
57
63
|
keepFrom: 2,
|
|
58
64
|
timestamp: i,
|
|
59
65
|
}, store);
|
|
@@ -80,7 +86,8 @@ test("S25-P2: RAPTOR_INJECT_SUMMARIES=true prepends a hierarchical overview head
|
|
|
80
86
|
assert.ok(r.block.length > 0, "recall block is non-empty");
|
|
81
87
|
assert.ok(r.block.includes("hierarchical overview"), "overview header present when flag ON");
|
|
82
88
|
assert.ok(r.block.includes("Recalled context"), "detailed recall block still present after overview");
|
|
83
|
-
assert.ok(r.block.indexOf("hierarchical overview") <
|
|
89
|
+
assert.ok(r.block.indexOf("hierarchical overview") <
|
|
90
|
+
r.block.indexOf("Recalled context"), "overview precedes detail");
|
|
84
91
|
});
|
|
85
92
|
// ─── 2. Flag OFF → no overview (detail-only, unchanged behavior) ────────────
|
|
86
93
|
test("S25-P2: RAPTOR_INJECT_SUMMARIES=false → no overview (detail-only)", () => {
|
package/dist/src/recall.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* extension decides where it lands.
|
|
15
15
|
*/
|
|
16
16
|
import { recall as searchRecall } from "./engine.js";
|
|
17
|
-
import { vectorWasInjected, vectorMarkInjected, vectorSearchAsync } from "./vectorStore.js";
|
|
17
|
+
import { vectorWasInjected, vectorMarkInjected, vectorSearchAsync, } from "./vectorStore.js";
|
|
18
18
|
import { estimateBlockTokens } from "./tokens.js";
|
|
19
19
|
import { defaultEmbedder, cosineSimilarity } from "./embedder.js";
|
|
20
20
|
import { rehydrateRaptorTree, isShadowMode } from "./dedup/raptor/index.js";
|
|
@@ -29,7 +29,9 @@ export function formatRecallBlock(hits) {
|
|
|
29
29
|
// S17: label a cross-repo hit with its source repo (the repoId doubles as
|
|
30
30
|
// that repo's stateDir, so the last path segment is the repo's display
|
|
31
31
|
// name). Same-repo hits (no repoId) stay unlabeled.
|
|
32
|
-
const repoName = h.repoId
|
|
32
|
+
const repoName = h.repoId
|
|
33
|
+
? ` (from repo ${h.repoId.split("/").filter(Boolean).pop() ?? h.repoId})`
|
|
34
|
+
: "";
|
|
33
35
|
// S42B: a RAPTOR cluster node hit (not a stored checkpoint) is labeled as a
|
|
34
36
|
// hierarchical summary and uses raptorSummary as its body. No Key files line
|
|
35
37
|
// (cluster nodes carry no file list).
|
|
@@ -58,7 +60,9 @@ export function formatRaptorBlock(nodes) {
|
|
|
58
60
|
if (nodes.length === 0)
|
|
59
61
|
return "";
|
|
60
62
|
const parts = nodes.map((n, i) => {
|
|
61
|
-
const score = n.score !== undefined
|
|
63
|
+
const score = n.score !== undefined
|
|
64
|
+
? ` (relevance ${(n.score * 100).toFixed(0)}%)`
|
|
65
|
+
: "";
|
|
62
66
|
const label = n.level === 0
|
|
63
67
|
? `Session overview [${i + 1}]${score}`
|
|
64
68
|
: `Cluster summary [${i + 1}] (level ${n.level})${score}`;
|
|
@@ -203,11 +207,19 @@ function raptorOverviewBlock(store, sessionId, query) {
|
|
|
203
207
|
const qv = store.embedder.embed(query);
|
|
204
208
|
// Root (level 0) first, then the top level-1 clusters by cosine to the query.
|
|
205
209
|
const nodes = [
|
|
206
|
-
{
|
|
210
|
+
{
|
|
211
|
+
summary: root.summary,
|
|
212
|
+
level: root.level,
|
|
213
|
+
score: cosineSimilarity(qv, root.embedding),
|
|
214
|
+
},
|
|
207
215
|
];
|
|
208
216
|
const level1 = [...tree.nodes.values()]
|
|
209
217
|
.filter((n) => n.level === 1 && n.summary)
|
|
210
|
-
.map((n) => ({
|
|
218
|
+
.map((n) => ({
|
|
219
|
+
summary: n.summary,
|
|
220
|
+
level: n.level,
|
|
221
|
+
score: cosineSimilarity(qv, n.embedding),
|
|
222
|
+
}))
|
|
211
223
|
.sort((a, b) => b.score - a.score)
|
|
212
224
|
.slice(0, 3);
|
|
213
225
|
nodes.push(...level1);
|
|
@@ -329,16 +341,19 @@ export async function recallAndInlineAsync(opts, store) {
|
|
|
329
341
|
const skipCrossRepoHits = !!opts.crossRepo && !opts.globalIndexDir;
|
|
330
342
|
if (skipCrossRepoHits) {
|
|
331
343
|
try {
|
|
332
|
-
console.warn("[mega-compact:recall] cross-repo recall enabled but globalIndexDir is unset — "
|
|
333
|
-
|
|
344
|
+
console.warn("[mega-compact:recall] cross-repo recall enabled but globalIndexDir is unset — " +
|
|
345
|
+
"skipping cross-repo injection to avoid re-injecting undeduped foreign checkpoints");
|
|
346
|
+
}
|
|
347
|
+
catch {
|
|
348
|
+
/* ignore */
|
|
334
349
|
}
|
|
335
|
-
catch { /* ignore */ }
|
|
336
350
|
}
|
|
337
351
|
for (const h of hits) {
|
|
338
352
|
// F2: skip foreign hits when we can't dedup them machine-wide.
|
|
339
353
|
if (skipCrossRepoHits && h.repoId)
|
|
340
354
|
continue;
|
|
341
|
-
if (skip &&
|
|
355
|
+
if (skip &&
|
|
356
|
+
vectorWasInjected(store, opts.sessionId, h.checkpoint.checkpointId))
|
|
342
357
|
continue;
|
|
343
358
|
// S18: machine-wide injected-set — a foreign checkpoint already injected
|
|
344
359
|
// (in any session) is never re-injected. Only applies to cross-repo hits
|
|
@@ -98,9 +98,7 @@ export function getTurn(conversationId, turnIndex, stateDir = getStateDir()) {
|
|
|
98
98
|
/** Get a turn by its global id. */
|
|
99
99
|
export function getTurnById(turnId, stateDir = getStateDir()) {
|
|
100
100
|
const db = openStore(stateDir);
|
|
101
|
-
const row = db
|
|
102
|
-
.prepare("SELECT * FROM turns WHERE id = ?")
|
|
103
|
-
.get(turnId);
|
|
101
|
+
const row = db.prepare("SELECT * FROM turns WHERE id = ?").get(turnId);
|
|
104
102
|
return row ? rowToTurn(row) : null;
|
|
105
103
|
}
|
|
106
104
|
/** All turn_recall rows for a turn (what was injected at that turn). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-mega-compact",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.26",
|
|
4
4
|
"description": "Layered, local, vector-backed context compressor for pi — supersede/collapse/cluster compaction with deduped inline recall.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BSD-3-Clause",
|
|
@@ -59,12 +59,18 @@ function msg(text: string, toolName?: string): EngineMessage {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
function seedTwoTopics(store: VectorStore, sid: string, per: number) {
|
|
62
|
-
const topics = [
|
|
62
|
+
const topics = [
|
|
63
|
+
"database connection pool postgres",
|
|
64
|
+
"user interface button react",
|
|
65
|
+
];
|
|
63
66
|
for (let i = 1; i <= per * 2; i++) {
|
|
64
67
|
compactSession(
|
|
65
68
|
{
|
|
66
69
|
sessionId: sid,
|
|
67
|
-
messages: [
|
|
70
|
+
messages: [
|
|
71
|
+
msg(`${topics[i % 2]} checkpoint ${i} alpha beta`),
|
|
72
|
+
msg(`ack ${i}`, "Edit"),
|
|
73
|
+
],
|
|
68
74
|
keepFrom: 2,
|
|
69
75
|
timestamp: i,
|
|
70
76
|
},
|
|
@@ -109,7 +115,8 @@ test("S25-P2: RAPTOR_INJECT_SUMMARIES=true prepends a hierarchical overview head
|
|
|
109
115
|
"detailed recall block still present after overview",
|
|
110
116
|
);
|
|
111
117
|
assert.ok(
|
|
112
|
-
r.block.indexOf("hierarchical overview") <
|
|
118
|
+
r.block.indexOf("hierarchical overview") <
|
|
119
|
+
r.block.indexOf("Recalled context"),
|
|
113
120
|
"overview precedes detail",
|
|
114
121
|
);
|
|
115
122
|
});
|
|
@@ -168,9 +175,18 @@ test("S25-P2: formatRaptorBlock labels root as 'Session overview' + clusters as
|
|
|
168
175
|
{ summary: "root overview text", level: 0, score: 0.9 },
|
|
169
176
|
{ summary: "cluster A text", level: 1, score: 0.7 },
|
|
170
177
|
]);
|
|
171
|
-
assert.ok(
|
|
172
|
-
|
|
173
|
-
|
|
178
|
+
assert.ok(
|
|
179
|
+
/The following hierarchical overview/.test(block),
|
|
180
|
+
"preamble present",
|
|
181
|
+
);
|
|
182
|
+
assert.ok(
|
|
183
|
+
/Session overview \[1\] \(relevance 90%\)/.test(block),
|
|
184
|
+
"root labeled",
|
|
185
|
+
);
|
|
186
|
+
assert.ok(
|
|
187
|
+
/Cluster summary \[2\] \(level 1\) \(relevance 70%\)/.test(block),
|
|
188
|
+
"cluster labeled",
|
|
189
|
+
);
|
|
174
190
|
assert.ok(block.includes("root overview text"), "root body present");
|
|
175
191
|
assert.ok(block.includes("cluster A text"), "cluster body present");
|
|
176
192
|
assert.equal(formatRaptorBlock([]), "", "empty input → empty string");
|