@yugenlab/vaayu 0.1.8 → 0.1.10

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.
Files changed (29) hide show
  1. package/README.md +1 -1
  2. package/chunks/{chunk-7UOXFHEB.js → chunk-77725AR7.js} +416 -397
  3. package/chunks/{chunk-YSC77CKZ.js → chunk-AGK3A7R7.js} +2844 -3208
  4. package/chunks/{chunk-DOQMEQ5S.js → chunk-AS3DJFY3.js} +5 -5
  5. package/chunks/{chunk-NHRBVSN3.js → chunk-HIYHTWFW.js} +44 -9
  6. package/chunks/{chunk-IGBRBFXX.js → chunk-JGI4SDWS.js} +2 -2
  7. package/chunks/{chunk-D3RVJGO7.js → chunk-M7THR63C.js} +48 -74
  8. package/chunks/{chunk-OBYBBGHA.js → chunk-N22M7D4P.js} +118 -106
  9. package/chunks/{chunk-PJEYJQ2C.js → chunk-O4KV7TFP.js} +2 -2
  10. package/chunks/{chunk-S2HDNNC7.js → chunk-OT4G2L46.js} +552 -641
  11. package/chunks/chunk-TND3MU4Z.js +426 -0
  12. package/chunks/{chunk-LVE2EOOH.js → chunk-VJHNE47S.js} +84 -75
  13. package/chunks/{consolidation-indexer-CD6DS2HO.js → consolidation-indexer-VKQ6DNU3.js} +4 -4
  14. package/chunks/{day-consolidation-U3X6P4ZG.js → day-consolidation-BH3QU2SZ.js} +6 -2
  15. package/chunks/{graphrag-LAZSXLLI.js → graphrag-D7OXWAWD.js} +2 -2
  16. package/chunks/{hierarchical-temporal-search-ETXYYJZK.js → hierarchical-temporal-search-PVHVA3NZ.js} +2 -2
  17. package/chunks/{hybrid-search-TX6T3KYH.js → hybrid-search-G2NAJKJ7.js} +4 -4
  18. package/chunks/{periodic-consolidation-4MACZE6S.js → periodic-consolidation-LMYMNS4Q.js} +2 -2
  19. package/chunks/{recall-IUPQCBYP.js → recall-ZNL4DJ2L.js} +3 -3
  20. package/chunks/{search-HHSVHBXC.js → search-35JMSGUT.js} +3 -3
  21. package/chunks/{session-store-NDUDYAC7.js → session-store-3BRPGC6P.js} +2 -2
  22. package/chunks/{src-ZAKUL232.js → src-Y3TGMINC.js} +12 -12
  23. package/chunks/vasana-engine-MU25OQ23.js +30 -0
  24. package/gateway.js +492 -226
  25. package/package.json +1 -1
  26. package/vaayu-mark-npm.png +0 -0
  27. package/chunks/chunk-TEQKXGIK.js +0 -752
  28. package/chunks/vasana-engine-G6BPOFX7.js +0 -10
  29. package/vaayu-mark.png +0 -0
@@ -17,10 +17,6 @@ import {
17
17
  getChitraguptaHome
18
18
  } from "./chunk-UZ6OIVEC.js";
19
19
 
20
- // ../chitragupta/packages/smriti/src/graphrag.ts
21
- import fs from "fs";
22
- import path from "path";
23
-
24
20
  // ../chitragupta/packages/smriti/src/graphrag-pagerank.ts
25
21
  var PAGERANK_DAMPING = 0.85;
26
22
  var PAGERANK_EPSILON = 1e-4;
@@ -77,152 +73,12 @@ function computePageRank(graph) {
77
73
  return ranks;
78
74
  }
79
75
 
