superlocalmemory 3.3.13 → 3.3.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "superlocalmemory",
3
- "version": "3.3.13",
3
+ "version": "3.3.15",
4
4
  "description": "Information-geometric agent memory with mathematical guarantees. 4-channel retrieval, Fisher-Rao similarity, zero-LLM mode, EU AI Act compliant. Works with Claude, Cursor, Windsurf, and 17+ AI tools.",
5
5
  "keywords": [
6
6
  "ai-memory",
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "superlocalmemory"
3
- version = "3.3.13"
3
+ version = "3.3.15"
4
4
  description = "Information-geometric agent memory with mathematical guarantees"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
@@ -301,11 +301,16 @@ class RetrievalEngine:
301
301
  def _load_facts(
302
302
  self, fused: list[FusionResult], profile_id: str,
303
303
  ) -> dict[str, AtomicFact]:
304
- needed = {fr.fact_id for fr in fused}
304
+ """Load facts by ID targeted query, not full-table scan.
305
+
306
+ V3.3.13: Was loading ALL facts (O(n) memory) then filtering.
307
+ Now uses get_facts_by_ids() for O(k) where k = pool size (~60).
308
+ """
309
+ needed = [fr.fact_id for fr in fused]
305
310
  if not needed:
306
311
  return {}
307
- all_facts = self._db.get_all_facts(profile_id)
308
- return {f.fact_id: f for f in all_facts if f.fact_id in needed}
312
+ facts = self._db.get_facts_by_ids(needed, profile_id)
313
+ return {f.fact_id: f for f in facts}
309
314
 
310
315
  # -- Cross-encoder rerank -----------------------------------------------
311
316
 
@@ -250,12 +250,20 @@ class DatabaseManager:
250
250
  )
251
251
  return [self._row_to_fact(r) for r in rows]
252
252
 
253
+ _MAX_FACTS_PER_ENTITY_LOOKUP: int = 100
254
+
253
255
  def get_facts_by_entity(self, entity_id: str, profile_id: str) -> list[AtomicFact]:
254
- """Facts whose canonical_entities JSON array contains *entity_id*."""
256
+ """Facts whose canonical_entities JSON array contains *entity_id*.
257
+
258
+ V3.3.14: LIMIT to _MAX_FACTS_PER_ENTITY_LOOKUP (100) to prevent
259
+ unbounded memory growth during ingestion. Previously loaded ALL
260
+ facts for popular entities (500+) causing 17GB+ memory usage.
261
+ Ordered by created_at DESC so newest facts are always included.
262
+ """
255
263
  rows = self.execute(
256
264
  "SELECT * FROM atomic_facts WHERE profile_id = ? AND canonical_entities_json LIKE ? "
257
- "ORDER BY created_at DESC",
258
- (profile_id, f'%"{entity_id}"%'),
265
+ "ORDER BY created_at DESC LIMIT ?",
266
+ (profile_id, f'%"{entity_id}"%', self._MAX_FACTS_PER_ENTITY_LOOKUP),
259
267
  )
260
268
  return [self._row_to_fact(r) for r in rows]
261
269