superlocalmemory 3.2.3 → 3.3.1
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/CHANGELOG.md +43 -1
- package/README.md +106 -71
- package/package.json +1 -2
- package/pyproject.toml +16 -1
- package/src/superlocalmemory/cli/commands.py +419 -15
- package/src/superlocalmemory/cli/main.py +44 -0
- package/src/superlocalmemory/core/config.py +276 -4
- package/src/superlocalmemory/core/consolidation_engine.py +37 -0
- package/src/superlocalmemory/core/engine.py +21 -0
- package/src/superlocalmemory/core/engine_wiring.py +58 -8
- package/src/superlocalmemory/dynamics/activation_guided_quantization.py +374 -0
- package/src/superlocalmemory/dynamics/eap_scheduler.py +276 -0
- package/src/superlocalmemory/dynamics/ebbinghaus_langevin_coupling.py +171 -0
- package/src/superlocalmemory/encoding/cognitive_consolidator.py +804 -0
- package/src/superlocalmemory/hooks/auto_invoker.py +46 -8
- package/src/superlocalmemory/hooks/auto_parameterize.py +147 -0
- package/src/superlocalmemory/infra/heartbeat_monitor.py +140 -0
- package/src/superlocalmemory/infra/pid_manager.py +193 -0
- package/src/superlocalmemory/infra/process_reaper.py +572 -0
- package/src/superlocalmemory/learning/consolidation_quantization_worker.py +115 -0
- package/src/superlocalmemory/learning/forgetting_scheduler.py +263 -0
- package/src/superlocalmemory/learning/quantization_scheduler.py +320 -0
- package/src/superlocalmemory/math/ebbinghaus.py +309 -0
- package/src/superlocalmemory/math/fisher_quantized.py +251 -0
- package/src/superlocalmemory/math/hopfield.py +279 -0
- package/src/superlocalmemory/math/polar_quant.py +379 -0
- package/src/superlocalmemory/math/qjl.py +115 -0
- package/src/superlocalmemory/mcp/server.py +2 -0
- package/src/superlocalmemory/mcp/tools_v3.py +10 -0
- package/src/superlocalmemory/mcp/tools_v33.py +351 -0
- package/src/superlocalmemory/parameterization/__init__.py +47 -0
- package/src/superlocalmemory/parameterization/pattern_extractor.py +534 -0
- package/src/superlocalmemory/parameterization/pii_filter.py +106 -0
- package/src/superlocalmemory/parameterization/prompt_injector.py +216 -0
- package/src/superlocalmemory/parameterization/prompt_lifecycle.py +275 -0
- package/src/superlocalmemory/parameterization/soft_prompt_generator.py +425 -0
- package/src/superlocalmemory/retrieval/engine.py +21 -3
- package/src/superlocalmemory/retrieval/forgetting_filter.py +145 -0
- package/src/superlocalmemory/retrieval/hopfield_channel.py +335 -0
- package/src/superlocalmemory/retrieval/quantization_aware_search.py +133 -0
- package/src/superlocalmemory/retrieval/strategy.py +16 -6
- package/src/superlocalmemory/server/routes/agents.py +68 -8
- package/src/superlocalmemory/server/routes/learning.py +18 -1
- package/src/superlocalmemory/server/routes/lifecycle.py +36 -17
- package/src/superlocalmemory/server/routes/v3_api.py +503 -1
- package/src/superlocalmemory/storage/database.py +206 -0
- package/src/superlocalmemory/storage/embedding_migrator.py +178 -0
- package/src/superlocalmemory/storage/migration_v33.py +140 -0
- package/src/superlocalmemory/storage/quantized_store.py +261 -0
- package/src/superlocalmemory/storage/schema_v32.py +137 -0
- 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
|
|
|
@@ -1118,27 +1129,122 @@ def cmd_hooks(args: Namespace) -> None:
|
|
|
1118
1129
|
|
|
1119
1130
|
|
|
1120
1131
|
def cmd_session_context(args: Namespace) -> None:
|
|
1121
|
-
"""Print session context (for hook scripts and piping).
|
|
1122
|
-
|
|
1132
|
+
"""Print session context (for hook scripts and piping).
|
|
1133
|
+
|
|
1134
|
+
Uses a FAST PATH that queries SQLite directly (no engine/Ollama needed).
|
|
1135
|
+
This ensures the SessionStart hook completes within its 15s timeout even
|
|
1136
|
+
when Ollama requires a 60s+ cold start. The fast path returns:
|
|
1137
|
+
- Core Memory blocks (always-on context)
|
|
1138
|
+
- Recent high-importance memories (last 7 days)
|
|
1139
|
+
- Session summary from last session
|
|
1140
|
+
Falls back to the full engine path only if --full is passed explicitly.
|
|
1141
|
+
"""
|
|
1142
|
+
import sqlite3
|
|
1143
|
+
from pathlib import Path
|
|
1123
1144
|
from superlocalmemory.core.config import SLMConfig
|
|
1124
|
-
from superlocalmemory.core.engine import MemoryEngine
|
|
1125
1145
|
|
|
1146
|
+
use_full = getattr(args, "full", False)
|
|
1147
|
+
|
|
1148
|
+
if use_full:
|
|
1149
|
+
# Full engine path (slow, requires Ollama) — for explicit CLI use
|
|
1150
|
+
try:
|
|
1151
|
+
from superlocalmemory.hooks.auto_recall import AutoRecall
|
|
1152
|
+
from superlocalmemory.core.engine import MemoryEngine
|
|
1153
|
+
config = SLMConfig.load()
|
|
1154
|
+
engine = MemoryEngine(config)
|
|
1155
|
+
engine.initialize()
|
|
1156
|
+
auto = AutoRecall(
|
|
1157
|
+
engine=engine,
|
|
1158
|
+
config={"enabled": True, "max_memories_injected": 10, "relevance_threshold": 0.3},
|
|
1159
|
+
)
|
|
1160
|
+
context = auto.get_session_context(
|
|
1161
|
+
query=getattr(args, "query", "") or "recent decisions and important context",
|
|
1162
|
+
)
|
|
1163
|
+
if context:
|
|
1164
|
+
print(context)
|
|
1165
|
+
except Exception as exc:
|
|
1166
|
+
logger.debug("session-context (full) failed: %s", exc)
|
|
1167
|
+
return
|
|
1168
|
+
|
|
1169
|
+
# ── FAST PATH: direct SQLite, no engine, <500ms ──────────────
|
|
1126
1170
|
try:
|
|
1127
1171
|
config = SLMConfig.load()
|
|
1128
|
-
|
|
1129
|
-
|
|
1172
|
+
db_path = config.base_dir / "memory.db"
|
|
1173
|
+
if not db_path.exists():
|
|
1174
|
+
return
|
|
1130
1175
|
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1176
|
+
pid = config.active_profile
|
|
1177
|
+
conn = sqlite3.connect(str(db_path))
|
|
1178
|
+
conn.row_factory = sqlite3.Row
|
|
1179
|
+
sections = []
|
|
1180
|
+
|
|
1181
|
+
# 1. Core Memory blocks (compiled high-value context)
|
|
1182
|
+
try:
|
|
1183
|
+
rows = conn.execute(
|
|
1184
|
+
"SELECT block_type, content FROM core_memory_blocks "
|
|
1185
|
+
"WHERE profile_id = ? ORDER BY block_type",
|
|
1186
|
+
(pid,),
|
|
1187
|
+
).fetchall()
|
|
1188
|
+
if rows:
|
|
1189
|
+
blocks = [f"[{r['block_type']}] {r['content']}" for r in rows]
|
|
1190
|
+
sections.append("## Core Memory\n" + "\n".join(blocks))
|
|
1191
|
+
except sqlite3.OperationalError:
|
|
1192
|
+
pass
|
|
1193
|
+
|
|
1194
|
+
# 2. Recent important memories (last 7 days, top 10 by importance)
|
|
1195
|
+
try:
|
|
1196
|
+
rows = conn.execute(
|
|
1197
|
+
"SELECT content, fact_type, created_at FROM atomic_facts "
|
|
1198
|
+
"WHERE profile_id = ? "
|
|
1199
|
+
"AND created_at >= datetime('now', '-7 days') "
|
|
1200
|
+
"AND lifecycle = 'active' "
|
|
1201
|
+
"ORDER BY importance DESC, created_at DESC LIMIT 10",
|
|
1202
|
+
(pid,),
|
|
1203
|
+
).fetchall()
|
|
1204
|
+
if rows:
|
|
1205
|
+
items = []
|
|
1206
|
+
for r in rows:
|
|
1207
|
+
content = r["content"][:200]
|
|
1208
|
+
items.append(f"- [{r['fact_type'] or 'fact'}] {content}")
|
|
1209
|
+
sections.append("## Recent Context (7 days)\n" + "\n".join(items))
|
|
1210
|
+
except sqlite3.OperationalError:
|
|
1211
|
+
pass
|
|
1212
|
+
|
|
1213
|
+
# 3. Session markers (last session summary)
|
|
1214
|
+
try:
|
|
1215
|
+
rows = conn.execute(
|
|
1216
|
+
"SELECT content, created_at FROM atomic_facts "
|
|
1217
|
+
"WHERE profile_id = ? AND content LIKE 'Session%' "
|
|
1218
|
+
"ORDER BY created_at DESC LIMIT 3",
|
|
1219
|
+
(pid,),
|
|
1220
|
+
).fetchall()
|
|
1221
|
+
if rows:
|
|
1222
|
+
items = [f"- {r['content'][:150]}" for r in rows]
|
|
1223
|
+
sections.append("## Recent Sessions\n" + "\n".join(items))
|
|
1224
|
+
except sqlite3.OperationalError:
|
|
1225
|
+
pass
|
|
1226
|
+
|
|
1227
|
+
# 4. V3.3 Soft prompts (auto-learned patterns)
|
|
1228
|
+
try:
|
|
1229
|
+
rows = conn.execute(
|
|
1230
|
+
"SELECT category, content FROM soft_prompt_templates "
|
|
1231
|
+
"WHERE profile_id = ? AND active = 1 "
|
|
1232
|
+
"ORDER BY confidence DESC LIMIT 5",
|
|
1233
|
+
(pid,),
|
|
1234
|
+
).fetchall()
|
|
1235
|
+
if rows:
|
|
1236
|
+
items = [f"- [{r['category']}] {r['content'][:150]}" for r in rows]
|
|
1237
|
+
sections.append("## Learned Patterns\n" + "\n".join(items))
|
|
1238
|
+
except sqlite3.OperationalError:
|
|
1239
|
+
pass
|
|
1240
|
+
|
|
1241
|
+
conn.close()
|
|
1242
|
+
|
|
1243
|
+
if sections:
|
|
1244
|
+
header = f"# SLM Session Context — {config.active_profile}"
|
|
1245
|
+
print(header + "\n\n" + "\n\n".join(sections))
|
|
1140
1246
|
except Exception as exc:
|
|
1141
|
-
logger.debug("session-context failed: %s", exc)
|
|
1247
|
+
logger.debug("session-context (fast) failed: %s", exc)
|
|
1142
1248
|
|
|
1143
1249
|
|
|
1144
1250
|
def cmd_observe(args: Namespace) -> None:
|
|
@@ -1174,3 +1280,301 @@ def cmd_observe(args: Namespace) -> None:
|
|
|
1174
1280
|
print(f"Not captured: {decision.reason}")
|
|
1175
1281
|
except Exception as exc:
|
|
1176
1282
|
logger.debug("observe failed: %s", exc)
|
|
1283
|
+
|
|
1284
|
+
|
|
1285
|
+
# -- V3.3 Commands -----------------------------------------------------------
|
|
1286
|
+
|
|
1287
|
+
|
|
1288
|
+
def cmd_decay(args: Namespace) -> None:
|
|
1289
|
+
"""Run Ebbinghaus forgetting decay cycle."""
|
|
1290
|
+
from superlocalmemory.core.config import SLMConfig
|
|
1291
|
+
from superlocalmemory.core.engine import MemoryEngine
|
|
1292
|
+
|
|
1293
|
+
use_json = getattr(args, "json", False)
|
|
1294
|
+
dry_run = getattr(args, "dry_run", True)
|
|
1295
|
+
profile = getattr(args, "profile", "")
|
|
1296
|
+
|
|
1297
|
+
try:
|
|
1298
|
+
config = SLMConfig.load()
|
|
1299
|
+
engine = MemoryEngine(config)
|
|
1300
|
+
engine.initialize()
|
|
1301
|
+
pid = profile or engine.profile_id
|
|
1302
|
+
|
|
1303
|
+
from superlocalmemory.math.ebbinghaus import EbbinghausCurve
|
|
1304
|
+
from superlocalmemory.learning.forgetting_scheduler import (
|
|
1305
|
+
ForgettingScheduler,
|
|
1306
|
+
)
|
|
1307
|
+
|
|
1308
|
+
ebbinghaus = EbbinghausCurve(config.forgetting)
|
|
1309
|
+
scheduler = ForgettingScheduler(
|
|
1310
|
+
engine._db, ebbinghaus, config.forgetting,
|
|
1311
|
+
)
|
|
1312
|
+
result = scheduler.run_decay_cycle(pid, force=True)
|
|
1313
|
+
except Exception as exc:
|
|
1314
|
+
if use_json:
|
|
1315
|
+
from superlocalmemory.cli.json_output import json_print
|
|
1316
|
+
json_print("decay", error={"code": "DECAY_ERROR", "message": str(exc)})
|
|
1317
|
+
sys.exit(1)
|
|
1318
|
+
raise
|
|
1319
|
+
|
|
1320
|
+
if use_json:
|
|
1321
|
+
from superlocalmemory.cli.json_output import json_print
|
|
1322
|
+
json_print("decay", data={"dry_run": dry_run, **result},
|
|
1323
|
+
next_actions=[
|
|
1324
|
+
{"command": "slm decay --execute --json", "description": "Apply transitions"},
|
|
1325
|
+
{"command": "slm status --json", "description": "Check system status"},
|
|
1326
|
+
])
|
|
1327
|
+
return
|
|
1328
|
+
|
|
1329
|
+
if result.get("skipped"):
|
|
1330
|
+
print(f"Skipped: {result.get('reason', 'unknown')}")
|
|
1331
|
+
return
|
|
1332
|
+
|
|
1333
|
+
total = result.get("total", 0)
|
|
1334
|
+
print(f"Decay cycle complete (dry_run={dry_run})")
|
|
1335
|
+
print(f" Total facts: {total}")
|
|
1336
|
+
print(f" Active: {result.get('active', 0)}")
|
|
1337
|
+
print(f" Warm: {result.get('warm', 0)}")
|
|
1338
|
+
print(f" Cold: {result.get('cold', 0)}")
|
|
1339
|
+
print(f" Archive: {result.get('archive', 0)}")
|
|
1340
|
+
print(f" Forgotten: {result.get('forgotten', 0)}")
|
|
1341
|
+
print(f" Transitions: {result.get('transitions', 0)}")
|
|
1342
|
+
|
|
1343
|
+
|
|
1344
|
+
def cmd_quantize(args: Namespace) -> None:
|
|
1345
|
+
"""Run EAP embedding quantization cycle."""
|
|
1346
|
+
from superlocalmemory.core.config import SLMConfig
|
|
1347
|
+
from superlocalmemory.core.engine import MemoryEngine
|
|
1348
|
+
|
|
1349
|
+
use_json = getattr(args, "json", False)
|
|
1350
|
+
dry_run = getattr(args, "dry_run", True)
|
|
1351
|
+
profile = getattr(args, "profile", "")
|
|
1352
|
+
|
|
1353
|
+
try:
|
|
1354
|
+
config = SLMConfig.load()
|
|
1355
|
+
engine = MemoryEngine(config)
|
|
1356
|
+
engine.initialize()
|
|
1357
|
+
pid = profile or engine.profile_id
|
|
1358
|
+
|
|
1359
|
+
from superlocalmemory.math.ebbinghaus import EbbinghausCurve
|
|
1360
|
+
from superlocalmemory.dynamics.eap_scheduler import EAPScheduler
|
|
1361
|
+
from superlocalmemory.storage.quantized_store import (
|
|
1362
|
+
QuantizedEmbeddingStore,
|
|
1363
|
+
)
|
|
1364
|
+
|
|
1365
|
+
from superlocalmemory.math.polar_quant import PolarQuantEncoder
|
|
1366
|
+
from superlocalmemory.math.qjl import QJLEncoder
|
|
1367
|
+
|
|
1368
|
+
ebbinghaus = EbbinghausCurve(config.forgetting)
|
|
1369
|
+
polar = PolarQuantEncoder(config.quantization.polar)
|
|
1370
|
+
qjl = QJLEncoder(config.quantization.qjl)
|
|
1371
|
+
qstore = QuantizedEmbeddingStore(
|
|
1372
|
+
engine._db, polar, qjl, config.quantization,
|
|
1373
|
+
)
|
|
1374
|
+
scheduler = EAPScheduler(
|
|
1375
|
+
engine._db, ebbinghaus, qstore, config.quantization,
|
|
1376
|
+
)
|
|
1377
|
+
result = scheduler.run_eap_cycle(pid)
|
|
1378
|
+
except Exception as exc:
|
|
1379
|
+
if use_json:
|
|
1380
|
+
from superlocalmemory.cli.json_output import json_print
|
|
1381
|
+
json_print("quantize", error={"code": "EAP_ERROR", "message": str(exc)})
|
|
1382
|
+
sys.exit(1)
|
|
1383
|
+
raise
|
|
1384
|
+
|
|
1385
|
+
if use_json:
|
|
1386
|
+
from superlocalmemory.cli.json_output import json_print
|
|
1387
|
+
json_print("quantize", data={"dry_run": dry_run, **result},
|
|
1388
|
+
next_actions=[
|
|
1389
|
+
{"command": "slm quantize --execute --json", "description": "Apply changes"},
|
|
1390
|
+
{"command": "slm status --json", "description": "Check status"},
|
|
1391
|
+
])
|
|
1392
|
+
return
|
|
1393
|
+
|
|
1394
|
+
print(f"EAP quantization cycle (dry_run={dry_run})")
|
|
1395
|
+
print(f" Total: {result.get('total', 0)}")
|
|
1396
|
+
print(f" Downgrades: {result.get('downgrades', 0)}")
|
|
1397
|
+
print(f" Upgrades: {result.get('upgrades', 0)}")
|
|
1398
|
+
print(f" Skipped: {result.get('skipped', 0)}")
|
|
1399
|
+
print(f" Errors: {result.get('errors', 0)}")
|
|
1400
|
+
|
|
1401
|
+
|
|
1402
|
+
def cmd_consolidate(args: Namespace) -> None:
|
|
1403
|
+
"""Run cognitive consolidation pipeline."""
|
|
1404
|
+
from superlocalmemory.core.config import SLMConfig
|
|
1405
|
+
from superlocalmemory.core.engine import MemoryEngine
|
|
1406
|
+
|
|
1407
|
+
use_json = getattr(args, "json", False)
|
|
1408
|
+
cognitive = getattr(args, "cognitive", False)
|
|
1409
|
+
profile = getattr(args, "profile", "")
|
|
1410
|
+
|
|
1411
|
+
if not cognitive:
|
|
1412
|
+
if use_json:
|
|
1413
|
+
from superlocalmemory.cli.json_output import json_print
|
|
1414
|
+
json_print("consolidate", error={
|
|
1415
|
+
"code": "MISSING_FLAG",
|
|
1416
|
+
"message": "Use --cognitive to run CCQ pipeline",
|
|
1417
|
+
})
|
|
1418
|
+
sys.exit(1)
|
|
1419
|
+
print("Use --cognitive to run CCQ consolidation pipeline.")
|
|
1420
|
+
print(" slm consolidate --cognitive")
|
|
1421
|
+
return
|
|
1422
|
+
|
|
1423
|
+
try:
|
|
1424
|
+
config = SLMConfig.load()
|
|
1425
|
+
engine = MemoryEngine(config)
|
|
1426
|
+
engine.initialize()
|
|
1427
|
+
pid = profile or engine.profile_id
|
|
1428
|
+
|
|
1429
|
+
from superlocalmemory.encoding.cognitive_consolidator import (
|
|
1430
|
+
CognitiveConsolidator,
|
|
1431
|
+
)
|
|
1432
|
+
|
|
1433
|
+
consolidator = CognitiveConsolidator(db=engine._db)
|
|
1434
|
+
result = consolidator.run_pipeline(pid)
|
|
1435
|
+
except Exception as exc:
|
|
1436
|
+
if use_json:
|
|
1437
|
+
from superlocalmemory.cli.json_output import json_print
|
|
1438
|
+
json_print("consolidate", error={
|
|
1439
|
+
"code": "CCQ_ERROR", "message": str(exc),
|
|
1440
|
+
})
|
|
1441
|
+
sys.exit(1)
|
|
1442
|
+
raise
|
|
1443
|
+
|
|
1444
|
+
if use_json:
|
|
1445
|
+
from superlocalmemory.cli.json_output import json_print
|
|
1446
|
+
json_print("consolidate", data={
|
|
1447
|
+
"clusters_found": result.clusters_found,
|
|
1448
|
+
"blocks_created": result.blocks_created,
|
|
1449
|
+
"facts_archived": result.facts_archived,
|
|
1450
|
+
"compression_ratio": round(result.compression_ratio, 3),
|
|
1451
|
+
}, next_actions=[
|
|
1452
|
+
{"command": "slm list --json", "description": "List recent memories"},
|
|
1453
|
+
{"command": "slm status --json", "description": "Check status"},
|
|
1454
|
+
])
|
|
1455
|
+
return
|
|
1456
|
+
|
|
1457
|
+
print("CCQ Cognitive Consolidation")
|
|
1458
|
+
print(f" Clusters found: {result.clusters_found}")
|
|
1459
|
+
print(f" Blocks created: {result.blocks_created}")
|
|
1460
|
+
print(f" Facts archived: {result.facts_archived}")
|
|
1461
|
+
print(f" Compression ratio: {result.compression_ratio:.3f}")
|
|
1462
|
+
|
|
1463
|
+
|
|
1464
|
+
def cmd_soft_prompts(args: Namespace) -> None:
|
|
1465
|
+
"""List active soft prompts (auto-learned user patterns)."""
|
|
1466
|
+
from superlocalmemory.core.config import SLMConfig
|
|
1467
|
+
from superlocalmemory.core.engine import MemoryEngine
|
|
1468
|
+
|
|
1469
|
+
use_json = getattr(args, "json", False)
|
|
1470
|
+
profile = getattr(args, "profile", "")
|
|
1471
|
+
|
|
1472
|
+
try:
|
|
1473
|
+
config = SLMConfig.load()
|
|
1474
|
+
engine = MemoryEngine(config)
|
|
1475
|
+
engine.initialize()
|
|
1476
|
+
pid = profile or engine.profile_id
|
|
1477
|
+
|
|
1478
|
+
rows = engine._db.execute(
|
|
1479
|
+
"SELECT prompt_id, category, content, confidence, "
|
|
1480
|
+
" effectiveness, token_count, version, created_at "
|
|
1481
|
+
"FROM soft_prompt_templates "
|
|
1482
|
+
"WHERE profile_id = ? AND active = 1 "
|
|
1483
|
+
"ORDER BY confidence DESC",
|
|
1484
|
+
(pid,),
|
|
1485
|
+
)
|
|
1486
|
+
prompts = []
|
|
1487
|
+
for row in rows:
|
|
1488
|
+
r = dict(row)
|
|
1489
|
+
prompts.append({
|
|
1490
|
+
"prompt_id": r["prompt_id"],
|
|
1491
|
+
"category": r["category"],
|
|
1492
|
+
"content": r["content"],
|
|
1493
|
+
"confidence": round(float(r["confidence"]), 3),
|
|
1494
|
+
"effectiveness": round(float(r["effectiveness"]), 3),
|
|
1495
|
+
"token_count": int(r["token_count"]),
|
|
1496
|
+
"version": int(r["version"]),
|
|
1497
|
+
"created_at": r["created_at"],
|
|
1498
|
+
})
|
|
1499
|
+
except Exception as exc:
|
|
1500
|
+
if use_json:
|
|
1501
|
+
from superlocalmemory.cli.json_output import json_print
|
|
1502
|
+
json_print("soft-prompts", error={
|
|
1503
|
+
"code": "QUERY_ERROR", "message": str(exc),
|
|
1504
|
+
})
|
|
1505
|
+
sys.exit(1)
|
|
1506
|
+
raise
|
|
1507
|
+
|
|
1508
|
+
if use_json:
|
|
1509
|
+
from superlocalmemory.cli.json_output import json_print
|
|
1510
|
+
json_print("soft-prompts", data={
|
|
1511
|
+
"prompts": prompts, "count": len(prompts), "profile": pid,
|
|
1512
|
+
}, next_actions=[
|
|
1513
|
+
{"command": "slm status --json", "description": "Check status"},
|
|
1514
|
+
])
|
|
1515
|
+
return
|
|
1516
|
+
|
|
1517
|
+
if not prompts:
|
|
1518
|
+
print("No active soft prompts.")
|
|
1519
|
+
return
|
|
1520
|
+
|
|
1521
|
+
print(f"Active soft prompts ({len(prompts)}):\n")
|
|
1522
|
+
for i, p in enumerate(prompts, 1):
|
|
1523
|
+
print(f" {i}. [{p['category']}] (conf={p['confidence']:.2f})")
|
|
1524
|
+
content_preview = p["content"][:100]
|
|
1525
|
+
if len(p["content"]) > 100:
|
|
1526
|
+
content_preview += "..."
|
|
1527
|
+
print(f" {content_preview}")
|
|
1528
|
+
|
|
1529
|
+
|
|
1530
|
+
def cmd_reap(args: Namespace) -> None:
|
|
1531
|
+
"""Find and kill orphaned SLM processes."""
|
|
1532
|
+
use_json = getattr(args, "json", False)
|
|
1533
|
+
dry_run = not getattr(args, "force", False)
|
|
1534
|
+
|
|
1535
|
+
try:
|
|
1536
|
+
from superlocalmemory.infra.process_reaper import (
|
|
1537
|
+
cleanup_all_orphans,
|
|
1538
|
+
ReaperConfig,
|
|
1539
|
+
)
|
|
1540
|
+
|
|
1541
|
+
config = ReaperConfig()
|
|
1542
|
+
result = cleanup_all_orphans(config, dry_run=dry_run)
|
|
1543
|
+
except Exception as exc:
|
|
1544
|
+
if use_json:
|
|
1545
|
+
from superlocalmemory.cli.json_output import json_print
|
|
1546
|
+
json_print("reap", error={
|
|
1547
|
+
"code": "REAP_ERROR", "message": str(exc),
|
|
1548
|
+
})
|
|
1549
|
+
sys.exit(1)
|
|
1550
|
+
raise
|
|
1551
|
+
|
|
1552
|
+
if use_json:
|
|
1553
|
+
from superlocalmemory.cli.json_output import json_print
|
|
1554
|
+
json_print("reap", data={
|
|
1555
|
+
"dry_run": dry_run,
|
|
1556
|
+
"total_found": result.get("total_found", 0),
|
|
1557
|
+
"orphans_found": result.get("orphans_found", 0),
|
|
1558
|
+
"killed": result.get("killed", 0),
|
|
1559
|
+
"skipped": result.get("skipped", 0),
|
|
1560
|
+
}, next_actions=[
|
|
1561
|
+
{"command": "slm reap --force --json", "description": "Kill orphans"},
|
|
1562
|
+
{"command": "slm status --json", "description": "Check status"},
|
|
1563
|
+
])
|
|
1564
|
+
return
|
|
1565
|
+
|
|
1566
|
+
total = result.get("total_found", 0)
|
|
1567
|
+
orphans = result.get("orphans_found", 0)
|
|
1568
|
+
killed = result.get("killed", 0)
|
|
1569
|
+
skipped = result.get("skipped", 0)
|
|
1570
|
+
|
|
1571
|
+
if dry_run:
|
|
1572
|
+
print(f"Process reaper (dry run)")
|
|
1573
|
+
else:
|
|
1574
|
+
print(f"Process reaper")
|
|
1575
|
+
print(f" Total SLM processes: {total}")
|
|
1576
|
+
print(f" Orphans found: {orphans}")
|
|
1577
|
+
print(f" Killed: {killed}")
|
|
1578
|
+
print(f" Skipped: {skipped}")
|
|
1579
|
+
if dry_run and orphans > 0:
|
|
1580
|
+
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:
|