superlocalmemory 3.2.2 → 3.3.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 (53) hide show
  1. package/CHANGELOG.md +43 -1
  2. package/README.md +106 -71
  3. package/package.json +1 -2
  4. package/pyproject.toml +16 -1
  5. package/src/superlocalmemory/cli/commands.py +309 -0
  6. package/src/superlocalmemory/cli/main.py +44 -0
  7. package/src/superlocalmemory/core/config.py +282 -11
  8. package/src/superlocalmemory/core/consolidation_engine.py +37 -0
  9. package/src/superlocalmemory/core/engine.py +21 -0
  10. package/src/superlocalmemory/core/engine_wiring.py +58 -8
  11. package/src/superlocalmemory/dynamics/activation_guided_quantization.py +374 -0
  12. package/src/superlocalmemory/dynamics/eap_scheduler.py +276 -0
  13. package/src/superlocalmemory/dynamics/ebbinghaus_langevin_coupling.py +171 -0
  14. package/src/superlocalmemory/encoding/cognitive_consolidator.py +804 -0
  15. package/src/superlocalmemory/hooks/auto_invoker.py +46 -8
  16. package/src/superlocalmemory/hooks/auto_parameterize.py +147 -0
  17. package/src/superlocalmemory/infra/heartbeat_monitor.py +140 -0
  18. package/src/superlocalmemory/infra/pid_manager.py +193 -0
  19. package/src/superlocalmemory/infra/process_reaper.py +572 -0
  20. package/src/superlocalmemory/learning/consolidation_quantization_worker.py +115 -0
  21. package/src/superlocalmemory/learning/forgetting_scheduler.py +263 -0
  22. package/src/superlocalmemory/learning/quantization_scheduler.py +320 -0
  23. package/src/superlocalmemory/math/ebbinghaus.py +309 -0
  24. package/src/superlocalmemory/math/fisher_quantized.py +251 -0
  25. package/src/superlocalmemory/math/hopfield.py +279 -0
  26. package/src/superlocalmemory/math/polar_quant.py +379 -0
  27. package/src/superlocalmemory/math/qjl.py +115 -0
  28. package/src/superlocalmemory/mcp/server.py +2 -0
  29. package/src/superlocalmemory/mcp/tools_v3.py +10 -0
  30. package/src/superlocalmemory/mcp/tools_v33.py +351 -0
  31. package/src/superlocalmemory/parameterization/__init__.py +47 -0
  32. package/src/superlocalmemory/parameterization/pattern_extractor.py +534 -0
  33. package/src/superlocalmemory/parameterization/pii_filter.py +106 -0
  34. package/src/superlocalmemory/parameterization/prompt_injector.py +216 -0
  35. package/src/superlocalmemory/parameterization/prompt_lifecycle.py +275 -0
  36. package/src/superlocalmemory/parameterization/soft_prompt_generator.py +425 -0
  37. package/src/superlocalmemory/retrieval/engine.py +21 -3
  38. package/src/superlocalmemory/retrieval/forgetting_filter.py +145 -0
  39. package/src/superlocalmemory/retrieval/hopfield_channel.py +335 -0
  40. package/src/superlocalmemory/retrieval/quantization_aware_search.py +133 -0
  41. package/src/superlocalmemory/retrieval/spreading_activation.py +1 -1
  42. package/src/superlocalmemory/retrieval/strategy.py +16 -6
  43. package/src/superlocalmemory/retrieval/vector_store.py +1 -1
  44. package/src/superlocalmemory/server/routes/agents.py +68 -8
  45. package/src/superlocalmemory/server/routes/learning.py +18 -1
  46. package/src/superlocalmemory/server/routes/lifecycle.py +36 -17
  47. package/src/superlocalmemory/server/routes/v3_api.py +503 -1
  48. package/src/superlocalmemory/storage/database.py +206 -0
  49. package/src/superlocalmemory/storage/embedding_migrator.py +178 -0
  50. package/src/superlocalmemory/storage/migration_v33.py +140 -0
  51. package/src/superlocalmemory/storage/quantized_store.py +261 -0
  52. package/src/superlocalmemory/storage/schema_v32.py +137 -0
  53. package/conftest.py +0 -5
@@ -41,6 +41,12 @@ def dispatch(args: Namespace) -> None:
41
41
  "hooks": cmd_hooks,
