rag-memory-epf-mcp 3.1.5 → 3.2.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.
Files changed (2) hide show
  1. package/dist/index.js +36 -18
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1345,11 +1345,14 @@ class RAGKnowledgeGraphManager {
1345
1345
  if (!document) {
1346
1346
  throw new Error(`Document with ID ${documentId} not found`);
1347
1347
  }
1348
- // Get chunks for this document
1348
+ // Get chunks for this document (with text for chunk-level matching)
1349
1349
  const chunks = this.db.prepare(`
1350
- SELECT rowid FROM chunk_metadata WHERE document_id = ?
1350
+ SELECT rowid, text FROM chunk_metadata WHERE document_id = ?
1351
1351
  `).all(documentId);
1352
1352
  let linkedCount = 0;
1353
+ const insertStmt = this.db.prepare(`
1354
+ INSERT OR IGNORE INTO chunk_entities (chunk_rowid, entity_id) VALUES (?, ?)
1355
+ `);
1353
1356
  for (const entityName of entityNames) {
1354
1357
  const entityId = `entity_${entityName.toLowerCase().replace(/[^a-z0-9]/g, '_')}`;
1355
1358
  // Verify entity exists
@@ -1360,14 +1363,17 @@ class RAGKnowledgeGraphManager {
1360
1363
  console.warn(`Entity ${entityName} not found, skipping`);
1361
1364
  continue;
1362
1365
  }
1363
- // Link entity to all chunks of the document
1366
+ // Chunk-level filtering: only link to chunks where entity actually appears
1367
+ const nameMatcher = this.buildEntityMatcher(entityName);
1368
+ let entityLinked = false;
1364
1369
  for (const chunk of chunks) {
1365
- this.db.prepare(`
1366
- INSERT OR IGNORE INTO chunk_entities (chunk_rowid, entity_id)
1367
- VALUES (?, ?)
1368
- `).run(chunk.rowid, entityId);
1370
+ if (nameMatcher(chunk.text)) {
1371
+ insertStmt.run(chunk.rowid, entityId);
1372
+ entityLinked = true;
1373
+ }
1369
1374
  }
1370
- linkedCount++;
1375
+ if (entityLinked)
1376
+ linkedCount++;
1371
1377
  }
1372
1378
  console.error(`✅ Entities linked: ${linkedCount} entities linked to document`);
1373
1379
  return { documentId, linkedEntities: linkedCount };
@@ -1868,42 +1874,54 @@ class RAGKnowledgeGraphManager {
1868
1874
  chunkEntities = [relEntities.source_name, relEntities.target_name];
1869
1875
  }
1870
1876
  }
1871
- // Enhanced graph boost calculation
1877
+ // Enhanced graph boost calculation with decay + cap
1872
1878
  let graphBoost = 0;
1873
1879
  if (useGraph) {
1874
1880
  const queryEntities = this.extractTermsFromText(query);
1875
1881
  // Base boost for knowledge graph chunks
1876
1882
  if (result.chunk_type === 'entity') {
1877
- graphBoost += 0.15; // Entities are inherently valuable
1883
+ graphBoost += 0.15;
1878
1884
  }
1879
1885
  else if (result.chunk_type === 'relationship') {
1880
- graphBoost += 0.25; // Relationships show connections
1886
+ graphBoost += 0.25;
1881
1887
  }
1882
- // Additional boost for entity matches
1888
+ // Collect per-entity scores (instead of blind accumulation)
1889
+ const entityScores = [];
1883
1890
  const queryLower = query.toLowerCase();
1884
1891
  for (const entity of chunkEntities) {
1892
+ let score = 0;
1885
1893
  const entityLower = entity.toLowerCase();
1886
1894
  // Vector-matched entity (cross-lingual: "할랄 인증" → "KMF")
1887
1895
  if (queryMatchedEntities.has(entity)) {
1888
- graphBoost += 0.3;
1896
+ score = 0.3;
1889
1897
  }
1890
1898
  // Exact text match with extracted terms
1891
1899
  else if (queryEntities.some(qe => qe.toLowerCase() === entityLower)) {
1892
- graphBoost += 0.3;
1900
+ score = 0.3;
1893
1901
  }
1894
1902
  // Partial match: entity name appears in query or vice versa
1895
1903
  else if (queryLower.includes(entityLower) || entityLower.includes(queryLower)) {
1896
- graphBoost += 0.2;
1904
+ score = 0.2;
1897
1905
  }
1898
1906
  // Word-level partial match
1899
1907
  else if (entityLower.split(/\s+/).some(word => word.length >= 3 && queryLower.includes(word))) {
1900
- graphBoost += 0.15;
1908
+ score = 0.15;
1901
1909
  }
1902
- // Connected to a vector-matched entity
1910
+ // Connected to a vector-matched entity (additive, independent)
1903
1911
  if (connectedEntities.has(entity)) {
1904
- graphBoost += 0.15;
1912
+ score += 0.15;
1905
1913
  }
1914
+ if (score > 0)
1915
+ entityScores.push(score);
1916
+ }
1917
+ // Geometric decay: sort descending, apply 0.5^i decay per entity
1918
+ entityScores.sort((a, b) => b - a);
1919
+ let entityBoost = 0;
1920
+ for (let i = 0; i < entityScores.length; i++) {
1921
+ entityBoost += entityScores[i] * Math.pow(0.5, i);
1906
1922
  }
1923
+ // Hard cap to prevent graph domination
1924
+ graphBoost += Math.min(entityBoost, 0.4);
1907
1925
  }
1908
1926
  // Generate semantic summary
1909
1927
  const { summary, keyHighlight, relevanceScore } = await this.generateContentSummary(result.text, primaryQueryEmbedding, chunkEntities, result.chunk_type === 'relationship' ? 1 : 2 // Shorter summary for relationships
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rag-memory-epf-mcp",
3
- "version": "3.1.5",
3
+ "version": "3.2.0",
4
4
  "description": "MCP server for project-local RAG memory with knowledge graph and multilingual vector search",
5
5
  "license": "MIT",
6
6
  "author": "bripin123",