rag-memory-epf-mcp 3.5.0 → 3.5.2

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.d.ts CHANGED
@@ -111,6 +111,7 @@ export declare class RAGKnowledgeGraphManager {
111
111
  searchNodes(query: string, limit?: number, since?: string, until?: string): Promise<KnowledgeGraph>;
112
112
  openNodes(names: string[]): Promise<KnowledgeGraph>;
113
113
  private generateEntityEmbeddingText;
114
+ private buildEntityEmbeddingText;
114
115
  private splitIntoSentences;
115
116
  private calculateSentenceSimilarities;
116
117
  private cosineSimilarity;
package/dist/index.js CHANGED
@@ -638,12 +638,49 @@ export class RAGKnowledgeGraphManager {
638
638
  return { entities, relations };
639
639
  }
640
640
  // === NEW RAG FUNCTIONALITY ===
641
- // Generate embedding text for an entity (combines name, type, and observations)
641
+ // Generate embedding text for an entity (identity + newest observations within a char budget).
642
642
  generateEntityEmbeddingText(entity) {
643
- const observationsText = entity.observations
644
- .filter(o => !o.startsWith('Source:') && !o.startsWith('Created:') && !o.startsWith('Type:') && !o.startsWith('Tags:') && !o.startsWith('Content length:'))
645
- .join('. ');
646
- return `${entity.entityType}: ${entity.name}. ${observationsText}`.trim();
643
+ return this.buildEntityEmbeddingText(entity).text;
644
+ }
645
+ // Build entity embedding text plus stats for instrumentation.
646
+ // The char budget keeps the entity vector representative of CURRENT state and stays under the
647
+ // bge-m3 8192-token ceiling; older history lives in RAG document chunks / dated entities.
648
+ // Returns selected/total observation counts and filtered (pre-cap) vs capped observation char
649
+ // sizes so callers can log truncation accurately (identity prefix is excluded from these sizes).
650
+ buildEntityEmbeddingText(entity) {
651
+ const maxObservationChars = Math.max(1000, Number.parseInt(process.env.ENTITY_EMBED_OBS_CHAR_BUDGET || '12000', 10) || 12000);
652
+ const observations = entity.observations.filter(o => !o.startsWith('Source:') && !o.startsWith('Created:') && !o.startsWith('Type:') &&
653
+ !o.startsWith('Tags:') && !o.startsWith('Content length:'));
654
+ const filteredObsChars = observations.join('. ').length;
655
+ const selected = [];
656
+ let remaining = maxObservationChars;
657
+ for (let i = observations.length - 1; i >= 0 && remaining > 0; i--) {
658
+ const obs = observations[i];
659
+ const separatorCost = selected.length > 0 ? 2 : 0; // '. ' joiner
660
+ const available = remaining - separatorCost;
661
+ if (available <= 0)
662
+ break;
663
+ if (obs.length <= available) {
664
+ selected.push(obs);
665
+ remaining -= obs.length + separatorCost;
666
+ }
667
+ else if (selected.length === 0) {
668
+ selected.push(obs.slice(0, available)); // single giant obs: keep a truncated head, never empty
669
+ break;
670
+ }
671
+ else {
672
+ break;
673
+ }
674
+ }
675
+ const observationsText = selected.reverse().join('. ');
676
+ const text = `${entity.entityType}: ${entity.name}. ${observationsText}`.trim();
677
+ return {
678
+ text,
679
+ filteredObsChars,
680
+ cappedObsChars: observationsText.length,
681
+ selectedObsCount: selected.length,
682
+ totalObsCount: observations.length,
683
+ };
647
684
  }
648
685
  // NEW: Generic semantic summary generation methods
649
686
  splitIntoSentences(text) {
@@ -775,13 +812,19 @@ export class RAGKnowledgeGraphManager {
775
812
  return false;
776
813
  }
777
814
  const parsedObservations = JSON.parse(entity.observations);
778
- const embeddingText = this.generateEntityEmbeddingText({
815
+ const built = this.buildEntityEmbeddingText({
779
816
  name: entity.name,
780
817
  entityType: entity.entityType,
781
818
  observations: parsedObservations
782
819
  });
783
- // Generate embedding
820
+ const embeddingText = built.text;
821
+ // Instrumentation (stderr only): observations kept vs total, filtered pre-cap vs capped obs
822
+ // char size (identity excluded), and embed duration. `capped` = some observation chars dropped.
823
+ const capped = built.cappedObsChars < built.filteredObsChars;
824
+ const embedStart = Date.now();
784
825
  const embedding = await this.generateEmbedding(embeddingText);
826
+ const embedMs = Date.now() - embedStart;
827
+ console.error(`[embed] ${entity.name}: ${built.selectedObsCount}/${built.totalObsCount} obs, ${built.filteredObsChars}ch -> ${built.cappedObsChars}ch${capped ? ' (capped)' : ''}, ${embedMs}ms`);
785
828
  try {
786
829
  // Delete existing embedding if any
787
830
  const existingMetadata = this.db.prepare(`
@@ -2660,9 +2703,12 @@ async function main() {
2660
2703
  process.exit(1);
2661
2704
  }
2662
2705
  }
2663
- // Only boot the server when run as the entry point not when imported (tests).
2664
- const isDirectRun = process.argv[1] === fileURLToPath(import.meta.url);
2665
- if (isDirectRun) {
2706
+ // Boot the server unless explicitly suppressed. Tests import this module with
2707
+ // RAG_MEMORY_NO_AUTOSTART=1 to access the class without starting the stdio server.
2708
+ // (An argv-vs-import.meta.url comparison is unreliable: npx/bin launches the entry
2709
+ // via a symlinked path, so the two never match and main() silently skips → the
2710
+ // MCP client cannot connect. v3.5.0 shipped that bug; env-var opt-out is robust.)
2711
+ if (process.env.RAG_MEMORY_NO_AUTOSTART !== '1') {
2666
2712
  main().catch((error) => {
2667
2713
  console.error("Fatal error in main():", error);
2668
2714
  ragKgManager.cleanup();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rag-memory-epf-mcp",
3
- "version": "3.5.0",
3
+ "version": "3.5.2",
4
4
  "description": "Project-local RAG memory MCP server — knowledge graph + multilingual vector + FTS5 in a single SQLite file. Per-project isolation, 30 MCP tools, codepoint-safe chunking (Korean/CJK/emoji).",
5
5
  "keywords": [
6
6
  "mcp",
@@ -41,7 +41,7 @@
41
41
  "prepare": "npm run build",
42
42
  "watch": "tsc --watch",
43
43
  "verify:invariants": "node test/chunk-invariants.test.mjs",
44
- "verify:engine": "node test/engine-smoke.test.mjs && node test/sync-atomicity.test.mjs && node test/dedup.test.mjs && node test/search-degradation.test.mjs",
44
+ "verify:engine": "node test/engine-smoke.test.mjs && node test/launch-smoke.test.mjs && node test/sync-atomicity.test.mjs && node test/dedup.test.mjs && node test/search-degradation.test.mjs && node test/entity-embed-cap.test.mjs",
45
45
  "test": "npm run build && npm run verify:invariants && npm run verify:engine",
46
46
  "prepublishOnly": "npm run build && npm run verify:invariants && npm run verify:engine"
47
47
  },