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
|
@@ -38,9 +38,9 @@ import dataclasses
|
|
|
38
38
|
import json
|
|
39
39
|
import logging
|
|
40
40
|
import os
|
|
41
|
+
import platform
|
|
41
42
|
import re
|
|
42
43
|
import sqlite3
|
|
43
|
-
import struct
|
|
44
44
|
import time
|
|
45
45
|
import uuid
|
|
46
46
|
import zlib
|
|
@@ -49,12 +49,13 @@ from pathlib import Path
|
|
|
49
49
|
from typing import Any
|
|
50
50
|
|
|
51
51
|
from cryptography.exceptions import InvalidTag
|
|
52
|
+
from cryptography.hazmat.primitives import hashes
|
|
52
53
|
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
53
54
|
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
|
54
|
-
from cryptography.hazmat.primitives import hashes
|
|
55
55
|
|
|
56
|
-
from superlocalmemory.
|
|
56
|
+
from superlocalmemory.infra.data_root import DynamicStatePath, state_path
|
|
57
57
|
from superlocalmemory.optimize.storage import schema as _schema
|
|
58
|
+
from superlocalmemory.storage.database import DatabaseManager
|
|
58
59
|
|
|
59
60
|
logger = logging.getLogger(__name__)
|
|
60
61
|
|
|
@@ -88,7 +89,7 @@ MID_FILENAME: str = ".llmcache_key"
|
|
|
88
89
|
SALT_PREFIX: str = "salt:"
|
|
89
90
|
|
|
90
91
|
# C-06: persisted AES key — survives machine-id changes after first run
|
|
91
|
-
_KEY_FILE
|
|
92
|
+
_KEY_FILE = DynamicStatePath("opt-key.bin")
|
|
92
93
|
|
|
93
94
|
_FORBIDDEN_MEMORY_TABLES: frozenset[str] = frozenset({
|
|
94
95
|
"memories", "atomic_facts", "profiles", "canonical_entities",
|
|
@@ -198,7 +199,7 @@ class CacheDB:
|
|
|
198
199
|
|
|
199
200
|
def __init__(self, db_path: Path | None = None) -> None:
|
|
200
201
|
if db_path is None:
|
|
201
|
-
db_path =
|
|
202
|
+
db_path = state_path(LLMCACHE_DBNAME)
|
|
202
203
|
self._db_path = Path(db_path)
|
|
203
204
|
self._db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
204
205
|
# If the file exists but is not a valid SQLite database (e.g. user
|
|
@@ -285,11 +286,10 @@ class CacheDB:
|
|
|
285
286
|
|
|
286
287
|
def _get_machine_id(self) -> str:
|
|
287
288
|
"""Return a stable machine identifier for AES key derivation."""
|
|
288
|
-
system =
|
|
289
|
+
system = platform.system()
|
|
289
290
|
mid: str | None = None
|
|
290
291
|
if system == "Darwin":
|
|
291
292
|
try:
|
|
292
|
-
import plistlib
|
|
293
293
|
import subprocess
|
|
294
294
|
out = subprocess.run(
|
|
295
295
|
["ioreg", "-rd1", "-c", "IOPlatformExpertDevice"],
|
|
@@ -310,7 +310,7 @@ class CacheDB:
|
|
|
310
310
|
except OSError:
|
|
311
311
|
mid = None
|
|
312
312
|
if not mid:
|
|
313
|
-
mid_file =
|
|
313
|
+
mid_file = state_path(MID_FILENAME)
|
|
314
314
|
if mid_file.exists():
|
|
315
315
|
try:
|
|
316
316
|
mid = mid_file.read_text(encoding="utf-8").strip()
|
|
@@ -334,9 +334,10 @@ class CacheDB:
|
|
|
334
334
|
so changing the underlying machine-id string cannot invalidate existing
|
|
335
335
|
cache entries.
|
|
336
336
|
"""
|
|
337
|
+
key_file = Path(_KEY_FILE)
|
|
337
338
|
try:
|
|
338
|
-
if
|
|
339
|
-
key =
|
|
339
|
+
if key_file.exists():
|
|
340
|
+
key = key_file.read_bytes()
|
|
340
341
|
if len(key) == 32:
|
|
341
342
|
return key
|
|
342
343
|
except Exception as exc:
|
|
@@ -346,9 +347,9 @@ class CacheDB:
|
|
|
346
347
|
machine_id = self._get_machine_id()
|
|
347
348
|
key = self._derive_aes_key(machine_id, salt)
|
|
348
349
|
try:
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
os.chmod(
|
|
350
|
+
key_file.parent.mkdir(parents=True, exist_ok=True)
|
|
351
|
+
key_file.write_bytes(key)
|
|
352
|
+
os.chmod(key_file, 0o600)
|
|
352
353
|
except Exception as exc:
|
|
353
354
|
logger.warning("CacheDB: could not persist AES key (fail-open): %s", exc)
|
|
354
355
|
return key
|
|
@@ -12,7 +12,7 @@ scale we target (up to 100K facts). At that scale, a single cosine
|
|
|
12
12
|
scan takes ~5ms on CPU which is well within our latency budget.
|
|
13
13
|
|
|
14
14
|
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
15
|
-
License:
|
|
15
|
+
License: AGPL-3.0-or-later
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
@@ -12,7 +12,7 @@ the entire index. This version persists tokens to the DB via
|
|
|
12
12
|
store_bm25_tokens / get_all_bm25_tokens and cold-loads on init.
|
|
13
13
|
|
|
14
14
|
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
15
|
-
License:
|
|
15
|
+
License: AGPL-3.0-or-later
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
@@ -23,6 +23,8 @@ from typing import TYPE_CHECKING
|
|
|
23
23
|
|
|
24
24
|
from rank_bm25 import BM25Plus
|
|
25
25
|
|
|
26
|
+
from superlocalmemory.storage.database import _scope_where
|
|
27
|
+
|
|
26
28
|
if TYPE_CHECKING:
|
|
27
29
|
from superlocalmemory.storage.database import DatabaseManager
|
|
28
30
|
|
|
@@ -72,6 +74,7 @@ class BM25Channel:
|
|
|
72
74
|
self._bm25: BM25Plus | None = None
|
|
73
75
|
self._dirty: bool = False
|
|
74
76
|
self._loaded_profiles: set[str] = set()
|
|
77
|
+
self._loaded_scope_key: tuple[str, bool, bool] | None = None
|
|
75
78
|
|
|
76
79
|
@property
|
|
77
80
|
def document_count(self) -> int:
|
|
@@ -82,18 +85,31 @@ class BM25Channel:
|
|
|
82
85
|
|
|
83
86
|
Idempotent: subsequent calls for the same profile are no-ops.
|
|
84
87
|
"""
|
|
85
|
-
|
|
88
|
+
include_global = bool(getattr(self, "include_global", False))
|
|
89
|
+
include_shared = bool(getattr(self, "include_shared", False))
|
|
90
|
+
scope_key = (profile_id, include_global, include_shared)
|
|
91
|
+
if scope_key == self._loaded_scope_key:
|
|
86
92
|
return
|
|
87
93
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
94
|
+
# A legacy in-memory index is a visibility-specific view. Reusing a
|
|
95
|
+
# corpus warmed under another profile/scope leaks private documents or
|
|
96
|
+
# omits newly enabled global/shared documents.
|
|
97
|
+
self._corpus = []
|
|
98
|
+
self._fact_ids = []
|
|
99
|
+
self._fact_id_set = set()
|
|
100
|
+
self._raw_texts = []
|
|
101
|
+
self._bm25 = None
|
|
102
|
+
token_map = self._db.get_all_bm25_tokens(
|
|
103
|
+
profile_id,
|
|
104
|
+
include_global=include_global,
|
|
105
|
+
include_shared=include_shared,
|
|
106
|
+
)
|
|
91
107
|
if not token_map:
|
|
92
108
|
# Fallback: tokenize facts directly if no pre-stored tokens
|
|
93
109
|
facts = self._db.get_all_facts(
|
|
94
110
|
profile_id,
|
|
95
|
-
include_global=
|
|
96
|
-
include_shared=
|
|
111
|
+
include_global=include_global,
|
|
112
|
+
include_shared=include_shared,
|
|
97
113
|
)
|
|
98
114
|
for fact in facts:
|
|
99
115
|
if fact.fact_id in self._fact_id_set:
|
|
@@ -112,8 +128,8 @@ class BM25Channel:
|
|
|
112
128
|
try:
|
|
113
129
|
facts = self._db.get_all_facts(
|
|
114
130
|
profile_id,
|
|
115
|
-
include_global=
|
|
116
|
-
include_shared=
|
|
131
|
+
include_global=include_global,
|
|
132
|
+
include_shared=include_shared,
|
|
117
133
|
)
|
|
118
134
|
fact_content_map = {f.fact_id: f.content for f in facts}
|
|
119
135
|
except Exception:
|
|
@@ -128,6 +144,7 @@ class BM25Channel:
|
|
|
128
144
|
|
|
129
145
|
self._dirty = True
|
|
130
146
|
self._loaded_profiles.add(profile_id)
|
|
147
|
+
self._loaded_scope_key = scope_key
|
|
131
148
|
logger.debug(
|
|
132
149
|
"BM25 cold-loaded %d documents for profile=%s",
|
|
133
150
|
len(token_map) if token_map else 0, profile_id,
|
|
@@ -176,14 +193,20 @@ class BM25Channel:
|
|
|
176
193
|
# Quote each token so query punctuation can't break FTS5 MATCH syntax;
|
|
177
194
|
# OR-join for high recall (any token may match).
|
|
178
195
|
match_expr = " OR ".join('"' + t.replace('"', "") + '"' for t in tokens)
|
|
196
|
+
where, params = _scope_where(
|
|
197
|
+
profile_id,
|
|
198
|
+
include_global=bool(getattr(self, "include_global", False)),
|
|
199
|
+
include_shared=bool(getattr(self, "include_shared", False)),
|
|
200
|
+
prefix="af",
|
|
201
|
+
)
|
|
179
202
|
sql = (
|
|
180
203
|
"SELECT af.fact_id AS fact_id, bm25(atomic_facts_fts) AS rank "
|
|
181
204
|
"FROM atomic_facts_fts "
|
|
182
205
|
"JOIN atomic_facts af ON af.rowid = atomic_facts_fts.rowid "
|
|
183
|
-
"WHERE atomic_facts_fts MATCH ? AND
|
|
206
|
+
f"WHERE atomic_facts_fts MATCH ? AND {where} "
|
|
184
207
|
"ORDER BY rank LIMIT ?"
|
|
185
208
|
)
|
|
186
|
-
rows = self._db.execute(sql, (match_expr,
|
|
209
|
+
rows = self._db.execute(sql, (match_expr, *params, int(top_k)))
|
|
187
210
|
out: list[tuple[str, float]] = []
|
|
188
211
|
for r in rows:
|
|
189
212
|
d = dict(r)
|
|
@@ -263,3 +286,4 @@ class BM25Channel:
|
|
|
263
286
|
self._bm25 = None
|
|
264
287
|
self._dirty = False
|
|
265
288
|
self._loaded_profiles = set()
|
|
289
|
+
self._loaded_scope_key = None
|
|
@@ -22,14 +22,18 @@ Parameters:
|
|
|
22
22
|
- typed mu: entity=1.2, causal=1.3, semantic=0.8, temporal=0.9
|
|
23
23
|
|
|
24
24
|
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
25
|
-
License:
|
|
25
|
+
License: AGPL-3.0-or-later
|
|
26
26
|
"""
|
|
27
27
|
from __future__ import annotations
|
|
28
28
|
|
|
29
29
|
import logging
|
|
30
|
-
import re
|
|
31
30
|
from typing import TYPE_CHECKING
|
|
32
31
|
|
|
32
|
+
from superlocalmemory.retrieval.scope_policy import (
|
|
33
|
+
authorized_fact_ids,
|
|
34
|
+
filter_authorized_results,
|
|
35
|
+
)
|
|
36
|
+
|
|
33
37
|
if TYPE_CHECKING:
|
|
34
38
|
from superlocalmemory.storage.database import DatabaseManager
|
|
35
39
|
|
|
@@ -64,6 +68,9 @@ class BridgeDiscovery:
|
|
|
64
68
|
seed_ids: list[str],
|
|
65
69
|
profile_id: str,
|
|
66
70
|
max_bridges: int = 10,
|
|
71
|
+
*,
|
|
72
|
+
include_global: bool = False,
|
|
73
|
+
include_shared: bool = False,
|
|
67
74
|
) -> list[tuple[str, float]]:
|
|
68
75
|
"""Find bridge facts connecting seed results.
|
|
69
76
|
|
|
@@ -78,13 +85,36 @@ class BridgeDiscovery:
|
|
|
78
85
|
if len(seed_ids) < 2:
|
|
79
86
|
return []
|
|
80
87
|
|
|
88
|
+
allowed_seeds = authorized_fact_ids(
|
|
89
|
+
self._db,
|
|
90
|
+
seed_ids,
|
|
91
|
+
profile_id,
|
|
92
|
+
include_global=include_global,
|
|
93
|
+
include_shared=include_shared,
|
|
94
|
+
)
|
|
95
|
+
if any(seed_id not in allowed_seeds for seed_id in seed_ids):
|
|
96
|
+
return []
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
visible_seed_facts = self._db.get_facts_by_ids(
|
|
100
|
+
seed_ids,
|
|
101
|
+
profile_id,
|
|
102
|
+
include_global=include_global,
|
|
103
|
+
include_shared=include_shared,
|
|
104
|
+
)
|
|
105
|
+
except Exception:
|
|
106
|
+
return []
|
|
107
|
+
seed_facts = {fact.fact_id: fact for fact in visible_seed_facts}
|
|
108
|
+
if any(seed_id not in seed_facts for seed_id in seed_ids):
|
|
109
|
+
return []
|
|
110
|
+
|
|
81
111
|
bridges: list[tuple[str, float]] = []
|
|
82
112
|
seen = set(seed_ids)
|
|
83
113
|
|
|
84
114
|
# Check consecutive pairs for entity overlap
|
|
85
115
|
for i in range(len(seed_ids) - 1):
|
|
86
|
-
fact_a =
|
|
87
|
-
fact_b =
|
|
116
|
+
fact_a = seed_facts.get(seed_ids[i])
|
|
117
|
+
fact_b = seed_facts.get(seed_ids[i + 1])
|
|
88
118
|
if not fact_a or not fact_b:
|
|
89
119
|
continue
|
|
90
120
|
|
|
@@ -98,7 +128,12 @@ class BridgeDiscovery:
|
|
|
98
128
|
# Strategy 1: Entity bridge (union minus intersection)
|
|
99
129
|
bridge_entities = (entities_a | entities_b) - (entities_a & entities_b)
|
|
100
130
|
for eid in bridge_entities:
|
|
101
|
-
entity_facts = self._db.get_facts_by_entity(
|
|
131
|
+
entity_facts = self._db.get_facts_by_entity(
|
|
132
|
+
eid,
|
|
133
|
+
profile_id,
|
|
134
|
+
include_global=include_global,
|
|
135
|
+
include_shared=include_shared,
|
|
136
|
+
)
|
|
102
137
|
for f in entity_facts[:5]:
|
|
103
138
|
if f.fact_id not in seen:
|
|
104
139
|
seen.add(f.fact_id)
|
|
@@ -112,7 +147,13 @@ class BridgeDiscovery:
|
|
|
112
147
|
break
|
|
113
148
|
|
|
114
149
|
bridges.sort(key=lambda x: x[1], reverse=True)
|
|
115
|
-
return
|
|
150
|
+
return filter_authorized_results(
|
|
151
|
+
self._db,
|
|
152
|
+
bridges,
|
|
153
|
+
profile_id,
|
|
154
|
+
include_global=include_global,
|
|
155
|
+
include_shared=include_shared,
|
|
156
|
+
)[:max_bridges]
|
|
116
157
|
|
|
117
158
|
def spreading_activation(
|
|
118
159
|
self,
|
|
@@ -120,6 +161,9 @@ class BridgeDiscovery:
|
|
|
120
161
|
profile_id: str,
|
|
121
162
|
max_depth: int = _MAX_DEPTH,
|
|
122
163
|
budget: int = _NODE_BUDGET,
|
|
164
|
+
*,
|
|
165
|
+
include_global: bool = False,
|
|
166
|
+
include_shared: bool = False,
|
|
123
167
|
) -> list[tuple[str, float]]:
|
|
124
168
|
"""Spreading activation from seed facts through the graph.
|
|
125
169
|
|
|
@@ -135,6 +179,16 @@ class BridgeDiscovery:
|
|
|
135
179
|
Returns:
|
|
136
180
|
List of (fact_id, activation_score) for activated facts.
|
|
137
181
|
"""
|
|
182
|
+
allowed_seeds = authorized_fact_ids(
|
|
183
|
+
self._db,
|
|
184
|
+
seed_ids,
|
|
185
|
+
profile_id,
|
|
186
|
+
include_global=include_global,
|
|
187
|
+
include_shared=include_shared,
|
|
188
|
+
)
|
|
189
|
+
if any(seed_id not in allowed_seeds for seed_id in seed_ids):
|
|
190
|
+
return []
|
|
191
|
+
|
|
138
192
|
activations: dict[str, float] = {fid: 1.0 for fid in seed_ids}
|
|
139
193
|
frontier = list(seed_ids)
|
|
140
194
|
|
|
@@ -145,7 +199,12 @@ class BridgeDiscovery:
|
|
|
145
199
|
if current_activation < 0.01:
|
|
146
200
|
continue
|
|
147
201
|
|
|
148
|
-
edges = self._db.get_edges_for_node(
|
|
202
|
+
edges = self._db.get_edges_for_node(
|
|
203
|
+
fid,
|
|
204
|
+
profile_id,
|
|
205
|
+
include_global=include_global,
|
|
206
|
+
include_shared=include_shared,
|
|
207
|
+
)
|
|
149
208
|
for edge in edges:
|
|
150
209
|
other_id = (
|
|
151
210
|
edge.target_id
|
|
@@ -171,4 +230,10 @@ class BridgeDiscovery:
|
|
|
171
230
|
if fid not in set(seed_ids) and score > 0.01
|
|
172
231
|
]
|
|
173
232
|
results.sort(key=lambda x: x[1], reverse=True)
|
|
174
|
-
return
|
|
233
|
+
return filter_authorized_results(
|
|
234
|
+
self._db,
|
|
235
|
+
results,
|
|
236
|
+
profile_id,
|
|
237
|
+
include_global=include_global,
|
|
238
|
+
include_shared=include_shared,
|
|
239
|
+
)[:budget]
|