superlocalmemory 3.8.0 → 3.8.2
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 +112 -0
- package/README.md +32 -120
- package/package.json +9 -2
- package/plugin/.claude-plugin/plugin.json +1 -2
- package/plugin/CLAUDE.md +3 -3
- package/plugin/agents/slm-governance-advisor.md +1 -1
- package/plugin/agents/slm-loop-runner.md +1 -1
- package/plugin/agents/slm-memory-advisor.md +2 -2
- package/plugin/agents/slm-optimize-advisor.md +1 -1
- package/plugin/requirements.txt +1 -1
- package/plugin/skills/slm-cache/SKILL.md +1 -1
- package/plugin/skills/slm-compress/SKILL.md +1 -1
- package/plugin/skills/slm-governance/SKILL.md +1 -1
- package/plugin/skills/slm-graph/SKILL.md +1 -1
- package/plugin/skills/slm-loop/SKILL.md +1 -1
- package/plugin/skills/slm-mesh/SKILL.md +1 -1
- package/plugin/skills/slm-profile/SKILL.md +1 -1
- package/plugin/skills/slm-recall/SKILL.md +3 -5
- package/plugin/skills/slm-remember/SKILL.md +1 -1
- package/plugin/skills/slm-scope/SKILL.md +1 -1
- package/plugin/skills/slm-session/SKILL.md +1 -1
- package/plugin/skills/slm-status/SKILL.md +1 -1
- package/plugin-src/rules/AGENTS.md +2 -1
- package/plugin-src/skills/slm-cache/SKILL.md +1 -1
- package/plugin-src/skills/slm-compress/SKILL.md +1 -1
- package/plugin-src/skills/slm-graph/SKILL.md +1 -1
- package/plugin-src/skills/slm-recall/SKILL.md +3 -5
- package/plugin-src/skills/slm-remember/SKILL.md +1 -1
- package/plugin-src/skills/slm-session/SKILL.md +1 -1
- package/plugin-src/skills/slm-status/SKILL.md +1 -1
- package/pyproject.toml +2 -1
- package/scripts/postinstall.js +7 -1
- package/src/superlocalmemory/__init__.py +1 -1
- package/src/superlocalmemory/cli/commands.py +494 -9
- package/src/superlocalmemory/cli/daemon.py +7 -0
- package/src/superlocalmemory/cli/loop_cmd.py +2 -7
- package/src/superlocalmemory/cli/main.py +72 -7
- package/src/superlocalmemory/cli/setup_wizard.py +142 -16
- package/src/superlocalmemory/cli/version_banner.py +17 -3
- package/src/superlocalmemory/core/backend_orchestrator.py +18 -16
- package/src/superlocalmemory/core/component_healer.py +144 -0
- package/src/superlocalmemory/core/component_registry.py +487 -0
- package/src/superlocalmemory/core/config.py +21 -0
- package/src/superlocalmemory/core/embedding_worker.py +4 -5
- package/src/superlocalmemory/core/embeddings.py +132 -45
- package/src/superlocalmemory/core/engine.py +29 -22
- package/src/superlocalmemory/core/engine_ingestion.py +332 -45
- package/src/superlocalmemory/core/ingestion_command.py +154 -25
- package/src/superlocalmemory/core/injection.py +12 -7
- package/src/superlocalmemory/core/maintenance.py +43 -0
- package/src/superlocalmemory/core/maintenance_scheduler.py +44 -6
- package/src/superlocalmemory/core/recall_pipeline.py +42 -4
- package/src/superlocalmemory/core/store_pipeline.py +195 -20
- package/src/superlocalmemory/hooks/hook_handlers.py +6 -1
- package/src/superlocalmemory/hooks/portable_kit.py +34 -2
- package/src/superlocalmemory/learning/model_rollback.py +3 -0
- package/src/superlocalmemory/learning/ranker_retrain_online.py +2 -0
- package/src/superlocalmemory/learning/reward.py +50 -0
- package/src/superlocalmemory/learning/source_quality.py +523 -1
- package/src/superlocalmemory/loops/ledger.py +25 -5
- package/src/superlocalmemory/mcp/_daemon_proxy.py +6 -2
- package/src/superlocalmemory/mcp/_pool_adapter.py +4 -1
- package/src/superlocalmemory/mcp/server.py +11 -30
- package/src/superlocalmemory/mcp/tools_active.py +1 -1
- package/src/superlocalmemory/mcp/tools_core.py +21 -5
- package/src/superlocalmemory/mcp/tools_learning.py +2 -2
- package/src/superlocalmemory/retrieval/bridge_discovery.py +14 -0
- package/src/superlocalmemory/retrieval/engine.py +53 -21
- package/src/superlocalmemory/retrieval/reranker.py +3 -4
- package/src/superlocalmemory/retrieval/spreading_activation.py +68 -38
- package/src/superlocalmemory/server/config_file.py +90 -0
- package/src/superlocalmemory/server/origin.py +50 -0
- package/src/superlocalmemory/server/routes/backup.py +293 -70
- package/src/superlocalmemory/server/routes/behavioral.py +342 -61
- package/src/superlocalmemory/server/routes/brain.py +57 -16
- package/src/superlocalmemory/server/routes/config_api.py +84 -82
- package/src/superlocalmemory/server/routes/entity.py +100 -23
- package/src/superlocalmemory/server/routes/evolution.py +103 -100
- package/src/superlocalmemory/server/routes/learning.py +286 -105
- package/src/superlocalmemory/server/routes/learning_telemetry.py +153 -0
- package/src/superlocalmemory/server/routes/memories.py +8 -3
- package/src/superlocalmemory/server/routes/mesh.py +121 -32
- package/src/superlocalmemory/server/routes/ratelimit.py +33 -25
- package/src/superlocalmemory/server/routes/stats.py +93 -155
- package/src/superlocalmemory/server/routes/token.py +3 -13
- package/src/superlocalmemory/server/routes/v3_api.py +184 -20
- package/src/superlocalmemory/server/unified_daemon.py +732 -41
- package/src/superlocalmemory/storage/embedding_migrator.py +235 -0
- package/src/superlocalmemory/storage/migration_runner.py +79 -1
- package/src/superlocalmemory/storage/migrations/M010_evolution_config.py +5 -0
- package/src/superlocalmemory/storage/migrations/M028_fact_entity_associations.py +270 -0
- package/src/superlocalmemory/storage/migrations/M029_behavioral_history_indexes.py +137 -0
- package/src/superlocalmemory/storage/migrations/M030_entity_explorer_indexes.py +93 -0
- package/src/superlocalmemory/storage/migrations/__init__.py +4 -0
- package/src/superlocalmemory/storage/schema.py +49 -1
- package/src/superlocalmemory/storage/schema_v32.py +2 -0
- package/src/superlocalmemory/storage/schema_v347.py +4 -0
- package/src/superlocalmemory/ui/index.html +6 -8
- package/src/superlocalmemory/ui/js/core.js +52 -9
- package/src/superlocalmemory/ui/js/dashboard.js +169 -82
- package/src/superlocalmemory/ui/js/od-backup.js +156 -65
- package/src/superlocalmemory/ui/js/od-brain.js +88 -51
- package/src/superlocalmemory/ui/js/od-components.js +147 -0
- package/src/superlocalmemory/ui/js/od-entities.js +65 -22
- package/src/superlocalmemory/ui/js/od-graph.js +46 -4
- package/src/superlocalmemory/ui/js/od-health.js +18 -0
- package/src/superlocalmemory/ui/js/od-memories.js +84 -5
- package/src/superlocalmemory/ui/js/od-mesh.js +23 -9
- package/src/superlocalmemory/ui/js/od-operations.js +36 -0
- package/src/superlocalmemory/ui/js/od-settings.js +186 -63
- package/src/superlocalmemory/ui/js/od-shell.js +249 -33
- package/src/superlocalmemory/ui/js/od-skills.js +44 -17
- package/src/superlocalmemory/ui/js/settings.js +15 -1
- package/plugin-src/.mcp.json +0 -12
- package/plugin-src/agents/slm-governance-advisor.md +0 -80
- package/plugin-src/agents/slm-loop-runner.md +0 -71
- package/plugin-src/agents/slm-memory-advisor.md +0 -49
- package/plugin-src/agents/slm-optimize-advisor.md +0 -44
- package/plugin-src/commands/slm-loop.md +0 -31
- package/plugin-src/hooks/.gitkeep +0 -0
- package/plugin-src/hooks/hooks.json +0 -102
- package/plugin-src/manifest.json +0 -30
- package/plugin-src/requirements.txt +0 -1
- package/plugin-src/rules/CLAUDE.md.fragment +0 -44
- package/plugin-src/scripts/ensure-venv.bat +0 -122
- package/plugin-src/scripts/ensure-venv.sh +0 -105
- package/plugin-src/scripts/slm-launch +0 -62
- package/plugin-src/scripts/slm-launch.bat +0 -23
- package/plugin-src/settings.json +0 -25
- package/plugin-src/skills/slm-governance/SKILL.md +0 -248
- package/plugin-src/skills/slm-loop/SKILL.md +0 -99
- package/plugin-src/skills/slm-mesh/SKILL.md +0 -282
- package/plugin-src/skills/slm-profile/SKILL.md +0 -148
- package/plugin-src/skills/slm-scope/SKILL.md +0 -176
|
@@ -52,6 +52,42 @@ async def _apply_runtime_config(request: Request, config, *, mode_change: bool)
|
|
|
52
52
|
authorization.complete()
|
|
53
53
|
|
|
54
54
|
|
|
55
|
+
def _require_manage(request: Request) -> None:
|
|
56
|
+
"""Use one explicit authorization boundary for dashboard configuration."""
|
|
57
|
+
from superlocalmemory.server.rbac_enforce import require_manage
|
|
58
|
+
|
|
59
|
+
require_manage(request)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _resolve_profile(request: Request, requested: str = "") -> str:
|
|
63
|
+
"""Resolve a profile override and enforce READ on its actual target."""
|
|
64
|
+
from superlocalmemory.access.rbac import Permission
|
|
65
|
+
from superlocalmemory.server.rbac_enforce import require_permission
|
|
66
|
+
from superlocalmemory.server.routes.helpers import get_active_profile
|
|
67
|
+
|
|
68
|
+
if requested and not isinstance(requested, str):
|
|
69
|
+
raise HTTPException(status_code=422, detail="profile must be a string")
|
|
70
|
+
profile = requested.strip() if requested else get_active_profile()
|
|
71
|
+
require_permission(request, Permission.READ, profile=profile)
|
|
72
|
+
return profile
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _resolve_mutation_profile(requested: object) -> str:
|
|
76
|
+
"""Resolve a body-supplied profile without silently coercing bad input."""
|
|
77
|
+
from superlocalmemory.server.routes.helpers import get_active_profile
|
|
78
|
+
|
|
79
|
+
if requested is not None and not isinstance(requested, str):
|
|
80
|
+
raise HTTPException(status_code=422, detail="profile must be a string")
|
|
81
|
+
return requested.strip() if requested and requested.strip() else get_active_profile()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _require_manage_for_profile(request: Request, profile: str) -> None:
|
|
85
|
+
"""Enforce MANAGE on the resolved mutation target before any side effect."""
|
|
86
|
+
from superlocalmemory.server.rbac_enforce import require_manage
|
|
87
|
+
|
|
88
|
+
require_manage(request, profile=profile)
|
|
89
|
+
|
|
90
|
+
|
|
55
91
|
# ── Dashboard ────────────────────────────────────────────────
|
|
56
92
|
|
|
57
93
|
@router.get("/dashboard")
|
|
@@ -448,9 +484,9 @@ async def set_scope_config(request: Request):
|
|
|
448
484
|
@router.post("/embedding/test")
|
|
449
485
|
async def test_embedding_endpoint(request: Request):
|
|
450
486
|
"""Test connectivity to a custom embedding endpoint."""
|
|
487
|
+
_require_manage(request)
|
|
451
488
|
try:
|
|
452
489
|
import httpx
|
|
453
|
-
from urllib.parse import urlparse
|
|
454
490
|
body = await request.json()
|
|
455
491
|
endpoint = body.get("api_endpoint", "").rstrip("/")
|
|
456
492
|
model = body.get("model_name", "test")
|
|
@@ -459,12 +495,10 @@ async def test_embedding_endpoint(request: Request):
|
|
|
459
495
|
if not endpoint:
|
|
460
496
|
return JSONResponse({"error": "No endpoint provided"}, status_code=400)
|
|
461
497
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
if host in ("169.254.169.254", "metadata.google.internal"):
|
|
467
|
-
return JSONResponse({"error": "Cloud metadata endpoints not allowed"}, status_code=400)
|
|
498
|
+
client_host = request.client.host if request.client else ""
|
|
499
|
+
error = _validate_provider_url(endpoint, client_host)
|
|
500
|
+
if error:
|
|
501
|
+
return JSONResponse({"error": error}, status_code=400)
|
|
468
502
|
|
|
469
503
|
if not endpoint.endswith("/embeddings"):
|
|
470
504
|
endpoint = f"{endpoint}/embeddings"
|
|
@@ -980,6 +1014,7 @@ async def get_auto_capture_config():
|
|
|
980
1014
|
@router.put("/auto-capture/config")
|
|
981
1015
|
async def set_auto_capture_config(request: Request):
|
|
982
1016
|
"""Update auto-capture config. Body: {"enabled": true, "capture_decisions": true, ...}"""
|
|
1017
|
+
_require_manage(request)
|
|
983
1018
|
try:
|
|
984
1019
|
body = await request.json()
|
|
985
1020
|
from superlocalmemory.hooks.rules_engine import RulesEngine
|
|
@@ -1010,6 +1045,7 @@ async def get_auto_recall_config():
|
|
|
1010
1045
|
@router.put("/auto-recall/config")
|
|
1011
1046
|
async def set_auto_recall_config(request: Request):
|
|
1012
1047
|
"""Update auto-recall config."""
|
|
1048
|
+
_require_manage(request)
|
|
1013
1049
|
try:
|
|
1014
1050
|
body = await request.json()
|
|
1015
1051
|
from superlocalmemory.hooks.rules_engine import RulesEngine
|
|
@@ -1024,6 +1060,126 @@ async def set_auto_recall_config(request: Request):
|
|
|
1024
1060
|
return _internal_error()
|
|
1025
1061
|
|
|
1026
1062
|
|
|
1063
|
+
# ── Runtime behaviour config (v3.8.2 UX-1) ──────────────────
|
|
1064
|
+
# User-facing settings that take effect LIVE (no restart) via the
|
|
1065
|
+
# reconfigure_daemon_engine hot-swap path AND persist across restarts via
|
|
1066
|
+
# SLMConfig.save(). Scoped deliberately to fields that save() round-trips:
|
|
1067
|
+
# retrieval (asdict) + injection (explicit). Excludes setup-time/internal
|
|
1068
|
+
# knobs and any field save() doesn't persist (which would silently revert).
|
|
1069
|
+
_RUNTIME_CONFIG_FIELDS = (
|
|
1070
|
+
# (section, field, kind, min, max)
|
|
1071
|
+
("retrieval", "top_k", "int", 1, 200),
|
|
1072
|
+
("retrieval", "use_cross_encoder", "bool", None, None),
|
|
1073
|
+
("injection", "enabled", "bool", None, None),
|
|
1074
|
+
("injection", "core_block_enabled", "bool", None, None),
|
|
1075
|
+
("injection", "core_block_max_facts", "int", 0, 50),
|
|
1076
|
+
)
|
|
1077
|
+
|
|
1078
|
+
|
|
1079
|
+
def _runtime_config_snapshot(config) -> dict:
|
|
1080
|
+
"""Current values of the exposed runtime fields, grouped by section."""
|
|
1081
|
+
out: dict = {}
|
|
1082
|
+
for section, field, *_ in _RUNTIME_CONFIG_FIELDS:
|
|
1083
|
+
sec = getattr(config, section, None)
|
|
1084
|
+
out.setdefault(section, {})[field] = (
|
|
1085
|
+
getattr(sec, field, None) if sec is not None else None
|
|
1086
|
+
)
|
|
1087
|
+
return out
|
|
1088
|
+
|
|
1089
|
+
|
|
1090
|
+
@router.get("/runtime/config")
|
|
1091
|
+
async def get_runtime_config(request: Request):
|
|
1092
|
+
"""User-facing runtime behaviour settings.
|
|
1093
|
+
|
|
1094
|
+
Recall depth (top_k), reranker on/off (use_cross_encoder), and memory
|
|
1095
|
+
injection (master + core-block). All apply live via the daemon hot-swap —
|
|
1096
|
+
no restart — and persist across restarts.
|
|
1097
|
+
"""
|
|
1098
|
+
try:
|
|
1099
|
+
from superlocalmemory.core.config import SLMConfig
|
|
1100
|
+
config = getattr(request.app.state, "config", None) or SLMConfig.load()
|
|
1101
|
+
return {"success": True, "config": _runtime_config_snapshot(config)}
|
|
1102
|
+
except Exception:
|
|
1103
|
+
return _internal_error()
|
|
1104
|
+
|
|
1105
|
+
|
|
1106
|
+
@router.put("/runtime/config")
|
|
1107
|
+
async def set_runtime_config(request: Request):
|
|
1108
|
+
"""Validate, persist, and hot-apply runtime behaviour settings.
|
|
1109
|
+
|
|
1110
|
+
Body: ``{"retrieval": {"top_k": 30}, "injection": {"enabled": false}}``.
|
|
1111
|
+
Only known fields are accepted; a bad type/range is rejected 400 and
|
|
1112
|
+
nothing is applied (fail-fast — never let a bad value wedge the engine).
|
|
1113
|
+
"""
|
|
1114
|
+
_require_manage(request)
|
|
1115
|
+
try:
|
|
1116
|
+
import dataclasses as _dc
|
|
1117
|
+
from superlocalmemory.core.config import SLMConfig
|
|
1118
|
+
|
|
1119
|
+
body = await request.json()
|
|
1120
|
+
if not isinstance(body, dict):
|
|
1121
|
+
return JSONResponse({"error": "body must be an object"}, status_code=400)
|
|
1122
|
+
|
|
1123
|
+
# Validate everything BEFORE mutating anything.
|
|
1124
|
+
updates: dict[str, dict] = {}
|
|
1125
|
+
for section, field, kind, lo, hi in _RUNTIME_CONFIG_FIELDS:
|
|
1126
|
+
sec_in = body.get(section)
|
|
1127
|
+
if not isinstance(sec_in, dict) or field not in sec_in:
|
|
1128
|
+
continue
|
|
1129
|
+
val = sec_in[field]
|
|
1130
|
+
if kind == "bool":
|
|
1131
|
+
if not isinstance(val, bool):
|
|
1132
|
+
return JSONResponse(
|
|
1133
|
+
{"error": f"{section}.{field} must be true or false"},
|
|
1134
|
+
status_code=400,
|
|
1135
|
+
)
|
|
1136
|
+
elif kind == "int":
|
|
1137
|
+
# bool is an int subclass — reject it explicitly.
|
|
1138
|
+
if isinstance(val, bool) or not isinstance(val, int):
|
|
1139
|
+
return JSONResponse(
|
|
1140
|
+
{"error": f"{section}.{field} must be an integer"},
|
|
1141
|
+
status_code=400,
|
|
1142
|
+
)
|
|
1143
|
+
if (lo is not None and val < lo) or (hi is not None and val > hi):
|
|
1144
|
+
return JSONResponse(
|
|
1145
|
+
{"error": f"{section}.{field} must be between {lo} and {hi}"},
|
|
1146
|
+
status_code=400,
|
|
1147
|
+
)
|
|
1148
|
+
updates.setdefault(section, {})[field] = val
|
|
1149
|
+
|
|
1150
|
+
if not updates:
|
|
1151
|
+
return JSONResponse(
|
|
1152
|
+
{"error": "no known runtime settings in request body"},
|
|
1153
|
+
status_code=400,
|
|
1154
|
+
)
|
|
1155
|
+
|
|
1156
|
+
# These are plain behaviour flags read per-recall — NOT model/mode
|
|
1157
|
+
# swaps. So we apply them the light way: swap the sub-config objects on
|
|
1158
|
+
# the LIVE config the daemon+engine already hold, then persist to disk.
|
|
1159
|
+
# This avoids the heavyweight engine drain+rebuild (which reloads
|
|
1160
|
+
# models and can time out draining in-flight recalls) — the running
|
|
1161
|
+
# engine simply reads the new values on its next recall/injection.
|
|
1162
|
+
live = getattr(request.app.state, "config", None) or SLMConfig.load()
|
|
1163
|
+
targets = [live]
|
|
1164
|
+
engine = getattr(request.app.state, "engine", None)
|
|
1165
|
+
eng_cfg = getattr(engine, "_config", None) if engine is not None else None
|
|
1166
|
+
if eng_cfg is not None and eng_cfg is not live:
|
|
1167
|
+
targets.append(eng_cfg)
|
|
1168
|
+
for cfg in targets:
|
|
1169
|
+
for section, changes in updates.items():
|
|
1170
|
+
sec = getattr(cfg, section, None)
|
|
1171
|
+
if sec is None:
|
|
1172
|
+
continue
|
|
1173
|
+
# replace() yields a NEW sub-config (works frozen or mutable);
|
|
1174
|
+
# assigning the attribute is an atomic reference swap.
|
|
1175
|
+
setattr(cfg, section, _dc.replace(sec, **changes))
|
|
1176
|
+
request.app.state.config = live
|
|
1177
|
+
live.save(mode_change=False) # durable across restart
|
|
1178
|
+
return {"success": True, "config": _runtime_config_snapshot(live)}
|
|
1179
|
+
except Exception:
|
|
1180
|
+
return _internal_error()
|
|
1181
|
+
|
|
1182
|
+
|
|
1027
1183
|
# ── IDE Status ───────────────────────────────────────────────
|
|
1028
1184
|
|
|
1029
1185
|
@router.get("/ide/status")
|
|
@@ -1096,6 +1252,7 @@ async def run_consolidation(request: Request):
|
|
|
1096
1252
|
|
|
1097
1253
|
config = SLMConfig.load()
|
|
1098
1254
|
learning_db = DB_PATH.parent / "learning.db"
|
|
1255
|
+
_require_manage_for_profile(request, config.active_profile)
|
|
1099
1256
|
authorization = authorize_route_mutation(
|
|
1100
1257
|
request,
|
|
1101
1258
|
operation="update",
|
|
@@ -1213,6 +1370,7 @@ async def set_auto_invoke_config(request: Request):
|
|
|
1213
1370
|
|
|
1214
1371
|
Body: {"enabled": true, "min_score": 0.15, "weights": {...}}
|
|
1215
1372
|
"""
|
|
1373
|
+
_require_manage(request)
|
|
1216
1374
|
try:
|
|
1217
1375
|
body = await request.json()
|
|
1218
1376
|
|
|
@@ -1254,10 +1412,10 @@ async def get_associations(
|
|
|
1254
1412
|
profile: str = "",
|
|
1255
1413
|
):
|
|
1256
1414
|
"""Get association edges for a profile with content previews."""
|
|
1415
|
+
pid = _resolve_profile(request, profile)
|
|
1257
1416
|
try:
|
|
1258
1417
|
from superlocalmemory.server.routes.helpers import get_active_profile, DB_PATH
|
|
1259
1418
|
import sqlite3
|
|
1260
|
-
pid = profile or get_active_profile()
|
|
1261
1419
|
|
|
1262
1420
|
if not DB_PATH.exists():
|
|
1263
1421
|
return {"edges": [], "total": 0}
|
|
@@ -1321,10 +1479,10 @@ async def get_associations(
|
|
|
1321
1479
|
@router.get("/associations/stats")
|
|
1322
1480
|
async def get_association_stats(request: Request, profile: str = ""):
|
|
1323
1481
|
"""Get association graph statistics."""
|
|
1482
|
+
pid = _resolve_profile(request, profile)
|
|
1324
1483
|
try:
|
|
1325
1484
|
from superlocalmemory.server.routes.helpers import get_active_profile, DB_PATH
|
|
1326
1485
|
import sqlite3
|
|
1327
|
-
pid = profile or get_active_profile()
|
|
1328
1486
|
|
|
1329
1487
|
if not DB_PATH.exists():
|
|
1330
1488
|
return {
|
|
@@ -1422,12 +1580,12 @@ async def get_association_stats(request: Request, profile: str = ""):
|
|
|
1422
1580
|
@router.get("/consolidation/status")
|
|
1423
1581
|
async def get_consolidation_status(request: Request, profile: str = ""):
|
|
1424
1582
|
"""Get consolidation status and last run results."""
|
|
1583
|
+
pid = _resolve_profile(request, profile)
|
|
1425
1584
|
try:
|
|
1426
1585
|
from superlocalmemory.server.routes.helpers import get_active_profile, DB_PATH
|
|
1427
1586
|
from superlocalmemory.core.config import SLMConfig
|
|
1428
1587
|
import sqlite3
|
|
1429
1588
|
|
|
1430
|
-
pid = profile or get_active_profile()
|
|
1431
1589
|
config = SLMConfig.load()
|
|
1432
1590
|
cons_cfg = config.consolidation
|
|
1433
1591
|
|
|
@@ -1520,7 +1678,8 @@ async def trigger_consolidation(request: Request):
|
|
|
1520
1678
|
profile = body.get("profile", "")
|
|
1521
1679
|
|
|
1522
1680
|
from superlocalmemory.server.routes.helpers import get_active_profile
|
|
1523
|
-
pid = profile
|
|
1681
|
+
pid = _resolve_mutation_profile(profile)
|
|
1682
|
+
_require_manage_for_profile(request, pid)
|
|
1524
1683
|
authorization = authorize_route_mutation(
|
|
1525
1684
|
request,
|
|
1526
1685
|
operation="update",
|
|
@@ -1590,10 +1749,10 @@ async def trigger_consolidation(request: Request):
|
|
|
1590
1749
|
@router.get("/core-memory")
|
|
1591
1750
|
async def get_core_memory(request: Request, profile: str = ""):
|
|
1592
1751
|
"""Get all Core Memory blocks for a profile."""
|
|
1752
|
+
pid = _resolve_profile(request, profile)
|
|
1593
1753
|
try:
|
|
1594
1754
|
from superlocalmemory.server.routes.helpers import get_active_profile, DB_PATH
|
|
1595
1755
|
import sqlite3
|
|
1596
|
-
pid = profile or get_active_profile()
|
|
1597
1756
|
|
|
1598
1757
|
if not DB_PATH.exists():
|
|
1599
1758
|
return {"blocks": [], "total_chars": 0, "char_limit": 2000}
|
|
@@ -1719,6 +1878,7 @@ async def update_core_memory_block(block_id: str, request: Request):
|
|
|
1719
1878
|
@router.get("/vector-store/status")
|
|
1720
1879
|
async def get_vector_store_status(request: Request, profile: str = ""):
|
|
1721
1880
|
"""Get VectorStore health and statistics."""
|
|
1881
|
+
pid = _resolve_profile(request, profile)
|
|
1722
1882
|
try:
|
|
1723
1883
|
from superlocalmemory.core.config import SLMConfig
|
|
1724
1884
|
from superlocalmemory.server.routes.helpers import DB_PATH
|
|
@@ -1749,7 +1909,8 @@ async def get_vector_store_status(request: Request, profile: str = ""):
|
|
|
1749
1909
|
try:
|
|
1750
1910
|
conn = sqlite3.connect(str(DB_PATH))
|
|
1751
1911
|
count = conn.execute(
|
|
1752
|
-
"SELECT COUNT(*) FROM embedding_metadata"
|
|
1912
|
+
"SELECT COUNT(*) FROM embedding_metadata WHERE profile_id = ?",
|
|
1913
|
+
(pid,),
|
|
1753
1914
|
).fetchone()[0]
|
|
1754
1915
|
result["total_vectors"] = count
|
|
1755
1916
|
conn.close()
|
|
@@ -1779,10 +1940,10 @@ async def get_vector_store_status(request: Request, profile: str = ""):
|
|
|
1779
1940
|
@router.get("/forgetting/stats")
|
|
1780
1941
|
async def forgetting_stats(request: Request, profile: str = ""):
|
|
1781
1942
|
"""Get memory retention zone distribution."""
|
|
1943
|
+
pid = _resolve_profile(request, profile)
|
|
1782
1944
|
try:
|
|
1783
1945
|
from superlocalmemory.server.routes.helpers import get_active_profile, DB_PATH
|
|
1784
1946
|
import sqlite3 as _sqlite3
|
|
1785
|
-
pid = profile or get_active_profile()
|
|
1786
1947
|
|
|
1787
1948
|
zones = {"active": 0, "warm": 0, "cold": 0, "archive": 0, "forgotten": 0}
|
|
1788
1949
|
total = 0
|
|
@@ -1830,7 +1991,8 @@ async def run_forgetting(request: Request):
|
|
|
1830
1991
|
|
|
1831
1992
|
from superlocalmemory.server.routes.helpers import get_active_profile, DB_PATH
|
|
1832
1993
|
import sqlite3 as _sqlite3
|
|
1833
|
-
pid = profile
|
|
1994
|
+
pid = _resolve_mutation_profile(profile)
|
|
1995
|
+
_require_manage_for_profile(request, pid)
|
|
1834
1996
|
|
|
1835
1997
|
if not DB_PATH.exists():
|
|
1836
1998
|
return {"success": False, "error": "Database not found"}
|
|
@@ -1907,10 +2069,10 @@ async def run_forgetting(request: Request):
|
|
|
1907
2069
|
@router.get("/quantization/stats")
|
|
1908
2070
|
async def quantization_stats(request: Request, profile: str = ""):
|
|
1909
2071
|
"""Get embedding quantization tier distribution."""
|
|
2072
|
+
pid = _resolve_profile(request, profile)
|
|
1910
2073
|
try:
|
|
1911
2074
|
from superlocalmemory.server.routes.helpers import get_active_profile, DB_PATH
|
|
1912
2075
|
import sqlite3 as _sqlite3
|
|
1913
|
-
pid = profile or get_active_profile()
|
|
1914
2076
|
|
|
1915
2077
|
tiers = {"float32": 0, "int8": 0, "polar4": 0, "polar2": 0}
|
|
1916
2078
|
total = 0
|
|
@@ -1970,10 +2132,10 @@ async def quantization_stats(request: Request, profile: str = ""):
|
|
|
1970
2132
|
@router.get("/ccq/blocks")
|
|
1971
2133
|
async def ccq_blocks(request: Request, profile: str = "", limit: int = 50):
|
|
1972
2134
|
"""Get CCQ consolidated blocks."""
|
|
2135
|
+
pid = _resolve_profile(request, profile)
|
|
1973
2136
|
try:
|
|
1974
2137
|
from superlocalmemory.server.routes.helpers import get_active_profile, DB_PATH
|
|
1975
2138
|
import sqlite3 as _sqlite3
|
|
1976
|
-
pid = profile or get_active_profile()
|
|
1977
2139
|
|
|
1978
2140
|
if not DB_PATH.exists():
|
|
1979
2141
|
return {"blocks": [], "total": 0}
|
|
@@ -2030,10 +2192,10 @@ async def ccq_blocks(request: Request, profile: str = "", limit: int = 50):
|
|
|
2030
2192
|
@router.get("/soft-prompts")
|
|
2031
2193
|
async def get_soft_prompts(request: Request, profile: str = ""):
|
|
2032
2194
|
"""Get active soft prompt templates."""
|
|
2195
|
+
pid = _resolve_profile(request, profile)
|
|
2033
2196
|
try:
|
|
2034
2197
|
from superlocalmemory.server.routes.helpers import get_active_profile, DB_PATH
|
|
2035
2198
|
import sqlite3 as _sqlite3
|
|
2036
|
-
pid = profile or get_active_profile()
|
|
2037
2199
|
|
|
2038
2200
|
if not DB_PATH.exists():
|
|
2039
2201
|
return {"prompts": [], "total": 0, "total_tokens": 0}
|
|
@@ -2083,6 +2245,7 @@ async def get_soft_prompts(request: Request, profile: str = ""):
|
|
|
2083
2245
|
@router.get("/health/processes")
|
|
2084
2246
|
async def process_health(request: Request):
|
|
2085
2247
|
"""Get SLM process health status."""
|
|
2248
|
+
_resolve_profile(request)
|
|
2086
2249
|
try:
|
|
2087
2250
|
import os as _os
|
|
2088
2251
|
|
|
@@ -2137,10 +2300,10 @@ async def get_graph_communities(request: Request, profile: str = ""):
|
|
|
2137
2300
|
at consolidation time). Falls back to inline word frequency if labels
|
|
2138
2301
|
not yet computed.
|
|
2139
2302
|
"""
|
|
2303
|
+
pid = _resolve_profile(request, profile)
|
|
2140
2304
|
try:
|
|
2141
2305
|
from superlocalmemory.server.routes.helpers import get_active_profile, DB_PATH
|
|
2142
2306
|
import sqlite3
|
|
2143
|
-
pid = profile or get_active_profile()
|
|
2144
2307
|
|
|
2145
2308
|
if not DB_PATH.exists():
|
|
2146
2309
|
return {"communities": [], "total": 0}
|
|
@@ -2267,6 +2430,7 @@ async def run_community_detection(request: Request):
|
|
|
2267
2430
|
from superlocalmemory.core.graph_analyzer import GraphAnalyzer
|
|
2268
2431
|
|
|
2269
2432
|
pid = get_active_profile()
|
|
2433
|
+
_require_manage_for_profile(request, pid)
|
|
2270
2434
|
authorization = authorize_route_mutation(
|
|
2271
2435
|
request,
|
|
2272
2436
|
operation="update",
|
|
@@ -2289,10 +2453,10 @@ async def run_community_detection(request: Request):
|
|
|
2289
2453
|
@router.get("/v33/overview")
|
|
2290
2454
|
async def v33_overview(request: Request, profile: str = ""):
|
|
2291
2455
|
"""Get SLM 3.3 feature overview -- all new capabilities at a glance."""
|
|
2456
|
+
pid = _resolve_profile(request, profile)
|
|
2292
2457
|
try:
|
|
2293
2458
|
from superlocalmemory.server.routes.helpers import get_active_profile, DB_PATH
|
|
2294
2459
|
import sqlite3 as _sqlite3
|
|
2295
|
-
pid = profile or get_active_profile()
|
|
2296
2460
|
|
|
2297
2461
|
overview: dict = {
|
|
2298
2462
|
"version": "3.3",
|