rag-memory-epf-mcp 1.6.0 â 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +286 -16
- package/dist/index.js.map +1 -1
- package/dist/src/migrations/migrations.d.ts.map +1 -1
- package/dist/src/migrations/migrations.js +105 -0
- package/dist/src/migrations/migrations.js.map +1 -1
- package/dist/src/tools/graph-query-tools.d.ts +4 -0
- package/dist/src/tools/graph-query-tools.d.ts.map +1 -1
- package/dist/src/tools/graph-query-tools.js +156 -1
- package/dist/src/tools/graph-query-tools.js.map +1 -1
- package/dist/src/tools/knowledge-graph-tools.d.ts +2 -0
- package/dist/src/tools/knowledge-graph-tools.d.ts.map +1 -1
- package/dist/src/tools/knowledge-graph-tools.js +62 -0
- package/dist/src/tools/knowledge-graph-tools.js.map +1 -1
- package/dist/src/tools/tool-registry.d.ts +3 -0
- package/dist/src/tools/tool-registry.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -24,6 +24,14 @@ const DB_FILE_PATH = process.env.DB_FILE_PATH
|
|
|
24
24
|
? process.env.DB_FILE_PATH
|
|
25
25
|
: path.join(path.dirname(fileURLToPath(import.meta.url)), process.env.DB_FILE_PATH)
|
|
26
26
|
: defaultDbPath;
|
|
27
|
+
// Safe rowid for vec0 virtual tables (require literal integer, not parameterized)
|
|
28
|
+
function safeRowid(value) {
|
|
29
|
+
const n = Number(value);
|
|
30
|
+
if (!Number.isInteger(n) || n < 0) {
|
|
31
|
+
throw new Error(`Invalid rowid: ${value}`);
|
|
32
|
+
}
|
|
33
|
+
return n;
|
|
34
|
+
}
|
|
27
35
|
// Enhanced RAG-enabled Knowledge Graph Manager
|
|
28
36
|
class RAGKnowledgeGraphManager {
|
|
29
37
|
db = null;
|
|
@@ -36,6 +44,14 @@ class RAGKnowledgeGraphManager {
|
|
|
36
44
|
this.db = new Database(DB_FILE_PATH);
|
|
37
45
|
// Load sqlite-vec extension
|
|
38
46
|
sqliteVec.load(this.db);
|
|
47
|
+
// SQLite performance & safety optimizations
|
|
48
|
+
this.db.pragma('journal_mode = WAL');
|
|
49
|
+
this.db.pragma('synchronous = NORMAL');
|
|
50
|
+
this.db.pragma('busy_timeout = 5000');
|
|
51
|
+
this.db.pragma('cache_size = -32000');
|
|
52
|
+
this.db.pragma('temp_store = MEMORY');
|
|
53
|
+
this.db.pragma('mmap_size = 268435456');
|
|
54
|
+
this.db.pragma('foreign_keys = ON');
|
|
39
55
|
// Initialize tiktoken
|
|
40
56
|
this.encoding = get_encoding("cl100k_base");
|
|
41
57
|
// Initialize embedding model
|
|
@@ -300,11 +316,50 @@ class RAGKnowledgeGraphManager {
|
|
|
300
316
|
const sourceId = `entity_${relation.from.toLowerCase().replace(/[^a-z0-9]/g, '_')}`;
|
|
301
317
|
const targetId = `entity_${relation.to.toLowerCase().replace(/[^a-z0-9]/g, '_')}`;
|
|
302
318
|
this.db.prepare(`
|
|
303
|
-
DELETE FROM relationships
|
|
319
|
+
DELETE FROM relationships
|
|
304
320
|
WHERE source_entity = ? AND target_entity = ? AND relationType = ?
|
|
305
321
|
`).run(sourceId, targetId, relation.relationType);
|
|
306
322
|
}
|
|
307
323
|
}
|
|
324
|
+
async updateRelations(updates) {
|
|
325
|
+
if (!this.db)
|
|
326
|
+
throw new Error('Database not initialized');
|
|
327
|
+
let updated = 0;
|
|
328
|
+
let notFound = 0;
|
|
329
|
+
for (const update of updates) {
|
|
330
|
+
const sourceId = `entity_${update.from.toLowerCase().replace(/[^a-z0-9]/g, '_')}`;
|
|
331
|
+
const targetId = `entity_${update.to.toLowerCase().replace(/[^a-z0-9]/g, '_')}`;
|
|
332
|
+
const relationId = `rel_${sourceId}_${update.relationType}_${targetId}`.toLowerCase();
|
|
333
|
+
// Check if relation exists
|
|
334
|
+
const existing = this.db.prepare(`
|
|
335
|
+
SELECT id FROM relationships WHERE id = ?
|
|
336
|
+
`).get(relationId);
|
|
337
|
+
if (!existing) {
|
|
338
|
+
notFound++;
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
// Build dynamic update
|
|
342
|
+
const setClauses = [];
|
|
343
|
+
const values = [];
|
|
344
|
+
if (update.confidence !== undefined) {
|
|
345
|
+
setClauses.push('confidence = ?');
|
|
346
|
+
values.push(update.confidence);
|
|
347
|
+
}
|
|
348
|
+
if (update.metadata !== undefined) {
|
|
349
|
+
setClauses.push('metadata = ?');
|
|
350
|
+
values.push(JSON.stringify(update.metadata));
|
|
351
|
+
}
|
|
352
|
+
if (setClauses.length === 0) {
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
values.push(relationId);
|
|
356
|
+
this.db.prepare(`
|
|
357
|
+
UPDATE relationships SET ${setClauses.join(', ')} WHERE id = ?
|
|
358
|
+
`).run(...values);
|
|
359
|
+
updated++;
|
|
360
|
+
}
|
|
361
|
+
return { updated, notFound };
|
|
362
|
+
}
|
|
308
363
|
async readGraph() {
|
|
309
364
|
if (!this.db)
|
|
310
365
|
throw new Error('Database not initialized');
|
|
@@ -330,7 +385,7 @@ class RAGKnowledgeGraphManager {
|
|
|
330
385
|
}));
|
|
331
386
|
return { entities, relations };
|
|
332
387
|
}
|
|
333
|
-
async searchNodes(query, limit = 10) {
|
|
388
|
+
async searchNodes(query, limit = 10, since, until) {
|
|
334
389
|
if (!this.db)
|
|
335
390
|
throw new Error('Database not initialized');
|
|
336
391
|
console.error(`đ Semantic entity search: "${query}"`);
|
|
@@ -353,11 +408,25 @@ class RAGKnowledgeGraphManager {
|
|
|
353
408
|
AND k = ?
|
|
354
409
|
ORDER BY ee.distance
|
|
355
410
|
`).all(Buffer.from(queryEmbedding.buffer), limit);
|
|
356
|
-
|
|
411
|
+
// Filter by temporal range if specified
|
|
412
|
+
let filteredResults = entityResults;
|
|
413
|
+
if (since || until) {
|
|
414
|
+
filteredResults = entityResults.filter(r => {
|
|
415
|
+
const entity = this.db.prepare('SELECT created_at FROM entities WHERE id = ?').get(r.entity_id);
|
|
416
|
+
if (!entity)
|
|
417
|
+
return false;
|
|
418
|
+
if (since && entity.created_at < since)
|
|
419
|
+
return false;
|
|
420
|
+
if (until && entity.created_at > until)
|
|
421
|
+
return false;
|
|
422
|
+
return true;
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
if (filteredResults.length === 0) {
|
|
357
426
|
console.error(`âšī¸ No semantic matches found for "${query}"`);
|
|
358
427
|
return { entities: [], relations: [] };
|
|
359
428
|
}
|
|
360
|
-
const entities =
|
|
429
|
+
const entities = filteredResults.map(result => ({
|
|
361
430
|
name: result.name,
|
|
362
431
|
entityType: result.entityType,
|
|
363
432
|
observations: JSON.parse(result.observations),
|
|
@@ -591,11 +660,11 @@ class RAGKnowledgeGraphManager {
|
|
|
591
660
|
SELECT id FROM entities
|
|
592
661
|
`).all();
|
|
593
662
|
let embeddedCount = 0;
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
663
|
+
const batchSize = 32;
|
|
664
|
+
for (let i = 0; i < entities.length; i += batchSize) {
|
|
665
|
+
const batch = entities.slice(i, i + batchSize);
|
|
666
|
+
const results = await Promise.all(batch.map(e => this.embedEntity(e.id)));
|
|
667
|
+
embeddedCount += results.filter(Boolean).length;
|
|
599
668
|
}
|
|
600
669
|
console.error(`â
Entity embeddings completed: ${embeddedCount}/${entities.length} entities embedded`);
|
|
601
670
|
return {
|
|
@@ -678,7 +747,7 @@ class RAGKnowledgeGraphManager {
|
|
|
678
747
|
for (const chunk of chunks) {
|
|
679
748
|
// Generate embedding
|
|
680
749
|
const embedding = await this.generateEmbedding(chunk.text);
|
|
681
|
-
const rowid =
|
|
750
|
+
const rowid = safeRowid(chunk.rowid);
|
|
682
751
|
try {
|
|
683
752
|
// Delete existing embedding if any
|
|
684
753
|
this.db.exec(`DELETE FROM chunks WHERE rowid = ${rowid}`);
|
|
@@ -722,7 +791,7 @@ class RAGKnowledgeGraphManager {
|
|
|
722
791
|
// Delete vectors and associations
|
|
723
792
|
for (const chunk of existingChunks) {
|
|
724
793
|
// Delete vector embeddings (vec0 needs literal integer, not parameterized)
|
|
725
|
-
this.db.exec(`DELETE FROM chunks WHERE rowid = ${
|
|
794
|
+
this.db.exec(`DELETE FROM chunks WHERE rowid = ${safeRowid(chunk.rowid)}`);
|
|
726
795
|
deletedVectors++;
|
|
727
796
|
// Delete chunk-entity associations
|
|
728
797
|
const associations = this.db.prepare(`
|
|
@@ -1099,7 +1168,7 @@ class RAGKnowledgeGraphManager {
|
|
|
1099
1168
|
// Store in vector table
|
|
1100
1169
|
try {
|
|
1101
1170
|
// First, delete any existing embedding for this rowid
|
|
1102
|
-
this.db.exec(`DELETE FROM chunks WHERE rowid = ${rowid}`);
|
|
1171
|
+
this.db.exec(`DELETE FROM chunks WHERE rowid = ${safeRowid(rowid)}`);
|
|
1103
1172
|
// Insert new embedding with explicit rowid to match chunk_metadata
|
|
1104
1173
|
// Use parameterized only for embedding blob, rowid as literal integer
|
|
1105
1174
|
this.db.prepare(`
|
|
@@ -1275,7 +1344,7 @@ class RAGKnowledgeGraphManager {
|
|
|
1275
1344
|
`).run(chunk.rowid);
|
|
1276
1345
|
deletedAssociations += associations.changes;
|
|
1277
1346
|
// Delete vector embeddings (vec0 needs literal integer, not parameterized)
|
|
1278
|
-
this.db.exec(`DELETE FROM chunks WHERE rowid = ${
|
|
1347
|
+
this.db.exec(`DELETE FROM chunks WHERE rowid = ${safeRowid(chunk.rowid)}`);
|
|
1279
1348
|
deletedVectors++;
|
|
1280
1349
|
}
|
|
1281
1350
|
// Delete chunk metadata
|
|
@@ -1405,6 +1474,117 @@ class RAGKnowledgeGraphManager {
|
|
|
1405
1474
|
console.error(`â
Found ${documents.length} documents`);
|
|
1406
1475
|
return { documents };
|
|
1407
1476
|
}
|
|
1477
|
+
async exportGraph() {
|
|
1478
|
+
if (!this.db)
|
|
1479
|
+
throw new Error('Database not initialized');
|
|
1480
|
+
console.error('đĻ Exporting knowledge graph...');
|
|
1481
|
+
const entities = this.db.prepare(`
|
|
1482
|
+
SELECT id, name, entityType, observations, metadata, created_at FROM entities
|
|
1483
|
+
`).all().map((row) => ({
|
|
1484
|
+
id: row.id,
|
|
1485
|
+
name: row.name,
|
|
1486
|
+
entityType: row.entityType,
|
|
1487
|
+
observations: JSON.parse(row.observations),
|
|
1488
|
+
metadata: JSON.parse(row.metadata || '{}'),
|
|
1489
|
+
created_at: row.created_at
|
|
1490
|
+
}));
|
|
1491
|
+
const relations = this.db.prepare(`
|
|
1492
|
+
SELECT id, source_entity, target_entity, relationType, confidence, metadata, created_at FROM relationships
|
|
1493
|
+
`).all().map((row) => ({
|
|
1494
|
+
id: row.id,
|
|
1495
|
+
source_entity: row.source_entity,
|
|
1496
|
+
target_entity: row.target_entity,
|
|
1497
|
+
relationType: row.relationType,
|
|
1498
|
+
confidence: row.confidence,
|
|
1499
|
+
metadata: JSON.parse(row.metadata || '{}'),
|
|
1500
|
+
created_at: row.created_at
|
|
1501
|
+
}));
|
|
1502
|
+
const documents = this.db.prepare(`
|
|
1503
|
+
SELECT id, content, metadata, created_at FROM documents
|
|
1504
|
+
`).all().map((row) => ({
|
|
1505
|
+
id: row.id,
|
|
1506
|
+
content: row.content,
|
|
1507
|
+
metadata: JSON.parse(row.metadata || '{}'),
|
|
1508
|
+
created_at: row.created_at
|
|
1509
|
+
}));
|
|
1510
|
+
console.error(`â
Export completed: ${entities.length} entities, ${relations.length} relations, ${documents.length} documents`);
|
|
1511
|
+
return {
|
|
1512
|
+
entities,
|
|
1513
|
+
relations,
|
|
1514
|
+
documents,
|
|
1515
|
+
metadata: {
|
|
1516
|
+
exportedAt: new Date().toISOString(),
|
|
1517
|
+
version: '1.0.0',
|
|
1518
|
+
entityCount: entities.length,
|
|
1519
|
+
relationCount: relations.length,
|
|
1520
|
+
documentCount: documents.length
|
|
1521
|
+
}
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
async importGraph(data, options = { merge: true }) {
|
|
1525
|
+
if (!this.db)
|
|
1526
|
+
throw new Error('Database not initialized');
|
|
1527
|
+
console.error(`đĨ Importing knowledge graph (merge: ${options.merge !== false})...`);
|
|
1528
|
+
const imported = { entities: 0, relations: 0, documents: 0 };
|
|
1529
|
+
const skipped = { entities: 0, relations: 0, documents: 0 };
|
|
1530
|
+
// If merge=false, clear existing data first
|
|
1531
|
+
if (options.merge === false) {
|
|
1532
|
+
this.db.exec(`DELETE FROM relationships`);
|
|
1533
|
+
this.db.exec(`DELETE FROM entities`);
|
|
1534
|
+
this.db.exec(`DELETE FROM documents`);
|
|
1535
|
+
console.error('đī¸ Cleared existing data for full import');
|
|
1536
|
+
}
|
|
1537
|
+
// Import entities using INSERT OR IGNORE
|
|
1538
|
+
if (data.entities && Array.isArray(data.entities)) {
|
|
1539
|
+
const stmt = this.db.prepare(`
|
|
1540
|
+
INSERT OR IGNORE INTO entities (id, name, entityType, observations, metadata, created_at)
|
|
1541
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
1542
|
+
`);
|
|
1543
|
+
for (const entity of data.entities) {
|
|
1544
|
+
const result = stmt.run(entity.id, entity.name, entity.entityType || 'CONCEPT', JSON.stringify(entity.observations || []), JSON.stringify(entity.metadata || {}), entity.created_at || new Date().toISOString());
|
|
1545
|
+
if (result.changes > 0) {
|
|
1546
|
+
imported.entities++;
|
|
1547
|
+
}
|
|
1548
|
+
else {
|
|
1549
|
+
skipped.entities++;
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
// Import relations using INSERT OR IGNORE
|
|
1554
|
+
if (data.relations && Array.isArray(data.relations)) {
|
|
1555
|
+
const stmt = this.db.prepare(`
|
|
1556
|
+
INSERT OR IGNORE INTO relationships (id, source_entity, target_entity, relationType, confidence, metadata, created_at)
|
|
1557
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1558
|
+
`);
|
|
1559
|
+
for (const relation of data.relations) {
|
|
1560
|
+
const result = stmt.run(relation.id, relation.source_entity, relation.target_entity, relation.relationType, relation.confidence ?? 1.0, JSON.stringify(relation.metadata || {}), relation.created_at || new Date().toISOString());
|
|
1561
|
+
if (result.changes > 0) {
|
|
1562
|
+
imported.relations++;
|
|
1563
|
+
}
|
|
1564
|
+
else {
|
|
1565
|
+
skipped.relations++;
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
// Import documents using INSERT OR REPLACE
|
|
1570
|
+
if (data.documents && Array.isArray(data.documents)) {
|
|
1571
|
+
const stmt = this.db.prepare(`
|
|
1572
|
+
INSERT OR REPLACE INTO documents (id, content, metadata, created_at)
|
|
1573
|
+
VALUES (?, ?, ?, ?)
|
|
1574
|
+
`);
|
|
1575
|
+
for (const doc of data.documents) {
|
|
1576
|
+
const result = stmt.run(doc.id, doc.content, JSON.stringify(doc.metadata || {}), doc.created_at || new Date().toISOString());
|
|
1577
|
+
if (result.changes > 0) {
|
|
1578
|
+
imported.documents++;
|
|
1579
|
+
}
|
|
1580
|
+
else {
|
|
1581
|
+
skipped.documents++;
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
console.error(`â
Import completed: ${imported.entities} entities, ${imported.relations} relations, ${imported.documents} documents imported`);
|
|
1586
|
+
return { imported, skipped };
|
|
1587
|
+
}
|
|
1408
1588
|
async hybridSearch(query, limit = 5, useGraph = true) {
|
|
1409
1589
|
if (!this.db)
|
|
1410
1590
|
throw new Error('Database not initialized');
|
|
@@ -1463,8 +1643,89 @@ class RAGKnowledgeGraphManager {
|
|
|
1463
1643
|
}
|
|
1464
1644
|
}
|
|
1465
1645
|
const vectorResults = Array.from(resultMap.values()).sort((a, b) => a.distance - b.distance);
|
|
1646
|
+
// FTS5 full-text search as additional signal (Reciprocal Rank Fusion)
|
|
1647
|
+
const ftsBoostMap = new Map();
|
|
1648
|
+
try {
|
|
1649
|
+
const ftsSearchQuery = (q) => {
|
|
1650
|
+
// Escape FTS5 special characters and build a query with OR between terms
|
|
1651
|
+
const sanitized = q.replace(/["\*\(\)\-]/g, ' ').trim();
|
|
1652
|
+
if (!sanitized)
|
|
1653
|
+
return [];
|
|
1654
|
+
const terms = sanitized.split(/\s+/).filter(t => t.length > 0);
|
|
1655
|
+
if (terms.length === 0)
|
|
1656
|
+
return [];
|
|
1657
|
+
const ftsExpr = terms.map(t => `"${t}"`).join(' OR ');
|
|
1658
|
+
return this.db.prepare(`
|
|
1659
|
+
SELECT cm.rowid, cm.chunk_id, bm25(chunks_fts) as fts_score
|
|
1660
|
+
FROM chunks_fts
|
|
1661
|
+
JOIN chunk_metadata cm ON chunks_fts.rowid = cm.rowid
|
|
1662
|
+
WHERE chunks_fts MATCH ?
|
|
1663
|
+
ORDER BY bm25(chunks_fts)
|
|
1664
|
+
LIMIT ?
|
|
1665
|
+
`).all(ftsExpr, limit * 3);
|
|
1666
|
+
};
|
|
1667
|
+
const ftsOriginal = ftsSearchQuery(query);
|
|
1668
|
+
const ftsTranslated = translatedQuery ? ftsSearchQuery(translatedQuery) : [];
|
|
1669
|
+
// Merge FTS5 results, keeping best score per chunk_id
|
|
1670
|
+
const ftsResultMap = new Map();
|
|
1671
|
+
let rank = 1;
|
|
1672
|
+
for (const r of ftsOriginal) {
|
|
1673
|
+
ftsResultMap.set(r.chunk_id, { chunk_id: r.chunk_id, fts_score: r.fts_score, rank });
|
|
1674
|
+
rank++;
|
|
1675
|
+
}
|
|
1676
|
+
for (const r of ftsTranslated) {
|
|
1677
|
+
if (!ftsResultMap.has(r.chunk_id)) {
|
|
1678
|
+
ftsResultMap.set(r.chunk_id, { chunk_id: r.chunk_id, fts_score: r.fts_score, rank });
|
|
1679
|
+
rank++;
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
// Build vector rank map for RRF
|
|
1683
|
+
const vectorRankMap = new Map();
|
|
1684
|
+
vectorResults.forEach((r, idx) => vectorRankMap.set(r.chunk_id, idx + 1));
|
|
1685
|
+
// Calculate RRF-based FTS5 boost (k=60)
|
|
1686
|
+
const k = 60;
|
|
1687
|
+
for (const [chunkId, ftsResult] of ftsResultMap) {
|
|
1688
|
+
const ftsComponent = 1 / (k + ftsResult.rank);
|
|
1689
|
+
ftsBoostMap.set(chunkId, ftsComponent);
|
|
1690
|
+
}
|
|
1691
|
+
// Add FTS5-only results to the vector result pool
|
|
1692
|
+
for (const [chunkId] of ftsResultMap) {
|
|
1693
|
+
if (!resultMap.has(chunkId)) {
|
|
1694
|
+
const chunkRow = this.db.prepare(`
|
|
1695
|
+
SELECT
|
|
1696
|
+
cm.rowid,
|
|
1697
|
+
cm.chunk_id,
|
|
1698
|
+
cm.chunk_type,
|
|
1699
|
+
cm.document_id,
|
|
1700
|
+
cm.entity_id,
|
|
1701
|
+
cm.relationship_id,
|
|
1702
|
+
cm.chunk_index,
|
|
1703
|
+
cm.text,
|
|
1704
|
+
cm.start_pos,
|
|
1705
|
+
cm.end_pos,
|
|
1706
|
+
COALESCE(cm.metadata, '{}') as chunk_metadata,
|
|
1707
|
+
COALESCE(d.metadata, '{}') as doc_metadata
|
|
1708
|
+
FROM chunk_metadata cm
|
|
1709
|
+
LEFT JOIN documents d ON cm.document_id = d.id
|
|
1710
|
+
WHERE cm.chunk_id = ?
|
|
1711
|
+
`).get(chunkId);
|
|
1712
|
+
if (chunkRow) {
|
|
1713
|
+
vectorResults.push({
|
|
1714
|
+
...chunkRow,
|
|
1715
|
+
distance: 2.0
|
|
1716
|
+
});
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
const ftsCount = ftsResultMap.size;
|
|
1721
|
+
const ftsOnlyCount = [...ftsResultMap.keys()].filter(id => !vectorRankMap.has(id)).length;
|
|
1722
|
+
console.error(`đ FTS5 search: ${ftsCount} matches (${ftsOnlyCount} FTS5-only), ${ftsBoostMap.size} boosted`);
|
|
1723
|
+
}
|
|
1724
|
+
catch (ftsError) {
|
|
1725
|
+
console.error(`â ī¸ FTS5 search unavailable (graceful degradation):`, ftsError instanceof Error ? ftsError.message : ftsError);
|
|
1726
|
+
}
|
|
1466
1727
|
if (vectorResults.length === 0) {
|
|
1467
|
-
console.error(`âšī¸ No vector matches found for "${query}"`);
|
|
1728
|
+
console.error(`âšī¸ No vector or FTS5 matches found for "${query}"`);
|
|
1468
1729
|
return [];
|
|
1469
1730
|
}
|
|
1470
1731
|
// Get entity information for graph enhancement via vector similarity
|
|
@@ -1617,7 +1878,8 @@ class RAGKnowledgeGraphManager {
|
|
|
1617
1878
|
const { summary, keyHighlight, relevanceScore } = await this.generateContentSummary(result.text, queryEmbedding, chunkEntities, result.chunk_type === 'relationship' ? 1 : 2 // Shorter summary for relationships
|
|
1618
1879
|
);
|
|
1619
1880
|
const vectorSimilarity = Math.max(0, 1 - result.distance / 2);
|
|
1620
|
-
const
|
|
1881
|
+
const ftsBoost = ftsBoostMap.get(result.chunk_id) || 0;
|
|
1882
|
+
const finalScore = Math.max(vectorSimilarity, relevanceScore) + graphBoost + ftsBoost;
|
|
1621
1883
|
// Determine document title and source ID
|
|
1622
1884
|
let documentTitle;
|
|
1623
1885
|
let sourceId;
|
|
@@ -1647,6 +1909,7 @@ class RAGKnowledgeGraphManager {
|
|
|
1647
1909
|
entities: chunkEntities,
|
|
1648
1910
|
vector_similarity: vectorSimilarity,
|
|
1649
1911
|
graph_boost: useGraph ? graphBoost : undefined,
|
|
1912
|
+
fts_boost: ftsBoost > 0 ? ftsBoost : undefined,
|
|
1650
1913
|
full_context_available: true,
|
|
1651
1914
|
chunk_type: result.chunk_type,
|
|
1652
1915
|
source_id: sourceId
|
|
@@ -1856,10 +2119,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1856
2119
|
case "deleteRelations":
|
|
1857
2120
|
await ragKgManager.deleteRelations(validatedArgs.relations);
|
|
1858
2121
|
return { content: [{ type: "text", text: "Relations deleted successfully" }] };
|
|
2122
|
+
case "updateRelations":
|
|
2123
|
+
return { content: [{ type: "text", text: JSON.stringify(await ragKgManager.updateRelations(validatedArgs.updates), null, 2) }] };
|
|
1859
2124
|
case "readGraph":
|
|
1860
2125
|
return { content: [{ type: "text", text: JSON.stringify(await ragKgManager.readGraph(), null, 2) }] };
|
|
1861
2126
|
case "searchNodes":
|
|
1862
|
-
return { content: [{ type: "text", text: JSON.stringify(await ragKgManager.searchNodes(validatedArgs.query, validatedArgs.limit || 10), null, 2) }] };
|
|
2127
|
+
return { content: [{ type: "text", text: JSON.stringify(await ragKgManager.searchNodes(validatedArgs.query, validatedArgs.limit || 10, validatedArgs.since, validatedArgs.until), null, 2) }] };
|
|
1863
2128
|
case "openNodes":
|
|
1864
2129
|
return { content: [{ type: "text", text: JSON.stringify(await ragKgManager.openNodes(validatedArgs.names), null, 2) }] };
|
|
1865
2130
|
// New RAG tools
|
|
@@ -1888,6 +2153,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1888
2153
|
// NEW: Entity embedding tools
|
|
1889
2154
|
case "embedAllEntities":
|
|
1890
2155
|
return { content: [{ type: "text", text: JSON.stringify(await ragKgManager.embedAllEntities(), null, 2) }] };
|
|
2156
|
+
// NEW: Export/Import tools
|
|
2157
|
+
case "exportGraph":
|
|
2158
|
+
return { content: [{ type: "text", text: JSON.stringify(await ragKgManager.exportGraph(), null, 2) }] };
|
|
2159
|
+
case "importGraph":
|
|
2160
|
+
return { content: [{ type: "text", text: JSON.stringify(await ragKgManager.importGraph(validatedArgs.data, { merge: validatedArgs.merge !== false }), null, 2) }] };
|
|
1891
2161
|
// NEW: Migration tools
|
|
1892
2162
|
case "getMigrationStatus":
|
|
1893
2163
|
return { content: [{ type: "text", text: JSON.stringify(await ragKgManager.getMigrationStatus(), null, 2) }] };
|