42
42
  "session-context": cmd_session_context,
43
43
  "observe": cmd_observe,
44
+ # V3.3 commands
45
+ "decay": cmd_decay,
46
+ "quantize": cmd_quantize,
47
+ "consolidate": cmd_consolidate,
48
+ "soft-prompts": cmd_soft_prompts,
49
+ "reap": cmd_reap,
44
50
  }
45
51
  handler = handlers.get(args.command)
46
52
  if handler:
@@ -102,6 +108,11 @@ def cmd_mode(args: Namespace) -> None:
102
108
  )
103
109
  updated.save()
104
110
  print(f"Mode set to: {args.value.upper()}")
111
+
112
+ # V3.3: Check if embedding model changed — inform about re-indexing
113
+ if (config.embedding.provider != updated.embedding.provider
114
+ or config.embedding.model_name != updated.embedding.model_name):
115
+ print(" ⚠ Embedding model changed. Re-indexing will run on next recall.")
105
116
  else:
106
117
  print(f"Current mode: {config.mode.value.upper()}")
107
118
 
@@ -1174,3 +1185,301 @@ def cmd_observe(args: Namespace) -> None:
1174
1185
  print(f"Not captured: {decision.reason}")
1175
1186
  except Exception as exc:
1176
1187
  logger.debug("observe failed: %s", exc)
