-
Notifications
You must be signed in to change notification settings - Fork 443
Description
Summary
After calling engine.import(data), engine.recall() always returns [] even though memories are fully present in this.memories. The HNSW vector index is not rebuilt during import, and the empty-result path in recall() does not fall through to the brute-force fallback.
Root Cause
import() populates this.memories but never calls vectorDb.insert(). After import, recall() calls vectorDb.search(), receives an empty array (no exception thrown), and returns it immediately. The brute-force fallback only triggers on a caught error — not on empty results:
// import() — only this.memories.set() is called, vectorDb is never touched
import(data) {
for (const mem of data.memories) {
this.memories.set(mem.id, mem); // ✓ Map populated
// vectorDb.insert() never called ✗ HNSW stays empty
}
}
// recall() — HNSW returns [] without throwing → brute-force never runs
const results = await this.vectorDb.search({ vector, k: topK });
return results.map(...).filter(...); // silently returns []
// catch block only fires on thrown errors, not on empty resultsBy contrast, remember() correctly calls both:
this.memories.set(id, entry);
await this.vectorDb.insert({ id, vector, metadata }); // ← import() misses thisReproduction
const engine = new IntelligenceEngine({ embeddingDim: 128 });
await engine.remember('test content about SWARM routing');
const exported = engine.export();
// Simulate reload
const engine2 = new IntelligenceEngine({ embeddingDim: 128 });
engine2.import(exported);
const results = await engine2.recall('SWARM routing');
console.log(results); // [] — expected non-empty
console.log(engine2.memories.size); // > 0 — memories are presentSuggested Fix
Option A — Rebuild HNSW in import():
import(data, merge = false) {
// ... existing import logic ...
// Rebuild HNSW index from restored memories
if (this.vectorDb && data.memories?.length) {
for (const mem of data.memories) {
if (mem.embedding?.length) {
this.vectorDb.insert({
id: mem.id,
vector: new Float32Array(mem.embedding),
metadata: JSON.stringify({ content: mem.content?.slice(0, 100), type: mem.type }),
}).catch(() => {});
}
}
}
}Option B — Guard in recall() (defensive, one line):
const results = await this.vectorDb.search({ vector, k: topK });
if (results.length > 0) return results.map(...).filter(...); // only return if HNSW has results
// fall through to brute-forceOption A is the correct fix. Option B is a safe short-term guard.
Impact
Every application that uses export() / import() for persistence gets broken recall after any restart or reload. The round-trip is effectively a recall-destroying operation with no error or warning. The this.memories Map is intact but the HNSW index is permanently empty until memories are individually re-added via remember().
Environment
ruvector@0.2.19- Node.js v20.19.5
dist/core/intelligence-engine.js—import()~line 876,recall()~line 367
Best regards,
Rob / Roble