pi-mega-compact 0.11.7 → 0.11.8
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/extensions/dashboard-server/routes-memory-map.js +8 -15
- package/dist/extensions/mega-commands.js +65 -5
- package/dist/extensions/mega-config.js +6 -0
- package/dist/extensions/mega-events/context-handler.js +65 -0
- package/dist/src/config/turns.js +2 -1
- package/dist/src/memoryGraph/embedding.js +50 -0
- package/dist/src/memoryGraph/gates.js +379 -0
- package/dist/src/memoryGraph/sources.js +390 -0
- package/dist/src/memoryGraph.js +133 -259
- package/dist/src/store/sqlite/embedding-cache.js +71 -0
- package/dist/src/store/sqlite.js +1 -0
- package/dist/src/topics/cluster.js +10 -2
- package/extensions/dashboard-client/dist/assets/{AchievementTiles-DR9cLGDL.js → AchievementTiles-BESF1HT1.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AchievementTiles-DR9cLGDL.js.map → AchievementTiles-BESF1HT1.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{AchievementsTab-svNUgJ1e.js → AchievementsTab-8RzNnz8u.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AchievementsTab-svNUgJ1e.js.map → AchievementsTab-8RzNnz8u.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-ASwhOMjR.js → CacheTab-CUIiUgh3.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-ASwhOMjR.js.map → CacheTab-CUIiUgh3.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ConfigTab-CmmWheuW.js → ConfigTab-D85fB9HP.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ConfigTab-CmmWheuW.js.map → ConfigTab-D85fB9HP.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-CFE7jLsa.js → EventsTab-TCLR81G5.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-CFE7jLsa.js.map → EventsTab-TCLR81G5.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{GameTab-CJAX_th_.js → GameTab-BqneNRYH.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{GameTab-CJAX_th_.js.map → GameTab-BqneNRYH.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-P4SUsvMf.js → MaintenanceTab-LblMg3NB.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-P4SUsvMf.js.map → MaintenanceTab-LblMg3NB.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-CpjMQwRr.js +2 -0
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-CpjMQwRr.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{MetricsTab-CHQscixy.js → MetricsTab-LxNtNzLQ.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-CHQscixy.js.map → MetricsTab-LxNtNzLQ.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-DBs4DJdd.js → OverviewTab-BXKUZbT6.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-DBs4DJdd.js.map → OverviewTab-BXKUZbT6.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-DeFz76L5.js → ReposTab-DTooKeAi.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-DeFz76L5.js.map → ReposTab-DTooKeAi.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-BWNsj1OB.js → SessionsTab-Devulgm9.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-BWNsj1OB.js.map → SessionsTab-Devulgm9.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SetupTab-uS6A0xmw.js → SetupTab-By9eqUoE.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SetupTab-uS6A0xmw.js.map → SetupTab-By9eqUoE.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-CiqCOQuL.js → TimeSavedCard-z4M8TDLm.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-CiqCOQuL.js.map → TimeSavedCard-z4M8TDLm.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/TopicsTab-bCGZSfLN.js +2 -0
- package/extensions/dashboard-client/dist/assets/TopicsTab-bCGZSfLN.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{TurnsTab-CVz4f4-o.js → TurnsTab-m_XzE0H4.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-CVz4f4-o.js.map → TurnsTab-m_XzE0H4.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-DrktNOoT.js → index-PRkilPXw.js} +3 -3
- package/extensions/dashboard-client/dist/assets/{index-DrktNOoT.js.map → index-PRkilPXw.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-Bf5LOLSi.js → useSSE-CGzgc2IK.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-Bf5LOLSi.js.map → useSSE-CGzgc2IK.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/src/memory-map-layout.ts +161 -0
- package/extensions/dashboard-client/src/memory-map-shapes.tsx +126 -0
- package/extensions/dashboard-client/src/tabs/MemoryMapTab.tsx +201 -165
- package/extensions/dashboard-client/src/tabs/ReposTab.tsx +21 -0
- package/extensions/dashboard-client/src/tabs/TopicsTab.tsx +1 -1
- package/extensions/dashboard-server/api-contracts/index.ts +2 -1
- package/extensions/dashboard-server/api-contracts/memory-map.ts +14 -6
- package/extensions/dashboard-server/routes-memory-map.ts +21 -29
- package/extensions/mega-commands.ts +63 -6
- package/extensions/mega-config.ts +18 -0
- package/extensions/mega-events/context-handler.ts +80 -0
- package/package.json +1 -1
- package/src/config/turns.ts +5 -2
- package/src/memoryGraph/embedding.ts +63 -0
- package/src/memoryGraph/gates.ts +451 -0
- package/src/memoryGraph/sources.ts +484 -0
- package/src/memoryGraph.ts +188 -350
- package/src/store/sqlite/embedding-cache.ts +81 -0
- package/src/store/sqlite.ts +1 -0
- package/src/topics/cluster.ts +11 -1
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-BBiH76eF.js +0 -2
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-BBiH76eF.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/TopicsTab-BWWQxltv.js +0 -2
- package/extensions/dashboard-client/dist/assets/TopicsTab-BWWQxltv.js.map +0 -1
|
@@ -11,13 +11,13 @@ import { sessionEntryToContextMessages } from "@earendil-works/pi-coding-agent";
|
|
|
11
11
|
import { normalizeSessionId } from "../src/store.js";
|
|
12
12
|
import { listCheckpoints, latestModelSnapshot, countInjectedGlobal, listRepoRegistry } from "../src/store/sqlite.js";
|
|
13
13
|
import { decompressSmart } from "../src/store/compression.js";
|
|
14
|
-
import { loadMetrics, fpRate, p95, defaultMetricsPath } from "../src/monitoring.js";
|
|
14
|
+
import { loadMetrics, fpRate, p95, defaultMetricsPath, defaultEventsPath } from "../src/monitoring.js";
|
|
15
15
|
import { type MegaRuntime, C, recentUserQuery } from "./mega-runtime.js";
|
|
16
16
|
import { runCompact, doRecall, doRecallAsync } from "./mega-pipeline.js";
|
|
17
17
|
import { vectorStats, vectorRepoStats, vectorDataInvariant } from "../src/vectorStore.js";
|
|
18
18
|
import type { MegaConfig } from "./mega-config.js";
|
|
19
19
|
import { spawnSync } from "node:child_process"; // guardrails-allow PREVENT-PI-004: local subprocess detection only (no network)
|
|
20
|
-
import { mkdirSync, writeFileSync } from "node:fs";
|
|
20
|
+
import { mkdirSync, writeFileSync, existsSync, readFileSync } from "node:fs";
|
|
21
21
|
import path from "node:path";
|
|
22
22
|
import os from "node:os";
|
|
23
23
|
import { createRequire } from "node:module";
|
|
@@ -280,6 +280,52 @@ export function registerCommands(pi: ExtensionAPI, runtime: MegaRuntime, config:
|
|
|
280
280
|
},
|
|
281
281
|
});
|
|
282
282
|
|
|
283
|
+
/**
|
|
284
|
+
* Try to peek at recent events.log and mega-compact.log for low recall quality signals.
|
|
285
|
+
* Best-effort, non-fatal — returns true if a low-quality signal was found.
|
|
286
|
+
*/
|
|
287
|
+
/**
|
|
288
|
+
* Try to peek at recent events.log and mega-compact.log for low recall quality signals.
|
|
289
|
+
* Best-effort, non-fatal — returns true if a low-quality signal was found.
|
|
290
|
+
*/
|
|
291
|
+
function checkRecallQuality(stateDir: string): boolean {
|
|
292
|
+
const eventPaths = [
|
|
293
|
+
defaultEventsPath(stateDir),
|
|
294
|
+
path.join(stateDir, "mega-compact.log"),
|
|
295
|
+
];
|
|
296
|
+
for (const fp of eventPaths) {
|
|
297
|
+
try {
|
|
298
|
+
if (!existsSync(fp)) continue;
|
|
299
|
+
const buf = readFileSync(fp, { encoding: "utf-8" });
|
|
300
|
+
// Only scan last 64 KiB of the file
|
|
301
|
+
const tail = buf.length > 65536 ? buf.slice(buf.length - 65536) : buf;
|
|
302
|
+
const lines = tail.split("\n").filter(Boolean);
|
|
303
|
+
for (const line of lines) {
|
|
304
|
+
try {
|
|
305
|
+
// guardrails-allow PREVENT-001: JSON.parse with null check
|
|
306
|
+
const parsed = JSON.parse(line);
|
|
307
|
+
if (typeof parsed !== "object" || parsed === null) continue;
|
|
308
|
+
// Check for recall_metrics_low_quality event (latest format)
|
|
309
|
+
if (parsed.event === "recall_metrics_low_quality" && parsed.score !== undefined) {
|
|
310
|
+
if (typeof parsed.score === "number" && parsed.score < 0.4) return true;
|
|
311
|
+
}
|
|
312
|
+
// Check for recall_metrics with low relevanceScore
|
|
313
|
+
if (parsed.event === "recall_metrics" && parsed.relevanceScore !== undefined) {
|
|
314
|
+
if (typeof parsed.relevanceScore === "number" && parsed.relevanceScore < 0.4) return true;
|
|
315
|
+
}
|
|
316
|
+
// Check for RecallQualityEvent format from monitoring.ts
|
|
317
|
+
if (parsed.score !== undefined && typeof parsed.score === "number" && parsed.score < 0.4) return true;
|
|
318
|
+
} catch {
|
|
319
|
+
// skip unparseable lines
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
} catch {
|
|
323
|
+
// non-fatal: skip if can't read
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
|
|
283
329
|
pi.registerCommand("mega-setup", {
|
|
284
330
|
description:
|
|
285
331
|
"Local embedding setup wizard — detect available embedders and configure mega-compact.",
|
|
@@ -355,6 +401,11 @@ export function registerCommands(pi: ExtensionAPI, runtime: MegaRuntime, config:
|
|
|
355
401
|
// require error
|
|
356
402
|
}
|
|
357
403
|
|
|
404
|
+
// --- 2.5 Quality check ---
|
|
405
|
+
// Scan recent events for low recall quality (best-effort, non-fatal)
|
|
406
|
+
const stateDir = process.env["MEGACOMPACT_STATE_DIR"] ?? path.join(os.homedir(), ".pi", "mega-compact");
|
|
407
|
+
const lowRecallQuality = checkRecallQuality(stateDir);
|
|
408
|
+
|
|
358
409
|
// --- 3. Build choices ---
|
|
359
410
|
interface Choice {
|
|
360
411
|
label: string;
|
|
@@ -402,6 +453,11 @@ export function registerCommands(pi: ExtensionAPI, runtime: MegaRuntime, config:
|
|
|
402
453
|
|
|
403
454
|
// --- 4. Prompt user ---
|
|
404
455
|
log(`Current embedder: ${currentEmbedder}`);
|
|
456
|
+
if (lowRecallQuality) {
|
|
457
|
+
log(
|
|
458
|
+
"Tap: recent recall quality is low. Consider switching to Ollama or llama.cpp for better results.",
|
|
459
|
+
);
|
|
460
|
+
}
|
|
405
461
|
let selected = "trigram";
|
|
406
462
|
if (choices.length > 1) {
|
|
407
463
|
try {
|
|
@@ -435,7 +491,6 @@ export function registerCommands(pi: ExtensionAPI, runtime: MegaRuntime, config:
|
|
|
435
491
|
const envContent = lines.join("\n") + "\n";
|
|
436
492
|
|
|
437
493
|
// Write .mega-compact.env to state dir
|
|
438
|
-
const stateDir = process.env["MEGACOMPACT_STATE_DIR"] ?? path.join(os.homedir(), ".pi", "mega-compact");
|
|
439
494
|
try {
|
|
440
495
|
mkdirSync(stateDir, { recursive: true });
|
|
441
496
|
writeFileSync(path.join(stateDir, ".mega-compact.env"), envContent, "utf-8");
|
|
@@ -457,8 +512,6 @@ export function registerCommands(pi: ExtensionAPI, runtime: MegaRuntime, config:
|
|
|
457
512
|
`export MEGACOMPACT_EMBEDDING_URL="${url}"`,
|
|
458
513
|
];
|
|
459
514
|
const envContent = lines.join("\n") + "\n";
|
|
460
|
-
|
|
461
|
-
const stateDir = process.env["MEGACOMPACT_STATE_DIR"] ?? path.join(os.homedir(), ".pi", "mega-compact");
|
|
462
515
|
try {
|
|
463
516
|
mkdirSync(stateDir, { recursive: true });
|
|
464
517
|
writeFileSync(path.join(stateDir, ".mega-compact.env"), envContent, "utf-8");
|
|
@@ -477,7 +530,11 @@ export function registerCommands(pi: ExtensionAPI, runtime: MegaRuntime, config:
|
|
|
477
530
|
"ONNX Runtime configuration is experimental.\n\nSet MEGACOMPACT_EMBEDDING_URL to your local ONNX server URL, or see docs for manual setup.\nKeeping TrigramEmbedder for now.",
|
|
478
531
|
);
|
|
479
532
|
} else {
|
|
480
|
-
|
|
533
|
+
const qualityTip =
|
|
534
|
+
lowRecallQuality
|
|
535
|
+
? " Recent recall quality is low — consider running /mega-setup again to switch to Ollama or llama.cpp for better results."
|
|
536
|
+
: "";
|
|
537
|
+
log(`Keeping TrigramEmbedder (default). No configuration needed.${qualityTip}`);
|
|
481
538
|
}
|
|
482
539
|
} catch (e) {
|
|
483
540
|
ctx.ui.notify(`[mega-compact] /mega-setup failed: ${String(e)}`);
|
|
@@ -183,6 +183,18 @@ export interface MegaConfig {
|
|
|
183
183
|
ragRecallMetrics: boolean;
|
|
184
184
|
/** S57 B4: Memory graph traversal (dashboard-oriented). */
|
|
185
185
|
ragMemoryGraph: boolean;
|
|
186
|
+
/** D1: Seed initial wiki topic model from live turns when no context_chunks exist yet. */
|
|
187
|
+
wikiSeedFromTurns: boolean;
|
|
188
|
+
/** D3 Source A: Include structural turn nodes in the memory graph (metadata only, no content). */
|
|
189
|
+
memoryGraphSeedTurns: boolean;
|
|
190
|
+
/** D3 Source B: Include raw_transcript content nodes in the memory graph (requires dbMirror). */
|
|
191
|
+
memoryGraphSeedTurnContent: boolean;
|
|
192
|
+
/** D3 Source C: Include memory review nodes in the memory graph. */
|
|
193
|
+
memoryGraphSeedMemories: boolean;
|
|
194
|
+
/** D3 edges: Stricter cosine floor for cross-type edges (e.g. turn↔checkpoint). */
|
|
195
|
+
memoryGraphCrossTypeThreshold: number;
|
|
196
|
+
/** D3 edges: Cosine floor for within-type semantic edges. */
|
|
197
|
+
memoryGraphWithinTypeThreshold: number;
|
|
186
198
|
}
|
|
187
199
|
|
|
188
200
|
function envFlag(name: string, fallback: number): number {
|
|
@@ -350,6 +362,12 @@ export function loadConfig(): MegaConfig {
|
|
|
350
362
|
ragTieredRouter: envBool("MEGACOMPACT_TIERED_ROUTER", false),
|
|
351
363
|
ragRecallMetrics: envBool("MEGACOMPACT_RECALL_METRICS", false),
|
|
352
364
|
ragMemoryGraph: envBool("MEGACOMPACT_MEMORY_GRAPH", false),
|
|
365
|
+
wikiSeedFromTurns: envBool("MEGACOMPACT_WIKI_SEED_FROM_TURNS", true),
|
|
366
|
+
memoryGraphSeedTurns: envBool("MEGACOMPACT_MEMORY_GRAPH_SEED_TURNS", true),
|
|
367
|
+
memoryGraphSeedTurnContent: envBool("MEGACOMPACT_MEMORY_GRAPH_SEED_TURN_CONTENT", true),
|
|
368
|
+
memoryGraphSeedMemories: envBool("MEGACOMPACT_MEMORY_GRAPH_SEED_MEMORIES", true),
|
|
369
|
+
memoryGraphCrossTypeThreshold: envFlag("MEGACOMPACT_MEMORY_GRAPH_CROSS_TYPE_THRESHOLD", 0.85),
|
|
370
|
+
memoryGraphWithinTypeThreshold: envFlag("MEGACOMPACT_MEMORY_GRAPH_WITHIN_TYPE_THRESHOLD", 0.7),
|
|
353
371
|
// D.1: env-overridable recompact delta (minimum context growth % before
|
|
354
372
|
// re-compacting instead of replaying the cached live trim). Default 50.
|
|
355
373
|
recompactPctDelta: envFlag("MEGACOMPACT_RECOMPACT_PCT_DELTA", 50),
|
|
@@ -29,6 +29,8 @@ import {
|
|
|
29
29
|
createTopicStore,
|
|
30
30
|
bumpWikiCompactCounter,
|
|
31
31
|
} from "../../src/topics/index.js";
|
|
32
|
+
import { TrigramEmbedder } from "../../src/embedder.js";
|
|
33
|
+
import type { EmbeddedChunk } from "../../src/topics/types.js";
|
|
32
34
|
import { computeLiveTrimCut, liveTrimSummaryMessage } from "../mega-trim.js";
|
|
33
35
|
import {
|
|
34
36
|
pressureFromPct,
|
|
@@ -306,6 +308,84 @@ export function registerContextHandler(
|
|
|
306
308
|
error: String(wikiErr),
|
|
307
309
|
});
|
|
308
310
|
}
|
|
311
|
+
|
|
312
|
+
// D1: seed the topic model from raw_transcript when context_chunks is
|
|
313
|
+
// thin (pre-compaction). Gated on WIKI_SEED_FROM_TURNS; non-fatal.
|
|
314
|
+
// Seeds buildTopicModel with on-the-fly trigram embeddings from
|
|
315
|
+
// recent raw_transcript rows for the current session.
|
|
316
|
+
try {
|
|
317
|
+
if (
|
|
318
|
+
config.autoWikiEnabled &&
|
|
319
|
+
config.turnsDbEnabled &&
|
|
320
|
+
TurnsConfig.WIKI_SEED_FROM_TURNS
|
|
321
|
+
) {
|
|
322
|
+
const floor = 50;
|
|
323
|
+
const countRow = db
|
|
324
|
+
.prepare(
|
|
325
|
+
`SELECT COUNT(*) AS cnt FROM context_chunks WHERE session_id = ?`,
|
|
326
|
+
)
|
|
327
|
+
.get(runtime.rt.sessionId) as { cnt: number } | undefined;
|
|
328
|
+
const chunkCount = countRow?.cnt ?? 0;
|
|
329
|
+
if (chunkCount < floor) {
|
|
330
|
+
const transcriptRows = db
|
|
331
|
+
.prepare(
|
|
332
|
+
`SELECT DISTINCT content_bytes
|
|
333
|
+
FROM raw_transcript
|
|
334
|
+
WHERE session_id = ?
|
|
335
|
+
AND length(content_bytes) > 10
|
|
336
|
+
ORDER BY seq ASC
|
|
337
|
+
LIMIT 200`,
|
|
338
|
+
)
|
|
339
|
+
.all(runtime.rt.sessionId) as Array<{
|
|
340
|
+
content_bytes: string;
|
|
341
|
+
}>;
|
|
342
|
+
if (transcriptRows.length > 0) {
|
|
343
|
+
const embedder = new TrigramEmbedder();
|
|
344
|
+
const seedChunks: EmbeddedChunk[] = [];
|
|
345
|
+
for (let i = 0; i < transcriptRows.length; i++) {
|
|
346
|
+
const text = transcriptRows[i].content_bytes.trim();
|
|
347
|
+
if (text.length === 0) continue;
|
|
348
|
+
const vec = embedder.embed(text);
|
|
349
|
+
seedChunks.push({
|
|
350
|
+
chunkId: `seed_transcript_${i}`,
|
|
351
|
+
sessionId: runtime.rt.sessionId,
|
|
352
|
+
vec,
|
|
353
|
+
text,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
if (seedChunks.length > 0) {
|
|
357
|
+
const model = buildTopicModel(
|
|
358
|
+
db,
|
|
359
|
+
{
|
|
360
|
+
kRange: [
|
|
361
|
+
TurnsConfig.WIKI_K_MIN,
|
|
362
|
+
TurnsConfig.WIKI_K_MAX,
|
|
363
|
+
],
|
|
364
|
+
labelTopTerms:
|
|
365
|
+
TurnsConfig.WIKI_LABEL_TOP_TERMS,
|
|
366
|
+
restarts: 5,
|
|
367
|
+
seed: 0x9e3779b9,
|
|
368
|
+
},
|
|
369
|
+
seedChunks,
|
|
370
|
+
);
|
|
371
|
+
createTopicStore(
|
|
372
|
+
runtime.currentStateDir,
|
|
373
|
+
).replaceTopicModel(model);
|
|
374
|
+
runtime.logger.info("wiki_seed", {
|
|
375
|
+
clusterCount: model.k,
|
|
376
|
+
sourceChunks: seedChunks.length,
|
|
377
|
+
totalChunks: model.totalChunks,
|
|
378
|
+
method: "kmeans+tfidf",
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
} catch (seedErr) {
|
|
385
|
+
runtime.logger.warn("wiki_seed_failed", {
|
|
386
|
+
error: String(seedErr),
|
|
387
|
+
});
|
|
388
|
+
}
|
|
309
389
|
// S27 Task 6: Fire-and-forget dedup pipeline.
|
|
310
390
|
// Deduplicates raw_transcript rows for the compacted range.
|
|
311
391
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-mega-compact",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.8",
|
|
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",
|
package/src/config/turns.ts
CHANGED
|
@@ -37,8 +37,10 @@ export interface TurnsConfigShape {
|
|
|
37
37
|
WIKI_K_MAX: number;
|
|
38
38
|
/** S51: how many TF-IDF terms form a topic label (default 5). */
|
|
39
39
|
WIKI_LABEL_TOP_TERMS: number;
|
|
40
|
-
/** S51: rebuild the topic model every Nth compaction (default
|
|
40
|
+
/** S51: rebuild the topic model every Nth compaction (default 3). */
|
|
41
41
|
WIKI_REBUILD_EVERY_N_COMPACTS: number;
|
|
42
|
+
/** D1: seed the topic model from raw_transcript when context_chunks is thin (default true). */
|
|
43
|
+
WIKI_SEED_FROM_TURNS: boolean;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
function envKRange(defMin: number, defMax: number): [number, number] {
|
|
@@ -62,7 +64,8 @@ export function loadTurnsConfig(): TurnsConfigShape {
|
|
|
62
64
|
WIKI_K_MIN: kMin,
|
|
63
65
|
WIKI_K_MAX: kMax,
|
|
64
66
|
WIKI_LABEL_TOP_TERMS: envNum("MEGACOMPACT_WIKI_LABEL_TOP_TERMS", 5),
|
|
65
|
-
WIKI_REBUILD_EVERY_N_COMPACTS: envNum("MEGACOMPACT_WIKI_REBUILD_EVERY",
|
|
67
|
+
WIKI_REBUILD_EVERY_N_COMPACTS: envNum("MEGACOMPACT_WIKI_REBUILD_EVERY", 3),
|
|
68
|
+
WIKI_SEED_FROM_TURNS: envBool("MEGACOMPACT_WIKI_SEED_FROM_TURNS", true),
|
|
66
69
|
};
|
|
67
70
|
}
|
|
68
71
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* embedding.ts — D3 embedding helpers for memoryGraph source builders.
|
|
3
|
+
*
|
|
4
|
+
* Uses TrigramEmbedder with an in-memory SQLite cache table.
|
|
5
|
+
* Isolated here so the main module stays under the 500-line limit.
|
|
6
|
+
*/
|
|
7
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
8
|
+
import { TrigramEmbedder } from "../embedder.js";
|
|
9
|
+
|
|
10
|
+
let _trigramEmbedder: TrigramEmbedder | null = null;
|
|
11
|
+
|
|
12
|
+
function lazyEmbedder(): TrigramEmbedder {
|
|
13
|
+
if (!_trigramEmbedder) {
|
|
14
|
+
_trigramEmbedder = new TrigramEmbedder();
|
|
15
|
+
}
|
|
16
|
+
return _trigramEmbedder;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function simpleHash(s: string): string {
|
|
20
|
+
let h = 0x811c9dc5;
|
|
21
|
+
for (let i = 0; i < s.length; i++) {
|
|
22
|
+
h ^= s.charCodeAt(i);
|
|
23
|
+
h = Math.imul(h, 0x01000193);
|
|
24
|
+
}
|
|
25
|
+
return h.toString(36);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Compute or retrieve a cached embedding vector for a text string.
|
|
29
|
+
* Uses the embedding_cache table (created by the A2 migration) for
|
|
30
|
+
* persistence. Falls back to uncached TrigramEmbedder on table error. */
|
|
31
|
+
export function getOrComputeEmbedding(db: DatabaseSync, content: string): number[] {
|
|
32
|
+
try {
|
|
33
|
+
const hash = simpleHash(content);
|
|
34
|
+
const cached = db
|
|
35
|
+
.prepare("SELECT embedding FROM embedding_cache WHERE content_hash = ?")
|
|
36
|
+
.get(hash) as { embedding: string } | undefined;
|
|
37
|
+
if (cached && typeof cached.embedding === "string") {
|
|
38
|
+
return JSON.parse(cached.embedding) as number[];
|
|
39
|
+
}
|
|
40
|
+
const vec = lazyEmbedder().embed(content);
|
|
41
|
+
db.prepare(
|
|
42
|
+
"INSERT OR REPLACE INTO embedding_cache (content_hash, content, embedding) VALUES (?, ?, ?)",
|
|
43
|
+
).run(hash, content, JSON.stringify(vec));
|
|
44
|
+
return vec;
|
|
45
|
+
} catch {
|
|
46
|
+
return lazyEmbedder().embed(content);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Cosine similarity between two equal-length vectors. */
|
|
51
|
+
export function cosineSimilarity(a: number[], b: number[]): number {
|
|
52
|
+
let dot = 0;
|
|
53
|
+
let na = 0;
|
|
54
|
+
let nb = 0;
|
|
55
|
+
const len = Math.min(a.length, b.length);
|
|
56
|
+
for (let i = 0; i < len; i++) {
|
|
57
|
+
dot += a[i] * b[i];
|
|
58
|
+
na += a[i] * a[i];
|
|
59
|
+
nb += b[i] * b[i];
|
|
60
|
+
}
|
|
61
|
+
const denom = Math.sqrt(na) * Math.sqrt(nb);
|
|
62
|
+
return denom > 0 ? dot / denom : 0;
|
|
63
|
+
}
|