superlocalmemory 3.8.3 → 3.8.6
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 +76 -0
- package/README.md +3 -2
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -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 +1 -1
- 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 +9 -4
- package/src/superlocalmemory/__init__.py +1 -1
- package/src/superlocalmemory/access/rbac.py +68 -76
- package/src/superlocalmemory/cli/commands.py +158 -404
- package/src/superlocalmemory/cli/ingest_cmd.py +11 -1
- package/src/superlocalmemory/cli/main.py +30 -0
- package/src/superlocalmemory/cli/pending_store.py +39 -14
- package/src/superlocalmemory/core/backend_orchestrator.py +93 -0
- package/src/superlocalmemory/core/component_registry.py +4 -2
- package/src/superlocalmemory/core/config.py +78 -0
- package/src/superlocalmemory/core/consolidation_engine.py +79 -73
- package/src/superlocalmemory/core/embeddings.py +33 -6
- package/src/superlocalmemory/core/engine.py +186 -60
- package/src/superlocalmemory/core/engine_ingestion.py +150 -63
- package/src/superlocalmemory/core/fact_consolidator.py +148 -30
- package/src/superlocalmemory/core/graph_pruner.py +436 -39
- package/src/superlocalmemory/core/ingestion_command.py +273 -32
- package/src/superlocalmemory/core/maintenance_scheduler.py +61 -1
- package/src/superlocalmemory/core/mutations.py +32 -10
- package/src/superlocalmemory/core/recall_pipeline.py +111 -74
- package/src/superlocalmemory/core/registry.py +5 -1
- package/src/superlocalmemory/core/remember_admission.py +152 -0
- package/src/superlocalmemory/core/remember_runtime.py +712 -0
- package/src/superlocalmemory/core/remote_mode.py +3 -1
- package/src/superlocalmemory/core/scale_engine.py +41 -18
- package/src/superlocalmemory/core/store_pipeline.py +18 -4
- package/src/superlocalmemory/encoding/entity_resolver.py +18 -11
- package/src/superlocalmemory/graph/cozo_backend.py +5 -5
- package/src/superlocalmemory/hooks/_outcome_common.py +9 -2
- package/src/superlocalmemory/hooks/adapter_base.py +58 -44
- package/src/superlocalmemory/hooks/ide_connector.py +26 -8
- package/src/superlocalmemory/hooks/portable_kit.py +105 -9
- package/src/superlocalmemory/hooks/prewarm_auth.py +21 -2
- package/src/superlocalmemory/infra/auth_middleware.py +3 -1
- package/src/superlocalmemory/infra/cloud_backup.py +26 -27
- package/src/superlocalmemory/infra/event_bus.py +250 -88
- package/src/superlocalmemory/learning/bandit.py +50 -1
- package/src/superlocalmemory/learning/consolidation_cycle.py +33 -16
- package/src/superlocalmemory/learning/entity_compiler.py +148 -132
- package/src/superlocalmemory/learning/memory_merge.py +97 -82
- package/src/superlocalmemory/learning/reward_archive.py +98 -90
- package/src/superlocalmemory/learning/reward_boost.py +40 -30
- package/src/superlocalmemory/learning/source_quality.py +38 -35
- package/src/superlocalmemory/mcp/_daemon_proxy.py +38 -15
- package/src/superlocalmemory/mcp/http_transport.py +335 -3
- package/src/superlocalmemory/mcp/tools_active.py +4 -41
- package/src/superlocalmemory/mcp/tools_core.py +26 -87
- package/src/superlocalmemory/mcp/tools_evolution.py +5 -10
- package/src/superlocalmemory/optimize/proxy/capture.py +196 -8
- package/src/superlocalmemory/retrieval/engine.py +15 -4
- package/src/superlocalmemory/retrieval/entity_channel.py +25 -1
- package/src/superlocalmemory/retrieval/reranker.py +130 -22
- package/src/superlocalmemory/retrieval/spreading_activation.py +20 -12
- package/src/superlocalmemory/retrieval/vector_store.py +84 -69
- package/src/superlocalmemory/server/loopback.py +85 -0
- package/src/superlocalmemory/server/origin.py +9 -4
- package/src/superlocalmemory/server/profile_runtime.py +14 -0
- package/src/superlocalmemory/server/routes/abstraction.py +2 -4
- package/src/superlocalmemory/server/routes/agents.py +3 -5
- package/src/superlocalmemory/server/routes/backup.py +6 -2
- package/src/superlocalmemory/server/routes/behavioral.py +11 -25
- package/src/superlocalmemory/server/routes/brain.py +6 -9
- package/src/superlocalmemory/server/routes/compliance.py +20 -23
- package/src/superlocalmemory/server/routes/config_api.py +83 -0
- package/src/superlocalmemory/server/routes/entity.py +3 -7
- package/src/superlocalmemory/server/routes/evolution.py +3 -5
- package/src/superlocalmemory/server/routes/helpers.py +57 -25
- package/src/superlocalmemory/server/routes/insights.py +2 -4
- package/src/superlocalmemory/server/routes/learning.py +2 -5
- package/src/superlocalmemory/server/routes/lifecycle.py +2 -4
- package/src/superlocalmemory/server/routes/memories.py +119 -98
- package/src/superlocalmemory/server/routes/mesh.py +7 -2
- package/src/superlocalmemory/server/routes/profiles.py +20 -21
- package/src/superlocalmemory/server/routes/rbac.py +0 -1
- package/src/superlocalmemory/server/routes/tiers.py +28 -35
- package/src/superlocalmemory/server/routes/timeline.py +2 -4
- package/src/superlocalmemory/server/routes/v3_api.py +85 -93
- package/src/superlocalmemory/server/unified_daemon.py +400 -140
- package/src/superlocalmemory/server/write_identity.py +22 -4
- package/src/superlocalmemory/storage/admission_codec.py +119 -0
- package/src/superlocalmemory/storage/admission_journal.py +728 -0
- package/src/superlocalmemory/storage/database.py +168 -19
- package/src/superlocalmemory/storage/deferred_writes.py +209 -0
- package/src/superlocalmemory/storage/embedding_migrator.py +19 -0
- package/src/superlocalmemory/storage/memory_write.py +115 -0
- package/src/superlocalmemory/storage/migration_runner.py +44 -0
- package/src/superlocalmemory/storage/migrations/M028_fact_entity_associations.py +113 -78
- package/src/superlocalmemory/storage/migrations/M031_dead_letter_operations.py +80 -0
- package/src/superlocalmemory/storage/migrations/M032_write_coordinator_admission.py +188 -0
- package/src/superlocalmemory/storage/read_connection.py +115 -0
- package/src/superlocalmemory/storage/write_coordinator.py +756 -0
- package/src/superlocalmemory/storage/write_lock.py +88 -0
- package/src/superlocalmemory/ui/index.html +1 -1
- package/src/superlocalmemory/ui/js/auto-settings.js +14 -1
- package/src/superlocalmemory/ui/js/od-settings.js +9 -3
|
@@ -11,7 +11,7 @@ import logging
|
|
|
11
11
|
import os
|
|
12
12
|
from fastapi import APIRouter, HTTPException, Request
|
|
13
13
|
from fastapi.responses import JSONResponse
|
|
14
|
-
from superlocalmemory.server.routes.helpers import SLM_VERSION
|
|
14
|
+
from superlocalmemory.server.routes.helpers import SLM_VERSION, get_read_connection
|
|
15
15
|
from superlocalmemory.server.route_mutations import authorize_route_mutation
|
|
16
16
|
|
|
17
17
|
logger = logging.getLogger(__name__)
|
|
@@ -101,13 +101,12 @@ async def dashboard(request: Request):
|
|
|
101
101
|
active_profile = get_profile_runtime(request.app.state).snapshot.profile_id
|
|
102
102
|
|
|
103
103
|
# Read stats directly from SQLite (dashboard doesn't load engine)
|
|
104
|
-
import sqlite3
|
|
105
104
|
memory_count = 0
|
|
106
105
|
fact_count = 0
|
|
107
106
|
db_path = config.base_dir / "memory.db"
|
|
108
107
|
if db_path.exists():
|
|
109
108
|
try:
|
|
110
|
-
conn =
|
|
109
|
+
conn = get_read_connection(db_path)
|
|
111
110
|
cursor = conn.cursor()
|
|
112
111
|
try:
|
|
113
112
|
cursor.execute(
|
|
@@ -358,7 +357,10 @@ async def get_embedding_config(request: Request):
|
|
|
358
357
|
"""Return current embedding configuration."""
|
|
359
358
|
try:
|
|
360
359
|
from superlocalmemory.core.config import SLMConfig
|
|
361
|
-
config
|
|
360
|
+
# The daemon may already be running a freshly hot-swapped config while
|
|
361
|
+
# a profile update is still being persisted. The dashboard must show
|
|
362
|
+
# that live truth, never silently replace it with disk defaults.
|
|
363
|
+
config = getattr(request.app.state, "config", None) or SLMConfig.load()
|
|
362
364
|
emb = config.embedding
|
|
363
365
|
return {
|
|
364
366
|
"provider": emb.provider,
|
|
@@ -382,7 +384,7 @@ async def set_embedding_config(request: Request):
|
|
|
382
384
|
try:
|
|
383
385
|
body = await request.json()
|
|
384
386
|
from superlocalmemory.core.config import SLMConfig, EmbeddingConfig
|
|
385
|
-
config = SLMConfig.load()
|
|
387
|
+
config = getattr(request.app.state, "config", None) or SLMConfig.load()
|
|
386
388
|
|
|
387
389
|
new_provider = body.get("provider", config.embedding.provider)
|
|
388
390
|
new_model = body.get("model_name", config.embedding.model_name)
|
|
@@ -596,7 +598,9 @@ def _validate_provider_url(url: str, client_host: str) -> str | None:
|
|
|
596
598
|
host = p.hostname or ""
|
|
597
599
|
if host.lower() in ("169.254.169.254", "metadata.google.internal", "metadata"):
|
|
598
600
|
return "Cloud metadata endpoints are not allowed"
|
|
599
|
-
|
|
601
|
+
from superlocalmemory.server.loopback import is_loopback as _is_loopback_host
|
|
602
|
+
|
|
603
|
+
if _is_loopback_host(client_host):
|
|
600
604
|
return None # local dashboard may target its own local/LAN endpoints
|
|
601
605
|
# SLM_REMOTE residue (#40): an allowlisted LAN dashboard is trusted exactly
|
|
602
606
|
# like the loopback one and may probe its own LAN LLM endpoint. This does
|
|
@@ -1420,7 +1424,7 @@ async def get_associations(
|
|
|
1420
1424
|
if not DB_PATH.exists():
|
|
1421
1425
|
return {"edges": [], "total": 0}
|
|
1422
1426
|
|
|
1423
|
-
conn =
|
|
1427
|
+
conn = get_read_connection(DB_PATH)
|
|
1424
1428
|
conn.row_factory = sqlite3.Row
|
|
1425
1429
|
|
|
1426
1430
|
# Build query with optional type filter (parameterized)
|
|
@@ -1493,7 +1497,7 @@ async def get_association_stats(request: Request, profile: str = ""):
|
|
|
1493
1497
|
"top_connected_facts": [],
|
|
1494
1498
|
}
|
|
1495
1499
|
|
|
1496
|
-
conn =
|
|
1500
|
+
conn = get_read_connection(DB_PATH)
|
|
1497
1501
|
conn.row_factory = sqlite3.Row
|
|
1498
1502
|
|
|
1499
1503
|
# Total edges
|
|
@@ -1605,7 +1609,7 @@ async def get_consolidation_status(request: Request, profile: str = ""):
|
|
|
1605
1609
|
if not DB_PATH.exists():
|
|
1606
1610
|
return result
|
|
1607
1611
|
|
|
1608
|
-
conn =
|
|
1612
|
+
conn = get_read_connection(DB_PATH)
|
|
1609
1613
|
conn.row_factory = sqlite3.Row
|
|
1610
1614
|
|
|
1611
1615
|
# Last consolidation log entry
|
|
@@ -1757,7 +1761,7 @@ async def get_core_memory(request: Request, profile: str = ""):
|
|
|
1757
1761
|
if not DB_PATH.exists():
|
|
1758
1762
|
return {"blocks": [], "total_chars": 0, "char_limit": 2000}
|
|
1759
1763
|
|
|
1760
|
-
conn =
|
|
1764
|
+
conn = get_read_connection(DB_PATH)
|
|
1761
1765
|
conn.row_factory = sqlite3.Row
|
|
1762
1766
|
|
|
1763
1767
|
rows = conn.execute(
|
|
@@ -1808,7 +1812,7 @@ async def update_core_memory_block(block_id: str, request: Request):
|
|
|
1808
1812
|
)
|
|
1809
1813
|
|
|
1810
1814
|
from superlocalmemory.server.routes.helpers import DB_PATH, get_active_profile
|
|
1811
|
-
import
|
|
1815
|
+
from superlocalmemory.storage.memory_write import memory_write
|
|
1812
1816
|
from datetime import datetime, timezone
|
|
1813
1817
|
|
|
1814
1818
|
if not DB_PATH.exists():
|
|
@@ -1827,46 +1831,38 @@ async def update_core_memory_block(block_id: str, request: Request):
|
|
|
1827
1831
|
content_preview=str(content),
|
|
1828
1832
|
)
|
|
1829
1833
|
|
|
1830
|
-
conn = sqlite3.connect(str(DB_PATH))
|
|
1831
|
-
conn.row_factory = sqlite3.Row
|
|
1832
|
-
|
|
1833
|
-
# Verify block exists
|
|
1834
|
-
existing = conn.execute(
|
|
1835
|
-
"SELECT block_id, profile_id, block_type, version "
|
|
1836
|
-
"FROM core_memory_blocks WHERE block_id = ? AND profile_id = ?",
|
|
1837
|
-
(block_id, pid),
|
|
1838
|
-
).fetchone()
|
|
1839
|
-
|
|
1840
|
-
if not existing:
|
|
1841
|
-
conn.close()
|
|
1842
|
-
return JSONResponse(
|
|
1843
|
-
{"error": f"Block {block_id} not found"},
|
|
1844
|
-
status_code=404,
|
|
1845
|
-
)
|
|
1846
|
-
|
|
1847
|
-
existing_dict = dict(existing)
|
|
1848
|
-
new_version = existing_dict["version"] + 1
|
|
1849
1834
|
now = datetime.now(timezone.utc).isoformat()
|
|
1835
|
+
# memory_write: process write lock + busy_timeout.
|
|
1836
|
+
# SELECT + UPDATE + read-back are atomic inside the same connection.
|
|
1837
|
+
with memory_write(DB_PATH) as conn:
|
|
1838
|
+
# Verify block exists
|
|
1839
|
+
existing = conn.execute(
|
|
1840
|
+
"SELECT block_id, profile_id, block_type, version "
|
|
1841
|
+
"FROM core_memory_blocks WHERE block_id = ? AND profile_id = ?",
|
|
1842
|
+
(block_id, pid),
|
|
1843
|
+
).fetchone()
|
|
1850
1844
|
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
"version = ?, compiled_by = 'manual', updated_at = ? "
|
|
1854
|
-
"WHERE block_id = ? AND profile_id = ?",
|
|
1855
|
-
(content, len(content), new_version, now, block_id, pid),
|
|
1856
|
-
)
|
|
1857
|
-
conn.commit()
|
|
1845
|
+
if not existing:
|
|
1846
|
+
raise HTTPException(status_code=404, detail=f"Block {block_id} not found")
|
|
1858
1847
|
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1848
|
+
new_version = dict(existing)["version"] + 1
|
|
1849
|
+
conn.execute(
|
|
1850
|
+
"UPDATE core_memory_blocks SET content = ?, char_count = ?, "
|
|
1851
|
+
"version = ?, compiled_by = 'manual', updated_at = ? "
|
|
1852
|
+
"WHERE block_id = ? AND profile_id = ?",
|
|
1853
|
+
(content, len(content), new_version, now, block_id, pid),
|
|
1854
|
+
)
|
|
1855
|
+
# Read back updated block while connection is still open.
|
|
1856
|
+
updated = conn.execute(
|
|
1857
|
+
"SELECT block_id, block_type, content, char_count, version, "
|
|
1858
|
+
"compiled_by, updated_at FROM core_memory_blocks "
|
|
1859
|
+
"WHERE block_id = ? AND profile_id = ?",
|
|
1860
|
+
(block_id, pid),
|
|
1861
|
+
).fetchone()
|
|
1862
|
+
updated_dict = dict(updated) if updated else {"block_id": block_id, "updated": True}
|
|
1867
1863
|
|
|
1868
1864
|
authorization.complete()
|
|
1869
|
-
return
|
|
1865
|
+
return updated_dict
|
|
1870
1866
|
except HTTPException:
|
|
1871
1867
|
raise
|
|
1872
1868
|
except Exception as e:
|
|
@@ -1907,7 +1903,7 @@ async def get_vector_store_status(request: Request, profile: str = ""):
|
|
|
1907
1903
|
# Count vectors in embedding_metadata
|
|
1908
1904
|
if DB_PATH.exists():
|
|
1909
1905
|
try:
|
|
1910
|
-
conn =
|
|
1906
|
+
conn = get_read_connection(DB_PATH)
|
|
1911
1907
|
count = conn.execute(
|
|
1912
1908
|
"SELECT COUNT(*) FROM embedding_metadata WHERE profile_id = ?",
|
|
1913
1909
|
(pid,),
|
|
@@ -1951,7 +1947,7 @@ async def forgetting_stats(request: Request, profile: str = ""):
|
|
|
1951
1947
|
if not DB_PATH.exists():
|
|
1952
1948
|
return {"total": total, "zones": zones}
|
|
1953
1949
|
|
|
1954
|
-
conn =
|
|
1950
|
+
conn = get_read_connection(DB_PATH)
|
|
1955
1951
|
conn.row_factory = _sqlite3.Row
|
|
1956
1952
|
|
|
1957
1953
|
try:
|
|
@@ -1990,7 +1986,7 @@ async def run_forgetting(request: Request):
|
|
|
1990
1986
|
profile = body.get("profile", "")
|
|
1991
1987
|
|
|
1992
1988
|
from superlocalmemory.server.routes.helpers import get_active_profile, DB_PATH
|
|
1993
|
-
import
|
|
1989
|
+
from superlocalmemory.storage.memory_write import memory_write as _memory_write
|
|
1994
1990
|
pid = _resolve_mutation_profile(profile)
|
|
1995
1991
|
_require_manage_for_profile(request, pid)
|
|
1996
1992
|
|
|
@@ -2003,59 +1999,55 @@ async def run_forgetting(request: Request):
|
|
|
2003
1999
|
source_agent_id="http-forgetting-run",
|
|
2004
2000
|
profile_id=pid,
|
|
2005
2001
|
)
|
|
2006
|
-
conn = _sqlite3.connect(str(DB_PATH))
|
|
2007
|
-
conn.row_factory = _sqlite3.Row
|
|
2008
2002
|
|
|
2003
|
+
# memory_write: process write lock + busy_timeout — all UPDATEs atomic.
|
|
2009
2004
|
updated = 0
|
|
2010
2005
|
try:
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
"UPDATE fact_retention "
|
|
2015
|
-
"SET retention_score = MAX(0.0, retention_score * 0.9), "
|
|
2016
|
-
" last_computed_at = datetime('now') "
|
|
2017
|
-
"WHERE profile_id = ? "
|
|
2018
|
-
"AND lifecycle_zone NOT IN ('archive', 'forgotten')",
|
|
2019
|
-
(pid,),
|
|
2020
|
-
)
|
|
2021
|
-
updated = conn.total_changes
|
|
2022
|
-
|
|
2023
|
-
# Transition zones based on new retention scores
|
|
2024
|
-
zone_thresholds = [
|
|
2025
|
-
("forgotten", 0.05),
|
|
2026
|
-
("archive", 0.15),
|
|
2027
|
-
("cold", 0.35),
|
|
2028
|
-
("warm", 0.65),
|
|
2029
|
-
]
|
|
2030
|
-
for zone, threshold in zone_thresholds:
|
|
2006
|
+
with _memory_write(DB_PATH) as conn:
|
|
2007
|
+
# Apply Ebbinghaus decay: reduce retention for facts not accessed recently
|
|
2008
|
+
# Formula: retention *= exp(-0.1) for each cycle (simplified batch decay)
|
|
2031
2009
|
conn.execute(
|
|
2032
2010
|
"UPDATE fact_retention "
|
|
2033
|
-
"SET
|
|
2011
|
+
"SET retention_score = MAX(0.0, retention_score * 0.9), "
|
|
2012
|
+
" last_computed_at = datetime('now') "
|
|
2034
2013
|
"WHERE profile_id = ? "
|
|
2035
|
-
"AND retention_score < ? "
|
|
2036
2014
|
"AND lifecycle_zone NOT IN ('archive', 'forgotten')",
|
|
2037
|
-
(
|
|
2015
|
+
(pid,),
|
|
2038
2016
|
)
|
|
2017
|
+
updated = conn.total_changes
|
|
2018
|
+
|
|
2019
|
+
# Transition zones based on new retention scores
|
|
2020
|
+
zone_thresholds = [
|
|
2021
|
+
("forgotten", 0.05),
|
|
2022
|
+
("archive", 0.15),
|
|
2023
|
+
("cold", 0.35),
|
|
2024
|
+
("warm", 0.65),
|
|
2025
|
+
]
|
|
2026
|
+
for zone, threshold in zone_thresholds:
|
|
2027
|
+
conn.execute(
|
|
2028
|
+
"UPDATE fact_retention "
|
|
2029
|
+
"SET lifecycle_zone = ? "
|
|
2030
|
+
"WHERE profile_id = ? "
|
|
2031
|
+
"AND retention_score < ? "
|
|
2032
|
+
"AND lifecycle_zone NOT IN ('archive', 'forgotten')",
|
|
2033
|
+
(zone, pid, threshold),
|
|
2034
|
+
)
|
|
2039
2035
|
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
from superlocalmemory.core.lifecycle_state import reconcile_profile_lifecycle
|
|
2050
|
-
reconcile_profile_lifecycle(conn, pid)
|
|
2036
|
+
# Ensure high-retention facts are active
|
|
2037
|
+
conn.execute(
|
|
2038
|
+
"UPDATE fact_retention "
|
|
2039
|
+
"SET lifecycle_zone = 'active' "
|
|
2040
|
+
"WHERE profile_id = ? AND retention_score >= 0.65 "
|
|
2041
|
+
"AND lifecycle_zone NOT IN ('archive', 'forgotten')",
|
|
2042
|
+
(pid,),
|
|
2043
|
+
)
|
|
2051
2044
|
|
|
2052
|
-
|
|
2045
|
+
from superlocalmemory.core.lifecycle_state import reconcile_profile_lifecycle
|
|
2046
|
+
reconcile_profile_lifecycle(conn, pid)
|
|
2053
2047
|
except Exception as exc:
|
|
2054
2048
|
logger.exception("run_forgetting decay failed")
|
|
2055
|
-
conn.close()
|
|
2056
2049
|
return {"success": False, "error": "internal error"}
|
|
2057
2050
|
|
|
2058
|
-
conn.close()
|
|
2059
2051
|
authorization.complete()
|
|
2060
2052
|
return {"success": True, "facts_decayed": updated, "profile": pid}
|
|
2061
2053
|
except HTTPException:
|
|
@@ -2081,7 +2073,7 @@ async def quantization_stats(request: Request, profile: str = ""):
|
|
|
2081
2073
|
if not DB_PATH.exists():
|
|
2082
2074
|
return {"total": total, "tiers": tiers, "compression_ratio": compression_ratio}
|
|
2083
2075
|
|
|
2084
|
-
conn =
|
|
2076
|
+
conn = get_read_connection(DB_PATH)
|
|
2085
2077
|
conn.row_factory = _sqlite3.Row
|
|
2086
2078
|
|
|
2087
2079
|
try:
|
|
@@ -2140,7 +2132,7 @@ async def ccq_blocks(request: Request, profile: str = "", limit: int = 50):
|
|
|
2140
2132
|
if not DB_PATH.exists():
|
|
2141
2133
|
return {"blocks": [], "total": 0}
|
|
2142
2134
|
|
|
2143
|
-
conn =
|
|
2135
|
+
conn = get_read_connection(DB_PATH)
|
|
2144
2136
|
conn.row_factory = _sqlite3.Row
|
|
2145
2137
|
|
|
2146
2138
|
blocks = []
|
|
@@ -2200,7 +2192,7 @@ async def get_soft_prompts(request: Request, profile: str = ""):
|
|
|
2200
2192
|
if not DB_PATH.exists():
|
|
2201
2193
|
return {"prompts": [], "total": 0, "total_tokens": 0}
|
|
2202
2194
|
|
|
2203
|
-
conn =
|
|
2195
|
+
conn = get_read_connection(DB_PATH)
|
|
2204
2196
|
conn.row_factory = _sqlite3.Row
|
|
2205
2197
|
|
|
2206
2198
|
prompts = []
|
|
@@ -2308,7 +2300,7 @@ async def get_graph_communities(request: Request, profile: str = ""):
|
|
|
2308
2300
|
if not DB_PATH.exists():
|
|
2309
2301
|
return {"communities": [], "total": 0}
|
|
2310
2302
|
|
|
2311
|
-
conn =
|
|
2303
|
+
conn = get_read_connection(DB_PATH)
|
|
2312
2304
|
conn.row_factory = sqlite3.Row
|
|
2313
2305
|
|
|
2314
2306
|
# Get community member counts and average pagerank
|
|
@@ -2475,7 +2467,7 @@ async def v33_overview(request: Request, profile: str = ""):
|
|
|
2475
2467
|
if not DB_PATH.exists():
|
|
2476
2468
|
return overview
|
|
2477
2469
|
|
|
2478
|
-
conn =
|
|
2470
|
+
conn = get_read_connection(DB_PATH)
|
|
2479
2471
|
conn.row_factory = _sqlite3.Row
|
|
2480
2472
|
|
|
2481
2473
|
# Forgetting stats
|