1188
+
1189
+
1190
+ # -- V3.3 Commands -----------------------------------------------------------
1191
+
1192
+
1193
+ def cmd_decay(args: Namespace) -> None:
1194
+ """Run Ebbinghaus forgetting decay cycle."""
1195
+ from superlocalmemory.core.config import SLMConfig
1196
+ from superlocalmemory.core.engine import MemoryEngine
1197
+
1198
+ use_json = getattr(args, "json", False)
1199
+ dry_run = getattr(args, "dry_run", True)
1200
+ profile = getattr(args, "profile", "")
1201
+
1202
+ try:
1203
+ config = SLMConfig.load()
1204
+ engine = MemoryEngine(config)
1205
+ engine.initialize()
1206
+ pid = profile or engine.profile_id
1207
+
1208
+ from superlocalmemory.math.ebbinghaus import EbbinghausCurve
1209
+ from superlocalmemory.learning.forgetting_scheduler import (
1210
+ ForgettingScheduler,
1211
+ )
1212
+
1213
+ ebbinghaus = EbbinghausCurve(config.forgetting)
1214
+ scheduler = ForgettingScheduler(
1215
+ engine._db, ebbinghaus, config.forgetting,
1216
+ )
1217
+ result = scheduler.run_decay_cycle(pid, force=True)
1218
+ except Exception as exc:
1219
+ if use_json:
1220
+ from superlocalmemory.cli.json_output import json_print
1221
+ json_print("decay", error={"code": "DECAY_ERROR", "message": str(exc)})
1222
+ sys.exit(1)
1223
+ raise
1224
+
1225
+ if use_json:
1226
+ from superlocalmemory.cli.json_output import json_print
1227
+ json_print("decay", data={"dry_run": dry_run, **result},
1228
+ next_actions=[
1229
+ {"command": "slm decay --execute --json", "description": "Apply transitions"},
1230
+ {"command": "slm status --json", "description": "Check system status"},
1231
+ ])
1232
+ return
1233
+
1234
+ if result.get("skipped"):
1235
+ print(f"Skipped: {result.get('reason', 'unknown')}")
1236
+ return
1237
+
1238
+ total = result.get("total", 0)
1239
+ print(f"Decay cycle complete (dry_run={dry_run})")
1240
+ print(f" Total facts: {total}")
1241
+ print(f" Active: {result.get('active', 0)}")
1242
+ print(f" Warm: {result.get('warm', 0)}")
1243
+ print(f" Cold: {result.get('cold', 0)}")
1244
+ print(f" Archive: {result.get('archive', 0)}")
1245
+ print(f" Forgotten: {result.get('forgotten', 0)}")
1246
+ print(f" Transitions: {result.get('transitions', 0)}")
1247
+
1248
+
1249
+ def cmd_quantize(args: Namespace) -> None:
1250
+ """Run EAP embedding quantization cycle."""
1251
+ from superlocalmemory.core.config import SLMConfig
1252
+ from superlocalmemory.core.engine import MemoryEngine
1253
+
1254
+ use_json = getattr(args, "json", False)
1255
+ dry_run = getattr(args, "dry_run", True)
1256
+ profile = getattr(args, "profile", "")
1257
+
1258
+ try:
1259
+ config = SLMConfig.load()
1260
+ engine = MemoryEngine(config)
1261
+ engine.initialize()
1262
+ pid = profile or engine.profile_id
1263
+
1264
+ from superlocalmemory.math.ebbinghaus import EbbinghausCurve
1265
+ from superlocalmemory.dynamics.eap_scheduler import EAPScheduler
1266
+ from superlocalmemory.storage.quantized_store import (
1267
+ QuantizedEmbeddingStore,
1268
+ )
1269
+
1270
+ from superlocalmemory.math.polar_quant import PolarQuantEncoder
1271
+ from superlocalmemory.math.qjl import QJLEncoder
1272
+
1273
+ ebbinghaus = EbbinghausCurve(config.forgetting)
1274
+ polar = PolarQuantEncoder(config.quantization.polar)
1275
+ qjl = QJLEncoder(config.quantization.qjl)
1276
+ qstore = QuantizedEmbeddingStore(
1277
+ engine._db, polar, qjl, config.quantization,
1278
+ )
1279
+ scheduler = EAPScheduler(
1280
+ engine._db, ebbinghaus, qstore, config.quantization,
1281
+ )
1282
+ result = scheduler.run_eap_cycle(pid)
1283
+ except Exception as exc:
1284
+ if use_json:
1285
+ from superlocalmemory.cli.json_output import json_print
1286
+ json_print("quantize", error={"code": "EAP_ERROR", "message": str(exc)})
1287
+ sys.exit(1)
1288
+ raise
1289
+
1290
+ if use_json:
1291
+ from superlocalmemory.cli.json_output import json_print
1292
+ json_print("quantize", data={"dry_run": dry_run, **result},
1293
+ next_actions=[
1294
+ {"command": "slm quantize --execute --json", "description": "Apply changes"},
1295
+ {"command": "slm status --json", "description": "Check status"},
1296
+ ])
1297
+ return
1298
+
1299
+ print(f"EAP quantization cycle (dry_run={dry_run})")
1300
+ print(f" Total: {result.get('total', 0)}")
1301
+ print(f" Downgrades: {result.get('downgrades', 0)}")
1302
+ print(f" Upgrades: {result.get('upgrades', 0)}")
1303
+ print(f" Skipped: {result.get('skipped', 0)}")
1304
+ print(f" Errors: {result.get('errors', 0)}")
1305
+
1306
+
1307
+ def cmd_consolidate(args: Namespace) -> None:
1308
+ """Run cognitive consolidation pipeline."""
1309
+ from superlocalmemory.core.config import SLMConfig
1310
+ from superlocalmemory.core.engine import MemoryEngine
1311
+
1312
+ use_json = getattr(args, "json", False)
1313
+ cognitive = getattr(args, "cognitive", False)
1314
+ profile = getattr(args, "profile", "")
1315
+
1316
+ if not cognitive:
1317
+ if use_json:
1318
+ from superlocalmemory.cli.json_output import json_print
1319
+ json_print("consolidate", error={
1320
+ "code": "MISSING_FLAG",
1321
+ "message": "Use --cognitive to run CCQ pipeline",
1322
+ })
1323
+ sys.exit(1)
1324
+ print("Use --cognitive to run CCQ consolidation pipeline.")
1325
+ print(" slm consolidate --cognitive")
1326
+ return
1327
+
1328
+ try:
1329
+ config = SLMConfig.load()
1330
+ engine = MemoryEngine(config)
1331
+ engine.initialize()
1332
+ pid = profile or engine.profile_id
1333
+
1334
+ from superlocalmemory.encoding.cognitive_consolidator import (
1335
+ CognitiveConsolidator,
1336
+ )
1337
+
1338
+ consolidator = CognitiveConsolidator(db=engine._db)
1339
+ result = consolidator.run_pipeline(pid)
1340
+ except Exception as exc:
1341
+ if use_json:
1342
+ from superlocalmemory.cli.json_output import json_print
1343
+ json_print("consolidate", error={
1344
+ "code": "CCQ_ERROR", "message": str(exc),
1345
+ })
1346
+ sys.exit(1)
1347
+ raise
1348
+
1349
+ if use_json:
1350
+ from superlocalmemory.cli.json_output import json_print
1351
+ json_print("consolidate", data={
1352
+ "clusters_found": result.clusters_found,
1353
+ "blocks_created": result.blocks_created,
1354
+ "facts_archived": result.facts_archived,
1355
+ "compression_ratio": round(result.compression_ratio, 3),
1356
+ }, next_actions=[
1357
+ {"command": "slm list --json", "description": "List recent memories"},
1358
+ {"command": "slm status --json", "description": "Check status"},
1359
+ ])
1360
+ return
1361
+
1362
+ print("CCQ Cognitive Consolidation")
1363
+ print(f" Clusters found: {result.clusters_found}")
1364
+ print(f" Blocks created: {result.blocks_created}")
1365
+ print(f" Facts archived: {result.facts_archived}")
1366
+ print(f" Compression ratio: {result.compression_ratio:.3f}")
1367
+
1368
+
1369
+ def cmd_soft_prompts(args: Namespace) -> None:
1370
+ """List active soft prompts (auto-learned user patterns)."""
1371
+ from superlocalmemory.core.config import SLMConfig
1372
+ from superlocalmemory.core.engine import MemoryEngine
1373
+
1374
+ use_json = getattr(args, "json", False)
1375
+ profile = getattr(args, "profile", "")
1376
+
1377
+ try:
1378
+ config = SLMConfig.load()
1379
+ engine = MemoryEngine(config)
1380
+ engine.initialize()
1381
+ pid = profile or engine.profile_id
1382
+
1383
+ rows = engine._db.execute(
1384
+ "SELECT prompt_id, category, content, confidence, "
1385
+ " effectiveness, token_count, version, created_at "
1386
+ "FROM soft_prompt_templates "
1387
+ "WHERE profile_id = ? AND active = 1 "
1388
+ "ORDER BY confidence DESC",
1389
+ (pid,),
1390
+ )
1391
+ prompts = []
1392
+ for row in rows:
1393
+ r = dict(row)
1394
+ prompts.append({
1395
+ "prompt_id": r["prompt_id"],
1396
+ "category": r["category"],
1397
+ "content": r["content"],
1398
+ "confidence": round(float(r["confidence"]), 3),
1399
+ "effectiveness": round(float(r["effectiveness"]), 3),
1400
+ "token_count": int(r["token_count"]),
1401
+ "version": int(r["version"]),
1402
+ "created_at": r["created_at"],
1403
+ })
1404
+ except Exception as exc:
1405
+ if use_json:
1406
+ from superlocalmemory.cli.json_output import json_print
1407
+ json_print("soft-prompts", error={
1408
+ "code": "QUERY_ERROR", "message": str(exc),
1409
+ })
1410
+ sys.exit(1)
1411
+ raise
1412
+
1413
+ if use_json:
1414
+ from superlocalmemory.cli.json_output import json_print
1415
+ json_print("soft-prompts", data={
1416
+ "prompts": prompts, "count": len(prompts), "profile": pid,
1417
+ }, next_actions=[
1418
+ {"command": "slm status --json", "description": "Check status"},
1419
+ ])
1420
+ return
1421
+
1422
+ if not prompts:
1423
+ print("No active soft prompts.")
1424
+ return
1425
+
1426
+ print(f"Active soft prompts ({len(prompts)}):\n")
1427
+ for i, p in enumerate(prompts, 1):
1428
+ print(f" {i}. [{p['category']}] (conf={p['confidence']:.2f})")
1429
+ content_preview = p["content"][:100]
1430
+ if len(p["content"]) > 100:
1431
+ content_preview += "..."
1432
+ print(f" {content_preview}")
1433
+
1434
+
1435
+ def cmd_reap(args: Namespace) -> None:
1436
+ """Find and kill orphaned SLM processes."""
1437
+ use_json = getattr(args, "json", False)
1438
+ dry_run = not getattr(args, "force", False)
1439
+
1440
+ try:
1441
+ from superlocalmemory.infra.process_reaper import (
1442
+ cleanup_all_orphans,
1443
+ ReaperConfig,
1444
+ )
1445
+
1446
+ config = ReaperConfig()
1447
+ result = cleanup_all_orphans(config, dry_run=dry_run)
1448
+ except Exception as exc:
1449
+ if use_json:
1450
+ from superlocalmemory.cli.json_output import json_print
1451
+ json_print("reap", error={
1452
+ "code": "REAP_ERROR", "message": str(exc),
1453
+ })
1454
+ sys.exit(1)
1455
+ raise
1456
+
1457
+ if use_json:
1458
+ from superlocalmemory.cli.json_output import json_print
1459
+ json_print("reap", data={
1460
+ "dry_run": dry_run,
1461
+ "total_found": result.get("total_found", 0),
1462
+ "orphans_found": result.get("orphans_found", 0),
1463
+ "killed": result.get("killed", 0),
1464
+ "skipped": result.get("skipped", 0),
1465
+ }, next_actions=[
1466
+ {"command": "slm reap --force --json", "description": "Kill orphans"},
1467
+ {"command": "slm status --json", "description": "Check status"},
1468
+ ])
1469
+ return
1470
+
1471
+ total = result.get("total_found", 0)
1472
+ orphans = result.get("orphans_found", 0)
1473
+ killed = result.get("killed", 0)
1474
+ skipped = result.get("skipped", 0)
1475
+
1476
+ if dry_run:
1477
+ print(f"Process reaper (dry run)")
1478
+ else:
1479
+ print(f"Process reaper")
1480
+ print(f" Total SLM processes: {total}")
1481
+ print(f" Orphans found: {orphans}")
1482
+ print(f" Killed: {killed}")
1483
+ print(f" Skipped: {skipped}")
1484
+ if dry_run and orphans > 0:
1485
+ print("\n Use --force to kill orphaned processes.")
@@ -187,6 +187,50 @@ def main() -> None:
187
187
  obs_p = sub.add_parser("observe", help="Auto-capture content (pipe or argument)")
