@yugenlab/vaayu 0.1.9 → 0.1.11
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/chunks/{agentic-tool-loop-2FZK72JO.js → agentic-tool-loop-O3NUV7KG.js} +1 -1
- package/chunks/{chunk-UZ6OIVEC.js → chunk-2OBLQJYJ.js} +1 -1
- package/chunks/{chunk-PJEYJQ2C.js → chunk-3AYSJ7WB.js} +30 -18
- package/chunks/{chunk-U62ABYKD.js → chunk-67DXWEKG.js} +3 -3
- package/chunks/{chunk-6556EKOB.js → chunk-7AYYXHYZ.js} +25 -24
- package/chunks/{chunk-IGBRBFXX.js → chunk-7XV5ISV7.js} +7 -5
- package/chunks/{chunk-JAWZ7ANC.js → chunk-A3HOZBC5.js} +11 -7
- package/chunks/{chunk-LVE2EOOH.js → chunk-D46QTN3G.js} +126 -136
- package/chunks/{chunk-PRXQW76U.js → chunk-EG37M4QL.js} +17 -6
- package/chunks/{chunk-7UOXFHEB.js → chunk-F6RNEGFX.js} +480 -432
- package/chunks/{chunk-MJ74G5RB.js → chunk-G2QREGXK.js} +2 -2
- package/chunks/{chunk-DOQMEQ5S.js → chunk-JZTFJE7M.js} +39 -39
- package/chunks/{chunk-S2HDNNC7.js → chunk-LJUEMPLG.js} +638 -679
- package/chunks/{chunk-C76USAC5.js → chunk-QFGAB4XD.js} +13 -5
- package/chunks/{chunk-D3RVJGO7.js → chunk-QV4GPIPT.js} +118 -135
- package/chunks/{chunk-YJRXLRTE.js → chunk-V2ZIKDN4.js} +9 -8
- package/chunks/{chunk-YSC77CKZ.js → chunk-VCUJES75.js} +3276 -3526
- package/chunks/{chunk-OBYBBGHA.js → chunk-W4PVGBUH.js} +190 -189
- package/chunks/chunk-Z576WVLG.js +434 -0
- package/chunks/{chunk-NHRBVSN3.js → chunk-ZYY6N3SP.js} +117 -110
- package/chunks/{consolidation-indexer-CD6DS2HO.js → consolidation-indexer-VIWOP6VO.js} +8 -8
- package/chunks/{day-consolidation-U3X6P4ZG.js → day-consolidation-HMHSXIOM.js} +8 -4
- package/chunks/{src-ZAKUL232.js → dist-CY5NX2IK.js} +17 -17
- package/chunks/graphrag-T2QWNX57.js +14 -0
- package/chunks/{hierarchical-temporal-search-ETXYYJZK.js → hierarchical-temporal-search-U6DG74IR.js} +2 -2
- package/chunks/hybrid-search-BYTXCOXP.js +20 -0
- package/chunks/{memory-store-A6WOWLWC.js → memory-store-LEERUQGL.js} +3 -3
- package/chunks/periodic-consolidation-D6SSKZ7H.js +11 -0
- package/chunks/{postgres-WLH3D5HG.js → postgres-7GZDDX77.js} +2 -2
- package/chunks/{recall-IUPQCBYP.js → recall-LNRQVATQ.js} +7 -7
- package/chunks/search-BIODUW2P.js +19 -0
- package/chunks/{session-store-NDUDYAC7.js → session-store-O3TS7DUY.js} +5 -5
- package/chunks/{sqlite-DHUQGPR5.js → sqlite-7BC4DJTN.js} +2 -2
- package/chunks/vasana-engine-BJFHJVGM.js +30 -0
- package/gateway.js +31671 -24786
- package/package.json +1 -1
- package/pair-cli.js +1 -1
- package/chunks/chunk-TEQKXGIK.js +0 -752
- package/chunks/graphrag-LAZSXLLI.js +0 -14
- package/chunks/hybrid-search-TX6T3KYH.js +0 -20
- package/chunks/periodic-consolidation-4MACZE6S.js +0 -11
- package/chunks/search-HHSVHBXC.js +0 -19
- package/chunks/vasana-engine-G6BPOFX7.js +0 -10
|
@@ -6,28 +6,25 @@ import {
|
|
|
6
6
|
STOP_WORDS,
|
|
7
7
|
cosineSimilarity,
|
|
8
8
|
textMatchScore
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-A3HOZBC5.js";
|
|
10
10
|
import {
|
|
11
11
|
initGraphSchema
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-V2ZIKDN4.js";
|
|
13
13
|
import {
|
|
14
14
|
DatabaseManager
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-67DXWEKG.js";
|
|
16
16
|
import {
|
|
17
17
|
getChitraguptaHome
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-2OBLQJYJ.js";
|
|
19
19
|
|
|
20
|
-
// ../chitragupta/packages/smriti/
|
|
21
|
-
import fs from "fs";
|
|
22
|
-
import path from "path";
|
|
23
|
-
|
|
24
|
-
// ../chitragupta/packages/smriti/src/graphrag-pagerank.ts
|
|
20
|
+
// ../chitragupta/packages/smriti/dist/graphrag-pagerank.js
|
|
25
21
|
var PAGERANK_DAMPING = 0.85;
|
|
26
22
|
var PAGERANK_EPSILON = 1e-4;
|
|
27
23
|
var PAGERANK_MAX_ITERATIONS = 100;
|
|
28
24
|
function computePageRank(graph) {
|
|
29
25
|
const N = graph.nodes.length;
|
|
30
|
-
if (N === 0)
|
|
26
|
+
if (N === 0)
|
|
27
|
+
return /* @__PURE__ */ new Map();
|
|
31
28
|
const ranks = /* @__PURE__ */ new Map();
|
|
32
29
|
const nodeIds = /* @__PURE__ */ new Set();
|
|
33
30
|
for (const node of graph.nodes) {
|
|
@@ -67,162 +64,24 @@ function computePageRank(graph) {
|
|
|
67
64
|
const rank = baseFactor + PAGERANK_DAMPING * (incomingRank + danglingRank / N);
|
|
68
65
|
newRanks.set(node.id, rank);
|
|
69
66
|
const delta = Math.abs(rank - (ranks.get(node.id) ?? 0));
|
|
70
|
-
if (delta > maxDelta)
|
|
67
|
+
if (delta > maxDelta)
|
|
68
|
+
maxDelta = delta;
|
|
71
69
|
}
|
|
72
70
|
for (const [id, rank] of newRanks) {
|
|
73
71
|
ranks.set(id, rank);
|
|
74
72
|
}
|
|
75
|
-
if (maxDelta < PAGERANK_EPSILON)
|
|
73
|
+
if (maxDelta < PAGERANK_EPSILON)
|
|
74
|
+
break;
|
|
76
75
|
}
|
|
77
76
|
return ranks;
|
|
78
77
|
}
|
|
79
78
|
|
|
80
|
-
// ../chitragupta/packages/smriti/
|
|
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
|
-
}
|
|
79
|
+
// ../chitragupta/packages/smriti/dist/graphrag-pagerank-incremental.js
|
|
221
80
|
var IncrementalPageRank = class {
|
|
222
81
|
ranks = /* @__PURE__ */ new Map();
|
|
223
82
|
outDegree = /* @__PURE__ */ new Map();
|
|
224
83
|
inLinks = /* @__PURE__ */ new Map();
|
|
225
|
-
/** Forward adjacency list: source
|
|
84
|
+
/** Forward adjacency list: source -> Set<target>. Avoids O(E) reverse lookup in getOutNeighbors(). */
|
|
226
85
|
outLinks = /* @__PURE__ */ new Map();
|
|
227
86
|
nodeSet = /* @__PURE__ */ new Set();
|
|
228
87
|
damping;
|
|
@@ -299,7 +158,8 @@ var IncrementalPageRank = class {
|
|
|
299
158
|
if (oldDeg > 0) {
|
|
300
159
|
const shareDelta = prU * (1 / newDeg - 1 / oldDeg);
|
|
301
160
|
for (const neighbor of this.getOutNeighbors(source)) {
|
|
302
|
-
if (neighbor === target)
|
|
161
|
+
if (neighbor === target)
|
|
162
|
+
continue;
|
|
303
163
|
const prev = residuals.get(neighbor) ?? 0;
|
|
304
164
|
residuals.set(neighbor, prev + this.damping * shareDelta);
|
|
305
165
|
}
|
|
@@ -313,11 +173,14 @@ var IncrementalPageRank = class {
|
|
|
313
173
|
* @param target - Target node ID.
|
|
314
174
|
*/
|
|
315
175
|
removeEdge(source, target) {
|
|
316
|
-
if (!this.nodeSet.has(source) || !this.nodeSet.has(target))
|
|
176
|
+
if (!this.nodeSet.has(source) || !this.nodeSet.has(target))
|
|
177
|
+
return;
|
|
317
178
|
const inSet = this.inLinks.get(target);
|
|
318
|
-
if (!inSet || !inSet.has(source))
|
|
179
|
+
if (!inSet || !inSet.has(source))
|
|
180
|
+
return;
|
|
319
181
|
const oldDeg = this.outDegree.get(source) ?? 0;
|
|
320
|
-
if (oldDeg <= 0)
|
|
182
|
+
if (oldDeg <= 0)
|
|
183
|
+
return;
|
|
321
184
|
const newDeg = oldDeg - 1;
|
|
322
185
|
this.outDegree.set(source, newDeg);
|
|
323
186
|
inSet.delete(source);
|
|
@@ -338,7 +201,7 @@ var IncrementalPageRank = class {
|
|
|
338
201
|
getRanks() {
|
|
339
202
|
return new Map(this.ranks);
|
|
340
203
|
}
|
|
341
|
-
//
|
|
204
|
+
// --- Private ---------------------------------------------------------
|
|
342
205
|
/**
|
|
343
206
|
* Propagate residuals through the graph until all are below epsilon.
|
|
344
207
|
*
|
|
@@ -360,7 +223,8 @@ var IncrementalPageRank = class {
|
|
|
360
223
|
maxNode = node;
|
|
361
224
|
}
|
|
362
225
|
}
|
|
363
|
-
if (maxNode === null || maxResidual < this.epsilon)
|
|
226
|
+
if (maxNode === null || maxResidual < this.epsilon)
|
|
227
|
+
break;
|
|
364
228
|
const res = residuals.get(maxNode);
|
|
365
229
|
this.ranks.set(maxNode, (this.ranks.get(maxNode) ?? 0) + res);
|
|
366
230
|
residuals.delete(maxNode);
|
|
@@ -384,7 +248,158 @@ var IncrementalPageRank = class {
|
|
|
384
248
|
}
|
|
385
249
|
};
|
|
386
250
|
|
|
387
|
-
// ../chitragupta/packages/smriti/
|
|
251
|
+
// ../chitragupta/packages/smriti/dist/graphrag-pagerank-personalized.js
|
|
252
|
+
function buildTopicBias(nodeIds, nodeContents, topic) {
|
|
253
|
+
const bias = /* @__PURE__ */ new Map();
|
|
254
|
+
const n = nodeIds.length;
|
|
255
|
+
if (!topic || n === 0) {
|
|
256
|
+
const uniform = 1 / Math.max(n, 1);
|
|
257
|
+
for (const id of nodeIds)
|
|
258
|
+
bias.set(id, uniform);
|
|
259
|
+
return bias;
|
|
260
|
+
}
|
|
261
|
+
const queryTerms = tokenizeSimple(topic);
|
|
262
|
+
const queryTf = termFrequency(queryTerms);
|
|
263
|
+
let totalSim = 0;
|
|
264
|
+
const similarities = new Array(n);
|
|
265
|
+
for (let i = 0; i < n; i++) {
|
|
266
|
+
const content = nodeContents.get(nodeIds[i]) ?? "";
|
|
267
|
+
const docTerms = tokenizeSimple(content);
|
|
268
|
+
const docTf = termFrequency(docTerms);
|
|
269
|
+
const sim = tfCosineSimilarity(queryTf, docTf);
|
|
270
|
+
similarities[i] = sim;
|
|
271
|
+
totalSim += sim;
|
|
272
|
+
}
|
|
273
|
+
if (totalSim === 0) {
|
|
274
|
+
const uniform = 1 / n;
|
|
275
|
+
for (const id of nodeIds)
|
|
276
|
+
bias.set(id, uniform);
|
|
277
|
+
return bias;
|
|
278
|
+
}
|
|
279
|
+
for (let i = 0; i < n; i++) {
|
|
280
|
+
bias.set(nodeIds[i], similarities[i] / totalSim);
|
|
281
|
+
}
|
|
282
|
+
return bias;
|
|
283
|
+
}
|
|
284
|
+
function tokenizeSimple(text) {
|
|
285
|
+
return text.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((t) => t.length >= 2);
|
|
286
|
+
}
|
|
287
|
+
function termFrequency(tokens) {
|
|
288
|
+
const tf = /* @__PURE__ */ new Map();
|
|
289
|
+
for (const t of tokens) {
|
|
290
|
+
tf.set(t, (tf.get(t) ?? 0) + 1);
|
|
291
|
+
}
|
|
292
|
+
return tf;
|
|
293
|
+
}
|
|
294
|
+
function tfCosineSimilarity(a, b) {
|
|
295
|
+
let dot = 0;
|
|
296
|
+
let normA = 0;
|
|
297
|
+
let normB = 0;
|
|
298
|
+
for (const [term, freqA] of a) {
|
|
299
|
+
normA += freqA * freqA;
|
|
300
|
+
const freqB = b.get(term);
|
|
301
|
+
if (freqB !== void 0)
|
|
302
|
+
dot += freqA * freqB;
|
|
303
|
+
}
|
|
304
|
+
for (const freqB of b.values()) {
|
|
305
|
+
normB += freqB * freqB;
|
|
306
|
+
}
|
|
307
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
308
|
+
return denom === 0 ? 0 : dot / denom;
|
|
309
|
+
}
|
|
310
|
+
function computePersonalizedPageRank(graph, topicBias, opts) {
|
|
311
|
+
const damping = opts?.damping ?? 0.85;
|
|
312
|
+
const epsilon = opts?.epsilon ?? 1e-6;
|
|
313
|
+
const maxIter = opts?.maxIterations ?? 150;
|
|
314
|
+
const useGS = opts?.useGaussSeidel ?? true;
|
|
315
|
+
const N = graph.nodes.length;
|
|
316
|
+
if (N === 0)
|
|
317
|
+
return /* @__PURE__ */ new Map();
|
|
318
|
+
const nodeIds = graph.nodes.map((n) => n.id);
|
|
319
|
+
const nodeIdSet = new Set(nodeIds);
|
|
320
|
+
const outDegree = /* @__PURE__ */ new Map();
|
|
321
|
+
const inLinks = /* @__PURE__ */ new Map();
|
|
322
|
+
for (const id of nodeIds) {
|
|
323
|
+
outDegree.set(id, 0);
|
|
324
|
+
inLinks.set(id, []);
|
|
325
|
+
}
|
|
326
|
+
for (const edge of graph.edges) {
|
|
327
|
+
if (nodeIdSet.has(edge.source) && nodeIdSet.has(edge.target)) {
|
|
328
|
+
outDegree.set(edge.source, (outDegree.get(edge.source) ?? 0) + 1);
|
|
329
|
+
inLinks.get(edge.target).push(edge.source);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
let bias;
|
|
333
|
+
if (typeof topicBias === "string") {
|
|
334
|
+
const nodeContents = /* @__PURE__ */ new Map();
|
|
335
|
+
for (const node of graph.nodes) {
|
|
336
|
+
nodeContents.set(node.id, node.content);
|
|
337
|
+
}
|
|
338
|
+
bias = buildTopicBias(nodeIds, nodeContents, topicBias);
|
|
339
|
+
} else if (topicBias instanceof Map) {
|
|
340
|
+
bias = topicBias;
|
|
341
|
+
} else {
|
|
342
|
+
const uniform = 1 / N;
|
|
343
|
+
bias = new Map(nodeIds.map((id) => [id, uniform]));
|
|
344
|
+
}
|
|
345
|
+
const ranks = /* @__PURE__ */ new Map();
|
|
346
|
+
for (const id of nodeIds) {
|
|
347
|
+
ranks.set(id, 1 / N);
|
|
348
|
+
}
|
|
349
|
+
const danglingNodes = [];
|
|
350
|
+
for (const id of nodeIds) {
|
|
351
|
+
if ((outDegree.get(id) ?? 0) === 0)
|
|
352
|
+
danglingNodes.push(id);
|
|
353
|
+
}
|
|
354
|
+
for (let iter = 0; iter < maxIter; iter++) {
|
|
355
|
+
let danglingSum = 0;
|
|
356
|
+
for (const id of danglingNodes) {
|
|
357
|
+
danglingSum += ranks.get(id) ?? 0;
|
|
358
|
+
}
|
|
359
|
+
const danglingContrib = damping * danglingSum / N;
|
|
360
|
+
let maxDelta = 0;
|
|
361
|
+
if (useGS) {
|
|
362
|
+
for (const id of nodeIds) {
|
|
363
|
+
const biasVal = bias.get(id) ?? 1 / N;
|
|
364
|
+
let incomingSum = 0;
|
|
365
|
+
for (const src of inLinks.get(id) ?? []) {
|
|
366
|
+
const srcRank = ranks.get(src) ?? 0;
|
|
367
|
+
const srcOut = outDegree.get(src) ?? 1;
|
|
368
|
+
incomingSum += srcRank / srcOut;
|
|
369
|
+
}
|
|
370
|
+
const newRank = (1 - damping) * biasVal + damping * incomingSum + danglingContrib;
|
|
371
|
+
const oldRank = ranks.get(id) ?? 0;
|
|
372
|
+
const delta = Math.abs(newRank - oldRank);
|
|
373
|
+
if (delta > maxDelta)
|
|
374
|
+
maxDelta = delta;
|
|
375
|
+
ranks.set(id, newRank);
|
|
376
|
+
}
|
|
377
|
+
} else {
|
|
378
|
+
const newRanks = /* @__PURE__ */ new Map();
|
|
379
|
+
for (const id of nodeIds) {
|
|
380
|
+
const biasVal = bias.get(id) ?? 1 / N;
|
|
381
|
+
let incomingSum = 0;
|
|
382
|
+
for (const src of inLinks.get(id) ?? []) {
|
|
383
|
+
const srcRank = ranks.get(src) ?? 0;
|
|
384
|
+
const srcOut = outDegree.get(src) ?? 1;
|
|
385
|
+
incomingSum += srcRank / srcOut;
|
|
386
|
+
}
|
|
387
|
+
const newRank = (1 - damping) * biasVal + damping * incomingSum + danglingContrib;
|
|
388
|
+
newRanks.set(id, newRank);
|
|
389
|
+
const delta = Math.abs(newRank - (ranks.get(id) ?? 0));
|
|
390
|
+
if (delta > maxDelta)
|
|
391
|
+
maxDelta = delta;
|
|
392
|
+
}
|
|
393
|
+
for (const [id, rank] of newRanks)
|
|
394
|
+
ranks.set(id, rank);
|
|
395
|
+
}
|
|
396
|
+
if (maxDelta < epsilon)
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
return ranks;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// ../chitragupta/packages/smriti/dist/graphrag-extraction.js
|
|
388
403
|
var CHUNK_TARGET_TOKENS = 350;
|
|
389
404
|
var CHUNK_MIN_TOKENS = 200;
|
|
390
405
|
var CHUNK_MAX_TOKENS = 500;
|
|
@@ -398,7 +413,8 @@ function estimateTokens(text) {
|
|
|
398
413
|
}
|
|
399
414
|
function semanticChunk(text) {
|
|
400
415
|
const sentences = splitSentences(text);
|
|
401
|
-
if (sentences.length === 0)
|
|
416
|
+
if (sentences.length === 0)
|
|
417
|
+
return [];
|
|
402
418
|
const totalTokens = estimateTokens(text);
|
|
403
419
|
if (totalTokens <= CHUNK_MAX_TOKENS) {
|
|
404
420
|
return [{ text, startSentence: 0, endSentence: sentences.length - 1 }];
|
|
@@ -427,7 +443,8 @@ function semanticChunk(text) {
|
|
|
427
443
|
overlapTokens += estimateTokens(sentences[overlapStart]);
|
|
428
444
|
}
|
|
429
445
|
startIdx = Math.max(overlapStart, startIdx + 1);
|
|
430
|
-
if (startIdx >= endIdx)
|
|
446
|
+
if (startIdx >= endIdx)
|
|
447
|
+
break;
|
|
431
448
|
}
|
|
432
449
|
return chunks;
|
|
433
450
|
}
|
|
@@ -485,8 +502,10 @@ function keywordExtractEntities(text) {
|
|
|
485
502
|
const entities = [];
|
|
486
503
|
const sorted = [...freq.entries()].sort((a, b) => b[1] - a[1]);
|
|
487
504
|
for (const [word, count] of sorted) {
|
|
488
|
-
if (count < 2)
|
|
489
|
-
|
|
505
|
+
if (count < 2)
|
|
506
|
+
break;
|
|
507
|
+
if (entities.length >= 20)
|
|
508
|
+
break;
|
|
490
509
|
entities.push({
|
|
491
510
|
name: word,
|
|
492
511
|
type: "concept",
|
|
@@ -496,7 +515,7 @@ function keywordExtractEntities(text) {
|
|
|
496
515
|
return entities;
|
|
497
516
|
}
|
|
498
517
|
|
|
499
|
-
// ../chitragupta/packages/smriti/
|
|
518
|
+
// ../chitragupta/packages/smriti/dist/ner-extractor.js
|
|
500
519
|
var DEFAULT_ENDPOINT = "http://localhost:8501";
|
|
501
520
|
var DEFAULT_MODEL = "gliner-large-v2.1";
|
|
502
521
|
var DEFAULT_MIN_CONFIDENCE = 0.5;
|
|
@@ -590,10 +609,7 @@ var TECHNOLOGY_WORDS = [
|
|
|
590
609
|
"yarn",
|
|
591
610
|
"zod"
|
|
592
611
|
];
|
|
593
|
-
var TECHNOLOGY_PATTERN = new RegExp(
|
|
594
|
-
`\\b(${TECHNOLOGY_WORDS.join("|")})\\b`,
|
|
595
|
-
"gi"
|
|
596
|
-
);
|
|
612
|
+
var TECHNOLOGY_PATTERN = new RegExp(`\\b(${TECHNOLOGY_WORDS.join("|")})\\b`, "gi");
|
|
597
613
|
var TOOL_WORDS = [
|
|
598
614
|
"read",
|
|
599
615
|
"write",
|
|
@@ -609,10 +625,7 @@ var TOOL_WORDS = [
|
|
|
609
625
|
"webfetch",
|
|
610
626
|
"websearch"
|
|
611
627
|
];
|
|
612
|
-
var TOOL_PATTERN = new RegExp(
|
|
613
|
-
`\\b(${TOOL_WORDS.join("|")})\\b`,
|
|
614
|
-
"gi"
|
|
615
|
-
);
|
|
628
|
+
var TOOL_PATTERN = new RegExp(`\\b(${TOOL_WORDS.join("|")})\\b`, "gi");
|
|
616
629
|
var ERROR_PATTERN = /\b((?:[A-Z]\w*Error)\b|(?:ENOENT|EACCES|ECONNREFUSED|ETIMEDOUT|EPERM)\b|(?:failed\s+to\s+\w+)|(?:error:\s*\S+))/gi;
|
|
617
630
|
var DECISION_PATTERN = /\b((?:decided|chose|selected|opted|switched|migrated|moved)\s+to\s+\w[\w\s]{0,30})/gi;
|
|
618
631
|
var ACTION_PATTERN = /\b((?:created|deleted|removed|modified|updated|installed|uninstalled|deployed|fixed|refactored|renamed|merged|rebased|reverted|committed|pushed|pulled)\s+\w[\w\s]{0,30})/gi;
|
|
@@ -641,7 +654,8 @@ var NERExtractor = class {
|
|
|
641
654
|
* and `useHeuristic` is enabled.
|
|
642
655
|
*/
|
|
643
656
|
async extract(text) {
|
|
644
|
-
if (!text || text.trim().length === 0)
|
|
657
|
+
if (!text || text.trim().length === 0)
|
|
658
|
+
return [];
|
|
645
659
|
const glinerUp = await this.isGLiNERAvailable();
|
|
646
660
|
if (glinerUp) {
|
|
647
661
|
try {
|
|
@@ -668,7 +682,8 @@ var NERExtractor = class {
|
|
|
668
682
|
* Caches the result after the first successful probe.
|
|
669
683
|
*/
|
|
670
684
|
async isGLiNERAvailable() {
|
|
671
|
-
if (this.glinerAvailable !== null)
|
|
685
|
+
if (this.glinerAvailable !== null)
|
|
686
|
+
return this.glinerAvailable;
|
|
672
687
|
try {
|
|
673
688
|
const response = await fetch(this.endpoint, {
|
|
674
689
|
method: "GET",
|
|
@@ -734,10 +749,12 @@ var NERExtractor = class {
|
|
|
734
749
|
const coveredSpans = [];
|
|
735
750
|
const addMatch = (match, type, groupIndex = 1) => {
|
|
736
751
|
const captured = match[groupIndex];
|
|
737
|
-
if (!captured)
|
|
752
|
+
if (!captured)
|
|
753
|
+
return;
|
|
738
754
|
const start = match.index + match[0].indexOf(captured);
|
|
739
755
|
const end = start + captured.length;
|
|
740
|
-
if (this.spanOverlaps([start, end], coveredSpans))
|
|
756
|
+
if (this.spanOverlaps([start, end], coveredSpans))
|
|
757
|
+
return;
|
|
741
758
|
coveredSpans.push([start, end]);
|
|
742
759
|
entities.push({
|
|
743
760
|
text: captured.trim(),
|
|
@@ -785,9 +802,7 @@ var NERExtractor = class {
|
|
|
785
802
|
*/
|
|
786
803
|
postProcess(entities) {
|
|
787
804
|
const typeSet = new Set(this.entityTypes);
|
|
788
|
-
let filtered = entities.filter(
|
|
789
|
-
(e) => typeSet.has(e.type) && e.confidence >= this.minConfidence
|
|
790
|
-
);
|
|
805
|
+
let filtered = entities.filter((e) => typeSet.has(e.type) && e.confidence >= this.minConfidence);
|
|
791
806
|
const seen = /* @__PURE__ */ new Map();
|
|
792
807
|
for (const entity of filtered) {
|
|
793
808
|
const key = `${entity.type}::${entity.text.toLowerCase().trim()}`;
|
|
@@ -798,14 +813,15 @@ var NERExtractor = class {
|
|
|
798
813
|
}
|
|
799
814
|
filtered = [...seen.values()];
|
|
800
815
|
filtered.sort((a, b) => {
|
|
801
|
-
if (b.confidence !== a.confidence)
|
|
816
|
+
if (b.confidence !== a.confidence)
|
|
817
|
+
return b.confidence - a.confidence;
|
|
802
818
|
return a.span[0] - b.span[0];
|
|
803
819
|
});
|
|
804
820
|
return filtered.slice(0, this.maxEntities);
|
|
805
821
|
}
|
|
806
822
|
};
|
|
807
823
|
|
|
808
|
-
// ../chitragupta/packages/smriti/
|
|
824
|
+
// ../chitragupta/packages/smriti/dist/bitemporal.js
|
|
809
825
|
function nowISO() {
|
|
810
826
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
811
827
|
}
|
|
@@ -846,14 +862,19 @@ function expireEdge(edge, validUntil) {
|
|
|
846
862
|
function queryEdgesAtTime(edges, asOfValid, asOfRecord) {
|
|
847
863
|
return edges.filter((e) => {
|
|
848
864
|
const vFrom = e.validFrom ?? EPOCH_ISO;
|
|
849
|
-
if (vFrom > asOfValid)
|
|
850
|
-
|
|
865
|
+
if (vFrom > asOfValid)
|
|
866
|
+
return false;
|
|
867
|
+
if (e.validUntil !== void 0 && e.validUntil <= asOfValid)
|
|
868
|
+
return false;
|
|
851
869
|
if (asOfRecord !== void 0) {
|
|
852
870
|
const rAt = e.recordedAt ?? EPOCH_ISO;
|
|
853
|
-
if (rAt > asOfRecord)
|
|
854
|
-
|
|
871
|
+
if (rAt > asOfRecord)
|
|
872
|
+
return false;
|
|
873
|
+
if (e.supersededAt !== void 0 && e.supersededAt <= asOfRecord)
|
|
874
|
+
return false;
|
|
855
875
|
} else {
|
|
856
|
-
if (e.supersededAt !== void 0)
|
|
876
|
+
if (e.supersededAt !== void 0)
|
|
877
|
+
return false;
|
|
857
878
|
}
|
|
858
879
|
return true;
|
|
859
880
|
});
|
|
@@ -864,19 +885,21 @@ function getEdgeHistory(edges, source, target) {
|
|
|
864
885
|
function temporalDecay(edge, now, halfLifeMs) {
|
|
865
886
|
const referenceTime = edge.validUntil !== void 0 ? new Date(edge.validUntil).getTime() : new Date(edge.validFrom ?? EPOCH_ISO).getTime();
|
|
866
887
|
const elapsed = now - referenceTime;
|
|
867
|
-
if (elapsed <= 0)
|
|
888
|
+
if (elapsed <= 0)
|
|
889
|
+
return edge.weight;
|
|
868
890
|
const decay = Math.exp(-Math.LN2 * elapsed / halfLifeMs);
|
|
869
891
|
return edge.weight * decay;
|
|
870
892
|
}
|
|
871
893
|
function compactEdges(edges, retentionMs) {
|
|
872
894
|
const cutoff = Date.now() - retentionMs;
|
|
873
895
|
return edges.filter((e) => {
|
|
874
|
-
if (e.supersededAt === void 0)
|
|
896
|
+
if (e.supersededAt === void 0)
|
|
897
|
+
return true;
|
|
875
898
|
return new Date(e.supersededAt).getTime() > cutoff;
|
|
876
899
|
});
|
|
877
900
|
}
|
|
878
901
|
|
|
879
|
-
// ../chitragupta/packages/smriti/
|
|
902
|
+
// ../chitragupta/packages/smriti/dist/graphrag-builder.js
|
|
880
903
|
function scopeToId(scope) {
|
|
881
904
|
switch (scope.type) {
|
|
882
905
|
case "global":
|
|
@@ -921,7 +944,8 @@ async function createSessionNode(ctx, session) {
|
|
|
921
944
|
async function extractConceptsFromNodes(ctx, nodes, edges) {
|
|
922
945
|
const entityIndex = /* @__PURE__ */ new Map();
|
|
923
946
|
for (const node of nodes) {
|
|
924
|
-
if (!node.content || node.type === "concept")
|
|
947
|
+
if (!node.content || node.type === "concept")
|
|
948
|
+
continue;
|
|
925
949
|
const entities = await ctx.extractEntities(node.content);
|
|
926
950
|
for (const entity of entities) {
|
|
927
951
|
const key = entity.name.toLowerCase();
|
|
@@ -937,9 +961,11 @@ async function extractConceptsFromNodes(ctx, nodes, edges) {
|
|
|
937
961
|
}
|
|
938
962
|
}
|
|
939
963
|
for (const [key, data] of entityIndex) {
|
|
940
|
-
if (data.sourceIds.length < 2)
|
|
964
|
+
if (data.sourceIds.length < 2)
|
|
965
|
+
continue;
|
|
941
966
|
const conceptId = `concept-${key.replace(/\s+/g, "-")}`;
|
|
942
|
-
if (nodes.some((n) => n.id === conceptId))
|
|
967
|
+
if (nodes.some((n) => n.id === conceptId))
|
|
968
|
+
continue;
|
|
943
969
|
const conceptNode = {
|
|
944
970
|
id: conceptId,
|
|
945
971
|
type: "concept",
|
|
@@ -1106,33 +1132,21 @@ function removeSessionFromGraph(graph, sessionId) {
|
|
|
1106
1132
|
}
|
|
1107
1133
|
}
|
|
1108
1134
|
graph.nodes = graph.nodes.filter((n) => !nodeIdsToRemove.has(n.id));
|
|
1109
|
-
graph.edges = graph.edges.filter(
|
|
1110
|
-
(e) => !nodeIdsToRemove.has(e.source) && !nodeIdsToRemove.has(e.target)
|
|
1111
|
-
);
|
|
1135
|
+
graph.edges = graph.edges.filter((e) => !nodeIdsToRemove.has(e.source) && !nodeIdsToRemove.has(e.target));
|
|
1112
1136
|
}
|
|
1113
1137
|
function removeMemoryFromGraph(graph, scope) {
|
|
1114
1138
|
const memoryId = scopeToId(scope);
|
|
1115
|
-
graph.nodes = graph.nodes.filter(
|
|
1116
|
-
|
|
1117
|
-
);
|
|
1118
|
-
graph.edges = graph.edges.filter(
|
|
1119
|
-
(e) => e.source !== memoryId && e.target !== memoryId && !e.source.startsWith(`${memoryId}-chunk-`) && !e.target.startsWith(`${memoryId}-chunk-`)
|
|
1120
|
-
);
|
|
1139
|
+
graph.nodes = graph.nodes.filter((n) => n.id !== memoryId && !n.id.startsWith(`${memoryId}-chunk-`));
|
|
1140
|
+
graph.edges = graph.edges.filter((e) => e.source !== memoryId && e.target !== memoryId && !e.source.startsWith(`${memoryId}-chunk-`) && !e.target.startsWith(`${memoryId}-chunk-`));
|
|
1121
1141
|
}
|
|
1122
1142
|
|
|
1123
|
-
// ../chitragupta/packages/smriti/
|
|
1124
|
-
var DEFAULT_LEIDEN_CONFIG = {
|
|
1125
|
-
resolution: 1,
|
|
1126
|
-
maxIterations: 10,
|
|
1127
|
-
minModularityGain: 1e-6,
|
|
1128
|
-
seed: 42,
|
|
1129
|
-
minCommunitySize: 1
|
|
1130
|
-
};
|
|
1143
|
+
// ../chitragupta/packages/smriti/dist/leiden-algorithm.js
|
|
1131
1144
|
var Xorshift32 = class {
|
|
1132
1145
|
state;
|
|
1133
1146
|
constructor(seed) {
|
|
1134
1147
|
this.state = seed | 0 || 1;
|
|
1135
1148
|
}
|
|
1149
|
+
/** Generate next pseudo-random number in [0, 1). */
|
|
1136
1150
|
next() {
|
|
1137
1151
|
let x = this.state;
|
|
1138
1152
|
x ^= x << 13;
|
|
@@ -1143,17 +1157,17 @@ var Xorshift32 = class {
|
|
|
1143
1157
|
}
|
|
1144
1158
|
};
|
|
1145
1159
|
var AdjacencyGraph = class {
|
|
1146
|
-
/** nodeIndex
|
|
1160
|
+
/** nodeIndex -> Map<neighborIndex, weight> */
|
|
1147
1161
|
adj;
|
|
1148
|
-
/** nodeIndex
|
|
1162
|
+
/** nodeIndex -> weighted degree (sum of edge weights) */
|
|
1149
1163
|
degree;
|
|
1150
1164
|
/** Total edge weight (2m). */
|
|
1151
1165
|
totalWeight;
|
|
1152
1166
|
/** Number of nodes. */
|
|
1153
1167
|
n;
|
|
1154
|
-
/** Node ID
|
|
1168
|
+
/** Node ID -> index mapping. */
|
|
1155
1169
|
idToIndex;
|
|
1156
|
-
/** Index
|
|
1170
|
+
/** Index -> node ID mapping. */
|
|
1157
1171
|
indexToId;
|
|
1158
1172
|
constructor(graph) {
|
|
1159
1173
|
const nodeIds = graph.nodes.map((n) => n.id);
|
|
@@ -1169,8 +1183,10 @@ var AdjacencyGraph = class {
|
|
|
1169
1183
|
for (const edge of graph.edges) {
|
|
1170
1184
|
const s = this.idToIndex.get(edge.source);
|
|
1171
1185
|
const t = this.idToIndex.get(edge.target);
|
|
1172
|
-
if (s === void 0 || t === void 0)
|
|
1173
|
-
|
|
1186
|
+
if (s === void 0 || t === void 0)
|
|
1187
|
+
continue;
|
|
1188
|
+
if (s === t)
|
|
1189
|
+
continue;
|
|
1174
1190
|
const w = edge.weight ?? 1;
|
|
1175
1191
|
this.adj[s].set(t, (this.adj[s].get(t) ?? 0) + w);
|
|
1176
1192
|
this.adj[t].set(s, (this.adj[t].get(s) ?? 0) + w);
|
|
@@ -1183,7 +1199,8 @@ var AdjacencyGraph = class {
|
|
|
1183
1199
|
};
|
|
1184
1200
|
function computeModularity(g, assignment, resolution) {
|
|
1185
1201
|
const twoM = g.totalWeight;
|
|
1186
|
-
if (twoM === 0)
|
|
1202
|
+
if (twoM === 0)
|
|
1203
|
+
return 0;
|
|
1187
1204
|
const internalWeight = /* @__PURE__ */ new Map();
|
|
1188
1205
|
const communityDegree = /* @__PURE__ */ new Map();
|
|
1189
1206
|
for (let i = 0; i < g.n; i++) {
|
|
@@ -1205,15 +1222,19 @@ function computeModularity(g, assignment, resolution) {
|
|
|
1205
1222
|
}
|
|
1206
1223
|
function modularityGain(g, i, cNew, assignment, communityDegree, resolution) {
|
|
1207
1224
|
const twoM = g.totalWeight;
|
|
1208
|
-
if (twoM === 0)
|
|
1225
|
+
if (twoM === 0)
|
|
1226
|
+
return 0;
|
|
1209
1227
|
const cOld = assignment[i];
|
|
1210
|
-
if (cOld === cNew)
|
|
1228
|
+
if (cOld === cNew)
|
|
1229
|
+
return 0;
|
|
1211
1230
|
const ki = g.degree[i];
|
|
1212
1231
|
let kInNew = 0;
|
|
1213
1232
|
let kInOld = 0;
|
|
1214
1233
|
for (const [j, w] of g.adj[i]) {
|
|
1215
|
-
if (assignment[j] === cNew)
|
|
1216
|
-
|
|
1234
|
+
if (assignment[j] === cNew)
|
|
1235
|
+
kInNew += w;
|
|
1236
|
+
if (assignment[j] === cOld)
|
|
1237
|
+
kInOld += w;
|
|
1217
1238
|
}
|
|
1218
1239
|
const sigmaTotNew = communityDegree.get(cNew) ?? 0;
|
|
1219
1240
|
const sigmaTotOld = communityDegree.get(cOld) ?? 0;
|
|
@@ -1243,7 +1264,8 @@ function localNodeMoving(g, assignment, resolution, rng) {
|
|
|
1243
1264
|
let bestCommunity = cOld;
|
|
1244
1265
|
let bestGain = 0;
|
|
1245
1266
|
for (const cNew of neighborCommunities) {
|
|
1246
|
-
if (cNew === cOld)
|
|
1267
|
+
if (cNew === cOld)
|
|
1268
|
+
continue;
|
|
1247
1269
|
const gain = modularityGain(g, i, cNew, assignment, communityDegree, resolution);
|
|
1248
1270
|
if (gain > bestGain) {
|
|
1249
1271
|
bestGain = gain;
|
|
@@ -1263,17 +1285,20 @@ function refineCommunities(g, assignment) {
|
|
|
1263
1285
|
const communityNodes = /* @__PURE__ */ new Map();
|
|
1264
1286
|
for (let i = 0; i < g.n; i++) {
|
|
1265
1287
|
const c = assignment[i];
|
|
1266
|
-
if (!communityNodes.has(c))
|
|
1288
|
+
if (!communityNodes.has(c))
|
|
1289
|
+
communityNodes.set(c, []);
|
|
1267
1290
|
communityNodes.get(c).push(i);
|
|
1268
1291
|
}
|
|
1269
1292
|
let nextCommunityId = Math.max(...assignment) + 1;
|
|
1270
1293
|
for (const [_communityId, nodes] of communityNodes) {
|
|
1271
|
-
if (nodes.length <= 1)
|
|
1294
|
+
if (nodes.length <= 1)
|
|
1295
|
+
continue;
|
|
1272
1296
|
const nodeSet = new Set(nodes);
|
|
1273
1297
|
const visited = /* @__PURE__ */ new Set();
|
|
1274
1298
|
const components = [];
|
|
1275
1299
|
for (const start of nodes) {
|
|
1276
|
-
if (visited.has(start))
|
|
1300
|
+
if (visited.has(start))
|
|
1301
|
+
continue;
|
|
1277
1302
|
const component = [];
|
|
1278
1303
|
const queue = [start];
|
|
1279
1304
|
visited.add(start);
|
|
@@ -1329,6 +1354,15 @@ function compactCommunities(assignment, minSize) {
|
|
|
1329
1354
|
}
|
|
1330
1355
|
return assignment.map((c) => remap.get(c) ?? 0);
|
|
1331
1356
|
}
|
|
1357
|
+
|
|
1358
|
+
// ../chitragupta/packages/smriti/dist/graphrag-leiden.js
|
|
1359
|
+
var DEFAULT_LEIDEN_CONFIG = {
|
|
1360
|
+
resolution: 1,
|
|
1361
|
+
maxIterations: 10,
|
|
1362
|
+
minModularityGain: 1e-6,
|
|
1363
|
+
seed: 42,
|
|
1364
|
+
minCommunitySize: 1
|
|
1365
|
+
};
|
|
1332
1366
|
function leiden(graph, config) {
|
|
1333
1367
|
const cfg = { ...DEFAULT_LEIDEN_CONFIG, ...config };
|
|
1334
1368
|
if (graph.nodes.length === 0) {
|
|
@@ -1361,10 +1395,22 @@ function leiden(graph, config) {
|
|
|
1361
1395
|
communities.set(g.indexToId[i], compacted[i]);
|
|
1362
1396
|
}
|
|
1363
1397
|
const finalModularity = computeModularity(g, compacted, cfg.resolution);
|
|
1398
|
+
const communityList = buildCommunityList(g, compacted, cfg.resolution);
|
|
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
|
+
};
|
|
1407
|
+
}
|
|
1408
|
+
function buildCommunityList(g, compacted, resolution) {
|
|
1364
1409
|
const communityNodes = /* @__PURE__ */ new Map();
|
|
1365
1410
|
for (let i = 0; i < g.n; i++) {
|
|
1366
1411
|
const c = compacted[i];
|
|
1367
|
-
if (!communityNodes.has(c))
|
|
1412
|
+
if (!communityNodes.has(c))
|
|
1413
|
+
communityNodes.set(c, []);
|
|
1368
1414
|
communityNodes.get(c).push(g.indexToId[i]);
|
|
1369
1415
|
}
|
|
1370
1416
|
const communityList = [];
|
|
@@ -1383,11 +1429,10 @@ function leiden(graph, config) {
|
|
|
1383
1429
|
const possibleEdges = members.length * (members.length - 1) / 2;
|
|
1384
1430
|
const density = possibleEdges > 0 ? internalEdges / possibleEdges : 0;
|
|
1385
1431
|
let ac = 0;
|
|
1386
|
-
for (const mi of memberSet)
|
|
1432
|
+
for (const mi of memberSet)
|
|
1387
1433
|
ac += g.degree[mi];
|
|
1388
|
-
}
|
|
1389
1434
|
const twoM = g.totalWeight;
|
|
1390
|
-
const communityMod = twoM > 0 ? 2 * internalWeight / twoM -
|
|
1435
|
+
const communityMod = twoM > 0 ? 2 * internalWeight / twoM - resolution * (ac / twoM) ** 2 : 0;
|
|
1391
1436
|
communityList.push({
|
|
1392
1437
|
id,
|
|
1393
1438
|
members,
|
|
@@ -1396,14 +1441,7 @@ function leiden(graph, config) {
|
|
|
1396
1441
|
level: 0
|
|
1397
1442
|
});
|
|
1398
1443
|
}
|
|
1399
|
-
communityList
|
|
1400
|
-
return {
|
|
1401
|
-
communities,
|
|
1402
|
-
communityList,
|
|
1403
|
-
modularity: finalModularity,
|
|
1404
|
-
iterations,
|
|
1405
|
-
levels: 1
|
|
1406
|
-
};
|
|
1444
|
+
return communityList;
|
|
1407
1445
|
}
|
|
1408
1446
|
function annotateCommunities(graph, result) {
|
|
1409
1447
|
for (const node of graph.nodes) {
|
|
@@ -1414,20 +1452,13 @@ function annotateCommunities(graph, result) {
|
|
|
1414
1452
|
}
|
|
1415
1453
|
}
|
|
1416
1454
|
function communitySummary(graph, communityId, maxLabels = 5) {
|
|
1417
|
-
const members = graph.nodes.filter(
|
|
1418
|
-
(n) => n.metadata.communityId === communityId
|
|
1419
|
-
);
|
|
1455
|
+
const members = graph.nodes.filter((n) => n.metadata.communityId === communityId);
|
|
1420
1456
|
const typeCount = {};
|
|
1421
1457
|
for (const node of members) {
|
|
1422
1458
|
typeCount[node.type] = (typeCount[node.type] ?? 0) + 1;
|
|
1423
1459
|
}
|
|
1424
1460
|
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
|
-
};
|
|
1461
|
+
return { communityId, labels, nodeTypes: typeCount, size: members.length };
|
|
1431
1462
|
}
|
|
1432
1463
|
function filterByCommunity(nodes, communityId) {
|
|
1433
1464
|
return nodes.filter((n) => n.metadata.communityId === communityId);
|
|
@@ -1443,8 +1474,10 @@ function findBridgeNodes(graph, result, minCommunities = 2) {
|
|
|
1443
1474
|
for (const edge of graph.edges) {
|
|
1444
1475
|
const sc = result.communities.get(edge.source);
|
|
1445
1476
|
const tc = result.communities.get(edge.target);
|
|
1446
|
-
if (sc === void 0 || tc === void 0)
|
|
1447
|
-
|
|
1477
|
+
if (sc === void 0 || tc === void 0)
|
|
1478
|
+
continue;
|
|
1479
|
+
if (sc === tc)
|
|
1480
|
+
continue;
|
|
1448
1481
|
touchedCommunities.get(edge.source)?.add(tc);
|
|
1449
1482
|
touchedCommunities.get(edge.target)?.add(sc);
|
|
1450
1483
|
}
|
|
@@ -1458,12 +1491,9 @@ function findBridgeNodes(graph, result, minCommunities = 2) {
|
|
|
1458
1491
|
return bridges;
|
|
1459
1492
|
}
|
|
1460
1493
|
|
|
1461
|
-
// ../chitragupta/packages/smriti/
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
model: "nomic-embed-text",
|
|
1465
|
-
generationModel: "llama3.2"
|
|
1466
|
-
};
|
|
1494
|
+
// ../chitragupta/packages/smriti/dist/graphrag-persistence.js
|
|
1495
|
+
import fs from "fs";
|
|
1496
|
+
import path from "path";
|
|
1467
1497
|
function getGraphDir() {
|
|
1468
1498
|
return path.join(getChitraguptaHome(), "graphrag");
|
|
1469
1499
|
}
|
|
@@ -1476,6 +1506,267 @@ function getEmbeddingsPath() {
|
|
|
1476
1506
|
function getPageRankPath() {
|
|
1477
1507
|
return path.join(getGraphDir(), "pagerank.json");
|
|
1478
1508
|
}
|
|
1509
|
+
function getGraphDb(initialized, markInitialized) {
|
|
1510
|
+
try {
|
|
1511
|
+
const dbm = DatabaseManager.instance();
|
|
1512
|
+
if (!initialized) {
|
|
1513
|
+
initGraphSchema(dbm);
|
|
1514
|
+
markInitialized();
|
|
1515
|
+
}
|
|
1516
|
+
return dbm.get("graph");
|
|
1517
|
+
} catch {
|
|
1518
|
+
return null;
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
function upsertNodeToDb(db, node) {
|
|
1522
|
+
db.prepare(`
|
|
1523
|
+
INSERT OR REPLACE INTO nodes (id, type, label, content, metadata, created_at, updated_at)
|
|
1524
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1525
|
+
`).run(node.id, node.type, node.label, node.content, JSON.stringify(node.metadata), Date.now(), Date.now());
|
|
1526
|
+
}
|
|
1527
|
+
function insertEdgeToDb(db, edge) {
|
|
1528
|
+
db.prepare(`
|
|
1529
|
+
INSERT OR IGNORE INTO edges (source, target, relationship, weight, pramana, viveka,
|
|
1530
|
+
valid_from, valid_until, recorded_at, superseded_at)
|
|
1531
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1532
|
+
`).run(edge.source, edge.target, edge.relationship, edge.weight, null, null, edge.validFrom ? new Date(edge.validFrom).getTime() : Date.now(), edge.validUntil ? new Date(edge.validUntil).getTime() : null, edge.recordedAt ? new Date(edge.recordedAt).getTime() : Date.now(), edge.supersededAt ? new Date(edge.supersededAt).getTime() : null);
|
|
1533
|
+
}
|
|
1534
|
+
function saveToSqlite(db, graph, pageRankScores, embeddingCache) {
|
|
1535
|
+
db.transaction(() => {
|
|
1536
|
+
db.prepare("DELETE FROM pagerank").run();
|
|
1537
|
+
db.prepare("DELETE FROM edges").run();
|
|
1538
|
+
db.prepare("DELETE FROM nodes").run();
|
|
1539
|
+
for (const node of graph.nodes)
|
|
1540
|
+
upsertNodeToDb(db, node);
|
|
1541
|
+
for (const edge of graph.edges)
|
|
1542
|
+
insertEdgeToDb(db, edge);
|
|
1543
|
+
for (const [nodeId, score] of pageRankScores) {
|
|
1544
|
+
db.prepare("INSERT OR REPLACE INTO pagerank (node_id, score, updated_at) VALUES (?, ?, ?)").run(nodeId, score, Date.now());
|
|
1545
|
+
}
|
|
1546
|
+
})();
|
|
1547
|
+
saveEmbeddingsJson(embeddingCache);
|
|
1548
|
+
}
|
|
1549
|
+
function loadFromSqlite(db) {
|
|
1550
|
+
const nodeCount = db.prepare("SELECT COUNT(*) as cnt FROM nodes").get().cnt;
|
|
1551
|
+
if (nodeCount === 0)
|
|
1552
|
+
return null;
|
|
1553
|
+
const nodeRows = db.prepare("SELECT * FROM nodes").all();
|
|
1554
|
+
const nodes = nodeRows.map((row) => ({
|
|
1555
|
+
id: row.id,
|
|
1556
|
+
type: row.type,
|
|
1557
|
+
label: row.label,
|
|
1558
|
+
content: row.content ?? "",
|
|
1559
|
+
metadata: (() => {
|
|
1560
|
+
try {
|
|
1561
|
+
return JSON.parse(row.metadata ?? "{}");
|
|
1562
|
+
} catch {
|
|
1563
|
+
return {};
|
|
1564
|
+
}
|
|
1565
|
+
})()
|
|
1566
|
+
}));
|
|
1567
|
+
const edgeRows = db.prepare("SELECT * FROM edges").all();
|
|
1568
|
+
const edges = edgeRows.map((row) => ({
|
|
1569
|
+
source: row.source,
|
|
1570
|
+
target: row.target,
|
|
1571
|
+
relationship: row.relationship,
|
|
1572
|
+
weight: row.weight,
|
|
1573
|
+
validFrom: row.valid_from ? new Date(row.valid_from).toISOString() : void 0,
|
|
1574
|
+
validUntil: row.valid_until ? new Date(row.valid_until).toISOString() : void 0,
|
|
1575
|
+
recordedAt: row.recorded_at ? new Date(row.recorded_at).toISOString() : void 0,
|
|
1576
|
+
supersededAt: row.superseded_at ? new Date(row.superseded_at).toISOString() : void 0
|
|
1577
|
+
}));
|
|
1578
|
+
const pageRankScores = /* @__PURE__ */ new Map();
|
|
1579
|
+
const prRows = db.prepare("SELECT * FROM pagerank").all();
|
|
1580
|
+
for (const row of prRows) {
|
|
1581
|
+
pageRankScores.set(row.node_id, row.score);
|
|
1582
|
+
}
|
|
1583
|
+
return { graph: { nodes, edges }, pageRankScores };
|
|
1584
|
+
}
|
|
1585
|
+
function migrateInMemoryToSqlite(db, graph, pageRankScores) {
|
|
1586
|
+
db.transaction(() => {
|
|
1587
|
+
for (const node of graph.nodes)
|
|
1588
|
+
upsertNodeToDb(db, node);
|
|
1589
|
+
for (const edge of graph.edges)
|
|
1590
|
+
insertEdgeToDb(db, edge);
|
|
1591
|
+
for (const [nodeId, score] of pageRankScores) {
|
|
1592
|
+
db.prepare("INSERT OR REPLACE INTO pagerank (node_id, score, updated_at) VALUES (?, ?, ?)").run(nodeId, score, Date.now());
|
|
1593
|
+
}
|
|
1594
|
+
})();
|
|
1595
|
+
}
|
|
1596
|
+
function saveToJson(graph, pageRankScores, embeddingCache) {
|
|
1597
|
+
try {
|
|
1598
|
+
const dir = getGraphDir();
|
|
1599
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1600
|
+
fs.writeFileSync(getGraphPath(), JSON.stringify(graph, null, " "), "utf-8");
|
|
1601
|
+
saveEmbeddingsJson(embeddingCache);
|
|
1602
|
+
const pageRankObj = {};
|
|
1603
|
+
for (const [key, value] of pageRankScores)
|
|
1604
|
+
pageRankObj[key] = value;
|
|
1605
|
+
fs.writeFileSync(getPageRankPath(), JSON.stringify(pageRankObj, null, " "), "utf-8");
|
|
1606
|
+
} catch {
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
function loadFromJson() {
|
|
1610
|
+
let graph = { nodes: [], edges: [] };
|
|
1611
|
+
const pageRankScores = /* @__PURE__ */ new Map();
|
|
1612
|
+
try {
|
|
1613
|
+
const graphPath = getGraphPath();
|
|
1614
|
+
if (fs.existsSync(graphPath)) {
|
|
1615
|
+
graph = JSON.parse(fs.readFileSync(graphPath, "utf-8"));
|
|
1616
|
+
}
|
|
1617
|
+
} catch {
|
|
1618
|
+
graph = { nodes: [], edges: [] };
|
|
1619
|
+
}
|
|
1620
|
+
try {
|
|
1621
|
+
const prPath = getPageRankPath();
|
|
1622
|
+
if (fs.existsSync(prPath)) {
|
|
1623
|
+
const ranks = JSON.parse(fs.readFileSync(prPath, "utf-8"));
|
|
1624
|
+
for (const [key, value] of Object.entries(ranks))
|
|
1625
|
+
pageRankScores.set(key, value);
|
|
1626
|
+
}
|
|
1627
|
+
} catch {
|
|
1628
|
+
}
|
|
1629
|
+
return { graph, pageRankScores };
|
|
1630
|
+
}
|
|
1631
|
+
function saveEmbeddingsJson(embeddingCache) {
|
|
1632
|
+
try {
|
|
1633
|
+
const dir = getGraphDir();
|
|
1634
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1635
|
+
const embeddings = {};
|
|
1636
|
+
for (const [key, value] of embeddingCache)
|
|
1637
|
+
embeddings[key] = value;
|
|
1638
|
+
fs.writeFileSync(getEmbeddingsPath(), JSON.stringify(embeddings, null, " "), "utf-8");
|
|
1639
|
+
} catch {
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
function loadEmbeddingsJson(maxCacheSize) {
|
|
1643
|
+
const cache = /* @__PURE__ */ new Map();
|
|
1644
|
+
try {
|
|
1645
|
+
const embPath = getEmbeddingsPath();
|
|
1646
|
+
if (fs.existsSync(embPath)) {
|
|
1647
|
+
const embeddings = JSON.parse(fs.readFileSync(embPath, "utf-8"));
|
|
1648
|
+
for (const [key, value] of Object.entries(embeddings))
|
|
1649
|
+
cache.set(key, value);
|
|
1650
|
+
while (cache.size > maxCacheSize) {
|
|
1651
|
+
const oldest = cache.keys().next().value;
|
|
1652
|
+
if (oldest !== void 0)
|
|
1653
|
+
cache.delete(oldest);
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
} catch {
|
|
1657
|
+
}
|
|
1658
|
+
return cache;
|
|
1659
|
+
}
|
|
1660
|
+
function lookupPramana(db, nodeId) {
|
|
1661
|
+
if (!db)
|
|
1662
|
+
return "shabda";
|
|
1663
|
+
try {
|
|
1664
|
+
const row = db.prepare(`
|
|
1665
|
+
SELECT pramana, COUNT(*) as cnt FROM edges
|
|
1666
|
+
WHERE (source = ? OR target = ?) AND pramana IS NOT NULL
|
|
1667
|
+
GROUP BY pramana ORDER BY cnt DESC LIMIT 1
|
|
1668
|
+
`).get(nodeId, nodeId);
|
|
1669
|
+
if (row?.pramana)
|
|
1670
|
+
return row.pramana;
|
|
1671
|
+
} catch {
|
|
1672
|
+
}
|
|
1673
|
+
return "shabda";
|
|
1674
|
+
}
|
|
1675
|
+
function lookupPramanaBatch(db, nodeIds) {
|
|
1676
|
+
const result = /* @__PURE__ */ new Map();
|
|
1677
|
+
if (nodeIds.length === 0)
|
|
1678
|
+
return result;
|
|
1679
|
+
if (!db) {
|
|
1680
|
+
for (const id of nodeIds)
|
|
1681
|
+
result.set(id, "shabda");
|
|
1682
|
+
return result;
|
|
1683
|
+
}
|
|
1684
|
+
try {
|
|
1685
|
+
const placeholders = nodeIds.map(() => "?").join(",");
|
|
1686
|
+
const rows = db.prepare(`
|
|
1687
|
+
SELECT node_id, pramana FROM (
|
|
1688
|
+
SELECT source AS node_id, pramana, COUNT(*) AS cnt FROM edges
|
|
1689
|
+
WHERE source IN (${placeholders}) AND pramana IS NOT NULL
|
|
1690
|
+
GROUP BY source, pramana
|
|
1691
|
+
UNION ALL
|
|
1692
|
+
SELECT target AS node_id, pramana, COUNT(*) AS cnt FROM edges
|
|
1693
|
+
WHERE target IN (${placeholders}) AND pramana IS NOT NULL
|
|
1694
|
+
GROUP BY target, pramana
|
|
1695
|
+
) GROUP BY node_id HAVING cnt = MAX(cnt)
|
|
1696
|
+
`).all(...nodeIds, ...nodeIds);
|
|
1697
|
+
for (const row of rows)
|
|
1698
|
+
result.set(row.node_id, row.pramana);
|
|
1699
|
+
} catch {
|
|
1700
|
+
}
|
|
1701
|
+
for (const id of nodeIds) {
|
|
1702
|
+
if (!result.has(id))
|
|
1703
|
+
result.set(id, "shabda");
|
|
1704
|
+
}
|
|
1705
|
+
return result;
|
|
1706
|
+
}
|
|
1707
|
+
function migrateGraphJson() {
|
|
1708
|
+
const graphPath = getGraphPath();
|
|
1709
|
+
const prPath = getPageRankPath();
|
|
1710
|
+
if (!fs.existsSync(graphPath))
|
|
1711
|
+
return { nodes: 0, edges: 0 };
|
|
1712
|
+
try {
|
|
1713
|
+
const graph = JSON.parse(fs.readFileSync(graphPath, "utf-8"));
|
|
1714
|
+
if (!graph.nodes || graph.nodes.length === 0)
|
|
1715
|
+
return { nodes: 0, edges: 0 };
|
|
1716
|
+
const pageRankScores = /* @__PURE__ */ new Map();
|
|
1717
|
+
try {
|
|
1718
|
+
if (fs.existsSync(prPath)) {
|
|
1719
|
+
const ranks = JSON.parse(fs.readFileSync(prPath, "utf-8"));
|
|
1720
|
+
for (const [key, value] of Object.entries(ranks))
|
|
1721
|
+
pageRankScores.set(key, value);
|
|
1722
|
+
}
|
|
1723
|
+
} catch {
|
|
1724
|
+
}
|
|
1725
|
+
const dbm = DatabaseManager.instance();
|
|
1726
|
+
initGraphSchema(dbm);
|
|
1727
|
+
const db = dbm.get("graph");
|
|
1728
|
+
db.transaction(() => {
|
|
1729
|
+
const upsertStmt = db.prepare(`
|
|
1730
|
+
INSERT OR REPLACE INTO nodes (id, type, label, content, metadata, created_at, updated_at)
|
|
1731
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1732
|
+
`);
|
|
1733
|
+
const edgeStmt = db.prepare(`
|
|
1734
|
+
INSERT OR IGNORE INTO edges (source, target, relationship, weight, pramana, viveka,
|
|
1735
|
+
valid_from, valid_until, recorded_at, superseded_at)
|
|
1736
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1737
|
+
`);
|
|
1738
|
+
const prStmt = db.prepare("INSERT OR REPLACE INTO pagerank (node_id, score, updated_at) VALUES (?, ?, ?)");
|
|
1739
|
+
const now = Date.now();
|
|
1740
|
+
for (const node of graph.nodes) {
|
|
1741
|
+
upsertStmt.run(node.id, node.type, node.label, node.content, JSON.stringify(node.metadata), now, now);
|
|
1742
|
+
}
|
|
1743
|
+
for (const edge of graph.edges) {
|
|
1744
|
+
edgeStmt.run(edge.source, edge.target, edge.relationship, edge.weight, null, null, edge.validFrom ? new Date(edge.validFrom).getTime() : now, edge.validUntil ? new Date(edge.validUntil).getTime() : null, edge.recordedAt ? new Date(edge.recordedAt).getTime() : now, edge.supersededAt ? new Date(edge.supersededAt).getTime() : null);
|
|
1745
|
+
}
|
|
1746
|
+
for (const [nodeId, score] of pageRankScores)
|
|
1747
|
+
prStmt.run(nodeId, score, now);
|
|
1748
|
+
})();
|
|
1749
|
+
try {
|
|
1750
|
+
fs.renameSync(graphPath, graphPath + ".bak");
|
|
1751
|
+
} catch {
|
|
1752
|
+
}
|
|
1753
|
+
try {
|
|
1754
|
+
if (fs.existsSync(prPath))
|
|
1755
|
+
fs.renameSync(prPath, prPath + ".bak");
|
|
1756
|
+
} catch {
|
|
1757
|
+
}
|
|
1758
|
+
return { nodes: graph.nodes.length, edges: graph.edges.length };
|
|
1759
|
+
} catch {
|
|
1760
|
+
return { nodes: 0, edges: 0 };
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
// ../chitragupta/packages/smriti/dist/graphrag.js
|
|
1765
|
+
var DEFAULT_CONFIG = {
|
|
1766
|
+
endpoint: process.env.OLLAMA_HOST ?? "http://localhost:11434",
|
|
1767
|
+
model: "nomic-embed-text",
|
|
1768
|
+
generationModel: "llama3.2"
|
|
1769
|
+
};
|
|
1479
1770
|
var GraphRAGEngine = class {
|
|
1480
1771
|
config;
|
|
1481
1772
|
graph;
|
|
@@ -1484,9 +1775,9 @@ var GraphRAGEngine = class {
|
|
|
1484
1775
|
maxEmbeddingCacheSize;
|
|
1485
1776
|
ollamaAvailable = null;
|
|
1486
1777
|
embeddingService;
|
|
1487
|
-
/** Push-based incremental PageRank engine
|
|
1778
|
+
/** Push-based incremental PageRank engine. */
|
|
1488
1779
|
incrementalPR = null;
|
|
1489
|
-
/** Whether
|
|
1780
|
+
/** Whether graph.db schema has been initialized in this process. */
|
|
1490
1781
|
_graphDbInitialized = false;
|
|
1491
1782
|
constructor(config) {
|
|
1492
1783
|
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
@@ -1497,9 +1788,9 @@ var GraphRAGEngine = class {
|
|
|
1497
1788
|
this.embeddingService = config?.embeddingService ?? new EmbeddingService();
|
|
1498
1789
|
this.loadFromDisk();
|
|
1499
1790
|
}
|
|
1500
|
-
// ─── Ollama Availability (for entity extraction) ──────────────────
|
|
1501
1791
|
async checkOllamaAvailability() {
|
|
1502
|
-
if (this.ollamaAvailable !== null)
|
|
1792
|
+
if (this.ollamaAvailable !== null)
|
|
1793
|
+
return this.ollamaAvailable;
|
|
1503
1794
|
try {
|
|
1504
1795
|
const response = await fetch(`${this.config.endpoint}/api/version`, {
|
|
1505
1796
|
method: "GET",
|
|
@@ -1511,24 +1802,25 @@ var GraphRAGEngine = class {
|
|
|
1511
1802
|
}
|
|
1512
1803
|
return this.ollamaAvailable;
|
|
1513
1804
|
}
|
|
1805
|
+
/** Get (or compute and cache) an embedding vector for the given text. */
|
|
1514
1806
|
async getEmbedding(text) {
|
|
1515
1807
|
const cached = this.embeddingCache.get(text);
|
|
1516
|
-
if (cached)
|
|
1808
|
+
if (cached)
|
|
1809
|
+
return cached;
|
|
1517
1810
|
const vector = await this.embeddingService.getEmbedding(text);
|
|
1518
1811
|
this.embeddingCache.set(text, vector);
|
|
1519
1812
|
if (this.embeddingCache.size > this.maxEmbeddingCacheSize) {
|
|
1520
1813
|
const oldest = this.embeddingCache.keys().next().value;
|
|
1521
|
-
if (oldest !== void 0)
|
|
1814
|
+
if (oldest !== void 0)
|
|
1522
1815
|
this.embeddingCache.delete(oldest);
|
|
1523
|
-
}
|
|
1524
1816
|
}
|
|
1525
1817
|
return vector;
|
|
1526
1818
|
}
|
|
1527
|
-
|
|
1819
|
+
/** Compute cosine similarity between two vectors. */
|
|
1528
1820
|
cosineSimilarity(a, b) {
|
|
1529
1821
|
return cosineSimilarity(a, b);
|
|
1530
1822
|
}
|
|
1531
|
-
|
|
1823
|
+
/** Extract named entities from text using LLM (with keyword + NER fallback). */
|
|
1532
1824
|
async extractEntities(text) {
|
|
1533
1825
|
const isAvailable = await this.checkOllamaAvailability();
|
|
1534
1826
|
let baseEntities;
|
|
@@ -1573,46 +1865,31 @@ var GraphRAGEngine = class {
|
|
|
1573
1865
|
return baseEntities;
|
|
1574
1866
|
}
|
|
1575
1867
|
// ─── 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
|
-
*/
|
|
1868
|
+
/** Initialize incremental PageRank from current graph state. */
|
|
1582
1869
|
initIncrementalPR() {
|
|
1583
1870
|
this.incrementalPR = new IncrementalPageRank();
|
|
1584
1871
|
this.incrementalPR.initialize(this.graph);
|
|
1585
1872
|
this.pageRankScores = this.incrementalPR.getRanks();
|
|
1586
1873
|
}
|
|
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
|
-
*/
|
|
1874
|
+
/** Feed removed edges into incremental PR engine. */
|
|
1591
1875
|
applyEdgeRemovals(edges) {
|
|
1592
|
-
if (!this.incrementalPR || edges.length === 0)
|
|
1593
|
-
|
|
1876
|
+
if (!this.incrementalPR || edges.length === 0)
|
|
1877
|
+
return;
|
|
1878
|
+
for (const edge of edges)
|
|
1594
1879
|
this.incrementalPR.removeEdge(edge.source, edge.target);
|
|
1595
|
-
}
|
|
1596
1880
|
this.pageRankScores = this.incrementalPR.getRanks();
|
|
1597
1881
|
}
|
|
1598
|
-
/**
|
|
1599
|
-
* Feed newly added edges into the incremental PR engine.
|
|
1600
|
-
*/
|
|
1882
|
+
/** Feed newly added edges into incremental PR engine. */
|
|
1601
1883
|
applyEdgeAdditions(edges) {
|
|
1602
|
-
if (!this.incrementalPR || edges.length === 0)
|
|
1603
|
-
|
|
1884
|
+
if (!this.incrementalPR || edges.length === 0)
|
|
1885
|
+
return;
|
|
1886
|
+
for (const edge of edges)
|
|
1604
1887
|
this.incrementalPR.addEdge(edge.source, edge.target);
|
|
1605
|
-
}
|
|
1606
1888
|
this.pageRankScores = this.incrementalPR.getRanks();
|
|
1607
1889
|
}
|
|
1608
|
-
// ─── PageRank (delegates) ──────────────────────────────────────────
|
|
1609
1890
|
/**
|
|
1610
|
-
* Compute PageRank
|
|
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.
|
|
1891
|
+
* Compute PageRank. With an explicit graph argument uses full power-iteration;
|
|
1892
|
+
* without arguments initializes the incremental engine on the internal graph.
|
|
1616
1893
|
*/
|
|
1617
1894
|
computePageRank(graph) {
|
|
1618
1895
|
if (graph) {
|
|
@@ -1622,82 +1899,75 @@ var GraphRAGEngine = class {
|
|
|
1622
1899
|
this.initIncrementalPR();
|
|
1623
1900
|
return this.pageRankScores;
|
|
1624
1901
|
}
|
|
1902
|
+
/** Get the PageRank score for a node. Returns 0 if not computed. */
|
|
1625
1903
|
getPageRank(nodeId) {
|
|
1626
1904
|
return this.pageRankScores.get(nodeId) ?? 0;
|
|
1627
1905
|
}
|
|
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
|
-
*/
|
|
1906
|
+
/** Force a full PageRank recompute and persist. */
|
|
1634
1907
|
recomputePageRank() {
|
|
1635
1908
|
this.initIncrementalPR();
|
|
1636
1909
|
this.saveToDisk();
|
|
1637
1910
|
}
|
|
1638
1911
|
// ─── Graph Building ───────────────────────────────────────────────
|
|
1912
|
+
/** Build a full knowledge graph from sessions and memories. */
|
|
1639
1913
|
async buildGraph(sessions, memories) {
|
|
1640
1914
|
const nodes = [];
|
|
1641
1915
|
const edges = [];
|
|
1642
1916
|
for (const session of sessions) {
|
|
1643
|
-
|
|
1644
|
-
nodes.push(sessionNode);
|
|
1917
|
+
nodes.push(await createSessionNode(this, session));
|
|
1645
1918
|
await indexSessionTurns(this, session, nodes, edges);
|
|
1646
1919
|
}
|
|
1647
|
-
for (const memory of memories)
|
|
1920
|
+
for (const memory of memories)
|
|
1648
1921
|
await buildMemoryNodes(this, memory, nodes, edges);
|
|
1649
|
-
}
|
|
1650
1922
|
await extractConceptsFromNodes(this, nodes, edges);
|
|
1651
1923
|
this.graph = { nodes, edges };
|
|
1652
1924
|
if (this.graph.nodes.length > 0 && this.graph.edges.length > 0) {
|
|
1653
|
-
|
|
1654
|
-
annotateCommunities(this.graph, leidenResult);
|
|
1925
|
+
annotateCommunities(this.graph, leiden(this.graph));
|
|
1655
1926
|
}
|
|
1656
1927
|
this.initIncrementalPR();
|
|
1657
1928
|
this.saveToDisk();
|
|
1658
1929
|
return this.graph;
|
|
1659
1930
|
}
|
|
1660
1931
|
// ─── Hybrid Search ───────────────────────────────────────────────
|
|
1932
|
+
/** Search the knowledge graph using hybrid scoring (cosine + PageRank + BM25). */
|
|
1661
1933
|
async search(query, graph, topK = 10) {
|
|
1662
1934
|
const searchGraph = graph ?? this.graph;
|
|
1663
|
-
if (searchGraph.nodes.length === 0)
|
|
1935
|
+
if (searchGraph.nodes.length === 0)
|
|
1936
|
+
return [];
|
|
1664
1937
|
const queryEmbedding = await this.getEmbedding(query);
|
|
1665
|
-
if (this.pageRankScores.size === 0)
|
|
1938
|
+
if (this.pageRankScores.size === 0)
|
|
1666
1939
|
this.computePageRank(searchGraph);
|
|
1667
|
-
}
|
|
1668
1940
|
let maxPageRank = 0;
|
|
1669
1941
|
for (const node of searchGraph.nodes) {
|
|
1670
1942
|
const pr = this.pageRankScores.get(node.id) ?? 0;
|
|
1671
|
-
if (pr > maxPageRank)
|
|
1943
|
+
if (pr > maxPageRank)
|
|
1944
|
+
maxPageRank = pr;
|
|
1672
1945
|
}
|
|
1673
1946
|
const scored = [];
|
|
1674
1947
|
for (const node of searchGraph.nodes) {
|
|
1675
1948
|
let cosineSim = 0;
|
|
1676
1949
|
if (node.embedding && node.embedding.length > 0) {
|
|
1677
|
-
cosineSim = cosineSimilarity(queryEmbedding, node.embedding);
|
|
1678
|
-
cosineSim = Math.max(0, cosineSim);
|
|
1950
|
+
cosineSim = Math.max(0, cosineSimilarity(queryEmbedding, node.embedding));
|
|
1679
1951
|
}
|
|
1680
|
-
const
|
|
1681
|
-
const
|
|
1952
|
+
const rawPR = this.pageRankScores.get(node.id) ?? 0;
|
|
1953
|
+
const normPR = maxPageRank > 0 ? rawPR / maxPageRank : 0;
|
|
1682
1954
|
const textScore = textMatchScore(query, node.content + " " + node.label);
|
|
1683
|
-
const finalScore = ALPHA * cosineSim + BETA *
|
|
1684
|
-
if (finalScore > 0)
|
|
1955
|
+
const finalScore = ALPHA * cosineSim + BETA * normPR + GAMMA * textScore;
|
|
1956
|
+
if (finalScore > 0)
|
|
1685
1957
|
scored.push({ node, score: finalScore });
|
|
1686
|
-
}
|
|
1687
1958
|
}
|
|
1688
1959
|
scored.sort((a, b) => b.score - a.score);
|
|
1689
1960
|
return scored.slice(0, topK).map((s) => s.node);
|
|
1690
1961
|
}
|
|
1691
1962
|
// ─── Incremental Indexing ─────────────────────────────────────────
|
|
1963
|
+
/** Index a single session incrementally. */
|
|
1692
1964
|
async indexSession(session) {
|
|
1693
1965
|
const edgesBefore = [...this.graph.edges];
|
|
1694
1966
|
removeSessionFromGraph(this.graph, session.meta.id);
|
|
1695
|
-
const
|
|
1696
|
-
const edgesAfterSet = new Set(edgesAfterRemoval);
|
|
1967
|
+
const edgesAfterSet = new Set(this.graph.edges);
|
|
1697
1968
|
const removedEdges = edgesBefore.filter((e) => !edgesAfterSet.has(e));
|
|
1698
1969
|
const edgeCountBeforeAdd = this.graph.edges.length;
|
|
1699
|
-
|
|
1700
|
-
this.graph.nodes.push(sessionNode);
|
|
1970
|
+
this.graph.nodes.push(await createSessionNode(this, session));
|
|
1701
1971
|
await indexSessionTurns(this, session, this.graph.nodes, this.graph.edges);
|
|
1702
1972
|
const newEdges = this.graph.edges.slice(edgeCountBeforeAdd);
|
|
1703
1973
|
if (!this.incrementalPR) {
|
|
@@ -1708,11 +1978,11 @@ var GraphRAGEngine = class {
|
|
|
1708
1978
|
}
|
|
1709
1979
|
this.saveToDisk();
|
|
1710
1980
|
}
|
|
1981
|
+
/** Index a memory scope incrementally. */
|
|
1711
1982
|
async indexMemory(scope, content) {
|
|
1712
1983
|
const edgesBefore = [...this.graph.edges];
|
|
1713
1984
|
removeMemoryFromGraph(this.graph, scope);
|
|
1714
|
-
const
|
|
1715
|
-
const edgesAfterSet = new Set(edgesAfterRemoval);
|
|
1985
|
+
const edgesAfterSet = new Set(this.graph.edges);
|
|
1716
1986
|
const removedEdges = edgesBefore.filter((e) => !edgesAfterSet.has(e));
|
|
1717
1987
|
const edgeCountBeforeAdd = this.graph.edges.length;
|
|
1718
1988
|
await buildMemoryNodesFromContent(this, scope, content, this.graph.nodes, this.graph.edges);
|
|
@@ -1725,422 +1995,111 @@ var GraphRAGEngine = class {
|
|
|
1725
1995
|
}
|
|
1726
1996
|
this.saveToDisk();
|
|
1727
1997
|
}
|
|
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
|
-
*/
|
|
1998
|
+
// ─── Pramana Lookup (delegates) ──────────────────────────────────
|
|
1999
|
+
/** Look up the dominant pramana type for a given node ID. */
|
|
1739
2000
|
lookupPramana(nodeId) {
|
|
1740
|
-
|
|
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";
|
|
2001
|
+
return lookupPramana(this.getGraphDbHandle(), nodeId);
|
|
1755
2002
|
}
|
|
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
|
-
*/
|
|
2003
|
+
/** Batch look up pramana types for multiple node IDs. */
|
|
1765
2004
|
lookupPramanaBatch(nodeIds) {
|
|
1766
|
-
|
|
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;
|
|
2005
|
+
return lookupPramanaBatch(this.getGraphDbHandle(), nodeIds);
|
|
1799
2006
|
}
|
|
1800
|
-
// ───
|
|
1801
|
-
/**
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
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
|
-
);
|
|
1831
|
-
}
|
|
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
|
-
);
|
|
1852
|
-
}
|
|
1853
|
-
// ─── Persistence ──────────────────────────────────────────────────
|
|
2007
|
+
// ─── Persistence (delegates) ─────────────────────────────────────
|
|
2008
|
+
/** Get graph database handle via persistence layer. */
|
|
2009
|
+
getGraphDbHandle() {
|
|
2010
|
+
return getGraphDb(this._graphDbInitialized, () => {
|
|
2011
|
+
this._graphDbInitialized = true;
|
|
2012
|
+
});
|
|
2013
|
+
}
|
|
2014
|
+
/** Save graph state to SQLite (primary) or JSON (fallback). */
|
|
1854
2015
|
saveToDisk() {
|
|
1855
2016
|
try {
|
|
1856
|
-
const db = this.
|
|
2017
|
+
const db = this.getGraphDbHandle();
|
|
1857
2018
|
if (db) {
|
|
1858
|
-
db.
|
|
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();
|
|
2019
|
+
saveToSqlite(db, this.graph, this.pageRankScores, this.embeddingCache);
|
|
1875
2020
|
return;
|
|
1876
2021
|
}
|
|
1877
2022
|
} catch {
|
|
1878
2023
|
}
|
|
1879
|
-
this.
|
|
2024
|
+
saveToJson(this.graph, this.pageRankScores, this.embeddingCache);
|
|
1880
2025
|
}
|
|
2026
|
+
/** Load graph state from SQLite (primary) or JSON (fallback). */
|
|
1881
2027
|
loadFromDisk() {
|
|
1882
|
-
let
|
|
2028
|
+
let loaded = false;
|
|
1883
2029
|
try {
|
|
1884
|
-
const db = this.
|
|
2030
|
+
const db = this.getGraphDbHandle();
|
|
1885
2031
|
if (db) {
|
|
1886
|
-
const
|
|
1887
|
-
if (
|
|
1888
|
-
|
|
1889
|
-
this.
|
|
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;
|
|
2032
|
+
const sqliteData = loadFromSqlite(db);
|
|
2033
|
+
if (sqliteData) {
|
|
2034
|
+
this.graph = sqliteData.graph;
|
|
2035
|
+
this.pageRankScores = sqliteData.pageRankScores;
|
|
1919
2036
|
} else {
|
|
1920
|
-
|
|
2037
|
+
const jsonData = loadFromJson();
|
|
2038
|
+
this.graph = jsonData.graph;
|
|
2039
|
+
this.pageRankScores = jsonData.pageRankScores;
|
|
1921
2040
|
if (this.graph.nodes.length > 0) {
|
|
1922
2041
|
try {
|
|
1923
|
-
db.
|
|
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
|
-
})();
|
|
2042
|
+
migrateInMemoryToSqlite(db, this.graph, this.pageRankScores);
|
|
1936
2043
|
} catch {
|
|
1937
2044
|
}
|
|
1938
2045
|
}
|
|
1939
|
-
loadedFromSqlite = true;
|
|
1940
2046
|
}
|
|
2047
|
+
loaded = true;
|
|
1941
2048
|
}
|
|
1942
2049
|
} catch {
|
|
1943
2050
|
}
|
|
1944
|
-
if (!
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
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();
|
|
2051
|
+
if (!loaded) {
|
|
2052
|
+
const jsonData = loadFromJson();
|
|
2053
|
+
this.graph = jsonData.graph;
|
|
2054
|
+
this.pageRankScores = jsonData.pageRankScores;
|
|
2000
2055
|
}
|
|
2001
|
-
|
|
2002
|
-
|
|
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();
|
|
2056
|
+
for (const [k, v] of loadEmbeddingsJson(this.maxEmbeddingCacheSize)) {
|
|
2057
|
+
this.embeddingCache.set(k, v);
|
|
2020
2058
|
}
|
|
2059
|
+
if (this.graph.edges.length > 0)
|
|
2060
|
+
this.initIncrementalPR();
|
|
2021
2061
|
}
|
|
2062
|
+
/** Get the current in-memory knowledge graph. */
|
|
2022
2063
|
getGraph() {
|
|
2023
2064
|
return this.graph;
|
|
2024
2065
|
}
|
|
2066
|
+
/** Clear all graph data, caches, and persisted state. */
|
|
2025
2067
|
async clear() {
|
|
2026
2068
|
this.graph = { nodes: [], edges: [] };
|
|
2027
2069
|
this.embeddingCache = /* @__PURE__ */ new Map();
|
|
2028
2070
|
this.pageRankScores = /* @__PURE__ */ new Map();
|
|
2029
2071
|
this.incrementalPR = null;
|
|
2030
2072
|
try {
|
|
2031
|
-
const db = this.
|
|
2032
|
-
if (db)
|
|
2073
|
+
const db = this.getGraphDbHandle();
|
|
2074
|
+
if (db)
|
|
2033
2075
|
db.exec("DELETE FROM pagerank; DELETE FROM edges; DELETE FROM nodes;");
|
|
2034
|
-
}
|
|
2035
2076
|
} catch {
|
|
2036
2077
|
}
|
|
2037
2078
|
this.saveToDisk();
|
|
2038
2079
|
}
|
|
2080
|
+
/** Reset the Ollama availability flag to force re-check. */
|
|
2039
2081
|
resetAvailability() {
|
|
2040
2082
|
this.ollamaAvailable = null;
|
|
2041
2083
|
this.embeddingService.resetAvailability();
|
|
2042
2084
|
}
|
|
2043
|
-
// ─── Graph Neighbor Queries ───────────────────────────────────────
|
|
2044
2085
|
/**
|
|
2045
2086
|
* Get edges connected to a node, optionally filtered by direction.
|
|
2046
2087
|
* Uses the in-memory graph for fast access.
|
|
2047
2088
|
*/
|
|
2048
2089
|
getNeighbors(nodeId, direction = "both") {
|
|
2049
2090
|
return this.graph.edges.filter((edge) => {
|
|
2050
|
-
if (direction === "out")
|
|
2051
|
-
|
|
2091
|
+
if (direction === "out")
|
|
2092
|
+
return edge.source === nodeId;
|
|
2093
|
+
if (direction === "in")
|
|
2094
|
+
return edge.target === nodeId;
|
|
2052
2095
|
return edge.source === nodeId || edge.target === nodeId;
|
|
2053
2096
|
});
|
|
2054
2097
|
}
|
|
2055
2098
|
};
|
|
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
2099
|
|
|
2141
2100
|
export {
|
|
2142
|
-
computePersonalizedPageRank,
|
|
2143
2101
|
IncrementalPageRank,
|
|
2102
|
+
computePersonalizedPageRank,
|
|
2144
2103
|
NERExtractor,
|
|
2145
2104
|
createEdge,
|
|
2146
2105
|
supersedEdge,
|
|
@@ -2154,7 +2113,7 @@ export {
|
|
|
2154
2113
|
communitySummary,
|
|
2155
2114
|
filterByCommunity,
|
|
2156
2115
|
findBridgeNodes,
|
|
2157
|
-
|
|
2158
|
-
|
|
2116
|
+
migrateGraphJson,
|
|
2117
|
+
GraphRAGEngine
|
|
2159
2118
|
};
|
|
2160
|
-
//# sourceMappingURL=chunk-
|
|
2119
|
+
//# sourceMappingURL=chunk-LJUEMPLG.js.map
|