superlocalmemory 3.6.23 → 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 +271 -71
- package/bin/slm-npm +43 -89
- 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 +18 -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 +453 -191
- 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 +1 -1
- 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 +123 -35
- 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 +219 -32
- 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 +1 -1
- 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 +12 -12
- 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 +8 -63
- 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/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 -516
- 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
|
@@ -9,10 +9,11 @@ Channels: semantic, BM25, entity_graph, temporal, spreading_activation, hopfield
|
|
|
9
9
|
Replaces V1's broken 10-channel triple-re-fusion pipeline.
|
|
10
10
|
|
|
11
11
|
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
12
|
-
License:
|
|
12
|
+
License: AGPL-3.0-or-later
|
|
13
13
|
"""
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
|
+
import concurrent.futures
|
|
16
17
|
import logging
|
|
17
18
|
import math
|
|
18
19
|
import re
|
|
@@ -24,7 +25,10 @@ from superlocalmemory.core.config import ChannelWeights, RetrievalConfig
|
|
|
24
25
|
from superlocalmemory.retrieval.fusion import FusionResult, weighted_rrf
|
|
25
26
|
from superlocalmemory.retrieval.strategy import QueryStrategy, QueryStrategyClassifier
|
|
26
27
|
from superlocalmemory.storage.models import (
|
|
27
|
-
AtomicFact,
|
|
28
|
+
AtomicFact,
|
|
29
|
+
Mode,
|
|
30
|
+
RecallResponse,
|
|
31
|
+
RetrievalResult,
|
|
28
32
|
)
|
|
29
33
|
|
|
30
34
|
if TYPE_CHECKING:
|
|
@@ -50,7 +54,7 @@ class EmbeddingProvider(Protocol):
|
|
|
50
54
|
|
|
51
55
|
|
|
52
56
|
class RetrievalEngine:
|
|
53
|
-
"""
|
|
57
|
+
"""Six-channel retrieval orchestrator.
|
|
54
58
|
|
|
55
59
|
Usage::
|
|
56
60
|
engine = RetrievalEngine(db, config, channels, embedder)
|
|
@@ -91,6 +95,17 @@ class RetrievalEngine:
|
|
|
91
95
|
# recall A's mid-flight on the shared channels. Uncontended for a single
|
|
92
96
|
# recall (~0 cost); only the channel phase of concurrent recalls serialises.
|
|
93
97
|
self._scope_lock = threading.Lock()
|
|
98
|
+
# One executor belongs to one retrieval engine. Creating/destroying six
|
|
99
|
+
# worker threads on every recall caused allocator/thread-stack RSS churn
|
|
100
|
+
# under sustained sessions. The scope lock already serializes channel
|
|
101
|
+
# execution, so one six-worker pool preserves the existing concurrency
|
|
102
|
+
# semantics while making ownership and shutdown deterministic.
|
|
103
|
+
self._channel_executor = concurrent.futures.ThreadPoolExecutor(
|
|
104
|
+
max_workers=6,
|
|
105
|
+
thread_name_prefix="slm-recall-channel",
|
|
106
|
+
)
|
|
107
|
+
self._close_lock = threading.Lock()
|
|
108
|
+
self._closed = False
|
|
94
109
|
|
|
95
110
|
# V3.3.4: LRU cache for query embeddings (avoids redundant Ollama API calls)
|
|
96
111
|
# V3.4.40 (2026-05-09): bumped 64 -> 512. Each cached embedding is ~3KB
|
|
@@ -124,14 +139,14 @@ class RetrievalEngine:
|
|
|
124
139
|
mode: Mode = Mode.A, limit: int = 20,
|
|
125
140
|
*,
|
|
126
141
|
extra_disabled_channels: set[str] | None = None,
|
|
127
|
-
include_global: bool =
|
|
128
|
-
include_shared: bool =
|
|
142
|
+
include_global: bool = False,
|
|
143
|
+
include_shared: bool = False,
|
|
129
144
|
) -> RecallResponse:
|
|
130
145
|
"""Full retrieval pipeline: strategy -> channels -> RRF -> rerank.
|
|
131
146
|
|
|
132
147
|
Multi-scope: ``include_global`` / ``include_shared`` control which
|
|
133
|
-
scopes participate in retrieval. Both default to
|
|
134
|
-
|
|
148
|
+
scopes participate in retrieval. Both default to False so direct
|
|
149
|
+
retrieval-engine callers are private unless they explicitly opt in.
|
|
135
150
|
|
|
136
151
|
V3.4.40 (2026-05-09): ``extra_disabled_channels`` allows callers to
|
|
137
152
|
skip specific channels for a single recall (e.g. SpreadingActivation
|
|
@@ -140,12 +155,6 @@ class RetrievalEngine:
|
|
|
140
155
|
t0 = time.monotonic()
|
|
141
156
|
self._extra_disabled = set(extra_disabled_channels or ())
|
|
142
157
|
|
|
143
|
-
# Multi-scope: scope flags are set on the (shared) channel instances +
|
|
144
|
-
# the channels executed atomically under self._scope_lock — see the
|
|
145
|
-
# `# 3. Run channels` block below. (profile_channel does not read scope.)
|
|
146
|
-
self._include_global = include_global
|
|
147
|
-
self._include_shared = include_shared
|
|
148
|
-
|
|
149
158
|
# v3.5.0 diagnostic: stage timing inside retrieval (SLM_RECALL_TIMING=1).
|
|
150
159
|
import os as _os_e
|
|
151
160
|
import time as _time_e
|
|
@@ -218,10 +227,17 @@ class RetrievalEngine:
|
|
|
218
227
|
fused_ids = {fr.fact_id for fr in fused}
|
|
219
228
|
fused_scores = {fr.fact_id: fr.fused_score for fr in fused}
|
|
220
229
|
|
|
221
|
-
|
|
230
|
+
bridge_query_types = ("multi_hop", "entity", "factual", "general")
|
|
231
|
+
if self._bridge is not None and strat.query_type in bridge_query_types:
|
|
222
232
|
try:
|
|
223
233
|
seed_ids = [fr.fact_id for fr in fused[:10]]
|
|
224
|
-
bridges = self._bridge.discover(
|
|
234
|
+
bridges = self._bridge.discover(
|
|
235
|
+
seed_ids,
|
|
236
|
+
profile_id,
|
|
237
|
+
max_bridges=10,
|
|
238
|
+
include_global=include_global,
|
|
239
|
+
include_shared=include_shared,
|
|
240
|
+
)
|
|
225
241
|
for fid, score in bridges:
|
|
226
242
|
if fid not in fused_ids:
|
|
227
243
|
new_score = score * 0.8
|
|
@@ -268,7 +284,11 @@ class RetrievalEngine:
|
|
|
268
284
|
try:
|
|
269
285
|
candidate_ids = [fr.fact_id for fr in fused[:100]]
|
|
270
286
|
eg_scores = self._entity.score_candidates(
|
|
271
|
-
query,
|
|
287
|
+
query,
|
|
288
|
+
candidate_ids,
|
|
289
|
+
profile_id,
|
|
290
|
+
include_global=include_global,
|
|
291
|
+
include_shared=include_shared,
|
|
272
292
|
)
|
|
273
293
|
if eg_scores:
|
|
274
294
|
boosted = []
|
|
@@ -293,7 +313,12 @@ class RetrievalEngine:
|
|
|
293
313
|
# 4. Load facts for rerank pool
|
|
294
314
|
pool = min(len(fused), max(effective_limit * 3, 30))
|
|
295
315
|
top = fused[:pool]
|
|
296
|
-
facts = self._load_facts(
|
|
316
|
+
facts = self._load_facts(
|
|
317
|
+
top,
|
|
318
|
+
profile_id,
|
|
319
|
+
include_global=include_global,
|
|
320
|
+
include_shared=include_shared,
|
|
321
|
+
)
|
|
297
322
|
_em("load_facts")
|
|
298
323
|
|
|
299
324
|
# V3.3.21: Session diversity for aggregation queries.
|
|
@@ -309,21 +334,20 @@ class RetrievalEngine:
|
|
|
309
334
|
self._reranker is not None
|
|
310
335
|
and getattr(self._reranker, '_worker_ready', False)
|
|
311
336
|
)
|
|
337
|
+
reranker_applied = False
|
|
338
|
+
reranker_status = (
|
|
339
|
+
"fallback_not_ready" if self._reranker is not None
|
|
340
|
+
else "not_configured"
|
|
341
|
+
)
|
|
312
342
|
if reranker_ready and facts:
|
|
313
343
|
ce_alpha = 0.5 if strat.query_type in ("multi_hop", "temporal") else 0.75
|
|
314
|
-
top = self._apply_reranker(
|
|
344
|
+
top, reranker_applied, reranker_status = self._apply_reranker(
|
|
345
|
+
query, top, facts, alpha=ce_alpha,
|
|
346
|
+
)
|
|
347
|
+
elif reranker_ready:
|
|
348
|
+
reranker_status = "no_candidates"
|
|
315
349
|
_em(f"rerank(ready={reranker_ready})")
|
|
316
350
|
|
|
317
|
-
# V3.4.11: Channel diversity — guarantee entity_graph results appear in
|
|
318
|
-
# the final output. Applied AFTER reranker so results can't be pushed out.
|
|
319
|
-
final_top = top[:effective_limit]
|
|
320
|
-
final_top = self._enforce_channel_diversity(
|
|
321
|
-
final_top, fused, ch_results, effective_limit,
|
|
322
|
-
)
|
|
323
|
-
# Reload facts for any newly injected results
|
|
324
|
-
if len(final_top) > len(top[:effective_limit]):
|
|
325
|
-
facts = self._load_facts(final_top, profile_id)
|
|
326
|
-
|
|
327
351
|
# v3.6.6: Evidence floor — gate on per-channel scores (NOT fused/RRF score).
|
|
328
352
|
# Nonsense queries fuse at 0.75-0.78 because RRF is rank-derived and
|
|
329
353
|
# uncalibrated. The discriminator is EARNED CHANNEL EVIDENCE:
|
|
@@ -339,10 +363,34 @@ class RetrievalEngine:
|
|
|
339
363
|
)
|
|
340
364
|
if floor_enabled:
|
|
341
365
|
min_sem = getattr(self._config, "min_semantic_evidence", 0.60)
|
|
342
|
-
|
|
343
|
-
#
|
|
344
|
-
|
|
345
|
-
|
|
366
|
+
# Qualify the rerank pool BEFORE applying the caller's limit. RRF
|
|
367
|
+
# can rank associative-only hits above an exact BM25 match; slicing
|
|
368
|
+
# first allowed those hits to occupy every output slot and then be
|
|
369
|
+
# removed by the floor, producing a false abstention even though a
|
|
370
|
+
# qualified candidate was immediately below the slice.
|
|
371
|
+
top = self._apply_evidence_floor(top, facts, min_sem)
|
|
372
|
+
|
|
373
|
+
# V3.4.11: Channel diversity — guarantee entity_graph results appear in
|
|
374
|
+
# the final output. Applied AFTER reranking and evidence qualification
|
|
375
|
+
# so an associative-only candidate cannot be reintroduced after the gate.
|
|
376
|
+
final_top = top[:effective_limit]
|
|
377
|
+
final_top = self._enforce_channel_diversity(
|
|
378
|
+
final_top, fused, ch_results, effective_limit,
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
# A channel-diversity promotion may come from outside the rerank pool.
|
|
382
|
+
# Load only when that happens; ordinary recalls reuse the existing map.
|
|
383
|
+
if any(fr.fact_id not in facts for fr in final_top):
|
|
384
|
+
facts.update(self._load_facts(
|
|
385
|
+
final_top,
|
|
386
|
+
profile_id,
|
|
387
|
+
include_global=include_global,
|
|
388
|
+
include_shared=include_shared,
|
|
389
|
+
))
|
|
390
|
+
|
|
391
|
+
# Trim facts to the selected, qualified result set.
|
|
392
|
+
selected_ids = {fr.fact_id for fr in final_top}
|
|
393
|
+
facts = {fid: f for fid, f in facts.items() if fid in selected_ids}
|
|
346
394
|
|
|
347
395
|
# 6. Build response
|
|
348
396
|
results = self._build_results(final_top, facts, strat)
|
|
@@ -353,6 +401,8 @@ class RetrievalEngine:
|
|
|
353
401
|
query_type=strat.query_type, channel_weights=strat.weights,
|
|
354
402
|
total_candidates=total, retrieval_time_ms=ms,
|
|
355
403
|
no_confident_match=no_match,
|
|
404
|
+
reranker_applied=reranker_applied,
|
|
405
|
+
reranker_status=reranker_status,
|
|
356
406
|
)
|
|
357
407
|
|
|
358
408
|
# -- Evidence floor (v3.6.6) -------------------------------------------
|
|
@@ -582,7 +632,6 @@ class RetrievalEngine:
|
|
|
582
632
|
down to max(semantic,bm25,entity,temporal,hopfield,sa) — roughly a
|
|
583
633
|
3-5x speedup for the channel phase.
|
|
584
634
|
"""
|
|
585
|
-
import concurrent.futures
|
|
586
635
|
import os as _os_e
|
|
587
636
|
import time as _time_e
|
|
588
637
|
_et = bool(_os_e.environ.get("SLM_RECALL_TIMING"))
|
|
@@ -627,41 +676,45 @@ class RetrievalEngine:
|
|
|
627
676
|
logger.warning("%s channel: %s", name, exc)
|
|
628
677
|
return (name, None)
|
|
629
678
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
679
|
+
executor = self._channel_executor
|
|
680
|
+
if self._semantic is not None and q_emb is not None and "semantic" not in disabled:
|
|
681
|
+
futures["semantic"] = executor.submit(
|
|
682
|
+
_safe_channel, "semantic",
|
|
683
|
+
self._semantic.search, q_emb, profile_id, self._config.semantic_top_k,
|
|
684
|
+
)
|
|
685
|
+
if self._bm25 is not None and "bm25" not in disabled:
|
|
686
|
+
futures["bm25"] = executor.submit(
|
|
687
|
+
_safe_channel, "bm25",
|
|
688
|
+
self._bm25.search, query, profile_id, self._config.bm25_top_k,
|
|
689
|
+
)
|
|
690
|
+
if self._temporal is not None and "temporal" not in disabled:
|
|
691
|
+
futures["temporal"] = executor.submit(
|
|
692
|
+
_safe_channel, "temporal",
|
|
693
|
+
self._temporal.search, query, profile_id, self._config.bm25_top_k,
|
|
694
|
+
)
|
|
695
|
+
if self._hopfield is not None and q_emb is not None and "hopfield" not in disabled:
|
|
696
|
+
futures["hopfield"] = executor.submit(
|
|
697
|
+
_safe_channel, "hopfield",
|
|
698
|
+
self._hopfield.search, q_emb, profile_id, self._config.hopfield_top_k,
|
|
699
|
+
)
|
|
700
|
+
if (
|
|
701
|
+
self._spreading_activation is not None
|
|
702
|
+
and q_emb is not None
|
|
703
|
+
and "spreading_activation" not in disabled
|
|
704
|
+
):
|
|
705
|
+
futures["spreading_activation"] = executor.submit(
|
|
706
|
+
_safe_channel, "spreading_activation",
|
|
707
|
+
self._spreading_activation.search, q_emb, profile_id, self._config.bm25_top_k,
|
|
708
|
+
)
|
|
656
709
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
710
|
+
# Collect results as channels complete.
|
|
711
|
+
for name, fut in futures.items():
|
|
712
|
+
try:
|
|
713
|
+
ch_name, result = fut.result(timeout=30)
|
|
714
|
+
if result:
|
|
715
|
+
out[ch_name] = result
|
|
716
|
+
except Exception as exc:
|
|
717
|
+
logger.warning("Channel %s timed out or failed: %s", name, exc)
|
|
665
718
|
|
|
666
719
|
# Apply registered post-retrieval filters (forgetting filter, etc.)
|
|
667
720
|
if hasattr(self, '_registry') and self._registry._filters:
|
|
@@ -673,10 +726,23 @@ class RetrievalEngine:
|
|
|
673
726
|
|
|
674
727
|
return out
|
|
675
728
|
|
|
729
|
+
def close(self) -> None:
|
|
730
|
+
"""Release the channel workers owned by this retrieval engine."""
|
|
731
|
+
with self._close_lock:
|
|
732
|
+
if self._closed:
|
|
733
|
+
return
|
|
734
|
+
self._closed = True
|
|
735
|
+
self._channel_executor.shutdown(wait=True, cancel_futures=True)
|
|
736
|
+
|
|
676
737
|
# -- Fact loading -------------------------------------------------------
|
|
677
738
|
|
|
678
739
|
def _load_facts(
|
|
679
|
-
self,
|
|
740
|
+
self,
|
|
741
|
+
fused: list[FusionResult],
|
|
742
|
+
profile_id: str,
|
|
743
|
+
*,
|
|
744
|
+
include_global: bool = False,
|
|
745
|
+
include_shared: bool = False,
|
|
680
746
|
) -> dict[str, AtomicFact]:
|
|
681
747
|
"""Load facts by ID — targeted query, not full-table scan.
|
|
682
748
|
|
|
@@ -688,8 +754,8 @@ class RetrievalEngine:
|
|
|
688
754
|
return {}
|
|
689
755
|
facts = self._db.get_facts_by_ids(
|
|
690
756
|
needed, profile_id,
|
|
691
|
-
include_global=
|
|
692
|
-
include_shared=
|
|
757
|
+
include_global=include_global,
|
|
758
|
+
include_shared=include_shared,
|
|
693
759
|
)
|
|
694
760
|
return {f.fact_id: f for f in facts}
|
|
695
761
|
|
|
@@ -705,7 +771,7 @@ class RetrievalEngine:
|
|
|
705
771
|
self, query: str, fused: list[FusionResult],
|
|
706
772
|
fact_map: dict[str, AtomicFact],
|
|
707
773
|
alpha: float = 0.75,
|
|
708
|
-
) -> list[FusionResult]:
|
|
774
|
+
) -> tuple[list[FusionResult], bool, str]:
|
|
709
775
|
"""Rerank with blended CE + RRF scores (Bug 1 fix).
|
|
710
776
|
|
|
711
777
|
Blended: alpha * sigmoid(CE_score) + (1 - alpha) * rrf_score.
|
|
@@ -717,7 +783,7 @@ class RetrievalEngine:
|
|
|
717
783
|
for fr in fused if fr.fact_id in fact_map
|
|
718
784
|
]
|
|
719
785
|
if not candidates:
|
|
720
|
-
return fused
|
|
786
|
+
return fused, False, "no_candidates"
|
|
721
787
|
|
|
722
788
|
# V3.3.16: Strip speaker tags WITHOUT copying full AtomicFact objects.
|
|
723
789
|
# Previously created full copies including 768-dim embeddings (~6KB each),
|
|
@@ -730,17 +796,33 @@ class RetrievalEngine:
|
|
|
730
796
|
originals.append((fact, orig))
|
|
731
797
|
|
|
732
798
|
try:
|
|
733
|
-
|
|
734
|
-
|
|
799
|
+
rerank_with_status = getattr(
|
|
800
|
+
self._reranker, "rerank_with_status", None,
|
|
735
801
|
)
|
|
802
|
+
# MagicMock fabricates arbitrary attributes; only use the richer
|
|
803
|
+
# contract when it is defined by the reranker type itself.
|
|
804
|
+
if callable(rerank_with_status) and hasattr(
|
|
805
|
+
type(self._reranker), "rerank_with_status",
|
|
806
|
+
):
|
|
807
|
+
scored, applied, status = rerank_with_status(
|
|
808
|
+
query, candidates, top_k=len(candidates),
|
|
809
|
+
)
|
|
810
|
+
else:
|
|
811
|
+
scored = self._reranker.rerank( # type: ignore[union-attr]
|
|
812
|
+
query, candidates, top_k=len(candidates),
|
|
813
|
+
)
|
|
814
|
+
applied, status = True, "applied"
|
|
736
815
|
except Exception as exc:
|
|
737
816
|
logger.warning("Cross-encoder rerank failed: %s", exc)
|
|
738
|
-
return fused
|
|
817
|
+
return fused, False, "error"
|
|
739
818
|
finally:
|
|
740
819
|
# Restore original content (with speaker tags)
|
|
741
820
|
for fact, orig_content in originals:
|
|
742
821
|
fact.content = orig_content
|
|
743
822
|
|
|
823
|
+
if not applied:
|
|
824
|
+
return fused, False, status
|
|
825
|
+
|
|
744
826
|
score_map = {fact.fact_id: score for fact, score in scored}
|
|
745
827
|
|
|
746
828
|
# Min-max normalize CE scores to [0, 1] within the batch instead of
|
|
@@ -768,7 +850,7 @@ class RetrievalEngine:
|
|
|
768
850
|
for fr in fused
|
|
769
851
|
]
|
|
770
852
|
updated.sort(key=lambda r: r.fused_score, reverse=True)
|
|
771
|
-
return updated
|
|
853
|
+
return updated, True, "applied"
|
|
772
854
|
|
|
773
855
|
# -- Agentic adapter -----------------------------------
|
|
774
856
|
|
|
@@ -871,11 +953,14 @@ class RetrievalEngine:
|
|
|
871
953
|
# boosts push raw scores well above 1 (observed: 27.97). A sigmoid
|
|
872
954
|
# preserves rank (monotonic) while giving users a readable 0-1 range.
|
|
873
955
|
normalized_score = 1.0 / (1.0 + math.exp(-boosted_score * 0.5))
|
|
874
|
-
confidence = min(1.0, normalized_score * 10.0) * fact.confidence
|
|
875
956
|
results.append(RetrievalResult(
|
|
876
957
|
fact=fact, score=round(normalized_score, 4),
|
|
877
958
|
channel_scores=fr.channel_scores,
|
|
878
|
-
confidence=confidence,
|
|
959
|
+
confidence=fact.confidence,
|
|
960
|
+
relevance_score=round(normalized_score, 4),
|
|
961
|
+
ranking_score=boosted_score,
|
|
962
|
+
memory_confidence=fact.confidence,
|
|
963
|
+
evidence_chain=evidence,
|
|
879
964
|
trust_score=raw_trust,
|
|
880
965
|
))
|
|
881
966
|
return results
|
|
@@ -924,10 +1009,15 @@ def apply_channel_weights(
|
|
|
924
1009
|
new_score = (base if base > 0.0 else float(c.score)) * ce_bias
|
|
925
1010
|
out.append(RetrievalResult(
|
|
926
1011
|
fact=c.fact,
|
|
927
|
-
score=
|
|
1012
|
+
score=c.score,
|
|
928
1013
|
channel_scores=new_cs,
|
|
929
1014
|
confidence=c.confidence,
|
|
1015
|
+
relevance_score=c.relevance_score,
|
|
1016
|
+
ranking_score=new_score,
|
|
1017
|
+
memory_confidence=c.memory_confidence,
|
|
1018
|
+
rank_position=c.rank_position,
|
|
930
1019
|
evidence_chain=c.evidence_chain,
|
|
931
1020
|
trust_score=c.trust_score,
|
|
1021
|
+
marker=c.marker,
|
|
932
1022
|
))
|
|
933
1023
|
return out
|