188
188
  obs_p.add_argument("content", nargs="?", default="", help="Content to evaluate")
189
189
 
190
+ # -- V3.3 Commands -------------------------------------------------
191
+ decay_p = sub.add_parser("decay", help="Run Ebbinghaus forgetting decay cycle")
192
+ decay_p.add_argument(
193
+ "--dry-run", action="store_true", default=True,
194
+ help="Preview without applying (default)",
195
+ )
196
+ decay_p.add_argument(
197
+ "--execute", dest="dry_run", action="store_false",
198
+ help="Apply zone transitions",
199
+ )
200
+ decay_p.add_argument("--profile", default="", help="Target profile")
201
+ decay_p.add_argument("--json", action="store_true", help="Output structured JSON (agent-native)")
202
+
203
+ quantize_p = sub.add_parser("quantize", help="Run EAP embedding quantization cycle")
204
+ quantize_p.add_argument(
205
+ "--dry-run", action="store_true", default=True,
206
+ help="Preview without applying (default)",
207
+ )
208
+ quantize_p.add_argument(
209
+ "--execute", dest="dry_run", action="store_false",
210
+ help="Apply precision changes",
211
+ )
212
+ quantize_p.add_argument("--profile", default="", help="Target profile")
213
+ quantize_p.add_argument("--json", action="store_true", help="Output structured JSON (agent-native)")
214
+
215
+ consolidate_p = sub.add_parser("consolidate", help="Run memory consolidation pipeline")
216
+ consolidate_p.add_argument(
217
+ "--cognitive", action="store_true",
218
+ help="Run CCQ cognitive consolidation",
219
+ )
220
+ consolidate_p.add_argument("--profile", default="", help="Target profile")
221
+ consolidate_p.add_argument("--json", action="store_true", help="Output structured JSON (agent-native)")
222
+
223
+ sp_p = sub.add_parser("soft-prompts", help="List active soft prompts (auto-learned patterns)")
224
+ sp_p.add_argument("--profile", default="", help="Target profile")
225
+ sp_p.add_argument("--json", action="store_true", help="Output structured JSON (agent-native)")
226
+
227
+ reap_p = sub.add_parser("reap", help="Find and kill orphaned SLM processes")
228
+ reap_p.add_argument(
229
+ "--force", action="store_true",
230
+ help="Kill orphans (default: dry run only)",
231
+ )
232
+ reap_p.add_argument("--json", action="store_true", help="Output structured JSON (agent-native)")
233
+
190
234
  args = parser.parse_args()
191
235
 
192
236
  if not args.command: