superlocalmemory 3.4.43 → 3.4.44

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.4.43",
3
+ "version": "3.4.44",
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.4.43"
3
+ version = "3.4.44"
4
4
  description = "Information-geometric agent memory with mathematical guarantees"
5
5
  readme = "README.md"
6
6
  license = {text = "AGPL-3.0-or-later"}
@@ -29,48 +29,42 @@ classifiers = [
29
29
  ]
30
30
 
31
31
  dependencies = [
32
- "httpx>=0.24.0",
33
- "numpy>=1.26.0,<3.0.0",
34
- "scipy>=1.12.0,<2.0.0",
35
- "networkx>=3.0",
36
- "mcp>=1.0.0",
37
- "python-dateutil>=2.9.0.post0",
38
- "rank-bm25>=0.2.2",
39
- "vadersentiment>=3.3.2",
40
- "einops>=0.8.2",
41
- "fastapi[all]>=0.135.1",
42
- "uvicorn>=0.42.0",
43
- "websockets>=16.0",
44
- "lightgbm>=4.0.0",
45
- "orjson>=3.9.0",
46
- # CodeGraph — code knowledge graph (v3.4)
47
- "tree-sitter>=0.23.0,<1",
48
- "tree-sitter-language-pack>=0.5,<1",
49
- "rustworkx>=0.15,<1",
50
- "watchdog>=4.0,<6",
51
- # V3.4.3: Unified Brain
52
- "psutil>=5.9.0",
53
- "structlog>=24.0.0,<27.0.0",
54
- # Cross-platform file locking for single-daemon enforcement.
55
- "portalocker>=2.7.0,<4.0.0",
56
- # V3.4.18: Semantic search + cross-encoder reranker (npm install parity).
57
- # Previously under [search] extra pip users silently lost 30pp of recall
58
- # quality vs. npm users. Now ships by default for both install paths.
59
- # v3.4.43: bumped from >=5.0.0 to >=5.2.0 so the resolver doesn't pick
60
- # 5.0.0-5.1.2 which cap transformers<5.0.0 (security headroom for when
61
- # optimum-onnx upstream eventually supports transformers 5.x).
62
- "sentence-transformers[onnx]>=5.2.0",
63
- # NOTE on CVE-2026-1839 (transformers Trainer.torch.load RCE):
64
- # SLM does NOT use transformers.Trainer (inference-only path via
65
- # sentence-transformers + ONNX backend). The vulnerable method
66
- # Trainer._load_rng_state is never called by SLM code, so the CVE is
67
- # unreachable through SLM's API surface. We CANNOT pin transformers>=5.0.0
68
- # because optimum-onnx 0.1.0 (latest upstream) caps transformers<4.58.0
69
- # and SLM's embedding_worker.py:68 hard-codes backend="onnx". Will
70
- # tighten this pin in a future release once optimum-onnx ships a
71
- # transformers-5.x-compatible build.
72
- "torch>=2.2.0",
73
- "scikit-learn>=1.3.0,<2.0.0",
32
+ # All versions hard-pinned to the verified-good combination.
33
+ # Mixing versions outside these pins triggers per-batch memory
34
+ # blow-up in the embedding worker on Apple Silicon and breaks
35
+ # recall/remember latency targets. Update only after benchmarking.
36
+ "httpx==0.28.1",
37
+ "numpy==2.4.4",
38
+ "scipy==1.17.1",
39
+ "networkx==3.6.1",
40
+ "mcp==1.27.1",
41
+ "python-dateutil==2.9.0.post0",
42
+ "rank-bm25==0.2.2",
43
+ "vadersentiment==3.3.2",
44
+ "einops==0.8.2",
45
+ "fastapi[all]==0.136.1",
46
+ "uvicorn==0.46.0",
47
+ "websockets==16.0",
48
+ "lightgbm==4.6.0",
49
+ "orjson==3.11.9",
50
+ "tree-sitter==0.25.2",
51
+ "tree-sitter-language-pack==0.13.0",
52
+ "rustworkx==0.17.1",
53
+ "watchdog==5.0.3",
54
+ "psutil==7.2.2",
55
+ "structlog==25.5.0",
56
+ "portalocker==3.2.0",
57
+ # Semantic search + cross-encoder reranker. Embedding stack is
58
+ # extremely sensitive to version drift on Apple Silicon newer
59
+ # versions allocate dramatically more per-batch memory.
60
+ "sentence-transformers[onnx]==5.3.0",
61
+ "onnxruntime==1.24.4",
62
+ "transformers==4.57.6",
63
+ "huggingface_hub==0.36.2",
64
+ "torch==2.11.0",
65
+ "scikit-learn==1.8.0",
66
+ # Vector KNN extension for the semantic channel.
67
+ "sqlite-vec==0.1.9",
74
68
  ]
75
69
 
76
70
  [project.optional-dependencies]
@@ -78,12 +72,13 @@ dependencies = [
78
72
  # moved into core in v3.4.18. ``pip install superlocalmemory[search]`` still
79
73
  # works but installs nothing extra.
80
74
  search = [
81
- "sentence-transformers[onnx]>=5.2.0",
82
- "einops>=0.8.2",
83
- "torch>=2.2.0",
84
- "scikit-learn>=1.3.0,<2.0.0",
75
+ # Same hard pin as core deps — see comment above.
76
+ "sentence-transformers[onnx]==5.3.0",
77
+ "einops==0.8.2",
78
+ "torch==2.11.0",
79
+ "scikit-learn==1.8.0",
85
80
  "geoopt>=0.5.0",
86
- "onnxruntime>=1.17.0",
81
+ "onnxruntime==1.24.4",
87
82
  ]
88
83
  ui = [
89
84
  "fastapi[all]>=0.135.1",
@@ -233,22 +233,31 @@ print('Database ready')
233
233
  Write-Host "WARNING: setup_validator.py not found, skipping database init" -ForegroundColor Yellow
234
234
  }
235
235
 
236
- # Install core dependencies (required for graph & dashboard)
236
+ # Install SuperLocalMemory and all dependencies via pyproject.toml (single source of truth)
237
237
  Write-Host ""
238
- Write-Host "Installing core dependencies..."
239
- Write-Host "INFO: This ensures graph visualization and patterns work out-of-box" -ForegroundColor Yellow
238
+ Write-Host "Installing SuperLocalMemory and all dependencies..."
239
+ Write-Host "INFO: Versions are pinned in pyproject.toml -- same versions for every install path" -ForegroundColor Yellow
240
+
241
+ # Find pyproject.toml (parent of scripts/ or scripts/ itself)
242
+ $ParentDir = Split-Path -Parent $REPO_DIR
243
+ if (Test-Path (Join-Path $ParentDir "pyproject.toml")) {
244
+ $ProjRoot = $ParentDir
245
+ } elseif (Test-Path (Join-Path $REPO_DIR "pyproject.toml")) {
246
+ $ProjRoot = $REPO_DIR
247
+ } else {
248
+ $ProjRoot = $null
249
+ }
240
250
 
241
- $coreRequirements = Join-Path $REPO_DIR "requirements-core.txt"
242
- if (Test-Path $coreRequirements) {
251
+ if ($ProjRoot) {
243
252
  try {
244
- & python -m pip install -q -r $coreRequirements 2>$null
245
- Write-Host "OK Core dependencies installed (graph, dashboard, patterns)" -ForegroundColor Green
253
+ & python -m pip install -q -e $ProjRoot 2>$null
254
+ Write-Host "OK SuperLocalMemory and all dependencies installed (pinned versions)" -ForegroundColor Green
246
255
  } catch {
247
- Write-Host "WARNING: Core dependency installation failed. Some features may not work." -ForegroundColor Yellow
248
- Write-Host " Install manually: python -m pip install -r $coreRequirements" -ForegroundColor Yellow
256
+ Write-Host "WARNING: Dependency installation failed." -ForegroundColor Yellow
257
+ Write-Host " Install manually: python -m pip install -e $ProjRoot" -ForegroundColor Yellow
249
258
  }
250
259
  } else {
251
- Write-Host "WARNING: requirements-core.txt not found, skipping dependency installation" -ForegroundColor Yellow
260
+ Write-Host "WARNING: pyproject.toml not found, cannot install dependencies" -ForegroundColor Yellow
252
261
  }
253
262
 
254
263
  # Initialize knowledge graph and pattern learning
@@ -358,8 +358,8 @@ except Exception as e:
358
358
 
359
359
  # Install core dependencies (required for graph & dashboard)
360
360
  echo ""
361
- echo "Installing core dependencies..."
362
- echo "⏳ This ensures graph visualization and patterns work out-of-box"
361
+ echo "Installing SuperLocalMemory and all dependencies..."
362
+ echo "⏳ Versions are pinned in pyproject.toml same versions for every install path"
363
363
 
364
364
  # Detect pip installation method
365
365
  if pip3 install --help | grep -q "break-system-packages"; then
@@ -368,31 +368,25 @@ else
368
368
  PIP_FLAGS=""
369
369
  fi
370
370
 
371
- if [ -f "${REPO_DIR}/requirements-core.txt" ]; then
372
- if pip3 install $PIP_FLAGS -q -r "${REPO_DIR}/requirements-core.txt"; then
373
- echo "✓ Core dependencies installed (graph, dashboard, patterns)"
374
- else
375
- echo "⚠️ Core dependency installation failed. Some features may not work."
376
- echo " Install manually: pip3 install -r ${REPO_DIR}/requirements-core.txt"
377
- fi
371
+ # Find the repo root (parent of scripts/)
372
+ PKG_ROOT="$(cd "${REPO_DIR}/.." && pwd)"
373
+ if [ -f "${PKG_ROOT}/pyproject.toml" ]; then
374
+ PROJ_ROOT="${PKG_ROOT}"
375
+ elif [ -f "${REPO_DIR}/pyproject.toml" ]; then
376
+ PROJ_ROOT="${REPO_DIR}"
378
377
  else
379
- echo "⚠️ requirements-core.txt not found, skipping dependency installation"
378
+ PROJ_ROOT=""
380
379
  fi
381
380
 
382
- # Install learning dependencies (v2.7+)
383
- echo ""
384
- echo "Installing learning dependencies..."
385
- echo " Enables intelligent pattern learning and personalized recall"
386
-
387
- if [ -f "${REPO_DIR}/requirements-learning.txt" ]; then
388
- if pip3 install $PIP_FLAGS -q -r "${REPO_DIR}/requirements-learning.txt" 2>/dev/null; then
389
- echo "✓ Learning dependencies installed (personalized ranking enabled)"
381
+ if [ -n "${PROJ_ROOT}" ]; then
382
+ if pip3 install $PIP_FLAGS -q -e "${PROJ_ROOT}"; then
383
+ echo " SuperLocalMemory and all dependencies installed (pinned versions)"
390
384
  else
391
- echo " Learning dependencies skipped (core features unaffected)"
392
- echo " To install later: pip3 install lightgbm scipy"
385
+ echo "⚠️ Dependency installation failed."
386
+ echo " Install manually: pip3 install -e ${PROJ_ROOT}"
393
387
  fi
394
388
  else
395
- echo "○ requirements-learning.txt not found (learning features will use rule-based ranking)"
389
+ echo "⚠️ pyproject.toml not found, cannot install dependencies"
396
390
  fi
397
391
 
398
392
  # Initialize knowledge graph and pattern learning
@@ -97,83 +97,15 @@ function pipInstall(packages, label) {
97
97
  return false;
98
98
  }
99
99
 
100
- // Core dependencies (REQUIRED product won't work without these)
101
- const coreDeps = [
102
- 'numpy>=1.26.0', 'scipy>=1.12.0', 'networkx>=3.0',
103
- 'httpx>=0.24.0', 'python-dateutil>=2.9.0',
104
- 'rank-bm25>=0.2.2', 'vaderSentiment>=3.3.2',
105
- 'einops>=0.8.2', 'mcp>=1.0.0',
106
- ];
107
-
108
- if (pipInstall(coreDeps, 'core')) {
109
- console.log('✓ Core dependencies installed (math, search, NLP)');
110
- } else {
111
- console.log('⚠ Core dependency installation failed.');
112
- console.log(' Run manually: pip install ' + coreDeps.join(' '));
113
- }
114
-
115
- // Search + ONNX reranking (V3.3.2 — enables 6-channel retrieval + cross-encoder)
116
- const searchDeps = [
117
- 'sentence-transformers[onnx]>=4.0.0',
118
- 'einops>=0.7.0', 'geoopt>=0.5.0',
119
- 'onnxruntime>=1.17.0',
120
- ];
121
-
122
- console.log('\nInstalling semantic search + ONNX reranking engine...');
123
- console.log(' (sentence-transformers 4+, ONNX Runtime, Fisher-Rao geometry)');
124
- if (pipInstall(searchDeps, 'search')) {
125
- console.log('✓ Search engine installed (sentence-transformers + ONNX + Fisher-Rao)');
126
- console.log(' Cross-encoder reranking enabled for ALL modes (+30pp quality)');
127
- console.log('');
128
- console.log(' Models auto-download on first use:');
129
- console.log(' - Embedding: nomic-ai/nomic-embed-text-v1.5 (~500MB)');
130
- console.log(' - Reranker: cross-encoder/ms-marco-MiniLM-L-6-v2 (~90MB)');
131
- console.log(' To pre-download now, run: slm warmup');
132
- } else {
133
- console.log('⚠ Search engine installation failed (BM25 keyword search still works).');
134
- console.log(' For full 6-channel retrieval + reranking, run:');
135
- console.log(' pip install "sentence-transformers[onnx]>=4.0.0" einops geoopt onnxruntime');
136
- }
137
-
138
- // Dashboard dependencies (IMPORTANT — enables web dashboard + MCP server)
139
- const dashboardDeps = ['fastapi[all]>=0.135.1', 'uvicorn>=0.42.0', 'websockets>=16.0'];
140
- console.log('\nInstalling dashboard & server dependencies...');
141
- if (pipInstall(dashboardDeps, 'dashboard')) {
142
- console.log('✓ Dashboard & MCP server dependencies installed (fastapi + uvicorn)');
143
- } else {
144
- console.log('⚠ Dashboard installation failed.');
145
- console.log(' Run manually: pip install \'fastapi[all]\' uvicorn websockets');
146
- }
147
-
148
- // Learning dependencies (enables adaptive retrieval after 200+ signals)
149
- const learningDeps = ['lightgbm>=4.0.0'];
150
- console.log('\nInstalling learning engine...');
151
- if (pipInstall(learningDeps, 'learning')) {
152
- console.log('✓ Learning engine installed (lightgbm — adaptive ranking)');
153
- } else {
154
- console.log('⚠ Learning installation failed (retrieval still works without it).');
155
- console.log(' Run manually: pip install lightgbm');
156
- }
157
-
158
- // Performance dependencies (optional — improves caching and JSON speed)
159
- const perfDeps = ['diskcache>=5.6.0', 'orjson>=3.9.0'];
160
- console.log('\nInstalling performance optimizations...');
161
- if (pipInstall(perfDeps, 'performance')) {
162
- console.log('✓ Performance optimizations installed (diskcache + orjson)');
163
- } else {
164
- console.log('⚠ Performance deps skipped (system works fine without them).');
165
- }
166
-
167
- // V3.4.3: Unified Brain dependencies (health monitor, structured logging, file watching)
168
- const brainDeps = ['psutil>=5.9.0', 'structlog>=24.0.0', 'watchdog>=4.0.0'];
169
- console.log('\nInstalling Unified Brain dependencies (health monitor, file watcher)...');
170
- if (pipInstall(brainDeps, 'brain')) {
171
- console.log('✓ Unified Brain deps installed (psutil + structlog + watchdog)');
172
- console.log(' Health monitoring, structured logging, and file watching enabled');
173
- } else {
174
- console.log('⚠ Unified Brain deps partially installed (health monitoring may be limited).');
175
- console.log(' Run manually: pip install psutil structlog watchdog');
176
- }
100
+ // Install the superlocalmemory package and all its pinned dependencies
101
+ // in one shot. pyproject.toml is the single source of truth for versions,
102
+ // so users via npm get exactly the same dep set as users via pip.
103
+ console.log('\nInstalling SuperLocalMemory and all dependencies...');
104
+ console.log(' (Single pip install — versions pinned in pyproject.toml)');
105
+ console.log(' This may take 1-3 minutes (downloads ~500MB of models on first use).');
106
+ console.log('');
107
+ console.log(' Includes: numpy, scipy, fastapi, sentence-transformers, onnxruntime,');
108
+ console.log(' torch, transformers, sqlite-vec, lightgbm, mcp, and more.');
177
109
 
178
110
  // --- Step 3b: Install the superlocalmemory package itself ---
179
111
  // This ensures `python -m superlocalmemory.cli.main` always resolves the
@@ -1,3 +1,3 @@
1
1
  """SuperLocalMemory — information-geometric agent memory."""
2
2
 
3
- __version__ = "3.4.39"
3
+ __version__ = "3.4.44"
@@ -53,24 +53,25 @@ def _start_parent_watchdog() -> None:
53
53
 
54
54
 
55
55
  def _load_embedding_model(name: str) -> tuple:
56
- """Load embedding model. ONNX first (no memory leak), PyTorch fallback.
57
-
58
- V3.3.17: PyTorch SentenceTransformer on ARM64 Mac leaks memory —
59
- grows from 300MB to 17GB after ~200 encode calls. ONNX Runtime
60
- has no such issue. Same approach as CrossEncoder ONNX migration.
56
+ """Load embedding model. ONNX CPU-only first, PyTorch fallback.
61
57
 
62
58
  Returns (model, backend_name) or (None, "").
63
59
  """
64
60
  from sentence_transformers import SentenceTransformer
65
61
 
66
- # Tier 1: ONNX (stable memory; ~1.1 GB for nomic-embed-text-v1.5)
62
+ # ONNX with explicit CPU provider avoids CoreML EP memory overhead.
67
63
  try:
68
- m = SentenceTransformer(name, backend="onnx", trust_remote_code=True)
64
+ m = SentenceTransformer(
65
+ name,
66
+ backend="onnx",
67
+ trust_remote_code=True,
68
+ model_kwargs={"provider": "CPUExecutionProvider"},
69
+ )
69
70
  return m, "onnx"
70
71
  except Exception:
71
72
  pass
72
73
 
73
- # Tier 2: PyTorch CPU (stable at ~1.4GB after 100+ calls, verified)
74
+ # PyTorch CPU fallback.
74
75
  try:
75
76
  import torch
76
77
  with torch.inference_mode():
@@ -78,38 +78,19 @@ def init_embedder(config: SLMConfig) -> Any | None:
78
78
  emb_cfg = config.embedding
79
79
  provider = emb_cfg.provider
80
80
 
81
- # --- Explicit ollama provider ---
82
- # V3.3.27: HYBRID MODE B use sentence-transformers subprocess for
83
- # embeddings (fast, batched, ~2s) instead of Ollama HTTP per-call (~30s).
84
- # Ollama is still used for LLM operations (fact extraction, context
85
- # generation) via llm/backbone.py that path is unchanged.
86
- #
87
- # Why: The store pipeline calls embed() 200+ times per remember
88
- # (scene_builder, type_router, consolidator, entropy_gate, etc.).
89
- # Ollama HTTP: 200 * 45ms = 9s minimum + cold starts.
90
- # sentence-transformers subprocess: 200 embeds batched = ~1s.
91
- #
92
- # The embedding model is the SAME (nomic-embed-text-v1.5, 768d) —
93
- # identical vectors, zero quality difference. Only the transport changes.
81
+ # All modes use sentence-transformers subprocess as primary so the
82
+ # embedding space matches stored vectors. Ollama is fallback only —
83
+ # Ollama's nomic-embed-text and sentence-transformers nomic-embed-text-v1.5
84
+ # produce different vectors, so mixing them against an ST-indexed
85
+ # corpus degrades semantic recall quality.
94
86
  if provider == "ollama":
95
- if config.mode == Mode.B:
96
- # Mode B hybrid: prefer subprocess embedder (fast, batched)
97
- st_emb = _try_service_embedder(EmbeddingService, emb_cfg)
98
- if st_emb is not None:
99
- logger.info(
100
- "Mode B hybrid: using sentence-transformers subprocess "
101
- "for embeddings (fast batched). Ollama used for LLM only."
102
- )
103
- return st_emb
104
- # Fallback: if subprocess unavailable, use Ollama embeddings
105
- logger.info("Mode B: sentence-transformers unavailable, using Ollama embeddings")
106
- result = _try_ollama_embedder(emb_cfg)
107
- if result is not None:
108
- return result
109
- return None
110
- # Mode A/C with explicit ollama: use Ollama embeddings
87
+ st_emb = _try_service_embedder(EmbeddingService, emb_cfg)
88
+ if st_emb is not None:
89
+ logger.info("Using sentence-transformers subprocess (matches stored embedding space)")
90
+ return st_emb
111
91
  result = _try_ollama_embedder(emb_cfg)
112
92
  if result is not None:
93
+ logger.warning("sentence-transformers unavailable; falling back to Ollama (semantic quality may degrade)")
113
94
  return result
114
95
  return None
115
96
 
@@ -1342,7 +1342,7 @@ def _start_memory_watchdog() -> None:
1342
1342
  """
1343
1343
  import threading
1344
1344
 
1345
- MAX_WORKER_MB = 1800 # V3.4.37: 1.8GB — ONNX nomic-embed is ~1.7GB loaded
1345
+ MAX_WORKER_MB = int(os.environ.get("SLM_MAX_WORKER_MB", "2500"))
1346
1346
 
1347
1347
  def watchdog_loop():
1348
1348
  while True:
@@ -1418,12 +1418,23 @@ def _start_pending_materializer() -> None:
1418
1418
  for item in pending:
1419
1419
  if _materializer_stop.is_set():
1420
1420
  break
1421
- # Yield to recalls: wait until none in flight
1422
1421
  waits = 0
1423
1422
  while _recalls_in_flight() > 0 and waits < 60:
1424
1423
  time.sleep(0.5)
1425
1424
  waits += 1
1426
1425
  try:
1426
+ import hashlib
1427
+ content = item["content"]
1428
+ # Dedup: skip if identical content already stored.
1429
+ content_hash = hashlib.md5(content.encode()).hexdigest()
1430
+ dup = engine._db.execute(
1431
+ "SELECT 1 FROM atomic_facts WHERE "
1432
+ "content = ? LIMIT 1",
1433
+ (content,),
1434
+ )
1435
+ if dup:
1436
+ mark_done(item["id"])
1437
+ continue
1427
1438
  import json as _json
1428
1439
  md_str = item.get("metadata") or "{}"
1429
1440
  try:
@@ -1432,7 +1443,29 @@ def _start_pending_materializer() -> None:
1432
1443
  md = {}
1433
1444
  if item.get("tags"):
1434
1445
  md.setdefault("tags", item["tags"])
1435
- engine.store(item["content"], metadata=md)
1446
+ # Create memory row (FK target for atomic_facts)
1447
+ from datetime import datetime, timezone
1448
+ from superlocalmemory.storage.models import (
1449
+ AtomicFact, FactType,
1450
+ )
1451
+ mem_id = content_hash[:16]
1452
+ engine._db.execute(
1453
+ "INSERT OR IGNORE INTO memories "
1454
+ "(memory_id, profile_id, content, "
1455
+ "session_id, speaker, role, created_at, "
1456
+ "metadata_json) VALUES (?,?,?,?,?,?,?,?)",
1457
+ (mem_id, engine._profile_id, content,
1458
+ "", "", "user",
1459
+ datetime.now(timezone.utc).isoformat(),
1460
+ _json.dumps(md)),
1461
+ )
1462
+ fact = AtomicFact(
1463
+ content=content,
1464
+ fact_type=FactType.EPISODIC,
1465
+ memory_id=mem_id,
1466
+ profile_id=engine._profile_id,
1467
+ )
1468
+ engine.store_fact_direct(fact)
1436
1469
  mark_done(item["id"])
1437
1470
  except Exception as exc:
1438
1471
  logger.warning(