80
- // ../chitragupta/packages/smriti/src/graphrag-pagerank-personalized.ts
81
- function buildTopicBias(nodeIds, nodeContents, topic) {
82
- const bias = /* @__PURE__ */ new Map();
83
- const n = nodeIds.length;
84
- if (!topic || n === 0) {
85
- const uniform = 1 / Math.max(n, 1);
86
- for (const id of nodeIds) bias.set(id, uniform);
87
- return bias;
88
- }
89
- const queryTerms = tokenizeSimple(topic);
90
- const queryTf = termFrequency(queryTerms);
91
- let totalSim = 0;
92
- const similarities = new Array(n);
93
- for (let i = 0; i < n; i++) {
94
- const content = nodeContents.get(nodeIds[i]) ?? "";
95
- const docTerms = tokenizeSimple(content);
96
- const docTf = termFrequency(docTerms);
97
- const sim = tfCosineSimilarity(queryTf, docTf);
98
- similarities[i] = sim;
99
- totalSim += sim;
100
- }
101
- if (totalSim === 0) {
102
- const uniform = 1 / n;
103
- for (const id of nodeIds) bias.set(id, uniform);
104
- return bias;
105
- }
106
- for (let i = 0; i < n; i++) {
107
- bias.set(nodeIds[i], similarities[i] / totalSim);
108
- }
109
- return bias;
110
- }
111
- function tokenizeSimple(text) {
112
- return text.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((t) => t.length >= 2);
113
- }
114
- function termFrequency(tokens) {
115
- const tf = /* @__PURE__ */ new Map();
116
- for (const t of tokens) {
117
- tf.set(t, (tf.get(t) ?? 0) + 1);
118
- }
119
- return tf;
120
- }
121
- function tfCosineSimilarity(a, b) {
122
- let dot = 0;
123
- let normA = 0;
124
- let normB = 0;
125
- for (const [term, freqA] of a) {
126
- normA += freqA * freqA;
127
- const freqB = b.get(term);
128
- if (freqB !== void 0) dot += freqA * freqB;
129
- }
130
- for (const freqB of b.values()) {
131
- normB += freqB * freqB;
132
- }
133
- const denom = Math.sqrt(normA) * Math.sqrt(normB);
134
- return denom === 0 ? 0 : dot / denom;
135
- }
136
- function computePersonalizedPageRank(graph, topicBias, opts) {
137
- const damping = opts?.damping ?? 0.85;
138
- const epsilon = opts?.epsilon ?? 1e-6;
139
- const maxIter = opts?.maxIterations ?? 150;
140
- const useGS = opts?.useGaussSeidel ?? true;
141
- const N = graph.nodes.length;
142
- if (N === 0) return /* @__PURE__ */ new Map();
143
- const nodeIds = graph.nodes.map((n) => n.id);
144
- const nodeIdSet = new Set(nodeIds);
145
- const outDegree = /* @__PURE__ */ new Map();
146
- const inLinks = /* @__PURE__ */ new Map();
147
- for (const id of nodeIds) {
148
- outDegree.set(id, 0);
149
- inLinks.set(id, []);
150
- }
151
- for (const edge of graph.edges) {
152
- if (nodeIdSet.has(edge.source) && nodeIdSet.has(edge.target)) {
153
- outDegree.set(edge.source, (outDegree.get(edge.source) ?? 0) + 1);
154
- inLinks.get(edge.target).push(edge.source);
155
- }
156
- }
157
- let bias;
158
- if (typeof topicBias === "string") {
159
- const nodeContents = /* @__PURE__ */ new Map();
160
- for (const node of graph.nodes) {
161
- nodeContents.set(node.id, node.content);
162
- }
163
- bias = buildTopicBias(nodeIds, nodeContents, topicBias);
164
- } else if (topicBias instanceof Map) {
165
- bias = topicBias;
166
- } else {
167
- const uniform = 1 / N;
168
- bias = new Map(nodeIds.map((id) => [id, uniform]));
169
- }
170
- const ranks = /* @__PURE__ */ new Map();
171
- for (const id of nodeIds) {
172
- ranks.set(id, 1 / N);
173
- }
174
- const danglingNodes = [];
175
- for (const id of nodeIds) {
176
- if ((outDegree.get(id) ?? 0) === 0) danglingNodes.push(id);
177
- }
178
- for (let iter = 0; iter < maxIter; iter++) {
179
- let danglingSum = 0;
180
- for (const id of danglingNodes) {
181
- danglingSum += ranks.get(id) ?? 0;
182
- }
183
- const danglingContrib = damping * danglingSum / N;
184
- let maxDelta = 0;
185
- if (useGS) {
186
- for (const id of nodeIds) {
187
- const biasVal = bias.get(id) ?? 1 / N;
188
- let incomingSum = 0;
189
- for (const src of inLinks.get(id) ?? []) {
190
- const srcRank = ranks.get(src) ?? 0;
191
- const srcOut = outDegree.get(src) ?? 1;
192
- incomingSum += srcRank / srcOut;
193
- }
194
- const newRank = (1 - damping) * biasVal + damping * incomingSum + danglingContrib;
195
- const oldRank = ranks.get(id) ?? 0;
196
- const delta = Math.abs(newRank - oldRank);
197
- if (delta > maxDelta) maxDelta = delta;
198
- ranks.set(id, newRank);
199
- }
200
- } else {
201
- const newRanks = /* @__PURE__ */ new Map();
202
- for (const id of nodeIds) {
203
- const biasVal = bias.get(id) ?? 1 / N;
204
- let incomingSum = 0;
205
- for (const src of inLinks.get(id) ?? []) {
206
- const srcRank = ranks.get(src) ?? 0;
207
- const srcOut = outDegree.get(src) ?? 1;
208
- incomingSum += srcRank / srcOut;
209
- }
210
- const newRank = (1 - damping) * biasVal + damping * incomingSum + danglingContrib;
211
- newRanks.set(id, newRank);
212
- const delta = Math.abs(newRank - (ranks.get(id) ?? 0));
213
- if (delta > maxDelta) maxDelta = delta;
214
- }
215
- for (const [id, rank] of newRanks) ranks.set(id, rank);
216
- }
217
- if (maxDelta < epsilon) break;
218
- }
219
- return ranks;
220
- }
76
+ // ../chitragupta/packages/smriti/src/graphrag-pagerank-incremental.ts
221
77
  var IncrementalPageRank = class {
222
78
  ranks = /* @__PURE__ */ new Map();
223
79
  outDegree = /* @__PURE__ */ new Map();
224
80
  inLinks = /* @__PURE__ */ new Map();
225
- /** Forward adjacency list: source Set<target>. Avoids O(E) reverse lookup in getOutNeighbors(). */
81
+ /** Forward adjacency list: source -> Set<target>. Avoids O(E) reverse lookup in getOutNeighbors(). */
226
82
  outLinks = /* @__PURE__ */ new Map();
227
83
  nodeSet = /* @__PURE__ */ new Set();
228
84
  damping;
@@ -338,7 +194,7 @@ var IncrementalPageRank = class {
338
194
  getRanks() {
339
195
  return new Map(this.ranks);
340
196
  }
341
- // ─── Private ──────────────────────────────────────────────────────
197
+ // --- Private ---------------------------------------------------------
342
198
  /**
343
199
  * Propagate residuals through the graph until all are below epsilon.
344
200
  *
@@ -384,6 +240,148 @@ var IncrementalPageRank = class {
384
240
  }
385
241
  };
386
242
 
243
+ // ../chitragupta/packages/smriti/src/graphrag-pagerank-personalized.ts
244
+ function buildTopicBias(nodeIds, nodeContents, topic) {
245
+ const bias = /* @__PURE__ */ new Map();
246
+ const n = nodeIds.length;
247
+ if (!topic || n === 0) {
248
+ const uniform = 1 / Math.max(n, 1);
249
+ for (const id of nodeIds) bias.set(id, uniform);
250
+ return bias;
251
+ }
252
+ const queryTerms = tokenizeSimple(topic);
253
+ const queryTf = termFrequency(queryTerms);
254
+ let totalSim = 0;
255
+ const similarities = new Array(n);
256
+ for (let i = 0; i < n; i++) {
257
+ const content = nodeContents.get(nodeIds[i]) ?? "";
258
+ const docTerms = tokenizeSimple(content);
259
+ const docTf = termFrequency(docTerms);
260
+ const sim = tfCosineSimilarity(queryTf, docTf);
261
+ similarities[i] = sim;
262
+ totalSim += sim;
263
+ }
264
+ if (totalSim === 0) {
265
+ const uniform = 1 / n;
266
+ for (const id of nodeIds) bias.set(id, uniform);
267
+ return bias;
268
+ }
269
+ for (let i = 0; i < n; i++) {
270
+ bias.set(nodeIds[i], similarities[i] / totalSim);
271
+ }
272
+ return bias;
273
+ }
274
+ function tokenizeSimple(text) {
275
+ return text.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((t) => t.length >= 2);
276
+ }
277
+ function termFrequency(tokens) {
278
+ const tf = /* @__PURE__ */ new Map();
279
+ for (const t of tokens) {
280
+ tf.set(t, (tf.get(t) ?? 0) + 1);
281
+ }
282
+ return tf;
283
+ }
284
+ function tfCosineSimilarity(a, b) {
285
+ let dot = 0;
286
+ let normA = 0;
287
+ let normB = 0;
288
+ for (const [term, freqA] of a) {
289
+ normA += freqA * freqA;
290
+ const freqB = b.get(term);
291
+ if (freqB !== void 0) dot += freqA * freqB;
292
+ }
293
+ for (const freqB of b.values()) {
294
+ normB += freqB * freqB;
295
+ }
296
+ const denom = Math.sqrt(normA) * Math.sqrt(normB);
297
+ return denom === 0 ? 0 : dot / denom;
298
+ }
299
+ function computePersonalizedPageRank(graph, topicBias, opts) {
300
+ const damping = opts?.damping ?? 0.85;
301
+ const epsilon = opts?.epsilon ?? 1e-6;
302
+ const maxIter = opts?.maxIterations ?? 150;
303
+ const useGS = opts?.useGaussSeidel ?? true;
304
+ const N = graph.nodes.length;
305
+ if (N === 0) return /* @__PURE__ */ new Map();
306
+ const nodeIds = graph.nodes.map((n) => n.id);
307
+ const nodeIdSet = new Set(nodeIds);
308
+ const outDegree = /* @__PURE__ */ new Map();
309
+ const inLinks = /* @__PURE__ */ new Map();
310
+ for (const id of nodeIds) {
311
+ outDegree.set(id, 0);
312
+ inLinks.set(id, []);
313
+ }
314
+ for (const edge of graph.edges) {
315
+ if (nodeIdSet.has(edge.source) && nodeIdSet.has(edge.target)) {
316
+ outDegree.set(edge.source, (outDegree.get(edge.source) ?? 0) + 1);
317
+ inLinks.get(edge.target).push(edge.source);
318
+ }
319
+ }
320
+ let bias;
321
+ if (typeof topicBias === "string") {
322
+ const nodeContents = /* @__PURE__ */ new Map();
323
+ for (const node of graph.nodes) {
324
+ nodeContents.set(node.id, node.content);
325
+ }
326
+ bias = buildTopicBias(nodeIds, nodeContents, topicBias);
327
+ } else if (topicBias instanceof Map) {
328
+ bias = topicBias;
329
+ } else {
330
+ const uniform = 1 / N;
331
+ bias = new Map(nodeIds.map((id) => [id, uniform]));
332
+ }
333
+ const ranks = /* @__PURE__ */ new Map();
334
+ for (const id of nodeIds) {
335
+ ranks.set(id, 1 / N);
336
+ }
337
+ const danglingNodes = [];
338
+ for (const id of nodeIds) {
339
+ if ((outDegree.get(id) ?? 0) === 0) danglingNodes.push(id);
340
+ }
341
+ for (let iter = 0; iter < maxIter; iter++) {
342
+ let danglingSum = 0;
343
+ for (const id of danglingNodes) {
344
+ danglingSum += ranks.get(id) ?? 0;
345
+ }
346
+ const danglingContrib = damping * danglingSum / N;
347
+ let maxDelta = 0;
348
+ if (useGS) {
349
+ for (const id of nodeIds) {
350
+ const biasVal = bias.get(id) ?? 1 / N;
351
+ let incomingSum = 0;
352
+ for (const src of inLinks.get(id) ?? []) {
353
+ const srcRank = ranks.get(src) ?? 0;
354
+ const srcOut = outDegree.get(src) ?? 1;
355
+ incomingSum += srcRank / srcOut;
356
+ }
357
+ const newRank = (1 - damping) * biasVal + damping * incomingSum + danglingContrib;
358
+ const oldRank = ranks.get(id) ?? 0;
359
+ const delta = Math.abs(newRank - oldRank);
360
+ if (delta > maxDelta) maxDelta = delta;
361
+ ranks.set(id, newRank);
362
+ }
363
+ } else {
364
+ const newRanks = /* @__PURE__ */ new Map();
365
+ for (const id of nodeIds) {
366
+ const biasVal = bias.get(id) ?? 1 / N;
367
+ let incomingSum = 0;
368
+ for (const src of inLinks.get(id) ?? []) {
369
+ const srcRank = ranks.get(src) ?? 0;
370
+ const srcOut = outDegree.get(src) ?? 1;
371
+ incomingSum += srcRank / srcOut;
372
+ }
373
+ const newRank = (1 - damping) * biasVal + damping * incomingSum + danglingContrib;
374
+ newRanks.set(id, newRank);
375
+ const delta = Math.abs(newRank - (ranks.get(id) ?? 0));
376
+ if (delta > maxDelta) maxDelta = delta;
377
+ }
378
+ for (const [id, rank] of newRanks) ranks.set(id, rank);
379
+ }
380
+ if (maxDelta < epsilon) break;
381
+ }
382
+ return ranks;
383
+ }
384
+
387
385
  // ../chitragupta/packages/smriti/src/graphrag-extraction.ts
388
386
  var CHUNK_TARGET_TOKENS = 350;
389
387
  var CHUNK_MIN_TOKENS = 200;
@@ -1120,19 +1118,13 @@ function removeMemoryFromGraph(graph, scope) {
1120
1118
  );
1121
1119
  }
1122
1120
 
