superlocalmemory 3.6.22 → 3.7.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.
- package/CHANGELOG.md +52 -0
- package/README.md +275 -72
- package/bin/slm-npm +43 -89
- package/docs/pi-dev-integration.md +43 -0
- package/ide/configs/antigravity-mcp.json +2 -2
- package/ide/configs/chatgpt-desktop-mcp.json +1 -1
- package/ide/configs/claude-desktop-mcp.json +2 -2
- package/ide/configs/windsurf-mcp.json +2 -2
- package/ide/hooks/context-hook.js +6 -2
- package/ide/hooks/post-recall-hook.js +7 -3
- package/ide/hooks/tool-event-hook.sh +2 -1
- package/package.json +19 -10
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/_GENERATED.md +1 -1
- package/plugin/agents/slm-memory-advisor.md +1 -1
- package/plugin/requirements.txt +1 -1
- package/plugin/skills/slm-session/SKILL.md +1 -1
- package/plugin-src/rules/AGENTS.md +1 -1
- package/pyproject.toml +40 -8
- package/scripts/postinstall-interactive.js +17 -94
- package/scripts/postinstall.js +185 -258
- package/scripts/preuninstall.js +9 -50
- package/src/superlocalmemory/__init__.py +2 -2
- package/src/superlocalmemory/attribution/mathematical_dna.py +1 -1
- package/src/superlocalmemory/attribution/signer.py +34 -19
- package/src/superlocalmemory/attribution/watermark.py +1 -1
- package/src/superlocalmemory/cli/_lazy_init.py +3 -5
- package/src/superlocalmemory/cli/commands.py +490 -195
- package/src/superlocalmemory/cli/context_commands.py +5 -4
- package/src/superlocalmemory/cli/daemon.py +282 -187
- package/src/superlocalmemory/cli/db_migrate.py +3 -1
- package/src/superlocalmemory/cli/diagnostics_cmd.py +28 -0
- package/src/superlocalmemory/cli/evidence_cmd.py +103 -0
- package/src/superlocalmemory/cli/ingest_cmd.py +7 -3
- package/src/superlocalmemory/cli/main.py +128 -31
- package/src/superlocalmemory/cli/pending_store.py +54 -38
- package/src/superlocalmemory/cli/scale_engine_cmd.py +37 -0
- package/src/superlocalmemory/cli/service_installer.py +57 -52
- package/src/superlocalmemory/cli/setup_wizard.py +142 -88
- package/src/superlocalmemory/cli/version_banner.py +2 -1
- package/src/superlocalmemory/code_graph/config.py +3 -1
- package/src/superlocalmemory/core/backend_orchestrator.py +81 -21
- package/src/superlocalmemory/core/config.py +65 -20
- package/src/superlocalmemory/core/consolidation_engine.py +9 -7
- package/src/superlocalmemory/core/context_cache.py +56 -8
- package/src/superlocalmemory/core/derivation_lineage.py +246 -0
- package/src/superlocalmemory/core/embedding_worker.py +32 -20
- package/src/superlocalmemory/core/embeddings.py +54 -18
- package/src/superlocalmemory/core/engine.py +150 -104
- package/src/superlocalmemory/core/engine_ingestion.py +513 -0
- package/src/superlocalmemory/core/engine_wiring.py +2 -0
- package/src/superlocalmemory/core/evidence_bundle.py +526 -0
- package/src/superlocalmemory/core/fact_consolidator.py +5 -11
- package/src/superlocalmemory/core/graph_analyzer.py +2 -2
- package/src/superlocalmemory/core/health_monitor.py +4 -2
- package/src/superlocalmemory/core/ingestion_command.py +636 -0
- package/src/superlocalmemory/core/injection.py +69 -18
- package/src/superlocalmemory/core/lifecycle_state.py +153 -0
- package/src/superlocalmemory/core/maintenance.py +23 -22
- package/src/superlocalmemory/core/maintenance_scheduler.py +51 -35
- package/src/superlocalmemory/core/mutations.py +143 -0
- package/src/superlocalmemory/core/platform_utils.py +7 -4
- package/src/superlocalmemory/core/ram_lock.py +16 -5
- package/src/superlocalmemory/core/rate_limit.py +1 -1
- package/src/superlocalmemory/core/recall_pipeline.py +60 -101
- package/src/superlocalmemory/core/recall_worker.py +76 -59
- package/src/superlocalmemory/core/registry.py +1 -1
- package/src/superlocalmemory/core/scale_engine.py +293 -0
- package/src/superlocalmemory/core/score_contract.py +62 -0
- package/src/superlocalmemory/core/security_primitives.py +3 -1
- package/src/superlocalmemory/core/slm_disabled.py +3 -5
- package/src/superlocalmemory/core/store_pipeline.py +172 -40
- package/src/superlocalmemory/core/tier_manager.py +32 -20
- package/src/superlocalmemory/core/worker_pool.py +13 -4
- package/src/superlocalmemory/dynamics/activation_guided_quantization.py +1 -1
- package/src/superlocalmemory/dynamics/eap_scheduler.py +10 -3
- package/src/superlocalmemory/dynamics/ebbinghaus_langevin_coupling.py +1 -1
- package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +1 -1
- package/src/superlocalmemory/encoding/auto_linker.py +1 -1
- package/src/superlocalmemory/encoding/cognitive_consolidator.py +7 -16
- package/src/superlocalmemory/encoding/consolidator.py +22 -5
- package/src/superlocalmemory/encoding/fact_extractor.py +1 -1
- package/src/superlocalmemory/encoding/foresight.py +2 -0
- package/src/superlocalmemory/encoding/graph_builder.py +1 -1
- package/src/superlocalmemory/encoding/temporal_parser.py +2 -0
- package/src/superlocalmemory/evaluation/__init__.py +13 -0
- package/src/superlocalmemory/evaluation/calibration.py +308 -0
- package/src/superlocalmemory/evolution/skill_evolver.py +2 -1
- package/src/superlocalmemory/graph/cozo_backend.py +256 -23
- package/src/superlocalmemory/hooks/_outcome_common.py +21 -11
- package/src/superlocalmemory/hooks/antigravity_adapter.py +10 -31
- package/src/superlocalmemory/hooks/auto_invoker.py +25 -27
- package/src/superlocalmemory/hooks/auto_recall.py +31 -6
- package/src/superlocalmemory/hooks/auto_recall_hook.py +13 -33
- package/src/superlocalmemory/hooks/before_web_hook.py +9 -7
- package/src/superlocalmemory/hooks/claude_code_hooks.py +126 -39
- package/src/superlocalmemory/hooks/codex_assets.py +59 -0
- package/src/superlocalmemory/hooks/codex_hooks.py +186 -0
- package/src/superlocalmemory/hooks/context_payload.py +1 -1
- package/src/superlocalmemory/hooks/copilot_adapter.py +9 -24
- package/src/superlocalmemory/hooks/cursor_adapter.py +10 -32
- package/src/superlocalmemory/hooks/hook_daemon.py +4 -2
- package/src/superlocalmemory/hooks/hook_handlers.py +241 -55
- package/src/superlocalmemory/hooks/memory_protocol.py +5 -3
- package/src/superlocalmemory/hooks/post_tool_async_hook.py +4 -2
- package/src/superlocalmemory/hooks/session_registry.py +15 -8
- package/src/superlocalmemory/hooks/stop_outcome_hook.py +10 -6
- package/src/superlocalmemory/hooks/topic_shift_hook.py +42 -12
- package/src/superlocalmemory/hooks/user_prompt_hook.py +9 -14
- package/src/superlocalmemory/hooks/user_prompt_rehash_hook.py +19 -11
- package/src/superlocalmemory/infra/auth_middleware.py +38 -5
- package/src/superlocalmemory/infra/backup.py +7 -5
- package/src/superlocalmemory/infra/cloud_backup.py +18 -8
- package/src/superlocalmemory/infra/daemon_identity.py +248 -0
- package/src/superlocalmemory/infra/data_root.py +199 -0
- package/src/superlocalmemory/infra/event_bus.py +3 -1
- package/src/superlocalmemory/infra/local_diagnostics.py +327 -0
- package/src/superlocalmemory/infra/process_reaper.py +23 -0
- package/src/superlocalmemory/ingestion/adapter_manager.py +27 -9
- package/src/superlocalmemory/ingestion/base_adapter.py +25 -31
- package/src/superlocalmemory/ingestion/calendar_adapter.py +13 -4
- package/src/superlocalmemory/ingestion/credentials.py +14 -7
- package/src/superlocalmemory/ingestion/gmail_adapter.py +13 -4
- package/src/superlocalmemory/ingestion/transcript_adapter.py +7 -2
- package/src/superlocalmemory/learning/consolidation_quantization_worker.py +1 -1
- package/src/superlocalmemory/learning/ensemble.py +11 -0
- package/src/superlocalmemory/learning/entity_compiler.py +1 -1
- package/src/superlocalmemory/learning/feedback.py +1 -1
- package/src/superlocalmemory/learning/forgetting_scheduler.py +12 -7
- package/src/superlocalmemory/learning/quantization_scheduler.py +1 -1
- package/src/superlocalmemory/learning/ranker.py +4 -1
- package/src/superlocalmemory/learning/source_quality.py +1 -1
- package/src/superlocalmemory/learning/trigram_index.py +3 -2
- package/src/superlocalmemory/llm/backbone.py +13 -8
- package/src/superlocalmemory/math/ebbinghaus.py +1 -1
- package/src/superlocalmemory/math/fisher.py +1 -1
- package/src/superlocalmemory/math/fisher_quantized.py +1 -1
- package/src/superlocalmemory/math/hopfield.py +1 -1
- package/src/superlocalmemory/math/langevin.py +1 -1
- package/src/superlocalmemory/math/polar_quant.py +3 -4
- package/src/superlocalmemory/math/qjl.py +1 -1
- package/src/superlocalmemory/math/sheaf.py +1 -1
- package/src/superlocalmemory/math/turbo_quant.py +3 -2
- package/src/superlocalmemory/mcp/_daemon_proxy.py +12 -11
- package/src/superlocalmemory/mcp/_pool_adapter.py +27 -0
- package/src/superlocalmemory/mcp/http_transport.py +53 -0
- package/src/superlocalmemory/mcp/server.py +39 -13
- package/src/superlocalmemory/mcp/shared.py +69 -3
- package/src/superlocalmemory/mcp/tools_active.py +141 -31
- package/src/superlocalmemory/mcp/tools_core.py +128 -29
- package/src/superlocalmemory/mcp/tools_evolution.py +5 -7
- package/src/superlocalmemory/mcp/tools_learning.py +42 -2
- package/src/superlocalmemory/mcp/tools_mesh.py +7 -23
- package/src/superlocalmemory/mcp/tools_optimize.py +8 -1
- package/src/superlocalmemory/mcp/tools_v28.py +23 -2
- package/src/superlocalmemory/mcp/tools_v3.py +26 -1
- package/src/superlocalmemory/mcp/tools_v33.py +56 -17
- package/src/superlocalmemory/mesh/broker.py +2 -0
- package/src/superlocalmemory/mesh/remote_sync.py +50 -12
- package/src/superlocalmemory/optimize/cache/manager.py +77 -1
- package/src/superlocalmemory/optimize/cache/semantic.py +23 -3
- package/src/superlocalmemory/optimize/compress/ccr.py +4 -0
- package/src/superlocalmemory/optimize/compress/router.py +6 -1
- package/src/superlocalmemory/optimize/config/__init__.py +5 -0
- package/src/superlocalmemory/optimize/config/store.py +6 -4
- package/src/superlocalmemory/optimize/proxy/_helpers.py +15 -5
- package/src/superlocalmemory/optimize/proxy/capture.py +3 -2
- package/src/superlocalmemory/optimize/proxy/server.py +2 -2
- package/src/superlocalmemory/optimize/storage/db.py +14 -13
- package/src/superlocalmemory/retrieval/agentic.py +1 -1
- package/src/superlocalmemory/retrieval/ann_index.py +1 -1
- package/src/superlocalmemory/retrieval/bm25_channel.py +35 -11
- package/src/superlocalmemory/retrieval/bridge_discovery.py +73 -8
- package/src/superlocalmemory/retrieval/engine.py +169 -79
- package/src/superlocalmemory/retrieval/entity_channel.py +289 -67
- package/src/superlocalmemory/retrieval/forgetting_filter.py +1 -1
- package/src/superlocalmemory/retrieval/fusion.py +1 -1
- package/src/superlocalmemory/retrieval/hopfield_channel.py +118 -30
- package/src/superlocalmemory/retrieval/profile_channel.py +1 -1
- package/src/superlocalmemory/retrieval/quantization_aware_search.py +16 -10
- package/src/superlocalmemory/retrieval/reranker.py +56 -20
- package/src/superlocalmemory/retrieval/scope_policy.py +85 -0
- package/src/superlocalmemory/retrieval/semantic_channel.py +122 -14
- package/src/superlocalmemory/retrieval/spreading_activation.py +141 -25
- package/src/superlocalmemory/retrieval/strategy.py +1 -1
- package/src/superlocalmemory/retrieval/temporal_channel.py +30 -15
- package/src/superlocalmemory/retrieval/vector_store.py +1 -1
- package/src/superlocalmemory/server/api.py +10 -7
- package/src/superlocalmemory/server/bandit_loops.py +4 -2
- package/src/superlocalmemory/server/recall_serializer.py +24 -0
- package/src/superlocalmemory/server/route_mutations.py +84 -0
- package/src/superlocalmemory/server/routes/agents.py +8 -6
- package/src/superlocalmemory/server/routes/brain.py +14 -12
- package/src/superlocalmemory/server/routes/chat.py +29 -12
- package/src/superlocalmemory/server/routes/data_io.py +55 -24
- package/src/superlocalmemory/server/routes/helpers.py +29 -4
- package/src/superlocalmemory/server/routes/ingest.py +53 -36
- package/src/superlocalmemory/server/routes/memories.py +104 -43
- package/src/superlocalmemory/server/routes/mesh.py +31 -0
- package/src/superlocalmemory/server/routes/profiles.py +26 -4
- package/src/superlocalmemory/server/routes/tiers.py +43 -11
- package/src/superlocalmemory/server/routes/timeline.py +5 -1
- package/src/superlocalmemory/server/routes/v3_api.py +76 -21
- package/src/superlocalmemory/server/security_middleware.py +1 -1
- package/src/superlocalmemory/server/ui.py +6 -3
- package/src/superlocalmemory/server/unified_daemon.py +680 -293
- package/src/superlocalmemory/server/write_identity.py +147 -0
- package/src/superlocalmemory/storage/access_log.py +4 -3
- package/src/superlocalmemory/storage/database.py +118 -25
- package/src/superlocalmemory/storage/migration_runner.py +84 -1
- package/src/superlocalmemory/storage/migration_v33.py +1 -1
- package/src/superlocalmemory/storage/migrations/M002_model_state_history.py +6 -60
- package/src/superlocalmemory/storage/migrations/M018_ingestion_operations.py +120 -0
- package/src/superlocalmemory/storage/migrations/M019_derivation_lineage.py +54 -0
- package/src/superlocalmemory/storage/migrations/M020_model_state_integrity.py +52 -0
- package/src/superlocalmemory/storage/migrations/__init__.py +5 -0
- package/src/superlocalmemory/storage/models.py +16 -0
- package/src/superlocalmemory/storage/quantized_store.py +20 -3
- package/src/superlocalmemory/storage/v2_migrator.py +5 -3
- package/src/superlocalmemory/ui/favicon.svg +5 -0
- package/src/superlocalmemory/ui/index.html +1 -0
- package/src/superlocalmemory/ui/js/compliance.js +1 -1
- package/src/superlocalmemory/ui/js/core.js +49 -8
- package/src/superlocalmemory/ui/js/dashboard.js +23 -2
- package/src/superlocalmemory/ui/js/feedback.js +1 -1
- package/src/superlocalmemory/ui/js/graph-filters.js +1 -1
- package/src/superlocalmemory/ui/js/graph-ui.js +1 -1
- package/src/superlocalmemory/ui/js/lifecycle.js +1 -1
- package/src/superlocalmemory/ui/js/ng-mesh.js +15 -49
- package/src/superlocalmemory/ui/js/settings.js +4 -2
- package/src/superlocalmemory/vector/lancedb_backend.py +57 -9
- package/bin/slm +0 -59
- package/bin/slm.bat +0 -77
- package/bin/slm.cmd +0 -5
- package/ide/integrations/langchain/README.md +0 -106
- package/ide/integrations/langchain/langchain_superlocalmemory/__init__.py +0 -9
- package/ide/integrations/langchain/langchain_superlocalmemory/chat_message_history.py +0 -201
- package/ide/integrations/langchain/pyproject.toml +0 -38
- package/ide/integrations/langchain/tests/__init__.py +0 -3
- package/ide/integrations/langchain/tests/test_chat_message_history.py +0 -215
- package/ide/integrations/langchain/tests/test_security.py +0 -117
- package/ide/integrations/llamaindex/README.md +0 -81
- package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/__init__.py +0 -9
- package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/base.py +0 -316
- package/ide/integrations/llamaindex/pyproject.toml +0 -43
- package/ide/integrations/llamaindex/tests/__init__.py +0 -3
- package/ide/integrations/llamaindex/tests/test_chat_store.py +0 -294
- package/ide/integrations/llamaindex/tests/test_security.py +0 -241
- package/plugin-src/.mcp.json +0 -12
- package/plugin-src/agents/slm-memory-advisor.md +0 -44
- package/plugin-src/agents/slm-optimize-advisor.md +0 -38
- package/plugin-src/hooks/.gitkeep +0 -0
- package/plugin-src/hooks/hooks.json +0 -23
- package/plugin-src/manifest.json +0 -25
- 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 -15
- package/plugin-src/scripts/slm-launch.bat +0 -17
- package/plugin-src/settings.json +0 -16
- package/plugin-src/skills/slm-cache/SKILL.md +0 -140
- package/plugin-src/skills/slm-compress/SKILL.md +0 -143
- package/plugin-src/skills/slm-graph/SKILL.md +0 -300
- package/plugin-src/skills/slm-recall/SKILL.md +0 -204
- package/plugin-src/skills/slm-remember/SKILL.md +0 -194
- package/plugin-src/skills/slm-session/SKILL.md +0 -207
- package/plugin-src/skills/slm-status/SKILL.md +0 -149
- package/scripts/__tests__/build-plugin.test.mjs +0 -613
- package/scripts/_savings_math.py +0 -270
- package/scripts/build-dmg.sh +0 -417
- package/scripts/build-plugin.js +0 -742
- package/scripts/build-slm-hook.ps1 +0 -40
- package/scripts/build-slm-hook.sh +0 -45
- package/scripts/build_entry.py +0 -452
- package/scripts/ci/stage5b_gate.sh +0 -50
- package/scripts/dogfood_savings.py +0 -490
- package/scripts/generate-thumbnails.py +0 -218
- package/scripts/install-skills.ps1 +0 -4
- package/scripts/install-skills.sh +0 -5
- package/scripts/install.ps1 +0 -701
- package/scripts/install.sh +0 -1015
- package/scripts/postinstall_binary.js +0 -287
- package/scripts/prepack.js +0 -33
- package/scripts/release_manifest.py +0 -273
- package/scripts/slm-hook.spec +0 -56
- package/scripts/start-dashboard.ps1 +0 -52
- package/scripts/start-dashboard.sh +0 -41
- package/scripts/sync-wiki.ps1 +0 -127
- package/scripts/sync-wiki.sh +0 -82
- package/scripts/test-dmg.sh +0 -161
- package/scripts/test-npm-package.ps1 +0 -252
- package/scripts/test-npm-package.sh +0 -207
- package/scripts/verify-install.ps1 +0 -294
- package/scripts/verify-install.sh +0 -266
- package/scripts/verify-v27.ps1 +0 -301
- package/scripts/verify-v27.sh +0 -233
- package/src/superlocalmemory.egg-info/PKG-INFO +0 -513
- package/src/superlocalmemory.egg-info/SOURCES.txt +0 -529
- package/src/superlocalmemory.egg-info/dependency_links.txt +0 -1
- package/src/superlocalmemory.egg-info/entry_points.txt +0 -2
- package/src/superlocalmemory.egg-info/requires.txt +0 -71
- package/src/superlocalmemory.egg-info/top_level.txt +0 -1
|
@@ -79,7 +79,16 @@ class LanceDBVectorBackend:
|
|
|
79
79
|
return self._db.create_table("embeddings", schema=schema)
|
|
80
80
|
|
|
81
81
|
def close(self) -> None:
|
|
82
|
-
"""
|
|
82
|
+
"""Release this backend's native table and connection references."""
|
|
83
|
+
for resource in (self._table, self._db):
|
|
84
|
+
close = getattr(resource, "close", None)
|
|
85
|
+
if callable(close):
|
|
86
|
+
try:
|
|
87
|
+
close()
|
|
88
|
+
except Exception:
|
|
89
|
+
logger.debug("LanceDB resource close failed", exc_info=True)
|
|
90
|
+
self._table = None
|
|
91
|
+
self._db = None
|
|
83
92
|
|
|
84
93
|
# ------------------------------------------------------------------
|
|
85
94
|
# Write Path
|
|
@@ -92,16 +101,33 @@ class LanceDBVectorBackend:
|
|
|
92
101
|
tiers: list[str],
|
|
93
102
|
profile_id: str = "default",
|
|
94
103
|
) -> int:
|
|
95
|
-
"""
|
|
104
|
+
"""Idempotently insert or replace vectors by canonical fact ID."""
|
|
96
105
|
if not fact_ids:
|
|
97
106
|
return 0
|
|
98
107
|
data = [
|
|
99
108
|
{"fact_id": fid, "vector": emb, "tier": tier, "profile_id": profile_id}
|
|
100
109
|
for fid, emb, tier in zip(fact_ids, embeddings, tiers)
|
|
101
110
|
]
|
|
102
|
-
|
|
111
|
+
# `add()` permits duplicate fact IDs on retries. Store/retry paths are
|
|
112
|
+
# at-least-once by design, so use LanceDB's merge operation to keep the
|
|
113
|
+
# projection one-row-per-fact and safe to replay after a restart.
|
|
114
|
+
(
|
|
115
|
+
self._table.merge_insert("fact_id")
|
|
116
|
+
.when_matched_update_all()
|
|
117
|
+
.when_not_matched_insert_all()
|
|
118
|
+
.execute(data)
|
|
119
|
+
)
|
|
103
120
|
return len(data)
|
|
104
121
|
|
|
122
|
+
@staticmethod
|
|
123
|
+
def _fact_predicate(fact_id: str) -> str:
|
|
124
|
+
"""Build a Lance SQL literal without allowing predicate injection."""
|
|
125
|
+
return "fact_id = '" + fact_id.replace("'", "''") + "'"
|
|
126
|
+
|
|
127
|
+
def remove_vector(self, fact_id: str) -> None:
|
|
128
|
+
"""Delete one derived vector after its canonical fact is deleted."""
|
|
129
|
+
self._table.delete(self._fact_predicate(fact_id))
|
|
130
|
+
|
|
105
131
|
# ------------------------------------------------------------------
|
|
106
132
|
# Read Path
|
|
107
133
|
# ------------------------------------------------------------------
|
|
@@ -111,6 +137,7 @@ class LanceDBVectorBackend:
|
|
|
111
137
|
query_vector: list[float],
|
|
112
138
|
top_k: int = 50,
|
|
113
139
|
tier_filter: list[str] | None = None,
|
|
140
|
+
profile_id: str = "default",
|
|
114
141
|
) -> list[tuple[str, float]]:
|
|
115
142
|
"""ANN search with optional tier filter.
|
|
116
143
|
|
|
@@ -131,7 +158,10 @@ class LanceDBVectorBackend:
|
|
|
131
158
|
|
|
132
159
|
# Build tier filter string for LanceDB SQL-like where clause
|
|
133
160
|
tier_str = ", ".join(f"'{t}'" for t in tier_filter)
|
|
134
|
-
|
|
161
|
+
profile_literal = profile_id.replace("'", "''")
|
|
162
|
+
results = search.where(
|
|
163
|
+
f"tier IN ({tier_str}) AND profile_id = '{profile_literal}'"
|
|
164
|
+
).to_list()
|
|
135
165
|
|
|
136
166
|
# Convert distance → similarity (F-08)
|
|
137
167
|
return [(r["fact_id"], 1.0 - r["_distance"]) for r in results]
|
|
@@ -143,7 +173,9 @@ class LanceDBVectorBackend:
|
|
|
143
173
|
# Bulk Import (sqlite-vec → LanceDB)
|
|
144
174
|
# ------------------------------------------------------------------
|
|
145
175
|
|
|
146
|
-
def bulk_import_from_sqlite(
|
|
176
|
+
def bulk_import_from_sqlite(
|
|
177
|
+
self, conn: sqlite3.Connection, profile_id: str = "default",
|
|
178
|
+
) -> int:
|
|
147
179
|
"""Export embeddings from sqlite-vec → LanceDB.
|
|
148
180
|
|
|
149
181
|
sqlite-vec stores vectors as raw float32 little-endian blobs
|
|
@@ -163,11 +195,15 @@ class LanceDBVectorBackend:
|
|
|
163
195
|
|
|
164
196
|
# Get tiers
|
|
165
197
|
tier_map: dict[str, str] = {}
|
|
198
|
+
profile_map: dict[str, str] = {}
|
|
166
199
|
try:
|
|
167
200
|
for row in conn.execute(
|
|
168
|
-
"SELECT fact_id, COALESCE(lifecycle, 'active')
|
|
201
|
+
"SELECT fact_id, COALESCE(lifecycle, 'active'), profile_id "
|
|
202
|
+
"FROM atomic_facts WHERE profile_id = ?",
|
|
203
|
+
(profile_id,),
|
|
169
204
|
):
|
|
170
205
|
tier_map[row[0]] = row[1]
|
|
206
|
+
profile_map[row[0]] = row[2] or "default"
|
|
171
207
|
except sqlite3.OperationalError:
|
|
172
208
|
pass
|
|
173
209
|
|
|
@@ -184,7 +220,10 @@ class LanceDBVectorBackend:
|
|
|
184
220
|
data = []
|
|
185
221
|
for rowid, blob in rows:
|
|
186
222
|
fact_id = row_map.get(rowid)
|
|
187
|
-
|
|
223
|
+
# The rowid mapping is global, but a staged Scale Engine is
|
|
224
|
+
# explicitly profile-scoped. Do not import a foreign profile by
|
|
225
|
+
# giving it a default tier/profile below.
|
|
226
|
+
if fact_id is None or fact_id not in profile_map:
|
|
188
227
|
continue
|
|
189
228
|
try:
|
|
190
229
|
vector = self._decode_vector_blob(blob)
|
|
@@ -196,11 +235,20 @@ class LanceDBVectorBackend:
|
|
|
196
235
|
"fact_id": fact_id,
|
|
197
236
|
"vector": vector,
|
|
198
237
|
"tier": tier,
|
|
199
|
-
"profile_id":
|
|
238
|
+
"profile_id": profile_map.get(fact_id, profile_id),
|
|
200
239
|
})
|
|
201
240
|
|
|
202
241
|
if data:
|
|
203
|
-
|
|
242
|
+
by_profile: dict[str, list[dict[str, Any]]] = {}
|
|
243
|
+
for item in data:
|
|
244
|
+
by_profile.setdefault(item["profile_id"], []).append(item)
|
|
245
|
+
for record_profile_id, records in by_profile.items():
|
|
246
|
+
self.add_vectors(
|
|
247
|
+
[item["fact_id"] for item in records],
|
|
248
|
+
[item["vector"] for item in records],
|
|
249
|
+
[item["tier"] for item in records],
|
|
250
|
+
record_profile_id,
|
|
251
|
+
)
|
|
204
252
|
|
|
205
253
|
logger.info("LanceDB: imported %d vectors from sqlite-vec", len(data))
|
|
206
254
|
return len(data)
|
package/bin/slm
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# SuperLocalMemory V3 CLI
|
|
3
|
-
# Part of Qualixar | https://superlocalmemory.com
|
|
4
|
-
|
|
5
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
-
PKG_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
7
|
-
SRC_DIR="$PKG_ROOT/src"
|
|
8
|
-
|
|
9
|
-
# Handle --version / -v directly (fast path)
|
|
10
|
-
for arg in "$@"; do
|
|
11
|
-
if [ "$arg" = "--version" ] || [ "$arg" = "-v" ]; then
|
|
12
|
-
VER=$(grep '"version"' "$PKG_ROOT/package.json" 2>/dev/null | head -1 | sed 's/.*"\([0-9][0-9.]*\)".*/\1/')
|
|
13
|
-
echo "superlocalmemory ${VER:-unknown}"
|
|
14
|
-
exit 0
|
|
15
|
-
fi
|
|
16
|
-
done
|
|
17
|
-
|
|
18
|
-
# LLD-06 §6.3 — prefer the PyInstaller-built binary on the hot hook path.
|
|
19
|
-
# If the onedir binary exists and isn't disabled, run it directly; the
|
|
20
|
-
# binary is stdlib-only and ~30ms cold on Windows. Otherwise fall back
|
|
21
|
-
# to the Python CLI below.
|
|
22
|
-
SLM_HOOK_BIN="${SLM_HOOK_BINARY:-$HOME/.superlocalmemory/bin/slm-hook/slm-hook}"
|
|
23
|
-
if [ "${1:-}" = "hook" ] && [ "${2:-}" = "user_prompt_submit" ] \
|
|
24
|
-
&& [ -x "$SLM_HOOK_BIN" ] \
|
|
25
|
-
&& [ "${SLM_HOOK_BINARY_DISABLED:-0}" != "1" ]; then
|
|
26
|
-
exec "$SLM_HOOK_BIN"
|
|
27
|
-
fi
|
|
28
|
-
|
|
29
|
-
# Find Python
|
|
30
|
-
PYTHON=""
|
|
31
|
-
for cmd in python3 python; do
|
|
32
|
-
if command -v "$cmd" &>/dev/null; then
|
|
33
|
-
PYTHON="$cmd"
|
|
34
|
-
break
|
|
35
|
-
fi
|
|
36
|
-
done
|
|
37
|
-
|
|
38
|
-
if [ -z "$PYTHON" ]; then
|
|
39
|
-
echo "Error: Python 3 not found. Install Python 3.11+ from python.org"
|
|
40
|
-
exit 1
|
|
41
|
-
fi
|
|
42
|
-
|
|
43
|
-
# Set PYTHONPATH so Python finds the npm package's src/ directory
|
|
44
|
-
if [ -n "$PYTHONPATH" ]; then
|
|
45
|
-
export PYTHONPATH="$SRC_DIR:$PYTHONPATH"
|
|
46
|
-
else
|
|
47
|
-
export PYTHONPATH="$SRC_DIR"
|
|
48
|
-
fi
|
|
49
|
-
|
|
50
|
-
# Prevent PyTorch Metal/MPS GPU memory reservation
|
|
51
|
-
export PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.0
|
|
52
|
-
export PYTORCH_MPS_MEM_LIMIT=0
|
|
53
|
-
export PYTORCH_ENABLE_MPS_FALLBACK=1
|
|
54
|
-
export TOKENIZERS_PARALLELISM=false
|
|
55
|
-
export TORCH_DEVICE=cpu
|
|
56
|
-
export CUDA_VISIBLE_DEVICES=""
|
|
57
|
-
|
|
58
|
-
# Run V3 CLI
|
|
59
|
-
exec "$PYTHON" -m superlocalmemory.cli.main "$@"
|
package/bin/slm.bat
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
@echo off
|
|
2
|
-
REM SuperLocalMemory V3 - Windows CLI Wrapper
|
|
3
|
-
REM Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
4
|
-
REM Licensed under MIT License
|
|
5
|
-
REM Repository: https://github.com/qualixar/superlocalmemory
|
|
6
|
-
|
|
7
|
-
setlocal enabledelayedexpansion
|
|
8
|
-
|
|
9
|
-
REM Resolve the package src/ directory for PYTHONPATH
|
|
10
|
-
set "SLM_PKG_DIR=%~dp0..\src"
|
|
11
|
-
|
|
12
|
-
REM Handle --version / -v directly (fast path, no Python needed)
|
|
13
|
-
if "%~1"=="--version" goto :show_version
|
|
14
|
-
if "%~1"=="-v" goto :show_version
|
|
15
|
-
|
|
16
|
-
REM LLD-06 §6.3 — prefer the PyInstaller-built binary on the hot hook path.
|
|
17
|
-
set "SLM_HOOK_BIN=%USERPROFILE%\.superlocalmemory\bin\slm-hook\slm-hook.exe"
|
|
18
|
-
if defined SLM_HOOK_BINARY set "SLM_HOOK_BIN=%SLM_HOOK_BINARY%"
|
|
19
|
-
if "%~1"=="hook" if "%~2"=="user_prompt_submit" (
|
|
20
|
-
if exist "%SLM_HOOK_BIN%" (
|
|
21
|
-
if not "%SLM_HOOK_BINARY_DISABLED%"=="1" (
|
|
22
|
-
"%SLM_HOOK_BIN%"
|
|
23
|
-
exit /b %ERRORLEVEL%
|
|
24
|
-
)
|
|
25
|
-
)
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
REM Find Python 3
|
|
29
|
-
where python3 >nul 2>&1
|
|
30
|
-
if %ERRORLEVEL% EQU 0 (
|
|
31
|
-
set PYTHON_CMD=python3
|
|
32
|
-
goto :run
|
|
33
|
-
)
|
|
34
|
-
where python >nul 2>&1
|
|
35
|
-
if %ERRORLEVEL% EQU 0 (
|
|
36
|
-
set PYTHON_CMD=python
|
|
37
|
-
goto :run
|
|
38
|
-
)
|
|
39
|
-
where py >nul 2>&1
|
|
40
|
-
if %ERRORLEVEL% EQU 0 (
|
|
41
|
-
set PYTHON_CMD=py -3
|
|
42
|
-
goto :run
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
echo Error: Python 3.11+ not found.
|
|
46
|
-
echo Install from: https://python.org/downloads/
|
|
47
|
-
exit /b 1
|
|
48
|
-
|
|
49
|
-
:show_version
|
|
50
|
-
REM Read version from package.json via findstr
|
|
51
|
-
for /f "tokens=2 delims=:," %%a in ('findstr /C:"\"version\"" "%~dp0..\package.json"') do (
|
|
52
|
-
set "VER=%%~a"
|
|
53
|
-
set "VER=!VER: =!"
|
|
54
|
-
echo superlocalmemory !VER!
|
|
55
|
-
exit /b 0
|
|
56
|
-
)
|
|
57
|
-
echo superlocalmemory unknown
|
|
58
|
-
exit /b 0
|
|
59
|
-
|
|
60
|
-
:run
|
|
61
|
-
REM Set PYTHONPATH so Python finds the npm package's src/ directory
|
|
62
|
-
if defined PYTHONPATH (
|
|
63
|
-
set "PYTHONPATH=%SLM_PKG_DIR%;%PYTHONPATH%"
|
|
64
|
-
) else (
|
|
65
|
-
set "PYTHONPATH=%SLM_PKG_DIR%"
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
REM Prevent PyTorch Metal/MPS GPU memory reservation
|
|
69
|
-
set "PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.0"
|
|
70
|
-
set "PYTORCH_MPS_MEM_LIMIT=0"
|
|
71
|
-
set "PYTORCH_ENABLE_MPS_FALLBACK=1"
|
|
72
|
-
set "TOKENIZERS_PARALLELISM=false"
|
|
73
|
-
set "TORCH_DEVICE=cpu"
|
|
74
|
-
set "CUDA_VISIBLE_DEVICES="
|
|
75
|
-
|
|
76
|
-
%PYTHON_CMD% -m superlocalmemory.cli.main %*
|
|
77
|
-
exit /b %ERRORLEVEL%
|
package/bin/slm.cmd
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
# langchain-superlocalmemory
|
|
2
|
-
|
|
3
|
-
LangChain chat message history backed by [SuperLocalMemory V2](https://github.com/qualixar/superlocalmemory) -- 100% local, zero cloud.
|
|
4
|
-
|
|
5
|
-
Every message stays on your machine in a SQLite database. No API keys, no subscriptions, no telemetry.
|
|
6
|
-
|
|
7
|
-
## Prerequisites
|
|
8
|
-
|
|
9
|
-
- Python 3.10+
|
|
10
|
-
- [SuperLocalMemory V2](https://github.com/qualixar/superlocalmemory) installed (`~/.superlocalmemory/` must exist)
|
|
11
|
-
- `langchain-core >= 1.0.0`
|
|
12
|
-
|
|
13
|
-
## Installation
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
pip install langchain-superlocalmemory
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Or install from source:
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
cd integrations/langchain
|
|
23
|
-
pip install -e .
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Quick Start
|
|
27
|
-
|
|
28
|
-
```python
|
|
29
|
-
from langchain_core.messages import AIMessage, HumanMessage
|
|
30
|
-
from langchain_superlocalmemory import SuperLocalMemoryChatMessageHistory
|
|
31
|
-
|
|
32
|
-
# Create a history for a conversation session
|
|
33
|
-
history = SuperLocalMemoryChatMessageHistory(session_id="my-chat-session")
|
|
34
|
-
|
|
35
|
-
# Add messages
|
|
36
|
-
history.add_messages([
|
|
37
|
-
HumanMessage(content="What is SuperLocalMemory?"),
|
|
38
|
-
AIMessage(content="It's a local-first memory system for AI assistants."),
|
|
39
|
-
])
|
|
40
|
-
|
|
41
|
-
# Retrieve messages (chronological order)
|
|
42
|
-
for msg in history.messages:
|
|
43
|
-
print(f"{msg.type}: {msg.content}")
|
|
44
|
-
|
|
45
|
-
# Clear the session
|
|
46
|
-
history.clear()
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Features
|
|
50
|
-
|
|
51
|
-
- **Local-first storage** -- all data stays in `~/.superlocalmemory/memory.db`
|
|
52
|
-
- **Session isolation** -- each `session_id` is completely independent
|
|
53
|
-
- **Full LangChain compatibility** -- implements `BaseChatMessageHistory`
|
|
54
|
-
- **Persistent across restarts** -- SQLite-backed, survives process exit
|
|
55
|
-
- **Works alongside SLM** -- messages are queryable via CLI, MCP, Skills, and REST API
|
|
56
|
-
- **All message types** -- HumanMessage, AIMessage, SystemMessage, FunctionMessage, ToolMessage
|
|
57
|
-
- **additional_kwargs preserved** -- metadata round-trips through serialization
|
|
58
|
-
|
|
59
|
-
## Multi-Session Example
|
|
60
|
-
|
|
61
|
-
```python
|
|
62
|
-
from langchain_superlocalmemory import SuperLocalMemoryChatMessageHistory
|
|
63
|
-
|
|
64
|
-
# Two independent conversations
|
|
65
|
-
support = SuperLocalMemoryChatMessageHistory(session_id="support-ticket-42")
|
|
66
|
-
coding = SuperLocalMemoryChatMessageHistory(session_id="code-review-pr-99")
|
|
67
|
-
|
|
68
|
-
# Messages are isolated -- support session cannot see coding session
|
|
69
|
-
support.add_messages([HumanMessage(content="My app is crashing")])
|
|
70
|
-
coding.add_messages([HumanMessage(content="Review this PR please")])
|
|
71
|
-
|
|
72
|
-
assert len(support.messages) == 1
|
|
73
|
-
assert len(coding.messages) == 1
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## Custom Database Path
|
|
77
|
-
|
|
78
|
-
By default the package uses `~/.superlocalmemory/memory.db`. You can point to a different database:
|
|
79
|
-
|
|
80
|
-
```python
|
|
81
|
-
history = SuperLocalMemoryChatMessageHistory(
|
|
82
|
-
session_id="my-session",
|
|
83
|
-
db_path="/path/to/custom/memory.db",
|
|
84
|
-
)
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
## How It Works
|
|
88
|
-
|
|
89
|
-
Each LangChain message is stored as an individual memory entry in SuperLocalMemory V2:
|
|
90
|
-
|
|
91
|
-
- **Content**: JSON-serialized message (type, content, additional_kwargs)
|
|
92
|
-
- **Tags**: `["langchain", "langchain:session:<session_id>"]`
|
|
93
|
-
- **Importance**: 3 (lower than user memories, so chat history does not crowd search results)
|
|
94
|
-
- **Project**: `"langchain"`
|
|
95
|
-
|
|
96
|
-
This means your LangChain conversations are visible in the SLM dashboard, searchable via `slm recall`, and accessible from any SLM-integrated tool.
|
|
97
|
-
|
|
98
|
-
## License
|
|
99
|
-
|
|
100
|
-
AGPL-3.0 -- see [LICENSE](../../LICENSE) for details.
|
|
101
|
-
|
|
102
|
-
## Links
|
|
103
|
-
|
|
104
|
-
- [SuperLocalMemory V2 Repository](https://github.com/qualixar/superlocalmemory)
|
|
105
|
-
- [Documentation](https://superlocalmemory.com/)
|
|
106
|
-
- [LangChain Documentation](https://python.langchain.com/)
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
|
-
# Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
|
|
4
|
-
from langchain_superlocalmemory.chat_message_history import (
|
|
5
|
-
SuperLocalMemoryChatMessageHistory,
|
|
6
|
-
)
|
|
7
|
-
|
|
8
|
-
__all__ = ["SuperLocalMemoryChatMessageHistory"]
|
|
9
|
-
__version__ = "0.1.0"
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
|
-
# Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
|
|
4
|
-
"""SuperLocalMemory V2 - LangChain Chat Message History
|
|
5
|
-
|
|
6
|
-
Implements LangChain's BaseChatMessageHistory backed by SuperLocalMemory V2's
|
|
7
|
-
local SQLite storage. All data stays on your machine -- zero cloud, zero telemetry.
|
|
8
|
-
|
|
9
|
-
Usage:
|
|
10
|
-
from langchain_superlocalmemory import SuperLocalMemoryChatMessageHistory
|
|
11
|
-
|
|
12
|
-
history = SuperLocalMemoryChatMessageHistory(session_id="my-session")
|
|
13
|
-
history.add_messages([HumanMessage(content="Hello")])
|
|
14
|
-
print(history.messages)
|
|
15
|
-
"""
|
|
16
|
-
import json
|
|
17
|
-
import sys
|
|
18
|
-
from pathlib import Path
|
|
19
|
-
from typing import List, Optional, Sequence
|
|
20
|
-
|
|
21
|
-
from langchain_core.chat_history import BaseChatMessageHistory
|
|
22
|
-
from langchain_core.messages import (
|
|
23
|
-
AIMessage,
|
|
24
|
-
BaseMessage,
|
|
25
|
-
FunctionMessage,
|
|
26
|
-
HumanMessage,
|
|
27
|
-
SystemMessage,
|
|
28
|
-
ToolMessage,
|
|
29
|
-
message_to_dict,
|
|
30
|
-
messages_from_dict,
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
# ---------------------------------------------------------------------------
|
|
34
|
-
# MemoryStoreV2 import strategy
|
|
35
|
-
# ---------------------------------------------------------------------------
|
|
36
|
-
# SuperLocalMemory V2 installs to ~/.superlocalmemory/. We add that path so
|
|
37
|
-
# the MemoryStoreV2 class can be imported. If SLM is not installed, we
|
|
38
|
-
# raise a clear error at construction time (not import time) so the package
|
|
39
|
-
# itself can still be imported for introspection.
|
|
40
|
-
# ---------------------------------------------------------------------------
|
|
41
|
-
|
|
42
|
-
_SLM_PATH = Path.home() / ".superlocalmemory"
|
|
43
|
-
_MemoryStoreV2 = None
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def _ensure_slm_imported():
|
|
47
|
-
"""Lazily import MemoryStoreV2, raising a clear error if unavailable."""
|
|
48
|
-
global _MemoryStoreV2
|
|
49
|
-
if _MemoryStoreV2 is not None:
|
|
50
|
-
return _MemoryStoreV2
|
|
51
|
-
|
|
52
|
-
slm_path_str = str(_SLM_PATH)
|
|
53
|
-
if slm_path_str not in sys.path:
|
|
54
|
-
sys.path.insert(0, slm_path_str)
|
|
55
|
-
|
|
56
|
-
try:
|
|
57
|
-
from superlocalmemory.core.engine import MemoryEngine # type: ignore[import-untyped]
|
|
58
|
-
|
|
59
|
-
_MemoryStoreV2 = MemoryStoreV2
|
|
60
|
-
return _MemoryStoreV2
|
|
61
|
-
except ImportError as exc:
|
|
62
|
-
raise ImportError(
|
|
63
|
-
"SuperLocalMemory V2 is not installed. "
|
|
64
|
-
"Run the installer from https://github.com/qualixar/superlocalmemory "
|
|
65
|
-
"or ensure ~/.superlocalmemory/is installed via npm/pip."
|
|
66
|
-
) from exc
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
# ---------------------------------------------------------------------------
|
|
70
|
-
# Message (de)serialization helpers
|
|
71
|
-
# ---------------------------------------------------------------------------
|
|
72
|
-
|
|
73
|
-
# Map from LangChain message type string to the concrete class used for
|
|
74
|
-
# deserialization. LangChain's own `messages_from_dict` handles this, but we
|
|
75
|
-
# keep a lookup for the fallback path in case the dict format diverges.
|
|
76
|
-
|
|
77
|
-
_MESSAGE_TYPE_MAP = {
|
|
78
|
-
"human": HumanMessage,
|
|
79
|
-
"ai": AIMessage,
|
|
80
|
-
"system": SystemMessage,
|
|
81
|
-
"function": FunctionMessage,
|
|
82
|
-
"tool": ToolMessage,
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def _serialize_message(message: BaseMessage) -> str:
|
|
87
|
-
"""Serialize a LangChain BaseMessage to a JSON string for SLM storage."""
|
|
88
|
-
return json.dumps(message_to_dict(message), ensure_ascii=False)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
def _deserialize_messages(dicts: List[dict]) -> List[BaseMessage]:
|
|
92
|
-
"""Deserialize a list of message dicts back to BaseMessage instances.
|
|
93
|
-
|
|
94
|
-
Uses LangChain's ``messages_from_dict`` which handles all known message
|
|
95
|
-
types including ``additional_kwargs``.
|
|
96
|
-
"""
|
|
97
|
-
return messages_from_dict(dicts)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
# ---------------------------------------------------------------------------
|
|
101
|
-
# Main class
|
|
102
|
-
# ---------------------------------------------------------------------------
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
class SuperLocalMemoryChatMessageHistory(BaseChatMessageHistory):
|
|
106
|
-
"""LangChain chat message history backed by SuperLocalMemory V2.
|
|
107
|
-
|
|
108
|
-
Each message is stored as an individual memory entry in the SLM SQLite
|
|
109
|
-
database, tagged with the session ID for isolation. This keeps the data
|
|
110
|
-
fully local and queryable via any SLM access method (MCP, CLI, Skills,
|
|
111
|
-
REST API).
|
|
112
|
-
|
|
113
|
-
Parameters
|
|
114
|
-
----------
|
|
115
|
-
session_id : str
|
|
116
|
-
Unique identifier for the conversation session. Messages from
|
|
117
|
-
different session IDs are completely isolated.
|
|
118
|
-
db_path : str or None
|
|
119
|
-
Path to the SQLite database file. Defaults to
|
|
120
|
-
``~/.superlocalmemory/memory.db``.
|
|
121
|
-
"""
|
|
122
|
-
|
|
123
|
-
# Tag prefix used to isolate LangChain session messages inside SLM.
|
|
124
|
-
_TAG_PREFIX = "langchain:session:"
|
|
125
|
-
|
|
126
|
-
def __init__(self, session_id: str, db_path: Optional[str] = None) -> None:
|
|
127
|
-
self.session_id = session_id
|
|
128
|
-
self.db_path = db_path
|
|
129
|
-
|
|
130
|
-
MemoryStoreV2 = _ensure_slm_imported()
|
|
131
|
-
store_path = Path(db_path) if db_path else None
|
|
132
|
-
self._store = MemoryStoreV2(db_path=store_path)
|
|
133
|
-
|
|
134
|
-
# -- property: messages ------------------------------------------------
|
|
135
|
-
|
|
136
|
-
@property
|
|
137
|
-
def messages(self) -> List[BaseMessage]: # type: ignore[override]
|
|
138
|
-
"""Return all messages for this session, ordered chronologically."""
|
|
139
|
-
session_tag = f"{self._TAG_PREFIX}{self.session_id}"
|
|
140
|
-
|
|
141
|
-
# Retrieve a generous batch from SLM. We filter by tag in Python
|
|
142
|
-
# because list_all does not accept a tag filter parameter.
|
|
143
|
-
all_memories = self._store.list_all(limit=10_000)
|
|
144
|
-
|
|
145
|
-
# Filter to memories belonging to this session.
|
|
146
|
-
session_memories = [
|
|
147
|
-
m for m in all_memories if session_tag in (m.get("tags") or [])
|
|
148
|
-
]
|
|
149
|
-
|
|
150
|
-
# list_all returns newest-first (ORDER BY created_at DESC).
|
|
151
|
-
# We need chronological (oldest-first) order for chat history.
|
|
152
|
-
session_memories.sort(key=lambda m: m.get("created_at", ""))
|
|
153
|
-
|
|
154
|
-
# Deserialize each memory's content back to a BaseMessage.
|
|
155
|
-
message_dicts: List[dict] = []
|
|
156
|
-
for mem in session_memories:
|
|
157
|
-
try:
|
|
158
|
-
parsed = json.loads(mem["content"])
|
|
159
|
-
message_dicts.append(parsed)
|
|
160
|
-
except (json.JSONDecodeError, KeyError, TypeError):
|
|
161
|
-
# Skip malformed entries silently -- they may be non-LangChain
|
|
162
|
-
# memories that happen to share the tag pattern.
|
|
163
|
-
continue
|
|
164
|
-
|
|
165
|
-
if not message_dicts:
|
|
166
|
-
return []
|
|
167
|
-
|
|
168
|
-
return _deserialize_messages(message_dicts)
|
|
169
|
-
|
|
170
|
-
# -- add_messages ------------------------------------------------------
|
|
171
|
-
|
|
172
|
-
def add_messages(self, messages: Sequence[BaseMessage]) -> None:
|
|
173
|
-
"""Persist messages to SuperLocalMemory V2.
|
|
174
|
-
|
|
175
|
-
Each message becomes a separate memory entry tagged with the session
|
|
176
|
-
identifier. Importance is set to 3 (lower than typical user
|
|
177
|
-
memories at 5) so LangChain history does not crowd out higher-value
|
|
178
|
-
entries in search results.
|
|
179
|
-
"""
|
|
180
|
-
session_tag = f"{self._TAG_PREFIX}{self.session_id}"
|
|
181
|
-
|
|
182
|
-
for message in messages:
|
|
183
|
-
serialized = _serialize_message(message)
|
|
184
|
-
self._store.add_memory(
|
|
185
|
-
content=serialized,
|
|
186
|
-
tags=["langchain", session_tag],
|
|
187
|
-
importance=3,
|
|
188
|
-
project_name="langchain",
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
# -- clear -------------------------------------------------------------
|
|
192
|
-
|
|
193
|
-
def clear(self) -> None:
|
|
194
|
-
"""Remove all messages for this session from the store."""
|
|
195
|
-
session_tag = f"{self._TAG_PREFIX}{self.session_id}"
|
|
196
|
-
|
|
197
|
-
all_memories = self._store.list_all(limit=10_000)
|
|
198
|
-
|
|
199
|
-
for mem in all_memories:
|
|
200
|
-
if session_tag in (mem.get("tags") or []):
|
|
201
|
-
self._store.delete_memory(mem["id"])
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["hatchling"]
|
|
3
|
-
build-backend = "hatchling.build"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
name = "langchain-superlocalmemory"
|
|
7
|
-
version = "0.1.0"
|
|
8
|
-
description = "LangChain chat message history backed by SuperLocalMemory — 100% local, zero cloud"
|
|
9
|
-
readme = "README.md"
|
|
10
|
-
license = "AGPL-3.0-or-later"
|
|
11
|
-
requires-python = ">=3.10"
|
|
12
|
-
authors = [
|
|
13
|
-
{ name = "Varun Pratap Bhardwaj" },
|
|
14
|
-
]
|
|
15
|
-
keywords = ["langchain", "memory", "local-first", "privacy", "sqlite", "chat-history", "mcp"]
|
|
16
|
-
classifiers = [
|
|
17
|
-
"Development Status :: 4 - Beta",
|
|
18
|
-
"Intended Audience :: Developers",
|
|
19
|
-
"License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
|
|
20
|
-
"Programming Language :: Python :: 3",
|
|
21
|
-
"Programming Language :: Python :: 3.10",
|
|
22
|
-
"Programming Language :: Python :: 3.11",
|
|
23
|
-
"Programming Language :: Python :: 3.12",
|
|
24
|
-
"Programming Language :: Python :: 3.13",
|
|
25
|
-
"Topic :: Software Development :: Libraries",
|
|
26
|
-
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
27
|
-
]
|
|
28
|
-
dependencies = [
|
|
29
|
-
"langchain-core>=1.0.0",
|
|
30
|
-
]
|
|
31
|
-
|
|
32
|
-
[project.urls]
|
|
33
|
-
Homepage = "https://github.com/qualixar/superlocalmemory"
|
|
34
|
-
Documentation = "https://superlocalmemory.com/"
|
|
35
|
-
Repository = "https://github.com/qualixar/superlocalmemory"
|
|
36
|
-
|
|
37
|
-
[tool.pytest.ini_options]
|
|
38
|
-
testpaths = ["tests"]
|