pi-mega-compact 0.11.5 → 0.11.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/dist/extensions/dashboard-server/api-contracts/cache-stripes.js +8 -0
- package/dist/extensions/dashboard-server/api-contracts/endpoints.js +13 -0
- package/dist/extensions/dashboard-server/api-contracts/memory-map.js +1 -0
- package/dist/extensions/dashboard-server/routes-cache.js +28 -0
- package/dist/extensions/dashboard-server/routes-game.js +3 -0
- package/dist/extensions/dashboard-server/routes-memory-map.js +50 -0
- package/dist/extensions/dashboard-server/routes-setup.js +105 -0
- package/dist/extensions/dashboard-server/routes.js +3 -1
- package/dist/extensions/dashboard-server/server.js +7 -1
- package/dist/extensions/mega-compact.js +5 -0
- package/dist/extensions/mega-config.js +5 -0
- package/dist/extensions/mega-events/context-handler.js +10 -2
- package/dist/extensions/mega-events/perf-handler.js +83 -1
- package/dist/extensions/mega-events/separated-prompt.js +248 -0
- package/dist/extensions/mega-runtime/env-loader.js +56 -0
- package/dist/extensions/mega-runtime/reset-runtime.js +1 -0
- package/dist/extensions/mega-runtime/runtime-snapshot.js +1 -0
- package/dist/extensions/mega-runtime/runtime.js +1 -0
- package/dist/extensions/mega-runtime/snapshot.js +1 -0
- package/dist/extensions/mega-runtime/widget.js +5 -0
- package/dist/src/cache-stripe-impl.js +288 -0
- package/dist/src/cache-stripe.js +11 -0
- package/dist/src/config/stopwords.js +24 -0
- package/dist/src/config.js +17 -0
- package/dist/src/memoryGraph.js +299 -0
- package/dist/src/monitoring.js +13 -0
- package/dist/src/queryExpansion.js +163 -0
- package/dist/src/queryReformulation/cache.js +123 -0
- package/dist/src/queryReformulation/rrf.js +29 -0
- package/dist/src/queryReformulation/tfidf.js +61 -0
- package/dist/src/queryReformulation/vagueness.js +33 -0
- package/dist/src/queryReformulation.js +225 -0
- package/dist/src/recallMetrics.js +258 -0
- package/dist/src/store/sqlite/cache-stripes.js +133 -0
- package/dist/src/store/sqlite/fts5-search.js +68 -0
- package/dist/src/store/sqlite/perf-samples.js +1 -0
- package/dist/src/store/sqlite/schema.js +60 -1
- package/dist/src/store/sqlite.js +1 -0
- package/dist/src/store/vectorIndex.js +29 -5
- package/dist/src/tiered-router/types.js +18 -0
- package/dist/src/tieredRouter.js +421 -0
- package/dist/src/vectorStore.js +8 -6
- package/extensions/dashboard-client/dist/assets/{AchievementTiles-BoL7qBOJ.js → AchievementTiles-DR9cLGDL.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AchievementTiles-BoL7qBOJ.js.map → AchievementTiles-DR9cLGDL.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{AchievementsTab-BFDE3HYc.js → AchievementsTab-svNUgJ1e.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AchievementsTab-BFDE3HYc.js.map → AchievementsTab-svNUgJ1e.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/CacheTab-ASwhOMjR.js +2 -0
- package/extensions/dashboard-client/dist/assets/CacheTab-ASwhOMjR.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{ConfigTab-DDFE-ckA.js → ConfigTab-CmmWheuW.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ConfigTab-DDFE-ckA.js.map → ConfigTab-CmmWheuW.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-B6UxZ7PM.js → EventsTab-CFE7jLsa.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-B6UxZ7PM.js.map → EventsTab-CFE7jLsa.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/GameTab-CJAX_th_.js +2 -0
- package/extensions/dashboard-client/dist/assets/{GameTab-DehUX-KE.js.map → GameTab-CJAX_th_.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-0GWRt4wn.js → MaintenanceTab-P4SUsvMf.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-0GWRt4wn.js.map → MaintenanceTab-P4SUsvMf.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-BBiH76eF.js +2 -0
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-BBiH76eF.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{MetricsTab-C3rwMVsN.js → MetricsTab-CHQscixy.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-C3rwMVsN.js.map → MetricsTab-CHQscixy.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-39O9_x5j.js → OverviewTab-DBs4DJdd.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-39O9_x5j.js.map → OverviewTab-DBs4DJdd.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-Cfj6Q5sH.js → ReposTab-DeFz76L5.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-Cfj6Q5sH.js.map → ReposTab-DeFz76L5.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-DXDTCi3r.js → SessionsTab-BWNsj1OB.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-DXDTCi3r.js.map → SessionsTab-BWNsj1OB.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/SetupTab-uS6A0xmw.js +2 -0
- package/extensions/dashboard-client/dist/assets/SetupTab-uS6A0xmw.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-Cw1dJ4e9.js → TimeSavedCard-CiqCOQuL.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-Cw1dJ4e9.js.map → TimeSavedCard-CiqCOQuL.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TopicsTab-BAw972BV.js → TopicsTab-BWWQxltv.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TopicsTab-BAw972BV.js.map → TopicsTab-BWWQxltv.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-Cf5mGIdl.js → TurnsTab-CVz4f4-o.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-Cf5mGIdl.js.map → TurnsTab-CVz4f4-o.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/index-CUNn22iV.css +1 -0
- package/extensions/dashboard-client/dist/assets/{index-4gQ7auad.js → index-DrktNOoT.js} +11 -11
- package/extensions/dashboard-client/dist/assets/index-DrktNOoT.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{useSSE-DNgMuyP6.js → useSSE-Bf5LOLSi.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-DNgMuyP6.js.map → useSSE-Bf5LOLSi.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +2 -2
- package/extensions/dashboard-client/src/App.tsx +24 -8
- package/extensions/dashboard-client/src/api/client.ts +15 -0
- package/extensions/dashboard-client/src/components/CacheHitRateTrendCard.tsx +100 -0
- package/extensions/dashboard-client/src/components/StripeDistributionCard.tsx +110 -0
- package/extensions/dashboard-client/src/components/TabBar.tsx +49 -4
- package/extensions/dashboard-client/src/main.tsx +1 -0
- package/extensions/dashboard-client/src/styles/base.css +30 -0
- package/extensions/dashboard-client/src/styles/cache.css +167 -0
- package/extensions/dashboard-client/src/tabs/CacheTab.tsx +75 -13
- package/extensions/dashboard-client/src/tabs/MemoryMapTab.tsx +440 -0
- package/extensions/dashboard-client/src/tabs/SetupTab.tsx +65 -2
- package/extensions/dashboard-server/api-contracts/cache-stripes.ts +49 -0
- package/extensions/dashboard-server/api-contracts/endpoints.ts +31 -1
- package/extensions/dashboard-server/api-contracts/index.ts +16 -1
- package/extensions/dashboard-server/api-contracts/memory-map.ts +50 -0
- package/extensions/dashboard-server/api-contracts/setup.ts +22 -0
- package/extensions/dashboard-server/routes-cache.ts +36 -1
- package/extensions/dashboard-server/routes-game.ts +3 -0
- package/extensions/dashboard-server/routes-memory-map.ts +105 -0
- package/extensions/dashboard-server/routes-setup.ts +112 -0
- package/extensions/dashboard-server/routes.ts +3 -1
- package/extensions/dashboard-server/server.ts +6 -0
- package/extensions/mega-compact.ts +5 -0
- package/extensions/mega-config.ts +17 -0
- package/extensions/mega-events/context-handler.ts +10 -2
- package/extensions/mega-events/perf-handler.ts +111 -1
- package/extensions/mega-events/separated-prompt.ts +329 -0
- package/extensions/mega-runtime/env-loader.ts +55 -0
- package/extensions/mega-runtime/helpers.ts +2 -0
- package/extensions/mega-runtime/reset-runtime.ts +1 -0
- package/extensions/mega-runtime/runtime-snapshot.ts +1 -0
- package/extensions/mega-runtime/runtime.ts +1 -0
- package/extensions/mega-runtime/snapshot.ts +6 -0
- package/extensions/mega-runtime/widget-types.ts +3 -0
- package/extensions/mega-runtime/widget.ts +5 -0
- package/package.json +1 -1
- package/src/cache-stripe-impl.ts +385 -0
- package/src/cache-stripe.ts +18 -0
- package/src/config/stopwords.ts +25 -0
- package/src/config.ts +26 -0
- package/src/memoryGraph.ts +438 -0
- package/src/monitoring.ts +35 -0
- package/src/queryExpansion.ts +206 -0
- package/src/queryReformulation/cache.ts +169 -0
- package/src/queryReformulation/rrf.ts +51 -0
- package/src/queryReformulation/tfidf.ts +79 -0
- package/src/queryReformulation/vagueness.ts +38 -0
- package/src/queryReformulation.ts +355 -0
- package/src/recallMetrics.ts +388 -0
- package/src/store/sqlite/cache-stripes.ts +196 -0
- package/src/store/sqlite/fts5-search.ts +90 -0
- package/src/store/sqlite/perf-samples.ts +3 -1
- package/src/store/sqlite/schema.ts +60 -1
- package/src/store/sqlite.ts +1 -0
- package/src/store/vectorIndex.ts +38 -7
- package/src/tiered-router/types.ts +64 -0
- package/src/tieredRouter.ts +489 -0
- package/src/vectorStore.ts +9 -5
- package/extensions/dashboard-client/dist/assets/CacheTab-eKVc-NV1.js +0 -2
- package/extensions/dashboard-client/dist/assets/CacheTab-eKVc-NV1.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/GameTab-DehUX-KE.js +0 -2
- package/extensions/dashboard-client/dist/assets/SetupTab-mMlfunJa.js +0 -2
- package/extensions/dashboard-client/dist/assets/SetupTab-mMlfunJa.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/index-4gQ7auad.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/index-BEdKlpAB.css +0 -1
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cache-stripe-impl.ts — Vector-Aware Cache Striping implementation.
|
|
3
|
+
*
|
|
4
|
+
* Computes a stability score for each context chunk and assigns it to a
|
|
5
|
+
* cache stripe / prompt-cache layer. Stable chunks (high recency + frequency +
|
|
6
|
+
* semantic density) go to early layers (cached prefix); volatile chunks append
|
|
7
|
+
* at Layer 4 (tail). Runs entirely offline — no network, no LLM (PREVENT-PI-004).
|
|
8
|
+
*
|
|
9
|
+
* The stability score is a weighted composite:
|
|
10
|
+
* stability = 0.5 * semanticSimilarity + 0.3 * recency + 0.2 * frequency
|
|
11
|
+
*
|
|
12
|
+
* - semanticSimilarity: cosine similarity of the chunk's embedding against the
|
|
13
|
+
* running session embedding (from TrigramEmbedder). High similarity means the
|
|
14
|
+
* chunk is topically relevant to current work.
|
|
15
|
+
* - recency: how recently the chunk appeared (normalized to 0.0-1.0 across all
|
|
16
|
+
* chunks in the epoch). Recent chunks are more likely to benefit from caching.
|
|
17
|
+
* - frequency: how often the chunk's content has been referenced (0.0-1.0,
|
|
18
|
+
* estimated from a simple access counter stored alongside).
|
|
19
|
+
*
|
|
20
|
+
* Reassignment happens at epoch boundaries via refreshStripeAssignments.
|
|
21
|
+
* All SQL is parameterized (PREVENT-002). No pi runtime types are imported,
|
|
22
|
+
* keeping this module pi-agnostic.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { randomBytes } from "node:crypto";
|
|
26
|
+
import { openStore, withTx } from "./store/sqlite/utils.js";
|
|
27
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
28
|
+
|
|
29
|
+
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
/** Cache stripe assigned to a single chunk. */
|
|
32
|
+
export interface CacheStripe {
|
|
33
|
+
/** Unique identifier for the chunk (e.g. context_chunks rowid or a content
|
|
34
|
+
* fingerprint). */
|
|
35
|
+
chunkId: string;
|
|
36
|
+
/** Cache stripe / layer number:
|
|
37
|
+
* 0 = permanent (system prompt, never evicted)
|
|
38
|
+
* 1 = epoch (stable across the whole session)
|
|
39
|
+
* 2 = topic (stable within a topic cluster)
|
|
40
|
+
* 3 = thread (current conversation thread)
|
|
41
|
+
* 4 = volatile (tail — appended, not cached)
|
|
42
|
+
*/
|
|
43
|
+
stripe: number;
|
|
44
|
+
/** Composite stability score (0.0-1.0). */
|
|
45
|
+
stability: number;
|
|
46
|
+
/** Unix-epoch seconds when this assignment was computed. */
|
|
47
|
+
assignedAt: number;
|
|
48
|
+
/** Epoch identifier this assignment belongs to. */
|
|
49
|
+
epochId: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Input shape for computeStabilityScore. Matches the context_chunks row shape
|
|
53
|
+
* the SQL caller extracts. */
|
|
54
|
+
export interface ChunkInput {
|
|
55
|
+
/** Primary key / rowid from context_chunks. */
|
|
56
|
+
chunkId: string;
|
|
57
|
+
/** The text content of the chunk. */
|
|
58
|
+
content: string;
|
|
59
|
+
/** How many times this chunk has been recalled/referenced (access count). */
|
|
60
|
+
accessCount: number;
|
|
61
|
+
/** Unix-epoch seconds of the most recent access. */
|
|
62
|
+
lastAccessedAt: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
/** Semantic similarity weight in the composite score. */
|
|
68
|
+
const WEIGHT_SEMANTIC = 0.5;
|
|
69
|
+
/** Recency weight in the composite score. */
|
|
70
|
+
const WEIGHT_RECENCY = 0.3;
|
|
71
|
+
/** Frequency weight in the composite score. */
|
|
72
|
+
const WEIGHT_FREQUENCY = 0.2;
|
|
73
|
+
|
|
74
|
+
/** Stripes a chunk lands in based on its stability score. Thresholds define
|
|
75
|
+
* the boundary between adjacent layers. */
|
|
76
|
+
const STRIPE_THRESHOLDS = [
|
|
77
|
+
{ minStability: 0.90, stripe: 0 },
|
|
78
|
+
{ minStability: 0.70, stripe: 1 },
|
|
79
|
+
{ minStability: 0.50, stripe: 2 },
|
|
80
|
+
{ minStability: 0.30, stripe: 3 },
|
|
81
|
+
{ minStability: -Infinity, stripe: 4 },
|
|
82
|
+
] as const;
|
|
83
|
+
|
|
84
|
+
// ─── Embedding helpers (no external dep) ─────────────────────────────────────
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* FNV-1a 32-bit hash for the content-based embedding fallback. The production
|
|
88
|
+
* path uses TrigramEmbedder from embedder.ts but we keep a self-contained hash
|
|
89
|
+
* for the case where no embedder is passed in.
|
|
90
|
+
*/
|
|
91
|
+
function fnv1a(text: string): number {
|
|
92
|
+
let hash = 0x811c9dc5;
|
|
93
|
+
for (let i = 0; i < text.length; i++) {
|
|
94
|
+
hash ^= text.charCodeAt(i);
|
|
95
|
+
hash = Math.imul(hash, 0x01000193);
|
|
96
|
+
}
|
|
97
|
+
return (hash >>> 0) / 0x100000000;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Produce a crude 128-dim pseudorandom embedding from text using hashed n-gram
|
|
102
|
+
* bins. Matches the approach in TrigramEmbedder._embedRaw conceptually. Used
|
|
103
|
+
* only as a fallback / test path; the caller should prefer TrigramEmbedder.
|
|
104
|
+
*/
|
|
105
|
+
function fallbackEmbed(text: string): number[] {
|
|
106
|
+
const dim = 128;
|
|
107
|
+
const vec = new Array<number>(dim).fill(0);
|
|
108
|
+
const norm = text.toLowerCase().replace(/\s+/g, " ");
|
|
109
|
+
if (norm.length === 0) return vec;
|
|
110
|
+
|
|
111
|
+
vec[Math.floor(fnv1a(norm) * dim)] += 1;
|
|
112
|
+
for (const word of norm.split(" ")) {
|
|
113
|
+
if (word.length === 0) continue;
|
|
114
|
+
vec[Math.floor(fnv1a(word) * dim)] += 0.5;
|
|
115
|
+
for (let i = 0; i < Math.max(1, word.length - 1); i++) {
|
|
116
|
+
const trigram = word.slice(i, i + 3);
|
|
117
|
+
if (trigram.length === 3) {
|
|
118
|
+
vec[Math.floor(fnv1a(trigram) * dim)] += 0.25;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return l2Normalize(vec);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function l2Normalize(v: number[]): number[] {
|
|
126
|
+
let sumSq = 0;
|
|
127
|
+
for (let i = 0; i < v.length; i++) sumSq += v[i] * v[i];
|
|
128
|
+
if (sumSq === 0) return v;
|
|
129
|
+
const norm = Math.sqrt(sumSq);
|
|
130
|
+
for (let i = 0; i < v.length; i++) v[i] /= norm;
|
|
131
|
+
return v;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Compute cosine similarity between two vectors of equal length. */
|
|
135
|
+
function cosineSimilarity(a: number[], b: number[]): number {
|
|
136
|
+
if (a.length !== b.length || a.length === 0) return 0;
|
|
137
|
+
let dot = 0;
|
|
138
|
+
let na = 0;
|
|
139
|
+
let nb = 0;
|
|
140
|
+
for (let i = 0; i < a.length; i++) {
|
|
141
|
+
dot += a[i] * b[i];
|
|
142
|
+
na += a[i] * a[i];
|
|
143
|
+
nb += b[i] * b[i];
|
|
144
|
+
}
|
|
145
|
+
const denom = Math.sqrt(na) * Math.sqrt(nb);
|
|
146
|
+
return denom === 0 ? 0 : dot / denom;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Embedder interface — matches the shape of TrigramEmbedder.embed. */
|
|
150
|
+
export interface EmbedderLike {
|
|
151
|
+
embed(text: string): number[];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ─── Stability Scoring ───────────────────────────────────────────────────────
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Compute a composite stability score for a single chunk.
|
|
158
|
+
*
|
|
159
|
+
* @param chunk The chunk metadata + content to score.
|
|
160
|
+
* @param allChunks All chunks in this epoch (used to compute relative recency).
|
|
161
|
+
* @param embedder Optional embedder instance. If omitted, uses the
|
|
162
|
+
* self-contained fallback (128-dim hashed n-gram).
|
|
163
|
+
* @param sessionEmbed Pre-computed embedding for the current session (the
|
|
164
|
+
* "query" vector). If omitted, computed on the fly from
|
|
165
|
+
* the chunk content alone, which degrades semantic scoring
|
|
166
|
+
* to a self-similarity baseline.
|
|
167
|
+
* @returns A number in [0.0, 1.0] where 1.0 = most stable.
|
|
168
|
+
*/
|
|
169
|
+
export function computeStabilityScore(
|
|
170
|
+
chunk: ChunkInput,
|
|
171
|
+
allChunks: ChunkInput[],
|
|
172
|
+
embedder?: EmbedderLike,
|
|
173
|
+
sessionEmbed?: number[],
|
|
174
|
+
): number {
|
|
175
|
+
// ── Semantic similarity (0.5 weight) ────────────────────────────────────
|
|
176
|
+
const emb = embedder
|
|
177
|
+
? embedder.embed(chunk.content)
|
|
178
|
+
: fallbackEmbed(chunk.content);
|
|
179
|
+
|
|
180
|
+
// If no session embedding is provided, use the chunk's own embedding as
|
|
181
|
+
// a self-similarity — this produces a baseline score based on content
|
|
182
|
+
// density (chunks with more meaningful content get higher internal
|
|
183
|
+
// similarity). Real deployments should pass the session embedding.
|
|
184
|
+
const sem = cosineSimilarity(
|
|
185
|
+
emb,
|
|
186
|
+
sessionEmbed ?? emb,
|
|
187
|
+
);
|
|
188
|
+
const semanticScore = isNaN(sem) ? 0 : sem;
|
|
189
|
+
|
|
190
|
+
// ── Recency (0.3 weight) ────────────────────────────────────────────────
|
|
191
|
+
// Relative recency: lastAccessedAt of this chunk vs. min/max across epoch.
|
|
192
|
+
// Falls back to 0.5 if there's only one chunk or no timestamp data.
|
|
193
|
+
let recencyScore = 0.5;
|
|
194
|
+
const accessed = allChunks
|
|
195
|
+
.map((c) => c.lastAccessedAt)
|
|
196
|
+
.filter((t) => t > 0);
|
|
197
|
+
if (accessed.length > 1) {
|
|
198
|
+
const minT = Math.min(...accessed);
|
|
199
|
+
const maxT = Math.max(...accessed);
|
|
200
|
+
const range = maxT - minT;
|
|
201
|
+
if (range > 0) {
|
|
202
|
+
recencyScore = (chunk.lastAccessedAt - minT) / range;
|
|
203
|
+
} else {
|
|
204
|
+
recencyScore = 1.0;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// ── Frequency (0.2 weight) ──────────────────────────────────────────────
|
|
209
|
+
// Access count relative to the max across the epoch.
|
|
210
|
+
const counts = allChunks.map((c) => c.accessCount);
|
|
211
|
+
const maxCount = Math.max(...counts, 1);
|
|
212
|
+
const freqScore = maxCount > 0 ? chunk.accessCount / maxCount : 0;
|
|
213
|
+
|
|
214
|
+
// ── Composite ───────────────────────────────────────────────────────────
|
|
215
|
+
const stability =
|
|
216
|
+
WEIGHT_SEMANTIC * semanticScore +
|
|
217
|
+
WEIGHT_RECENCY * recencyScore +
|
|
218
|
+
WEIGHT_FREQUENCY * freqScore;
|
|
219
|
+
|
|
220
|
+
// Clamp to [0.0, 1.0] as a safety net.
|
|
221
|
+
return Math.max(0, Math.min(1, stability));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Determine the cache stripe (layer) for a given stability score.
|
|
226
|
+
*
|
|
227
|
+
* @param stability Composite stability score in [0.0, 1.0].
|
|
228
|
+
* @returns Stripe number 0-4.
|
|
229
|
+
*/
|
|
230
|
+
export function stabilityToStripe(stability: number): number {
|
|
231
|
+
for (const t of STRIPE_THRESHOLDS) {
|
|
232
|
+
if (stability >= t.minStability) return t.stripe;
|
|
233
|
+
}
|
|
234
|
+
return 4;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// ─── Stripe Reassignment ─────────────────────────────────────────────────────
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Refresh stripe assignments for all chunks in the current epoch.
|
|
241
|
+
*
|
|
242
|
+
* Steps:
|
|
243
|
+
* 1. Read all context_chunks from the SQLite store that belong to the
|
|
244
|
+
* current epoch (or all chunks if no epoch filter).
|
|
245
|
+
* 2. For each chunk, compute a stability score via computeStabilityScore.
|
|
246
|
+
* 3. Map stability -> stripe via stabilityToStripe.
|
|
247
|
+
* 4. UPSERT into cache_stripes.
|
|
248
|
+
* 5. (Stale entries for this epoch are implicitly overwritten by the UPSERT.)
|
|
249
|
+
*
|
|
250
|
+
* The optional embedder parameter allows injecting the production
|
|
251
|
+
* TrigramEmbedder. If omitted, the fallback hashed n-gram embedder is used
|
|
252
|
+
* (works offline in all scenarios).
|
|
253
|
+
*
|
|
254
|
+
* Non-fatal: failures are logged via a provided logger callback and never
|
|
255
|
+
* thrown. Returns the count of chunks reassigned.
|
|
256
|
+
*
|
|
257
|
+
* @param store An open SQLite DatabaseSync handle (or a stateDir string
|
|
258
|
+
* to open lazily). Accepts either to match the caller's
|
|
259
|
+
* convenience. When a string is passed, opens the store for
|
|
260
|
+
* this call only (does not cache the connection).
|
|
261
|
+
* @param epochId The epoch to reassign. If omitted, generates a new epoch
|
|
262
|
+
* ID (random hex). Pass '' to reassign all chunks without
|
|
263
|
+
* filtering by epoch.
|
|
264
|
+
* @param embedder Optional TrigramEmbedder instance. When provided, uses it
|
|
265
|
+
* for semantic similarity; otherwise uses the fallback.
|
|
266
|
+
* @param logFn Optional logging callback (defaults to no-op).
|
|
267
|
+
* @returns The number of chunks that were reassigned.
|
|
268
|
+
*/
|
|
269
|
+
export function refreshStripeAssignments(
|
|
270
|
+
store: DatabaseSync | string,
|
|
271
|
+
epochId?: string,
|
|
272
|
+
embedder?: EmbedderLike,
|
|
273
|
+
logFn?: (msg: string) => void,
|
|
274
|
+
): number {
|
|
275
|
+
const db: DatabaseSync =
|
|
276
|
+
typeof store === "string" ? openStore(store) : store;
|
|
277
|
+
const log = logFn ?? (() => {});
|
|
278
|
+
const actualEpochId = epochId ?? nextEpochId();
|
|
279
|
+
const now = Math.floor(Date.now() / 1000);
|
|
280
|
+
|
|
281
|
+
try {
|
|
282
|
+
// 1. Read all relevant context_chunks, using the summary as text content.
|
|
283
|
+
// The summary field holds the compressed checkpoint content; for fresh
|
|
284
|
+
// chunks that have no summary yet, fall back to normalized_text or
|
|
285
|
+
// concatenated key_decisions.
|
|
286
|
+
const rows = db
|
|
287
|
+
.prepare(
|
|
288
|
+
`SELECT c.rowid AS chunk_id,
|
|
289
|
+
COALESCE(c.summary, c.normalized_text, c.key_decisions, '') AS content,
|
|
290
|
+
COALESCE(s.access_count, 0) AS access_count,
|
|
291
|
+
COALESCE(s.last_accessed_at, 0) AS last_accessed_at
|
|
292
|
+
FROM context_chunks c
|
|
293
|
+
LEFT JOIN cache_stripes s ON s.chunk_id = CAST(c.rowid AS TEXT)
|
|
294
|
+
WHERE (? = '' OR s.epoch_id = ? OR s.epoch_id IS NULL)`,
|
|
295
|
+
)
|
|
296
|
+
.all(actualEpochId, actualEpochId) as Array<{
|
|
297
|
+
chunk_id: number;
|
|
298
|
+
content: string;
|
|
299
|
+
access_count: number;
|
|
300
|
+
last_accessed_at: number;
|
|
301
|
+
}>;
|
|
302
|
+
|
|
303
|
+
if (rows.length === 0) {
|
|
304
|
+
log("cache-stripe: no chunks to reassign");
|
|
305
|
+
return 0;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Build the allChunks array for relative scoring.
|
|
309
|
+
const allChunks: ChunkInput[] = rows.map((r) => ({
|
|
310
|
+
chunkId: String(r.chunk_id),
|
|
311
|
+
content: r.content,
|
|
312
|
+
accessCount: r.access_count,
|
|
313
|
+
lastAccessedAt: r.last_accessed_at,
|
|
314
|
+
}));
|
|
315
|
+
|
|
316
|
+
// Compute a session embedding (mean of all chunk embeddings) for semantic
|
|
317
|
+
// similarity comparison.
|
|
318
|
+
let sessionEmbed: number[] | undefined;
|
|
319
|
+
try {
|
|
320
|
+
const dim = embedder ? embedder.embed("").length : 128;
|
|
321
|
+
const sumEmb = new Array<number>(dim).fill(0);
|
|
322
|
+
let count = 0;
|
|
323
|
+
for (const chunk of allChunks) {
|
|
324
|
+
const vec = embedder
|
|
325
|
+
? embedder.embed(chunk.content)
|
|
326
|
+
: fallbackEmbed(chunk.content);
|
|
327
|
+
for (let i = 0; i < sumEmb.length; i++) sumEmb[i] += vec[i];
|
|
328
|
+
count++;
|
|
329
|
+
}
|
|
330
|
+
if (count > 0) {
|
|
331
|
+
for (let i = 0; i < sumEmb.length; i++) sumEmb[i] /= count;
|
|
332
|
+
sessionEmbed = l2Normalize(sumEmb);
|
|
333
|
+
}
|
|
334
|
+
} catch {
|
|
335
|
+
log("cache-stripe: session embedding failed, skipping semantic weight");
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// 2. Compute stability for each chunk.
|
|
339
|
+
const results: Array<{
|
|
340
|
+
chunkId: string;
|
|
341
|
+
stripe: number;
|
|
342
|
+
stability: number;
|
|
343
|
+
}> = [];
|
|
344
|
+
|
|
345
|
+
for (const chunk of allChunks) {
|
|
346
|
+
const stability = computeStabilityScore(
|
|
347
|
+
chunk,
|
|
348
|
+
allChunks,
|
|
349
|
+
embedder,
|
|
350
|
+
sessionEmbed,
|
|
351
|
+
);
|
|
352
|
+
const stripe = stabilityToStripe(stability);
|
|
353
|
+
results.push({ chunkId: chunk.chunkId, stripe, stability });
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// 3. UPSERT into cache_stripes using a savepoint for atomicity.
|
|
357
|
+
const upsert = db.prepare(
|
|
358
|
+
`INSERT OR REPLACE INTO cache_stripes(chunk_id, stripe, stability, assigned_at, epoch_id)
|
|
359
|
+
VALUES (?, ?, ?, ?, ?)`,
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
withTx(db, () => {
|
|
363
|
+
for (const r of results) {
|
|
364
|
+
upsert.run(r.chunkId, r.stripe, r.stability, now, actualEpochId);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
log(
|
|
369
|
+
`cache-stripe: reassigned ${results.length} chunks to epoch ${actualEpochId}`,
|
|
370
|
+
);
|
|
371
|
+
return results.length;
|
|
372
|
+
} catch (err) {
|
|
373
|
+
log(
|
|
374
|
+
`cache-stripe: refresh failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
375
|
+
);
|
|
376
|
+
return 0;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Generate a random epoch ID (16 hex chars) for tokenizing stripe cohorts.
|
|
382
|
+
*/
|
|
383
|
+
function nextEpochId(): string {
|
|
384
|
+
return randomBytes(8).toString("hex");
|
|
385
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cache-stripe.ts — Vector-Aware Cache Striping shell (PLAN_V2 Phase 3).
|
|
3
|
+
*
|
|
4
|
+
* Shell: re-exports types and function signatures from the implementation.
|
|
5
|
+
* The heavy logic lives in cache-stripe-impl.ts. This keeps the public API
|
|
6
|
+
* surface clean and respects the src/ 300-line soft limit.
|
|
7
|
+
*
|
|
8
|
+
* Pi-agnostic: no pi runtime types, no network (PREVENT-PI-004).
|
|
9
|
+
* All SQL is parameterized (PREVENT-002).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
computeStabilityScore,
|
|
14
|
+
stabilityToStripe,
|
|
15
|
+
refreshStripeAssignments,
|
|
16
|
+
} from "./cache-stripe-impl.js";
|
|
17
|
+
|
|
18
|
+
export type { CacheStripe, ChunkInput, EmbedderLike } from "./cache-stripe-impl.js";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* config/stopwords.ts — shared stopword set for query expansion, coverage
|
|
3
|
+
* metrics, and term tokenization (S45, PREREQUISITE P2).
|
|
4
|
+
*
|
|
5
|
+
* Single source of truth: both `src/queryExpansion.ts` and `src/recallMetrics.ts`
|
|
6
|
+
* import from here instead of duplicating inline sets.
|
|
7
|
+
*
|
|
8
|
+
* Pi-agnostic, zero deps, tree-shake safe.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** Default English stopwords. */
|
|
12
|
+
export const STOP_WORDS: ReadonlySet<string> = new Set([
|
|
13
|
+
"a", "an", "the", "and", "or", "but", "in", "on", "at", "to", "for",
|
|
14
|
+
"of", "with", "by", "from", "as", "is", "was", "are", "were", "be",
|
|
15
|
+
"been", "has", "have", "had", "do", "does", "did", "will", "would",
|
|
16
|
+
"could", "should", "may", "might", "shall", "can", "not", "no", "nor",
|
|
17
|
+
"so", "if", "then", "than", "that", "this", "these", "those", "it",
|
|
18
|
+
"its", "i", "me", "my", "we", "our", "you", "your", "he", "she",
|
|
19
|
+
"they", "them", "their", "what", "which", "who", "whom", "when",
|
|
20
|
+
"where", "why", "how", "all", "each", "every", "both", "few", "more",
|
|
21
|
+
"most", "other", "some", "such", "only", "own", "same", "so", "than",
|
|
22
|
+
"too", "very", "just", "about", "above", "after", "again", "against",
|
|
23
|
+
"because", "before", "between", "down", "during", "out", "over",
|
|
24
|
+
"through", "under", "up", "also", "into", "off", "onto", "upon",
|
|
25
|
+
]);
|
package/src/config.ts
CHANGED
|
@@ -102,3 +102,29 @@ export function memoryReviewCadence(band: PressureBand, baseInterval: number): n
|
|
|
102
102
|
default: return base;
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
|
+
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
// S57 RAG Suite feature flags — each maps to MEGACOMPACT_* env var, default OFF
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
|
|
110
|
+
function ragFlag(name: string): boolean {
|
|
111
|
+
const v = process.env[name];
|
|
112
|
+
if (v === undefined) return false;
|
|
113
|
+
return v === "true" || v === "1";
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** B1: Query reformulation (keyword expansion via embedding neighbors). */
|
|
117
|
+
export const RAG_QUERY_REFORMULATION = (): boolean =>
|
|
118
|
+
ragFlag("MEGACOMPACT_QUERY_REFORMULATION");
|
|
119
|
+
|
|
120
|
+
/** B2: Tiered recall router (L0 cache → L1 FTS5 → L2 HNSW). */
|
|
121
|
+
export const RAG_TIERED_ROUTER = (): boolean =>
|
|
122
|
+
ragFlag("MEGACOMPACT_TIERED_ROUTER");
|
|
123
|
+
|
|
124
|
+
/** B3: Recall quality metrics (precision/recall scoring + logging). */
|
|
125
|
+
export const RAG_RECALL_METRICS = (): boolean =>
|
|
126
|
+
ragFlag("MEGACOMPACT_RECALL_METRICS");
|
|
127
|
+
|
|
128
|
+
/** B4: Memory graph traversal (dashboard-oriented). */
|
|
129
|
+
export const RAG_MEMORY_GRAPH = (): boolean =>
|
|
130
|
+
ragFlag("MEGACOMPACT_MEMORY_GRAPH");
|