1123
- // ../chitragupta/packages/smriti/src/graphrag-leiden.ts
1124
- var DEFAULT_LEIDEN_CONFIG = {
1125
- resolution: 1,
1126
- maxIterations: 10,
1127
- minModularityGain: 1e-6,
1128
- seed: 42,
1129
- minCommunitySize: 1
1130
- };
1121
+ // ../chitragupta/packages/smriti/src/leiden-algorithm.ts
1131
1122
  var Xorshift32 = class {
1132
1123
  state;
1133
1124
  constructor(seed) {
1134
1125
  this.state = seed | 0 || 1;
1135
1126
  }
1127
+ /** Generate next pseudo-random number in [0, 1). */
1136
1128
  next() {
1137
1129
  let x = this.state;
1138
1130
  x ^= x << 13;
@@ -1143,17 +1135,17 @@ var Xorshift32 = class {
1143
1135
  }
1144
1136
  };
1145
1137
  var AdjacencyGraph = class {
1146
- /** nodeIndex Map<neighborIndex, weight> */
1138
+ /** nodeIndex -> Map<neighborIndex, weight> */
1147
1139
  adj;
1148
- /** nodeIndex weighted degree (sum of edge weights) */
1140
+ /** nodeIndex -> weighted degree (sum of edge weights) */
1149
1141
  degree;
1150
1142
  /** Total edge weight (2m). */
1151
1143
  totalWeight;
1152
1144
  /** Number of nodes. */
1153
1145
  n;
1154
- /** Node ID index mapping. */
1146
+ /** Node ID -> index mapping. */
1155
1147
  idToIndex;
1156
- /** Index node ID mapping. */
1148
+ /** Index -> node ID mapping. */
1157
1149
  indexToId;
1158
1150
  constructor(graph) {
1159
1151
  const nodeIds = graph.nodes.map((n) => n.id);
@@ -1329,6 +1321,15 @@ function compactCommunities(assignment, minSize) {
1329
1321
  }
1330
1322
  return assignment.map((c) => remap.get(c) ?? 0);
1331
1323
  }
1324
+
1325
+ // ../chitragupta/packages/smriti/src/graphrag-leiden.ts
1326
+ var DEFAULT_LEIDEN_CONFIG = {
1327
+ resolution: 1,
1328
+ maxIterations: 10,
1329
+ minModularityGain: 1e-6,
1330
+ seed: 42,
1331
+ minCommunitySize: 1
1332
+ };
1332
1333
  function leiden(graph, config) {
1333
1334
  const cfg = { ...DEFAULT_LEIDEN_CONFIG, ...config };
1334
1335
  if (graph.nodes.length === 0) {
@@ -1361,6 +1362,17 @@ function leiden(graph, config) {
1361
1362
  communities.set(g.indexToId[i], compacted[i]);
1362
1363
  }
1363
1364
  const finalModularity = computeModularity(g, compacted, cfg.resolution);
1365
+ const communityList = buildCommunityList(g, compacted, cfg.resolution);
1366
+ communityList.sort((a, b) => b.members.length - a.members.length);
1367
+ return {
1368
+ communities,
1369
+ communityList,
1370
+ modularity: finalModularity,
1371
+ iterations,
1372
+ levels: 1
1373
+ };
1374
+ }
1375
+ function buildCommunityList(g, compacted, resolution) {
1364
1376
  const communityNodes = /* @__PURE__ */ new Map();
1365
1377
  for (let i = 0; i < g.n; i++) {
1366
1378
  const c = compacted[i];
@@ -1383,11 +1395,9 @@ function leiden(graph, config) {
1383
1395
  const possibleEdges = members.length * (members.length - 1) / 2;
1384
1396
  const density = possibleEdges > 0 ? internalEdges / possibleEdges : 0;
1385
1397
  let ac = 0;
1386
- for (const mi of memberSet) {
1387
- ac += g.degree[mi];
1388
- }
1398
+ for (const mi of memberSet) ac += g.degree[mi];
1389
1399
  const twoM = g.totalWeight;
1390
- const communityMod = twoM > 0 ? 2 * internalWeight / twoM - cfg.resolution * (ac / twoM) ** 2 : 0;
1400
+ const communityMod = twoM > 0 ? 2 * internalWeight / twoM - resolution * (ac / twoM) ** 2 : 0;
1391
1401
  communityList.push({
1392
1402
  id,
1393
1403
  members,
@@ -1396,14 +1406,7 @@ function leiden(graph, config) {
1396
1406
  level: 0
1397
1407
  });
1398
1408
  }
1399
- communityList.sort((a, b) => b.members.length - a.members.length);
1400
- return {
1401
- communities,
1402
- communityList,
1403
- modularity: finalModularity,
1404
- iterations,
1405
- levels: 1
1406
- };
1409
+ return communityList;
1407
1410
  }
1408
1411
  function annotateCommunities(graph, result) {
1409
1412
  for (const node of graph.nodes) {
@@ -1412,50 +1415,318 @@ function annotateCommunities(graph, result) {
1412
1415
  node.metadata.communityId = communityId;
1413
1416
  }
1414
1417
  }
1415
- }
1416
- function communitySummary(graph, communityId, maxLabels = 5) {
1417
- const members = graph.nodes.filter(
1418
- (n) => n.metadata.communityId === communityId
1419
- );
1420
- const typeCount = {};
1421
- for (const node of members) {
1422
- typeCount[node.type] = (typeCount[node.type] ?? 0) + 1;
1418
+ }
1419
+ function communitySummary(graph, communityId, maxLabels = 5) {
1420
+ const members = graph.nodes.filter(
1421
+ (n) => n.metadata.communityId === communityId
1422
+ );
1423
+ const typeCount = {};
1424
+ for (const node of members) {
1425
+ typeCount[node.type] = (typeCount[node.type] ?? 0) + 1;
1426
+ }
1427
+ const labels = members.filter((n) => n.type === "concept" || n.type === "session").slice(0, maxLabels).map((n) => n.label);
1428
+ return { communityId, labels, nodeTypes: typeCount, size: members.length };
1429
+ }
1430
+ function filterByCommunity(nodes, communityId) {
1431
+ return nodes.filter((n) => n.metadata.communityId === communityId);
1432
+ }
1433
+ function findBridgeNodes(graph, result, minCommunities = 2) {
1434
+ const touchedCommunities = /* @__PURE__ */ new Map();
1435
+ for (const node of graph.nodes) {
1436
+ const ownC = result.communities.get(node.id);
1437
+ if (ownC !== void 0) {
1438
+ touchedCommunities.set(node.id, /* @__PURE__ */ new Set([ownC]));
1439
+ }
1440
+ }
1441
+ for (const edge of graph.edges) {
1442
+ const sc = result.communities.get(edge.source);
1443
+ const tc = result.communities.get(edge.target);
1444
+ if (sc === void 0 || tc === void 0) continue;
1445
+ if (sc === tc) continue;
1446
+ touchedCommunities.get(edge.source)?.add(tc);
1447
+ touchedCommunities.get(edge.target)?.add(sc);
1448
+ }
1449
+ const bridges = [];
1450
+ for (const node of graph.nodes) {
1451
+ const communities = touchedCommunities.get(node.id);
1452
+ if (communities && communities.size >= minCommunities) {
1453
+ bridges.push(node);
1454
+ }
1455
+ }
1456
+ return bridges;
1457
+ }
1458
+
1459
+ // ../chitragupta/packages/smriti/src/graphrag-persistence.ts
1460
+ import fs from "fs";
1461
+ import path from "path";
1462
+ function getGraphDir() {
1463
+ return path.join(getChitraguptaHome(), "graphrag");
1464
+ }
1465
+ function getGraphPath() {
1466
+ return path.join(getGraphDir(), "graph.json");
1467
+ }
1468
+ function getEmbeddingsPath() {
1469
+ return path.join(getGraphDir(), "embeddings.json");
1470
+ }
1471
+ function getPageRankPath() {
1472
+ return path.join(getGraphDir(), "pagerank.json");
1473
+ }
1474
+ function getGraphDb(initialized, markInitialized) {
1475
+ try {
1476
+ const dbm = DatabaseManager.instance();
1477
+ if (!initialized) {
1478
+ initGraphSchema(dbm);
1479
+ markInitialized();
1480
+ }
1481
+ return dbm.get("graph");
1482
+ } catch {
1483
+ return null;
1484
+ }
1485
+ }
1486
+ function upsertNodeToDb(db, node) {
1487
+ db.prepare(`
1488
+ INSERT OR REPLACE INTO nodes (id, type, label, content, metadata, created_at, updated_at)
1489
+ VALUES (?, ?, ?, ?, ?, ?, ?)
1490
+ `).run(node.id, node.type, node.label, node.content, JSON.stringify(node.metadata), Date.now(), Date.now());
1491
+ }
1492
+ function insertEdgeToDb(db, edge) {
1493
+ db.prepare(`
1494
+ INSERT OR IGNORE INTO edges (source, target, relationship, weight, pramana, viveka,
1495
+ valid_from, valid_until, recorded_at, superseded_at)
1496
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1497
+ `).run(
1498
+ edge.source,
1499
+ edge.target,
1500
+ edge.relationship,
1501
+ edge.weight,
1502
+ null,
1503
+ null,
1504
+ edge.validFrom ? new Date(edge.validFrom).getTime() : Date.now(),
1505
+ edge.validUntil ? new Date(edge.validUntil).getTime() : null,
1506
+ edge.recordedAt ? new Date(edge.recordedAt).getTime() : Date.now(),
1507
+ edge.supersededAt ? new Date(edge.supersededAt).getTime() : null
1508
+ );
1509
+ }
1510
+ function saveToSqlite(db, graph, pageRankScores, embeddingCache) {
1511
+ db.transaction(() => {
1512
+ db.prepare("DELETE FROM pagerank").run();
1513
+ db.prepare("DELETE FROM edges").run();
1514
+ db.prepare("DELETE FROM nodes").run();
1515
+ for (const node of graph.nodes) upsertNodeToDb(db, node);
1516
+ for (const edge of graph.edges) insertEdgeToDb(db, edge);
1517
+ for (const [nodeId, score] of pageRankScores) {
1518
+ db.prepare("INSERT OR REPLACE INTO pagerank (node_id, score, updated_at) VALUES (?, ?, ?)").run(nodeId, score, Date.now());
1519
+ }
1520
+ })();
1521
+ saveEmbeddingsJson(embeddingCache);
1522
+ }
1523
+ function loadFromSqlite(db) {
1524
+ const nodeCount = db.prepare("SELECT COUNT(*) as cnt FROM nodes").get().cnt;
1525
+ if (nodeCount === 0) return null;
1526
+ const nodeRows = db.prepare("SELECT * FROM nodes").all();
1527
+ const nodes = nodeRows.map((row) => ({
1528
+ id: row.id,
1529
+ type: row.type,
1530
+ label: row.label,
1531
+ content: row.content ?? "",
1532
+ metadata: (() => {
1533
+ try {
1534
+ return JSON.parse(row.metadata ?? "{}");
1535
+ } catch {
1536
+ return {};
1537
+ }
1538
+ })()
1539
+ }));
1540
+ const edgeRows = db.prepare("SELECT * FROM edges").all();
1541
+ const edges = edgeRows.map((row) => ({
1542
+ source: row.source,
1543
+ target: row.target,
1544
+ relationship: row.relationship,
1545
+ weight: row.weight,
1546
+ validFrom: row.valid_from ? new Date(row.valid_from).toISOString() : void 0,
1547
+ validUntil: row.valid_until ? new Date(row.valid_until).toISOString() : void 0,
1548
+ recordedAt: row.recorded_at ? new Date(row.recorded_at).toISOString() : void 0,
1549
+ supersededAt: row.superseded_at ? new Date(row.superseded_at).toISOString() : void 0
1550
+ }));
1551
+ const pageRankScores = /* @__PURE__ */ new Map();
1552
+ const prRows = db.prepare("SELECT * FROM pagerank").all();
1553
+ for (const row of prRows) {
1554
+ pageRankScores.set(row.node_id, row.score);
1555
+ }
1556
+ return { graph: { nodes, edges }, pageRankScores };
1557
+ }
1558
+ function migrateInMemoryToSqlite(db, graph, pageRankScores) {
1559
+ db.transaction(() => {
1560
+ for (const node of graph.nodes) upsertNodeToDb(db, node);
1561
+ for (const edge of graph.edges) insertEdgeToDb(db, edge);
1562
+ for (const [nodeId, score] of pageRankScores) {
1563
+ db.prepare("INSERT OR REPLACE INTO pagerank (node_id, score, updated_at) VALUES (?, ?, ?)").run(nodeId, score, Date.now());
1564
+ }
1565
+ })();
1566
+ }
1567
+ function saveToJson(graph, pageRankScores, embeddingCache) {
1568
+ try {
1569
+ const dir = getGraphDir();
1570
+ fs.mkdirSync(dir, { recursive: true });
1571
+ fs.writeFileSync(getGraphPath(), JSON.stringify(graph, null, " "), "utf-8");
1572
+ saveEmbeddingsJson(embeddingCache);
1573
+ const pageRankObj = {};
1574
+ for (const [key, value] of pageRankScores) pageRankObj[key] = value;
1575
+ fs.writeFileSync(getPageRankPath(), JSON.stringify(pageRankObj, null, " "), "utf-8");
1576
+ } catch {
1577
+ }
1578
+ }
1579
+ function loadFromJson() {
1580
+ let graph = { nodes: [], edges: [] };
1581
+ const pageRankScores = /* @__PURE__ */ new Map();
1582
+ try {
1583
+ const graphPath = getGraphPath();
1584
+ if (fs.existsSync(graphPath)) {
1585
+ graph = JSON.parse(fs.readFileSync(graphPath, "utf-8"));
1586
+ }
1587
+ } catch {
1588
+ graph = { nodes: [], edges: [] };
1589
+ }
1590
+ try {
1591
+ const prPath = getPageRankPath();
1592
+ if (fs.existsSync(prPath)) {
1593
+ const ranks = JSON.parse(fs.readFileSync(prPath, "utf-8"));
1594
+ for (const [key, value] of Object.entries(ranks)) pageRankScores.set(key, value);
1595
+ }
1596
+ } catch {
1423
1597
  }
1424
- const labels = members.filter((n) => n.type === "concept" || n.type === "session").slice(0, maxLabels).map((n) => n.label);
1425
- return {
1426
- communityId,
1427
- labels,
1428
- nodeTypes: typeCount,
1429
- size: members.length
1430
- };
1598
+ return { graph, pageRankScores };
1431
1599
  }
1432
- function filterByCommunity(nodes, communityId) {
1433
- return nodes.filter((n) => n.metadata.communityId === communityId);
1600
+ function saveEmbeddingsJson(embeddingCache) {
1601
+ try {
1602
+ const dir = getGraphDir();
1603
+ fs.mkdirSync(dir, { recursive: true });
1604
+ const embeddings = {};
1605
+ for (const [key, value] of embeddingCache) embeddings[key] = value;
1606
+ fs.writeFileSync(getEmbeddingsPath(), JSON.stringify(embeddings, null, " "), "utf-8");
1607
+ } catch {
1608
+ }
1434
1609
  }
1435
- function findBridgeNodes(graph, result, minCommunities = 2) {
1436
- const touchedCommunities = /* @__PURE__ */ new Map();
1437
- for (const node of graph.nodes) {
1438
- const ownC = result.communities.get(node.id);
1439
- if (ownC !== void 0) {
1440
- touchedCommunities.set(node.id, /* @__PURE__ */ new Set([ownC]));
1610
+ function loadEmbeddingsJson(maxCacheSize) {
1611
+ const cache = /* @__PURE__ */ new Map();
1612
+ try {
1613
+ const embPath = getEmbeddingsPath();
1614
+ if (fs.existsSync(embPath)) {
1615
+ const embeddings = JSON.parse(fs.readFileSync(embPath, "utf-8"));
1616
+ for (const [key, value] of Object.entries(embeddings)) cache.set(key, value);
1617
+ while (cache.size > maxCacheSize) {
1618
+ const oldest = cache.keys().next().value;
1619
+ if (oldest !== void 0) cache.delete(oldest);
1620
+ }
1441
1621
  }
1622
+ } catch {
1442
1623
  }
1443
- for (const edge of graph.edges) {
1444
- const sc = result.communities.get(edge.source);
1445
- const tc = result.communities.get(edge.target);
1446
- if (sc === void 0 || tc === void 0) continue;
1447
- if (sc === tc) continue;
1448
- touchedCommunities.get(edge.source)?.add(tc);
1449
- touchedCommunities.get(edge.target)?.add(sc);
1624
+ return cache;
1625
+ }
1626
+ function lookupPramana(db, nodeId) {
1627
+ if (!db) return "shabda";
1628
+ try {
1629
+ const row = db.prepare(`
1630
+ SELECT pramana, COUNT(*) as cnt FROM edges
1631
+ WHERE (source = ? OR target = ?) AND pramana IS NOT NULL
1632
+ GROUP BY pramana ORDER BY cnt DESC LIMIT 1
1633
+ `).get(nodeId, nodeId);
1634
+ if (row?.pramana) return row.pramana;
1635
+ } catch {
1450
1636
  }
1451
- const bridges = [];
1452
- for (const node of graph.nodes) {
1453
- const communities = touchedCommunities.get(node.id);
1454
- if (communities && communities.size >= minCommunities) {
1455
- bridges.push(node);
1637
+ return "shabda";
1638
+ }
1639
+ function lookupPramanaBatch(db, nodeIds) {
1640
+ const result = /* @__PURE__ */ new Map();
1641
+ if (nodeIds.length === 0) return result;
1642
+ if (!db) {
1643
+ for (const id of nodeIds) result.set(id, "shabda");
1644
+ return result;
1645
+ }
1646
+ try {
1647
+ const placeholders = nodeIds.map(() => "?").join(",");
1648
+ const rows = db.prepare(`
1649
+ SELECT node_id, pramana FROM (
1650
+ SELECT source AS node_id, pramana, COUNT(*) AS cnt FROM edges
1651
+ WHERE source IN (${placeholders}) AND pramana IS NOT NULL
1652
+ GROUP BY source, pramana
1653
+ UNION ALL
1654
+ SELECT target AS node_id, pramana, COUNT(*) AS cnt FROM edges
1655
+ WHERE target IN (${placeholders}) AND pramana IS NOT NULL
1656
+ GROUP BY target, pramana
1657
+ ) GROUP BY node_id HAVING cnt = MAX(cnt)
1658
+ `).all(...nodeIds, ...nodeIds);
1659
+ for (const row of rows) result.set(row.node_id, row.pramana);
1660
+ } catch {
1661
+ }
1662
+ for (const id of nodeIds) {
1663
+ if (!result.has(id)) result.set(id, "shabda");
1664
+ }
1665
+ return result;
1666
+ }
1667
+ function migrateGraphJson() {
1668
+ const graphPath = getGraphPath();
1669
+ const prPath = getPageRankPath();
1670
+ if (!fs.existsSync(graphPath)) return { nodes: 0, edges: 0 };
1671
+ try {
1672
+ const graph = JSON.parse(fs.readFileSync(graphPath, "utf-8"));
1673
+ if (!graph.nodes || graph.nodes.length === 0) return { nodes: 0, edges: 0 };
1674
+ const pageRankScores = /* @__PURE__ */ new Map();
1675
+ try {
1676
+ if (fs.existsSync(prPath)) {
1677
+ const ranks = JSON.parse(fs.readFileSync(prPath, "utf-8"));
1678
+ for (const [key, value] of Object.entries(ranks)) pageRankScores.set(key, value);
1679
+ }
1680
+ } catch {
1681
+ }
1682
+ const dbm = DatabaseManager.instance();
1683
+ initGraphSchema(dbm);
1684
+ const db = dbm.get("graph");
1685
+ db.transaction(() => {
1686
+ const upsertStmt = db.prepare(`
1687
+ INSERT OR REPLACE INTO nodes (id, type, label, content, metadata, created_at, updated_at)
1688
+ VALUES (?, ?, ?, ?, ?, ?, ?)
1689
+ `);
1690
+ const edgeStmt = db.prepare(`
1691
+ INSERT OR IGNORE INTO edges (source, target, relationship, weight, pramana, viveka,
1692
+ valid_from, valid_until, recorded_at, superseded_at)
1693
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1694
+ `);
1695
+ const prStmt = db.prepare(
1696
+ "INSERT OR REPLACE INTO pagerank (node_id, score, updated_at) VALUES (?, ?, ?)"
1697
+ );
1698
+ const now = Date.now();
1699
+ for (const node of graph.nodes) {
1700
+ upsertStmt.run(node.id, node.type, node.label, node.content, JSON.stringify(node.metadata), now, now);
1701
+ }
1702
+ for (const edge of graph.edges) {
1703
+ edgeStmt.run(
1704
+ edge.source,
1705
+ edge.target,
1706
+ edge.relationship,
1707
+ edge.weight,
1708
+ null,
1709
+ null,
1710
+ edge.validFrom ? new Date(edge.validFrom).getTime() : now,
1711
+ edge.validUntil ? new Date(edge.validUntil).getTime() : null,
1712
+ edge.recordedAt ? new Date(edge.recordedAt).getTime() : now,
1713
+ edge.supersededAt ? new Date(edge.supersededAt).getTime() : null
1714
+ );
1715
+ }
1716
+ for (const [nodeId, score] of pageRankScores) prStmt.run(nodeId, score, now);
1717
+ })();
1718
+ try {
1719
+ fs.renameSync(graphPath, graphPath + ".bak");
1720
+ } catch {
1721
+ }
1722
+ try {
1723
+ if (fs.existsSync(prPath)) fs.renameSync(prPath, prPath + ".bak");
1724
+ } catch {
1456
1725
  }
1726
+ return { nodes: graph.nodes.length, edges: graph.edges.length };
1727
+ } catch {
1728
+ return { nodes: 0, edges: 0 };
1457
1729
  }
1458
- return bridges;
1459
1730
  }
1460
1731
 
1461
1732
  // ../chitragupta/packages/smriti/src/graphrag.ts
@@ -1464,18 +1735,6 @@ var DEFAULT_CONFIG = {
1464
1735
  model: "nomic-embed-text",
1465
1736
  generationModel: "llama3.2"
1466
1737
  };
1467
- function getGraphDir() {
1468
- return path.join(getChitraguptaHome(), "graphrag");
1469
- }
1470
- function getGraphPath() {
1471
- return path.join(getGraphDir(), "graph.json");
1472
- }
1473
- function getEmbeddingsPath() {
1474
- return path.join(getGraphDir(), "embeddings.json");
1475
- }
1476
- function getPageRankPath() {
1477
- return path.join(getGraphDir(), "pagerank.json");
1478
- }
1479
1738
  var GraphRAGEngine = class {
1480
1739
  config;
1481
1740
  graph;
@@ -1484,9 +1743,9 @@ var GraphRAGEngine = class {
1484
1743
  maxEmbeddingCacheSize;
1485
1744
  ollamaAvailable = null;
1486
1745
  embeddingService;
1487
- /** Push-based incremental PageRank engine — avoids full recompute on edge changes. */
1746
+ /** Push-based incremental PageRank engine. */
1488
1747
  incrementalPR = null;
1489
- /** Whether the graph.db schema has been initialized in this process. */
1748
+ /** Whether graph.db schema has been initialized in this process. */
1490
1749
  _graphDbInitialized = false;
1491
1750
  constructor(config) {
1492
1751
  this.config = { ...DEFAULT_CONFIG, ...config };
@@ -1497,7 +1756,6 @@ var GraphRAGEngine = class {
1497
1756
  this.embeddingService = config?.embeddingService ?? new EmbeddingService();
1498
1757
  this.loadFromDisk();
1499
1758
  }
1500
- // ─── Ollama Availability (for entity extraction) ──────────────────
1501
1759
  async checkOllamaAvailability() {
1502
1760
  if (this.ollamaAvailable !== null) return this.ollamaAvailable;
1503
1761
  try {
@@ -1511,6 +1769,7 @@ var GraphRAGEngine = class {
1511
1769
  }
1512
1770
  return this.ollamaAvailable;
1513
1771
  }
1772
+ /** Get (or compute and cache) an embedding vector for the given text. */
1514
1773
  async getEmbedding(text) {
1515
1774
  const cached = this.embeddingCache.get(text);
1516
1775
  if (cached) return cached;
@@ -1518,17 +1777,15 @@ var GraphRAGEngine = class {
1518
1777
  this.embeddingCache.set(text, vector);
1519
1778
  if (this.embeddingCache.size > this.maxEmbeddingCacheSize) {
1520
1779
  const oldest = this.embeddingCache.keys().next().value;
1521
- if (oldest !== void 0) {
1522
- this.embeddingCache.delete(oldest);
1523
- }
1780
+ if (oldest !== void 0) this.embeddingCache.delete(oldest);
1524
1781
  }
1525
1782
  return vector;
1526
1783
  }
1527
- // ─── Cosine Similarity (delegate) ──────────────────────────────────
1784
+ /** Compute cosine similarity between two vectors. */
1528
1785
  cosineSimilarity(a, b) {
1529
1786
  return cosineSimilarity(a, b);
1530
1787
  }
1531
- // ─── Entity Extraction (delegates) ─────────────────────────────────
1788
+ /** Extract named entities from text using LLM (with keyword + NER fallback). */
1532
1789
  async extractEntities(text) {
1533
1790
  const isAvailable = await this.checkOllamaAvailability();
1534
1791
  let baseEntities;
@@ -1573,46 +1830,27 @@ var GraphRAGEngine = class {
1573
1830
  return baseEntities;
1574
1831
  }
1575
1832
  // ─── Incremental PageRank ────────────────────────────────────────
1576
- /**
1577
- * Initialize (or reinitialize) the incremental PageRank engine from
1578
- * the current graph state. Performs one full Gauss-Seidel computation
1579
- * internally, then subsequent edge changes are O(1/ε) each via
1580
- * push-based residual propagation.
1581
- */
1833
+ /** Initialize incremental PageRank from current graph state. */
1582
1834
  initIncrementalPR() {
1583
1835
  this.incrementalPR = new IncrementalPageRank();
1584
1836
  this.incrementalPR.initialize(this.graph);
1585
1837
  this.pageRankScores = this.incrementalPR.getRanks();
1586
1838
  }
1587
- /**
1588
- * Feed removed edges into the incremental PR engine so it can adjust
1589
- * scores via residual propagation rather than a full recompute.
1590
- */
1839
+ /** Feed removed edges into incremental PR engine. */
1591
1840
  applyEdgeRemovals(edges) {
1592
1841
  if (!this.incrementalPR || edges.length === 0) return;
1593
- for (const edge of edges) {
1594
- this.incrementalPR.removeEdge(edge.source, edge.target);
1595
- }
1842
+ for (const edge of edges) this.incrementalPR.removeEdge(edge.source, edge.target);
1596
1843
  this.pageRankScores = this.incrementalPR.getRanks();
1597
1844
  }
1598
- /**
1599
- * Feed newly added edges into the incremental PR engine.
1600
- */
1845
+ /** Feed newly added edges into incremental PR engine. */
1601
1846
  applyEdgeAdditions(edges) {
1602
1847
  if (!this.incrementalPR || edges.length === 0) return;
1603
- for (const edge of edges) {
1604
- this.incrementalPR.addEdge(edge.source, edge.target);
1605
- }
1848
+ for (const edge of edges) this.incrementalPR.addEdge(edge.source, edge.target);
1606
1849
  this.pageRankScores = this.incrementalPR.getRanks();
1607
1850
  }
1608
- // ─── PageRank (delegates) ──────────────────────────────────────────
1609
1851
  /**
1610
- * Compute PageRank for the given graph (or the internal graph if none provided).
1611
- *
1612
- * When called with an explicit graph argument, uses the full power-iteration
1613
- * algorithm (useful for one-off computations on external graphs and tests).
1614
- * When called without arguments, initializes the incremental PageRank engine
1615
- * on the internal graph.
1852
+ * Compute PageRank. With an explicit graph argument uses full power-iteration;
1853
+ * without arguments initializes the incremental engine on the internal graph.
1616
1854
  */
1617
1855
  computePageRank(graph) {
1618
1856
  if (graph) {
@@ -1622,49 +1860,41 @@ var GraphRAGEngine = class {
1622
1860
  this.initIncrementalPR();
1623
1861
  return this.pageRankScores;
1624
1862
  }
1863
+ /** Get the PageRank score for a node. Returns 0 if not computed. */
1625
1864
  getPageRank(nodeId) {
1626
1865
  return this.pageRankScores.get(nodeId) ?? 0;
1627
1866
  }
1628
- /**
1629
- * Force a full PageRank recompute from the current graph state.
1630
- * Reinitializes the incremental engine from scratch. Useful after
1631
- * graph compaction or any structural change that bypasses the
1632
- * normal indexSession/indexMemory paths.
1633
- */
1867
+ /** Force a full PageRank recompute and persist. */
1634
1868
  recomputePageRank() {
1635
1869
  this.initIncrementalPR();
1636
1870
  this.saveToDisk();
1637
1871
  }
1638
1872
  // ─── Graph Building ───────────────────────────────────────────────
1873
+ /** Build a full knowledge graph from sessions and memories. */
1639
1874
  async buildGraph(sessions, memories) {
1640
1875
  const nodes = [];
1641
1876
  const edges = [];
1642
1877
  for (const session of sessions) {
1643
- const sessionNode = await createSessionNode(this, session);
1644
- nodes.push(sessionNode);
1878
+ nodes.push(await createSessionNode(this, session));
1645
1879
  await indexSessionTurns(this, session, nodes, edges);
1646
1880
  }
1647
- for (const memory of memories) {
1648
- await buildMemoryNodes(this, memory, nodes, edges);
1649
- }
1881
+ for (const memory of memories) await buildMemoryNodes(this, memory, nodes, edges);
1650
1882
  await extractConceptsFromNodes(this, nodes, edges);
1651
1883
  this.graph = { nodes, edges };
1652
1884
  if (this.graph.nodes.length > 0 && this.graph.edges.length > 0) {
1653
- const leidenResult = leiden(this.graph);
1654
- annotateCommunities(this.graph, leidenResult);
1885
+ annotateCommunities(this.graph, leiden(this.graph));
1655
1886
  }
1656
1887
  this.initIncrementalPR();
1657
1888
  this.saveToDisk();
1658
1889
  return this.graph;
1659
1890
  }
1660
1891
  // ─── Hybrid Search ───────────────────────────────────────────────
1892
+ /** Search the knowledge graph using hybrid scoring (cosine + PageRank + BM25). */
1661
1893
  async search(query, graph, topK = 10) {
1662
1894
  const searchGraph = graph ?? this.graph;
1663
1895
  if (searchGraph.nodes.length === 0) return [];
1664
1896
  const queryEmbedding = await this.getEmbedding(query);
1665
- if (this.pageRankScores.size === 0) {
1666
- this.computePageRank(searchGraph);
1667
- }
1897
+ if (this.pageRankScores.size === 0) this.computePageRank(searchGraph);
1668
1898
  let maxPageRank = 0;
1669
1899
  for (const node of searchGraph.nodes) {
1670
1900
  const pr = this.pageRankScores.get(node.id) ?? 0;
@@ -1674,30 +1904,26 @@ var GraphRAGEngine = class {
1674
1904
  for (const node of searchGraph.nodes) {
1675
1905
  let cosineSim = 0;
1676
1906
  if (node.embedding && node.embedding.length > 0) {
1677
- cosineSim = cosineSimilarity(queryEmbedding, node.embedding);
1678
- cosineSim = Math.max(0, cosineSim);
1907
+ cosineSim = Math.max(0, cosineSimilarity(queryEmbedding, node.embedding));
1679
1908
  }
1680
- const rawPageRank = this.pageRankScores.get(node.id) ?? 0;
1681
- const normalizedPageRank = maxPageRank > 0 ? rawPageRank / maxPageRank : 0;
1909
+ const rawPR = this.pageRankScores.get(node.id) ?? 0;
1910
+ const normPR = maxPageRank > 0 ? rawPR / maxPageRank : 0;
1682
1911
  const textScore = textMatchScore(query, node.content + " " + node.label);
1683
- const finalScore = ALPHA * cosineSim + BETA * normalizedPageRank + GAMMA * textScore;
1684
- if (finalScore > 0) {
1685
- scored.push({ node, score: finalScore });
1686
- }
1912
+ const finalScore = ALPHA * cosineSim + BETA * normPR + GAMMA * textScore;
1913
+ if (finalScore > 0) scored.push({ node, score: finalScore });
1687
1914
  }
1688
1915
  scored.sort((a, b) => b.score - a.score);
1689
1916
  return scored.slice(0, topK).map((s) => s.node);
1690
1917
  }
1691
1918
  // ─── Incremental Indexing ─────────────────────────────────────────
1919
+ /** Index a single session incrementally. */
1692
1920
  async indexSession(session) {
1693
1921
  const edgesBefore = [...this.graph.edges];
1694
1922
  removeSessionFromGraph(this.graph, session.meta.id);
1695
- const edgesAfterRemoval = this.graph.edges;
1696
- const edgesAfterSet = new Set(edgesAfterRemoval);
1923
+ const edgesAfterSet = new Set(this.graph.edges);
1697
1924
  const removedEdges = edgesBefore.filter((e) => !edgesAfterSet.has(e));
1698
1925
  const edgeCountBeforeAdd = this.graph.edges.length;
1699
- const sessionNode = await createSessionNode(this, session);
1700
- this.graph.nodes.push(sessionNode);
1926
+ this.graph.nodes.push(await createSessionNode(this, session));
1701
1927
  await indexSessionTurns(this, session, this.graph.nodes, this.graph.edges);
1702
1928
  const newEdges = this.graph.edges.slice(edgeCountBeforeAdd);
1703
1929
  if (!this.incrementalPR) {
@@ -1708,11 +1934,11 @@ var GraphRAGEngine = class {
1708
1934
  }
1709
1935
  this.saveToDisk();
1710
1936
  }
1937
+ /** Index a memory scope incrementally. */
1711
1938
  async indexMemory(scope, content) {
1712
1939
  const edgesBefore = [...this.graph.edges];
1713
1940
  removeMemoryFromGraph(this.graph, scope);
1714
- const edgesAfterRemoval = this.graph.edges;
1715
- const edgesAfterSet = new Set(edgesAfterRemoval);
1941
+ const edgesAfterSet = new Set(this.graph.edges);
1716
1942
  const removedEdges = edgesBefore.filter((e) => !edgesAfterSet.has(e));
1717
1943
  const edgeCountBeforeAdd = this.graph.edges.length;
1718
1944
  await buildMemoryNodesFromContent(this, scope, content, this.graph.nodes, this.graph.edges);
@@ -1725,322 +1951,91 @@ var GraphRAGEngine = class {
1725
1951
  }
1726
1952
  this.saveToDisk();
1727
1953
  }
1728
- // ─── Pramana Lookup ──────────────────────────────────────────────
1729
- /**
1730
- * Look up the dominant pramana type for a given node ID.
1731
- *
1732
- * Queries the edges table for edges involving this node (as source or target)
1733
- * and returns the most common non-null pramana value. Falls back to 'shabda'
1734
- * (testimony/documentation) if no pramana data is found.
1735
- *
1736
- * @param nodeId - The graph node ID to look up.
1737
- * @returns The dominant PramanaType for this node.
1738
- */
1954
+ // ─── Pramana Lookup (delegates) ──────────────────────────────────
1955
+ /** Look up the dominant pramana type for a given node ID. */
1739
1956
  lookupPramana(nodeId) {
1740
- const db = this.getGraphDb();
1741
- if (!db) return "shabda";
1742
- try {
1743
- const row = db.prepare(`
1744
- SELECT pramana, COUNT(*) as cnt
1745
- FROM edges
1746
- WHERE (source = ? OR target = ?) AND pramana IS NOT NULL
1747
- GROUP BY pramana
1748
- ORDER BY cnt DESC
1749
- LIMIT 1
1750
- `).get(nodeId, nodeId);
1751
- if (row?.pramana) return row.pramana;
1752
- } catch {
1753
- }
1754
- return "shabda";
1957
+ return lookupPramana(this.getGraphDbHandle(), nodeId);
1755
1958
  }
1756
- /**
1757
- * Batch look up pramana types for multiple node IDs.
1758
- *
1759
- * More efficient than calling lookupPramana() individually for each node.
1760
- * Returns a Map from node ID to PramanaType. Missing entries default to 'shabda'.
1761
- *
1762
- * @param nodeIds - Array of node IDs to look up.
1763
- * @returns Map from node ID to its dominant PramanaType.
1764
- */
1959
+ /** Batch look up pramana types for multiple node IDs. */
1765
1960
  lookupPramanaBatch(nodeIds) {
1766
- const result = /* @__PURE__ */ new Map();
1767
- if (nodeIds.length === 0) return result;
1768
- const db = this.getGraphDb();
1769
- if (!db) {
1770
- for (const id of nodeIds) result.set(id, "shabda");
1771
- return result;
1772
- }
1773
- try {
1774
- const placeholders = nodeIds.map(() => "?").join(",");
1775
- const rows = db.prepare(`
1776
- SELECT node_id, pramana FROM (
1777
- SELECT source AS node_id, pramana, COUNT(*) AS cnt
1778
- FROM edges
1779
- WHERE source IN (${placeholders}) AND pramana IS NOT NULL
1780
- GROUP BY source, pramana
1781
- UNION ALL
1782
- SELECT target AS node_id, pramana, COUNT(*) AS cnt
1783
- FROM edges
1784
- WHERE target IN (${placeholders}) AND pramana IS NOT NULL
1785
- GROUP BY target, pramana
1786
- )
1787
- GROUP BY node_id
1788
- HAVING cnt = MAX(cnt)
1789
- `).all(...nodeIds, ...nodeIds);
1790
- for (const row of rows) {
1791
- result.set(row.node_id, row.pramana);
1792
- }
1793
- } catch {
1794
- }
1795
- for (const id of nodeIds) {
1796
- if (!result.has(id)) result.set(id, "shabda");
1797
- }
1798
- return result;
1799
- }
1800
- // ─── SQLite Persistence Helpers ─────────────────────────────────
1801
- /**
1802
- * Get the graph database handle, initializing the schema on first access.
1803
- * Returns null if the database layer is unavailable (e.g. mocked fs in tests).
1804
- */
1805
- getGraphDb() {
1806
- try {
1807
- const dbm = DatabaseManager.instance();
1808
- if (!this._graphDbInitialized) {
1809
- initGraphSchema(dbm);
1810
- this._graphDbInitialized = true;
1811
- }
1812
- return dbm.get("graph");
1813
- } catch {
1814
- return null;
1815
- }
1816
- }
1817
- /** Insert or replace a node in the SQLite nodes table. */
1818
- upsertNode(db, node) {
1819
- db.prepare(`
1820
- INSERT OR REPLACE INTO nodes (id, type, label, content, metadata, created_at, updated_at)
1821
- VALUES (?, ?, ?, ?, ?, ?, ?)
1822
- `).run(
1823
- node.id,
1824
- node.type,
1825
- node.label,
1826
- node.content,
1827
- JSON.stringify(node.metadata),
1828
- Date.now(),
1829
- Date.now()
1830
- );
1961
+ return lookupPramanaBatch(this.getGraphDbHandle(), nodeIds);
1831
1962
  }
1832
- /** Insert an edge into the SQLite edges table (bi-temporal fields mapped). */
1833
- insertEdge(db, edge) {
1834
- db.prepare(`
1835
- INSERT OR IGNORE INTO edges (source, target, relationship, weight, pramana, viveka,
1836
- valid_from, valid_until, recorded_at, superseded_at)
1837
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1838
- `).run(
1839
- edge.source,
1840
- edge.target,
1841
- edge.relationship,
1842
- edge.weight,
1843
- null,
1844
- // pramana — not yet used
1845
- null,
1846
- // viveka — not yet used
1847
- edge.validFrom ? new Date(edge.validFrom).getTime() : Date.now(),
1848
- edge.validUntil ? new Date(edge.validUntil).getTime() : null,
1849
- edge.recordedAt ? new Date(edge.recordedAt).getTime() : Date.now(),
1850
- edge.supersededAt ? new Date(edge.supersededAt).getTime() : null
1851
- );
1963
+ // ─── Persistence (delegates) ─────────────────────────────────────
1964
+ /** Get graph database handle via persistence layer. */
1965
+ getGraphDbHandle() {
1966
+ return getGraphDb(this._graphDbInitialized, () => {
1967
+ this._graphDbInitialized = true;
1968
+ });
1852
1969
  }
1853
- // ─── Persistence ──────────────────────────────────────────────────
1970
+ /** Save graph state to SQLite (primary) or JSON (fallback). */
1854
1971
  saveToDisk() {
1855
1972
  try {
1856
- const db = this.getGraphDb();
1973
+ const db = this.getGraphDbHandle();
1857
1974
  if (db) {
1858
- db.transaction(() => {
1859
- db.prepare("DELETE FROM pagerank").run();
1860
- db.prepare("DELETE FROM edges").run();
1861
- db.prepare("DELETE FROM nodes").run();
1862
- for (const node of this.graph.nodes) {
1863
- this.upsertNode(db, node);
1864
- }
1865
- for (const edge of this.graph.edges) {
1866
- this.insertEdge(db, edge);
1867
- }
1868
- for (const [nodeId, score] of this.pageRankScores) {
1869
- db.prepare(
1870
- "INSERT OR REPLACE INTO pagerank (node_id, score, updated_at) VALUES (?, ?, ?)"
1871
- ).run(nodeId, score, Date.now());
1872
- }
1873
- })();
1874
- this.saveEmbeddingsJson();
1975
+ saveToSqlite(db, this.graph, this.pageRankScores, this.embeddingCache);
1875
1976
  return;
1876
1977
  }
1877
1978
  } catch {
1878
1979
  }
1879
- this.saveToDiskJson();
1980
+ saveToJson(this.graph, this.pageRankScores, this.embeddingCache);
1880
1981
  }
1982
+ /** Load graph state from SQLite (primary) or JSON (fallback). */
1881
1983
  loadFromDisk() {
1882
- let loadedFromSqlite = false;
1984
+ let loaded = false;
1883
1985
  try {
1884
- const db = this.getGraphDb();
1986
+ const db = this.getGraphDbHandle();
1885
1987
  if (db) {
1886
- const nodeCount = db.prepare("SELECT COUNT(*) as cnt FROM nodes").get().cnt;
1887
- if (nodeCount > 0) {
1888
- const nodeRows = db.prepare("SELECT * FROM nodes").all();
1889
- this.graph.nodes = nodeRows.map((row) => ({
1890
- id: row.id,
1891
- type: row.type,
1892
- label: row.label,
1893
- content: row.content ?? "",
1894
- metadata: (() => {
1895
- try {
1896
- return JSON.parse(row.metadata ?? "{}");
1897
- } catch {
1898
- return {};
1899
- }
1900
- })()
1901
- // Note: embedding not stored in nodes table — it's in vectors.db (Phase 0.6)
1902
- }));
1903
- const edgeRows = db.prepare("SELECT * FROM edges").all();
1904
- this.graph.edges = edgeRows.map((row) => ({
1905
- source: row.source,
1906
- target: row.target,
1907
- relationship: row.relationship,
1908
- weight: row.weight,
1909
- validFrom: row.valid_from ? new Date(row.valid_from).toISOString() : void 0,
1910
- validUntil: row.valid_until ? new Date(row.valid_until).toISOString() : void 0,
1911
- recordedAt: row.recorded_at ? new Date(row.recorded_at).toISOString() : void 0,
1912
- supersededAt: row.superseded_at ? new Date(row.superseded_at).toISOString() : void 0
1913
- }));
1914
- const prRows = db.prepare("SELECT * FROM pagerank").all();
1915
- for (const row of prRows) {
1916
- this.pageRankScores.set(row.node_id, row.score);
1917
- }
1918
- loadedFromSqlite = true;
1988
+ const sqliteData = loadFromSqlite(db);
1989
+ if (sqliteData) {
1990
+ this.graph = sqliteData.graph;
1991
+ this.pageRankScores = sqliteData.pageRankScores;
1919
1992
  } else {
1920
- this.loadFromDiskJson();
1993
+ const jsonData = loadFromJson();
1994
+ this.graph = jsonData.graph;
1995
+ this.pageRankScores = jsonData.pageRankScores;
1921
1996
  if (this.graph.nodes.length > 0) {
1922
1997
  try {
1923
- db.transaction(() => {
1924
- for (const node of this.graph.nodes) {
1925
- this.upsertNode(db, node);
1926
- }
1927
- for (const edge of this.graph.edges) {
1928
- this.insertEdge(db, edge);
1929
- }
1930
- for (const [nodeId, score] of this.pageRankScores) {
1931
- db.prepare(
1932
- "INSERT OR REPLACE INTO pagerank (node_id, score, updated_at) VALUES (?, ?, ?)"
1933
- ).run(nodeId, score, Date.now());
1934
- }
1935
- })();
1998
+ migrateInMemoryToSqlite(db, this.graph, this.pageRankScores);
1936
1999
  } catch {
1937
2000
  }
1938
2001
  }
1939
- loadedFromSqlite = true;
1940
2002
  }
2003
+ loaded = true;
1941
2004
  }
1942
2005
  } catch {
1943
2006
  }
1944
- if (!loadedFromSqlite) {
1945
- this.loadFromDiskJson();
1946
- }
1947
- this.loadEmbeddingsJson();
1948
- if (this.graph.edges.length > 0) {
1949
- this.initIncrementalPR();
1950
- }
1951
- }
1952
- // ─── JSON Fallback Persistence (legacy / migration) ───────────────
1953
- saveToDiskJson() {
1954
- try {
1955
- const dir = getGraphDir();
1956
- fs.mkdirSync(dir, { recursive: true });
1957
- fs.writeFileSync(getGraphPath(), JSON.stringify(this.graph, null, " "), "utf-8");
1958
- this.saveEmbeddingsJson();
1959
- const pageRankObj = {};
1960
- for (const [key, value] of this.pageRankScores) {
1961
- pageRankObj[key] = value;
1962
- }
1963
- fs.writeFileSync(getPageRankPath(), JSON.stringify(pageRankObj, null, " "), "utf-8");
1964
- } catch {
1965
- }
1966
- }
1967
- saveEmbeddingsJson() {
1968
- try {
1969
- const dir = getGraphDir();
1970
- fs.mkdirSync(dir, { recursive: true });
1971
- const embeddings = {};
1972
- for (const [key, value] of this.embeddingCache) {
1973
- embeddings[key] = value;
1974
- }
1975
- fs.writeFileSync(getEmbeddingsPath(), JSON.stringify(embeddings, null, " "), "utf-8");
1976
- } catch {
1977
- }
1978
- }
1979
- loadFromDiskJson() {
1980
- try {
1981
- const graphPath = getGraphPath();
1982
- if (fs.existsSync(graphPath)) {
1983
- const raw = fs.readFileSync(graphPath, "utf-8");
1984
- this.graph = JSON.parse(raw);
1985
- }
1986
- } catch {
1987
- this.graph = { nodes: [], edges: [] };
1988
- }
1989
- try {
1990
- const prPath = getPageRankPath();
1991
- if (fs.existsSync(prPath)) {
1992
- const raw = fs.readFileSync(prPath, "utf-8");
1993
- const ranks = JSON.parse(raw);
1994
- for (const [key, value] of Object.entries(ranks)) {
1995
- this.pageRankScores.set(key, value);
1996
- }
1997
- }
1998
- } catch {
1999
- this.pageRankScores = /* @__PURE__ */ new Map();
2007
+ if (!loaded) {
2008
+ const jsonData = loadFromJson();
2009
+ this.graph = jsonData.graph;
2010
+ this.pageRankScores = jsonData.pageRankScores;
2000
2011
  }
2001
- }
2002
- loadEmbeddingsJson() {
2003
- try {
2004
- const embPath = getEmbeddingsPath();
2005
- if (fs.existsSync(embPath)) {
2006
- const raw = fs.readFileSync(embPath, "utf-8");
2007
- const embeddings = JSON.parse(raw);
2008
- for (const [key, value] of Object.entries(embeddings)) {
2009
- this.embeddingCache.set(key, value);
2010
- }
2011
- while (this.embeddingCache.size > this.maxEmbeddingCacheSize) {
2012
- const oldest = this.embeddingCache.keys().next().value;
2013
- if (oldest !== void 0) {
2014
- this.embeddingCache.delete(oldest);
2015
- }
2016
- }
2017
- }
2018
- } catch {
2019
- this.embeddingCache = /* @__PURE__ */ new Map();
2012
+ for (const [k, v] of loadEmbeddingsJson(this.maxEmbeddingCacheSize)) {
2013
+ this.embeddingCache.set(k, v);
2020
2014
  }
2015
+ if (this.graph.edges.length > 0) this.initIncrementalPR();
2021
2016
  }
2017
+ /** Get the current in-memory knowledge graph. */
2022
2018
  getGraph() {
2023
2019
  return this.graph;
2024
2020
  }
2021
+ /** Clear all graph data, caches, and persisted state. */
2025
2022
  async clear() {
2026
2023
  this.graph = { nodes: [], edges: [] };
2027
2024
  this.embeddingCache = /* @__PURE__ */ new Map();
2028
2025
  this.pageRankScores = /* @__PURE__ */ new Map();
2029
2026
  this.incrementalPR = null;
2030
2027
  try {
2031
- const db = this.getGraphDb();
2032
- if (db) {
2033
- db.exec("DELETE FROM pagerank; DELETE FROM edges; DELETE FROM nodes;");
2034
- }
2028
+ const db = this.getGraphDbHandle();
2029
+ if (db) db.exec("DELETE FROM pagerank; DELETE FROM edges; DELETE FROM nodes;");
2035
2030
  } catch {
2036
2031
  }
2037
2032
  this.saveToDisk();
2038
2033
  }
2034
+ /** Reset the Ollama availability flag to force re-check. */
2039
2035
  resetAvailability() {
2040
2036
  this.ollamaAvailable = null;
2041
2037
  this.embeddingService.resetAvailability();
2042
2038
  }
2043
- // ─── Graph Neighbor Queries ───────────────────────────────────────
2044
2039
  /**
2045
2040
  * Get edges connected to a node, optionally filtered by direction.
2046
2041
  * Uses the in-memory graph for fast access.
@@ -2053,94 +2048,10 @@ var GraphRAGEngine = class {
2053
2048
  });
2054
2049
  }
2055
2050
  };
2056
- function migrateGraphJson() {
2057
- const graphPath = getGraphPath();
2058
- const prPath = getPageRankPath();
2059
- if (!fs.existsSync(graphPath)) {
2060
- return { nodes: 0, edges: 0 };
2061
- }
2062
- try {
2063
- const raw = fs.readFileSync(graphPath, "utf-8");
2064
- const graph = JSON.parse(raw);
2065
- if (!graph.nodes || graph.nodes.length === 0) {
2066
- return { nodes: 0, edges: 0 };
2067
- }
2068
- const pageRankScores = /* @__PURE__ */ new Map();
2069
- try {
2070
- if (fs.existsSync(prPath)) {
2071
- const prRaw = fs.readFileSync(prPath, "utf-8");
2072
- const ranks = JSON.parse(prRaw);
2073
- for (const [key, value] of Object.entries(ranks)) {
2074
- pageRankScores.set(key, value);
2075
- }
2076
- }
2077
- } catch {
2078
- }
2079
- const dbm = DatabaseManager.instance();
2080
- initGraphSchema(dbm);
2081
- const db = dbm.get("graph");
2082
- db.transaction(() => {
2083
- const upsertNodeStmt = db.prepare(`
2084
- INSERT OR REPLACE INTO nodes (id, type, label, content, metadata, created_at, updated_at)
2085
- VALUES (?, ?, ?, ?, ?, ?, ?)
2086
- `);
2087
- const insertEdgeStmt = db.prepare(`
2088
- INSERT OR IGNORE INTO edges (source, target, relationship, weight, pramana, viveka,
2089
- valid_from, valid_until, recorded_at, superseded_at)
2090
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2091
- `);
2092
- const upsertPRStmt = db.prepare(
2093
- "INSERT OR REPLACE INTO pagerank (node_id, score, updated_at) VALUES (?, ?, ?)"
2094
- );
2095
- const now = Date.now();
2096
- for (const node of graph.nodes) {
2097
- upsertNodeStmt.run(
2098
- node.id,
2099
- node.type,
2100
- node.label,
2101
- node.content,
2102
- JSON.stringify(node.metadata),
2103
- now,
2104
- now
2105
- );
2106
- }
2107
- for (const edge of graph.edges) {
2108
- insertEdgeStmt.run(
2109
- edge.source,
2110
- edge.target,
2111
- edge.relationship,
2112
- edge.weight,
2113
- null,
2114
- null,
2115
- edge.validFrom ? new Date(edge.validFrom).getTime() : now,
2116
- edge.validUntil ? new Date(edge.validUntil).getTime() : null,
2117
- edge.recordedAt ? new Date(edge.recordedAt).getTime() : now,
2118
- edge.supersededAt ? new Date(edge.supersededAt).getTime() : null
2119
- );
2120
- }
2121
- for (const [nodeId, score] of pageRankScores) {
2122
- upsertPRStmt.run(nodeId, score, now);
2123
- }
2124
- })();
2125
- try {
2126
- fs.renameSync(graphPath, graphPath + ".bak");
2127
- } catch {
2128
- }
2129
- try {
2130
- if (fs.existsSync(prPath)) {
2131
- fs.renameSync(prPath, prPath + ".bak");
2132
- }
2133
- } catch {
2134
- }
2135
- return { nodes: graph.nodes.length, edges: graph.edges.length };
2136
- } catch {
2137
- return { nodes: 0, edges: 0 };
2138
- }
2139
- }
2140
2051
 
2141
2052
  export {
2142
- computePersonalizedPageRank,
2143
2053
  IncrementalPageRank,
2054
+ computePersonalizedPageRank,
2144
2055
  NERExtractor,
2145
2056
  createEdge,
2146
2057
  supersedEdge,
@@ -2154,7 +2065,7 @@ export {
2154
2065
  communitySummary,
2155
2066
  filterByCommunity,
2156
2067
  findBridgeNodes,
2157
- GraphRAGEngine,
2158
- migrateGraphJson
2068
+ migrateGraphJson,
2069
+ GraphRAGEngine
2159
2070
  };
2160
- //# sourceMappingURL=chunk-S2HDNNC7.js.map
2071
+ //# sourceMappingURL=chunk-OT4G2L46.js.map