superlocalmemory 3.2.1 → 3.2.3
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 +23 -1
- package/README.md +61 -1
- package/package.json +1 -1
- package/pyproject.toml +26 -1
- package/src/superlocalmemory/attribution/signer.py +6 -1
- package/src/superlocalmemory/core/config.py +113 -1
- package/src/superlocalmemory/core/consolidation_engine.py +595 -0
- package/src/superlocalmemory/core/embeddings.py +0 -1
- package/src/superlocalmemory/core/engine.py +164 -674
- package/src/superlocalmemory/core/engine_wiring.py +474 -0
- package/src/superlocalmemory/core/graph_analyzer.py +199 -0
- package/src/superlocalmemory/core/recall_pipeline.py +247 -0
- package/src/superlocalmemory/core/store_pipeline.py +483 -0
- package/src/superlocalmemory/core/worker_pool.py +35 -12
- package/src/superlocalmemory/encoding/auto_linker.py +308 -0
- package/src/superlocalmemory/encoding/context_generator.py +175 -0
- package/src/superlocalmemory/encoding/temporal_validator.py +513 -0
- package/src/superlocalmemory/hooks/auto_invoker.py +484 -0
- package/src/superlocalmemory/retrieval/channel_registry.py +154 -0
- package/src/superlocalmemory/retrieval/engine.py +12 -0
- package/src/superlocalmemory/retrieval/semantic_channel.py +87 -3
- package/src/superlocalmemory/retrieval/spreading_activation.py +311 -0
- package/src/superlocalmemory/retrieval/strategy.py +6 -6
- package/src/superlocalmemory/retrieval/vector_store.py +386 -0
- package/src/superlocalmemory/server/routes/v3_api.py +576 -0
- package/src/superlocalmemory/storage/access_log.py +169 -0
- package/src/superlocalmemory/storage/database.py +288 -0
- package/src/superlocalmemory/storage/schema.py +10 -0
- package/src/superlocalmemory/storage/schema_v32.py +252 -0
- package/src/superlocalmemory/storage/v2_migrator.py +24 -2
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
2
|
+
# Licensed under the MIT License - see LICENSE file
|
|
3
|
+
# Part of SuperLocalMemory V3
|
|
4
|
+
|
|
5
|
+
"""SuperLocalMemory V3.2 -- Schema Extensions (Associative Memory).
|
|
6
|
+
|
|
7
|
+
Phase 0.5 creates this file with EMPTY V32_DDL.
|
|
8
|
+
Each implementing phase OWNS its DDL and appends here:
|
|
9
|
+
- Phase 1 adds: fact_access_log, fact_embeddings (vec0), embedding_metadata
|
|
10
|
+
- Phase 2 adds: fact_context
|
|
11
|
+
- Phase 3 adds: association_edges, activation_cache, fact_importance
|
|
12
|
+
- Phase 4 adds: fact_temporal_validity
|
|
13
|
+
- Phase 5 adds: core_memory_blocks
|
|
14
|
+
|
|
15
|
+
Design rules:
|
|
16
|
+
- profile_id + FK CASCADE on every table (Rule 01)
|
|
17
|
+
- CREATE IF NOT EXISTS for idempotency (Rule 02)
|
|
18
|
+
- Rollback SQL in V32_ROLLBACK (Rule 20)
|
|
19
|
+
- Never ALTER existing tables (Rule 02)
|
|
20
|
+
|
|
21
|
+
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
from typing import Final
|
|
27
|
+
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
# Table names (for drop_all parity -- Rule 20)
|
|
30
|
+
# All 9 names listed upfront. DDL added by each phase.
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
V32_TABLES: Final[tuple[str, ...]] = (
|
|
34
|
+
"fact_access_log",
|
|
35
|
+
"fact_embeddings",
|
|
36
|
+
"embedding_metadata",
|
|
37
|
+
"fact_context",
|
|
38
|
+
"association_edges",
|
|
39
|
+
"activation_cache",
|
|
40
|
+
"fact_importance",
|
|
41
|
+
"fact_temporal_validity",
|
|
42
|
+
"core_memory_blocks",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
# DDL Statements -- EMPTY at Phase 0.5. Each phase appends its DDL.
|
|
47
|
+
# Phase LLDs are AUTHORITATIVE for column names and constraints.
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
V32_DDL: list[str] = [
|
|
51
|
+
# --- Phase 1: Vector Foundation ---
|
|
52
|
+
"""
|
|
53
|
+
CREATE TABLE IF NOT EXISTS fact_access_log (
|
|
54
|
+
log_id TEXT PRIMARY KEY,
|
|
55
|
+
fact_id TEXT NOT NULL,
|
|
56
|
+
profile_id TEXT NOT NULL DEFAULT 'default',
|
|
57
|
+
accessed_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
58
|
+
access_type TEXT NOT NULL DEFAULT 'recall'
|
|
59
|
+
CHECK (access_type IN ('recall', 'auto_invoke', 'search', 'consolidation')),
|
|
60
|
+
session_id TEXT NOT NULL DEFAULT '',
|
|
61
|
+
|
|
62
|
+
FOREIGN KEY (fact_id) REFERENCES atomic_facts (fact_id)
|
|
63
|
+
ON DELETE CASCADE,
|
|
64
|
+
FOREIGN KEY (profile_id) REFERENCES profiles (profile_id)
|
|
65
|
+
ON DELETE CASCADE
|
|
66
|
+
);
|
|
67
|
+
CREATE INDEX IF NOT EXISTS idx_access_log_fact
|
|
68
|
+
ON fact_access_log (fact_id, accessed_at DESC);
|
|
69
|
+
CREATE INDEX IF NOT EXISTS idx_access_log_profile
|
|
70
|
+
ON fact_access_log (profile_id, accessed_at DESC);
|
|
71
|
+
CREATE INDEX IF NOT EXISTS idx_access_log_profile_fact
|
|
72
|
+
ON fact_access_log (profile_id, fact_id);
|
|
73
|
+
""",
|
|
74
|
+
"""
|
|
75
|
+
CREATE TABLE IF NOT EXISTS embedding_metadata (
|
|
76
|
+
vec_rowid INTEGER PRIMARY KEY,
|
|
77
|
+
fact_id TEXT NOT NULL UNIQUE,
|
|
78
|
+
profile_id TEXT NOT NULL DEFAULT 'default',
|
|
79
|
+
model_name TEXT NOT NULL DEFAULT '',
|
|
80
|
+
dimension INTEGER NOT NULL DEFAULT 768,
|
|
81
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
82
|
+
|
|
83
|
+
FOREIGN KEY (fact_id) REFERENCES atomic_facts (fact_id)
|
|
84
|
+
ON DELETE CASCADE,
|
|
85
|
+
FOREIGN KEY (profile_id) REFERENCES profiles (profile_id)
|
|
86
|
+
ON DELETE CASCADE
|
|
87
|
+
);
|
|
88
|
+
CREATE INDEX IF NOT EXISTS idx_embmeta_fact
|
|
89
|
+
ON embedding_metadata (fact_id);
|
|
90
|
+
CREATE INDEX IF NOT EXISTS idx_embmeta_profile
|
|
91
|
+
ON embedding_metadata (profile_id);
|
|
92
|
+
""",
|
|
93
|
+
# --- Phase 2: Auto-Invoke Engine ---
|
|
94
|
+
"""
|
|
95
|
+
CREATE TABLE IF NOT EXISTS fact_context (
|
|
96
|
+
fact_id TEXT PRIMARY KEY,
|
|
97
|
+
profile_id TEXT NOT NULL,
|
|
98
|
+
contextual_description TEXT NOT NULL,
|
|
99
|
+
keywords TEXT,
|
|
100
|
+
generated_by TEXT NOT NULL DEFAULT 'rules',
|
|
101
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
102
|
+
|
|
103
|
+
FOREIGN KEY (fact_id) REFERENCES atomic_facts (fact_id)
|
|
104
|
+
ON DELETE CASCADE,
|
|
105
|
+
FOREIGN KEY (profile_id) REFERENCES profiles (profile_id)
|
|
106
|
+
ON DELETE CASCADE
|
|
107
|
+
);
|
|
108
|
+
CREATE INDEX IF NOT EXISTS idx_fact_context_profile
|
|
109
|
+
ON fact_context (profile_id);
|
|
110
|
+
""",
|
|
111
|
+
# Phase 4 will add: fact_temporal_validity
|
|
112
|
+
# Phase 5 will add: core_memory_blocks
|
|
113
|
+
|
|
114
|
+
# --- Phase 3: Association Graph ---
|
|
115
|
+
"""
|
|
116
|
+
CREATE TABLE IF NOT EXISTS association_edges (
|
|
117
|
+
edge_id TEXT PRIMARY KEY,
|
|
118
|
+
profile_id TEXT NOT NULL,
|
|
119
|
+
source_fact_id TEXT NOT NULL,
|
|
120
|
+
target_fact_id TEXT NOT NULL,
|
|
121
|
+
association_type TEXT NOT NULL CHECK(association_type IN (
|
|
122
|
+
'auto_link', 'hebbian', 'consolidation', 'user_defined'
|
|
123
|
+
)),
|
|
124
|
+
weight REAL NOT NULL DEFAULT 0.5,
|
|
125
|
+
co_access_count INTEGER NOT NULL DEFAULT 0,
|
|
126
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
127
|
+
last_strengthened TEXT,
|
|
128
|
+
FOREIGN KEY (source_fact_id) REFERENCES atomic_facts(fact_id) ON DELETE CASCADE,
|
|
129
|
+
FOREIGN KEY (target_fact_id) REFERENCES atomic_facts(fact_id) ON DELETE CASCADE,
|
|
130
|
+
FOREIGN KEY (profile_id) REFERENCES profiles(profile_id) ON DELETE CASCADE
|
|
131
|
+
);
|
|
132
|
+
CREATE INDEX IF NOT EXISTS idx_assoc_source
|
|
133
|
+
ON association_edges(source_fact_id, profile_id);
|
|
134
|
+
CREATE INDEX IF NOT EXISTS idx_assoc_target
|
|
135
|
+
ON association_edges(target_fact_id, profile_id);
|
|
136
|
+
CREATE INDEX IF NOT EXISTS idx_assoc_profile
|
|
137
|
+
ON association_edges(profile_id);
|
|
138
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_assoc_unique_pair
|
|
139
|
+
ON association_edges(profile_id, source_fact_id, target_fact_id, association_type);
|
|
140
|
+
""",
|
|
141
|
+
"""
|
|
142
|
+
CREATE TABLE IF NOT EXISTS activation_cache (
|
|
143
|
+
cache_id TEXT PRIMARY KEY,
|
|
144
|
+
profile_id TEXT NOT NULL,
|
|
145
|
+
query_hash TEXT NOT NULL,
|
|
146
|
+
node_id TEXT NOT NULL,
|
|
147
|
+
activation_value REAL NOT NULL,
|
|
148
|
+
iteration INTEGER NOT NULL,
|
|
149
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
150
|
+
expires_at TEXT NOT NULL DEFAULT (datetime('now', '+1 hour')),
|
|
151
|
+
FOREIGN KEY (profile_id) REFERENCES profiles(profile_id) ON DELETE CASCADE
|
|
152
|
+
);
|
|
153
|
+
CREATE INDEX IF NOT EXISTS idx_actcache_profile_query
|
|
154
|
+
ON activation_cache(profile_id, query_hash);
|
|
155
|
+
CREATE INDEX IF NOT EXISTS idx_actcache_expires
|
|
156
|
+
ON activation_cache(expires_at);
|
|
157
|
+
""",
|
|
158
|
+
"""
|
|
159
|
+
CREATE TABLE IF NOT EXISTS fact_importance (
|
|
160
|
+
fact_id TEXT PRIMARY KEY,
|
|
161
|
+
profile_id TEXT NOT NULL,
|
|
162
|
+
pagerank_score REAL NOT NULL DEFAULT 0.0,
|
|
163
|
+
community_id INTEGER,
|
|
164
|
+
degree_centrality REAL DEFAULT 0.0,
|
|
165
|
+
computed_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
166
|
+
FOREIGN KEY (fact_id) REFERENCES atomic_facts(fact_id) ON DELETE CASCADE,
|
|
167
|
+
FOREIGN KEY (profile_id) REFERENCES profiles(profile_id) ON DELETE CASCADE
|
|
168
|
+
);
|
|
169
|
+
CREATE INDEX IF NOT EXISTS idx_fact_importance_profile
|
|
170
|
+
ON fact_importance(profile_id);
|
|
171
|
+
CREATE INDEX IF NOT EXISTS idx_fact_importance_pagerank
|
|
172
|
+
ON fact_importance(profile_id, pagerank_score DESC);
|
|
173
|
+
CREATE INDEX IF NOT EXISTS idx_fact_importance_community
|
|
174
|
+
ON fact_importance(profile_id, community_id);
|
|
175
|
+
""",
|
|
176
|
+
# --- Phase 5: Core Memory Blocks (Sleep-Time Consolidation) ---
|
|
177
|
+
"""
|
|
178
|
+
CREATE TABLE IF NOT EXISTS core_memory_blocks (
|
|
179
|
+
block_id TEXT PRIMARY KEY,
|
|
180
|
+
profile_id TEXT NOT NULL,
|
|
181
|
+
block_type TEXT NOT NULL CHECK(block_type IN (
|
|
182
|
+
'user_profile', 'project_context', 'behavioral_patterns',
|
|
183
|
+
'active_decisions', 'learned_preferences', 'custom'
|
|
184
|
+
)),
|
|
185
|
+
content TEXT NOT NULL,
|
|
186
|
+
source_fact_ids TEXT NOT NULL DEFAULT '[]',
|
|
187
|
+
char_count INTEGER NOT NULL DEFAULT 0,
|
|
188
|
+
version INTEGER NOT NULL DEFAULT 1,
|
|
189
|
+
compiled_by TEXT NOT NULL DEFAULT 'rules',
|
|
190
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
191
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
192
|
+
FOREIGN KEY (profile_id) REFERENCES profiles(profile_id) ON DELETE CASCADE
|
|
193
|
+
);
|
|
194
|
+
CREATE INDEX IF NOT EXISTS idx_core_blocks_profile
|
|
195
|
+
ON core_memory_blocks(profile_id);
|
|
196
|
+
CREATE INDEX IF NOT EXISTS idx_core_blocks_type
|
|
197
|
+
ON core_memory_blocks(profile_id, block_type);
|
|
198
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_core_blocks_unique
|
|
199
|
+
ON core_memory_blocks(profile_id, block_type);
|
|
200
|
+
""",
|
|
201
|
+
# --- Phase 4: Temporal Intelligence ---
|
|
202
|
+
"""
|
|
203
|
+
CREATE TABLE IF NOT EXISTS fact_temporal_validity (
|
|
204
|
+
fact_id TEXT PRIMARY KEY,
|
|
205
|
+
profile_id TEXT NOT NULL,
|
|
206
|
+
|
|
207
|
+
-- Event Time (when was this fact true in the real world?)
|
|
208
|
+
valid_from TEXT,
|
|
209
|
+
valid_until TEXT,
|
|
210
|
+
|
|
211
|
+
-- Transaction Time (when did the system learn/invalidate?)
|
|
212
|
+
system_created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
213
|
+
system_expired_at TEXT,
|
|
214
|
+
|
|
215
|
+
-- Invalidation metadata
|
|
216
|
+
invalidated_by TEXT,
|
|
217
|
+
invalidation_reason TEXT,
|
|
218
|
+
|
|
219
|
+
FOREIGN KEY (fact_id) REFERENCES atomic_facts(fact_id) ON DELETE CASCADE,
|
|
220
|
+
FOREIGN KEY (profile_id) REFERENCES profiles(profile_id) ON DELETE CASCADE
|
|
221
|
+
);
|
|
222
|
+
CREATE INDEX IF NOT EXISTS idx_temporal_valid
|
|
223
|
+
ON fact_temporal_validity(profile_id, valid_until);
|
|
224
|
+
CREATE INDEX IF NOT EXISTS idx_temporal_system_expired
|
|
225
|
+
ON fact_temporal_validity(profile_id, system_expired_at);
|
|
226
|
+
CREATE INDEX IF NOT EXISTS idx_temporal_invalidated_by
|
|
227
|
+
ON fact_temporal_validity(invalidated_by);
|
|
228
|
+
""",
|
|
229
|
+
]
|
|
230
|
+
|
|
231
|
+
# vec0 virtual table DDL — executed by VectorStore ONLY (requires extension loaded first).
|
|
232
|
+
# NOT in V32_DDL because executescript cannot load extensions mid-script.
|
|
233
|
+
V32_VEC0_DDL: Final[str] = """
|
|
234
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS fact_embeddings USING vec0(
|
|
235
|
+
profile_id TEXT PARTITION KEY,
|
|
236
|
+
embedding float[768] distance_metric=cosine
|
|
237
|
+
);
|
|
238
|
+
"""
|
|
239
|
+
|
|
240
|
+
# ---------------------------------------------------------------------------
|
|
241
|
+
# Rollback DDL (reverse FK order -- Rule 20)
|
|
242
|
+
# ---------------------------------------------------------------------------
|
|
243
|
+
|
|
244
|
+
V32_ROLLBACK: Final[tuple[str, ...]] = tuple(
|
|
245
|
+
f"DROP TABLE IF EXISTS {table}" for table in reversed(V32_TABLES)
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def rollback_v32(conn) -> None:
|
|
250
|
+
"""Drop all V32 tables in reverse FK order. For testing/rollback only."""
|
|
251
|
+
for sql in V32_ROLLBACK:
|
|
252
|
+
conn.execute(sql)
|
|
@@ -13,6 +13,7 @@ from __future__ import annotations
|
|
|
13
13
|
import logging
|
|
14
14
|
import shutil
|
|
15
15
|
import sqlite3
|
|
16
|
+
import sys
|
|
16
17
|
from datetime import datetime, UTC
|
|
17
18
|
from pathlib import Path
|
|
18
19
|
|
|
@@ -390,8 +391,29 @@ class V2Migrator:
|
|
|
390
391
|
original_backup = self._home / ".claude-memory-v2-original"
|
|
391
392
|
if not original_backup.exists():
|
|
392
393
|
self._v2_base.rename(original_backup)
|
|
393
|
-
|
|
394
|
-
|
|
394
|
+
try:
|
|
395
|
+
if sys.platform == "win32":
|
|
396
|
+
# On Windows, symlinks require admin privileges.
|
|
397
|
+
# Use a directory junction instead (works without elevation).
|
|
398
|
+
import subprocess
|
|
399
|
+
subprocess.run(
|
|
400
|
+
["cmd", "/c", "mklink", "/J",
|
|
401
|
+
str(self._v2_base), str(self._v3_base)],
|
|
402
|
+
check=True, capture_output=True,
|
|
403
|
+
)
|
|
404
|
+
else:
|
|
405
|
+
self._v2_base.symlink_to(self._v3_base)
|
|
406
|
+
stats["steps"].append(
|
|
407
|
+
"Created symlink: .claude-memory -> .superlocalmemory"
|
|
408
|
+
)
|
|
409
|
+
except (OSError, subprocess.CalledProcessError) as exc:
|
|
410
|
+
logger.warning(
|
|
411
|
+
"Could not create symlink/junction: %s. "
|
|
412
|
+
"V2 backward compatibility link skipped.", exc,
|
|
413
|
+
)
|
|
414
|
+
stats["steps"].append(
|
|
415
|
+
f"Symlink skipped (OS error: {exc})"
|
|
416
|
+
)
|
|
395
417
|
else:
|
|
396
418
|
stats["steps"].append("Symlink skipped (backup dir already exists)")
|
|
397
419
|
else:
|