superlocalmemory 4.1.13 → 4.1.14
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/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +48 -0
- package/README.md +3 -3
- package/package.json +1 -8
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/CLAUDE.md +3 -3
- package/plugin/agents/slm-governance-advisor.md +1 -1
- package/plugin/agents/slm-loop-runner.md +1 -1
- package/plugin/agents/slm-memory-advisor.md +1 -1
- package/plugin/agents/slm-optimize-advisor.md +1 -1
- package/plugin/requirements.txt +1 -1
- package/plugin/skills/slm-cache/SKILL.md +1 -1
- package/plugin/skills/slm-compress/SKILL.md +1 -1
- package/plugin/skills/slm-governance/SKILL.md +1 -1
- package/plugin/skills/slm-graph/SKILL.md +1 -1
- package/plugin/skills/slm-loop/SKILL.md +1 -1
- package/plugin/skills/slm-mesh/SKILL.md +1 -1
- package/plugin/skills/slm-profile/SKILL.md +1 -1
- package/plugin/skills/slm-recall/SKILL.md +1 -1
- package/plugin/skills/slm-remember/SKILL.md +1 -1
- package/plugin/skills/slm-scope/SKILL.md +1 -1
- package/plugin/skills/slm-session/SKILL.md +1 -1
- package/plugin/skills/slm-status/SKILL.md +1 -1
- package/plugin-src/agents/slm-memory-advisor.md +1 -1
- package/plugin-src/agents/slm-optimize-advisor.md +1 -1
- package/plugin-src/rules/AGENTS.md +1 -1
- package/plugin-src/skills/slm-cache/SKILL.md +1 -1
- package/plugin-src/skills/slm-compress/SKILL.md +1 -1
- package/plugin-src/skills/slm-governance/SKILL.md +1 -1
- package/plugin-src/skills/slm-graph/SKILL.md +1 -1
- package/plugin-src/skills/slm-loop/SKILL.md +1 -1
- package/plugin-src/skills/slm-mesh/SKILL.md +1 -1
- package/plugin-src/skills/slm-profile/SKILL.md +1 -1
- package/plugin-src/skills/slm-recall/SKILL.md +1 -1
- package/plugin-src/skills/slm-remember/SKILL.md +1 -1
- package/plugin-src/skills/slm-scope/SKILL.md +1 -1
- package/plugin-src/skills/slm-session/SKILL.md +1 -1
- package/plugin-src/skills/slm-status/SKILL.md +1 -1
- package/scripts/postinstall.js +71 -2
- package/pyproject.toml +0 -250
- package/src/superlocalmemory/__init__.py +0 -82
- package/src/superlocalmemory/access/__init__.py +0 -3
- package/src/superlocalmemory/access/rbac.py +0 -575
- package/src/superlocalmemory/attribution/__init__.py +0 -9
- package/src/superlocalmemory/attribution/signer.py +0 -173
- package/src/superlocalmemory/attribution/watermark.py +0 -189
- package/src/superlocalmemory/brain/__init__.py +0 -5
- package/src/superlocalmemory/brain/truth.py +0 -418
- package/src/superlocalmemory/cli/__init__.py +0 -5
- package/src/superlocalmemory/cli/__main__.py +0 -17
- package/src/superlocalmemory/cli/_lazy_init.py +0 -115
- package/src/superlocalmemory/cli/cache_cmd.py +0 -198
- package/src/superlocalmemory/cli/commands.py +0 -4710
- package/src/superlocalmemory/cli/compress_cmd.py +0 -151
- package/src/superlocalmemory/cli/context_commands.py +0 -193
- package/src/superlocalmemory/cli/daemon.py +0 -909
- package/src/superlocalmemory/cli/db_migrate.py +0 -150
- package/src/superlocalmemory/cli/diagnostics_cmd.py +0 -101
- package/src/superlocalmemory/cli/escape_hatch.py +0 -220
- package/src/superlocalmemory/cli/evidence_cmd.py +0 -103
- package/src/superlocalmemory/cli/gdpr_cmd.py +0 -792
- package/src/superlocalmemory/cli/gdpr_io.py +0 -109
- package/src/superlocalmemory/cli/help_cmd.py +0 -197
- package/src/superlocalmemory/cli/host_upgrades.py +0 -189
- package/src/superlocalmemory/cli/ingest_cmd.py +0 -327
- package/src/superlocalmemory/cli/json_output.py +0 -81
- package/src/superlocalmemory/cli/loop_cmd.py +0 -187
- package/src/superlocalmemory/cli/main.py +0 -1145
- package/src/superlocalmemory/cli/mesh_cmd.py +0 -38
- package/src/superlocalmemory/cli/migrate_cmd.py +0 -55
- package/src/superlocalmemory/cli/ops_cmd.py +0 -281
- package/src/superlocalmemory/cli/optimize_cmd.py +0 -179
- package/src/superlocalmemory/cli/optimize_constants.py +0 -31
- package/src/superlocalmemory/cli/pending_store.py +0 -296
- package/src/superlocalmemory/cli/proxy_cmd.py +0 -108
- package/src/superlocalmemory/cli/scale_engine_cmd.py +0 -56
- package/src/superlocalmemory/cli/service_installer.py +0 -373
- package/src/superlocalmemory/cli/setup_wizard.py +0 -1162
- package/src/superlocalmemory/cli/summary_cmd.py +0 -215
- package/src/superlocalmemory/cli/version_banner.py +0 -202
- package/src/superlocalmemory/cli/wizard_v3426_options.py +0 -129
- package/src/superlocalmemory/code_graph/__init__.py +0 -46
- package/src/superlocalmemory/code_graph/blast_radius.py +0 -177
- package/src/superlocalmemory/code_graph/bridge/__init__.py +0 -36
- package/src/superlocalmemory/code_graph/bridge/entity_resolver.py +0 -490
- package/src/superlocalmemory/code_graph/bridge/event_listeners.py +0 -206
- package/src/superlocalmemory/code_graph/bridge/fact_enricher.py +0 -159
- package/src/superlocalmemory/code_graph/bridge/hebbian_linker.py +0 -170
- package/src/superlocalmemory/code_graph/bridge/maintenance.py +0 -220
- package/src/superlocalmemory/code_graph/bridge/temporal_checker.py +0 -152
- package/src/superlocalmemory/code_graph/changes.py +0 -363
- package/src/superlocalmemory/code_graph/communities.py +0 -299
- package/src/superlocalmemory/code_graph/config.py +0 -154
- package/src/superlocalmemory/code_graph/database.py +0 -526
- package/src/superlocalmemory/code_graph/extractors/__init__.py +0 -95
- package/src/superlocalmemory/code_graph/extractors/python.py +0 -413
- package/src/superlocalmemory/code_graph/extractors/typescript.py +0 -556
- package/src/superlocalmemory/code_graph/flows.py +0 -350
- package/src/superlocalmemory/code_graph/git_hooks.py +0 -226
- package/src/superlocalmemory/code_graph/graph_engine.py +0 -295
- package/src/superlocalmemory/code_graph/graph_store.py +0 -335
- package/src/superlocalmemory/code_graph/incremental.py +0 -200
- package/src/superlocalmemory/code_graph/models.py +0 -130
- package/src/superlocalmemory/code_graph/parser.py +0 -687
- package/src/superlocalmemory/code_graph/resolver.py +0 -321
- package/src/superlocalmemory/code_graph/search.py +0 -460
- package/src/superlocalmemory/code_graph/service.py +0 -95
- package/src/superlocalmemory/code_graph/watcher.py +0 -207
- package/src/superlocalmemory/compliance/__init__.py +0 -0
- package/src/superlocalmemory/compliance/abac.py +0 -204
- package/src/superlocalmemory/compliance/audit.py +0 -385
- package/src/superlocalmemory/compliance/eu_ai_act.py +0 -101
- package/src/superlocalmemory/compliance/gdpr.py +0 -1479
- package/src/superlocalmemory/compliance/lifecycle.py +0 -158
- package/src/superlocalmemory/compliance/retention.py +0 -415
- package/src/superlocalmemory/compliance/scheduler.py +0 -217
- package/src/superlocalmemory/contracts/__init__.py +0 -1
- package/src/superlocalmemory/contracts/schemas/agent-experience-v1.schema.json +0 -92
- package/src/superlocalmemory/contracts/schemas/agent-integration-contract-v2.schema.json +0 -46
- package/src/superlocalmemory/contracts/schemas/cognitive-turn-receipt-v1.schema.json +0 -59
- package/src/superlocalmemory/contracts/v402.py +0 -62
- package/src/superlocalmemory/core/__init__.py +0 -0
- package/src/superlocalmemory/core/actor_context.py +0 -166
- package/src/superlocalmemory/core/admission.py +0 -769
- package/src/superlocalmemory/core/backend_orchestrator.py +0 -637
- package/src/superlocalmemory/core/block_hygiene.py +0 -147
- package/src/superlocalmemory/core/community_summary.py +0 -267
- package/src/superlocalmemory/core/component_healer.py +0 -144
- package/src/superlocalmemory/core/component_registry.py +0 -514
- package/src/superlocalmemory/core/config.py +0 -2204
- package/src/superlocalmemory/core/consolidation_engine.py +0 -983
- package/src/superlocalmemory/core/context_cache.py +0 -574
- package/src/superlocalmemory/core/derivation_lineage.py +0 -246
- package/src/superlocalmemory/core/embedding_worker.py +0 -208
- package/src/superlocalmemory/core/embeddings.py +0 -1052
- package/src/superlocalmemory/core/engine.py +0 -1395
- package/src/superlocalmemory/core/engine_capabilities.py +0 -24
- package/src/superlocalmemory/core/engine_ingestion.py +0 -983
- package/src/superlocalmemory/core/engine_lock.py +0 -75
- package/src/superlocalmemory/core/engine_wiring.py +0 -776
- package/src/superlocalmemory/core/entity_community.py +0 -178
- package/src/superlocalmemory/core/error_envelope.py +0 -60
- package/src/superlocalmemory/core/evidence_bundle.py +0 -528
- package/src/superlocalmemory/core/fact_consolidator.py +0 -812
- package/src/superlocalmemory/core/file_lock.py +0 -92
- package/src/superlocalmemory/core/graph_analyzer.py +0 -456
- package/src/superlocalmemory/core/graph_metrics.py +0 -597
- package/src/superlocalmemory/core/graph_pruner.py +0 -939
- package/src/superlocalmemory/core/health_monitor.py +0 -338
- package/src/superlocalmemory/core/hooks.py +0 -65
- package/src/superlocalmemory/core/ingest_gate.py +0 -133
- package/src/superlocalmemory/core/ingest_policy.py +0 -38
- package/src/superlocalmemory/core/ingestion_command.py +0 -1042
- package/src/superlocalmemory/core/injection.py +0 -434
- package/src/superlocalmemory/core/install_detector.py +0 -131
- package/src/superlocalmemory/core/key_expander.py +0 -138
- package/src/superlocalmemory/core/lifecycle_state.py +0 -153
- package/src/superlocalmemory/core/maintenance.py +0 -777
- package/src/superlocalmemory/core/maintenance_scheduler.py +0 -507
- package/src/superlocalmemory/core/materialization_control.py +0 -20
- package/src/superlocalmemory/core/mcp_embedder_proxy.py +0 -89
- package/src/superlocalmemory/core/memory_health.py +0 -266
- package/src/superlocalmemory/core/mode_capability.py +0 -111
- package/src/superlocalmemory/core/modes.py +0 -168
- package/src/superlocalmemory/core/mutations.py +0 -688
- package/src/superlocalmemory/core/ollama_embedder.py +0 -266
- package/src/superlocalmemory/core/ollama_validator.py +0 -315
- package/src/superlocalmemory/core/operation_policy.py +0 -92
- package/src/superlocalmemory/core/operation_policy_registry.py +0 -542
- package/src/superlocalmemory/core/operation_request.py +0 -127
- package/src/superlocalmemory/core/ops_remediation.py +0 -542
- package/src/superlocalmemory/core/pii.py +0 -105
- package/src/superlocalmemory/core/platform_utils.py +0 -138
- package/src/superlocalmemory/core/profiles.py +0 -234
- package/src/superlocalmemory/core/progressive_abstraction.py +0 -208
- package/src/superlocalmemory/core/projection_drain.py +0 -380
- package/src/superlocalmemory/core/queue_consumer.py +0 -168
- package/src/superlocalmemory/core/ram_lock.py +0 -160
- package/src/superlocalmemory/core/rate_limit.py +0 -151
- package/src/superlocalmemory/core/recall_gate.py +0 -95
- package/src/superlocalmemory/core/recall_pipeline.py +0 -1337
- package/src/superlocalmemory/core/recall_queue.py +0 -377
- package/src/superlocalmemory/core/recall_worker.py +0 -414
- package/src/superlocalmemory/core/registry.py +0 -121
- package/src/superlocalmemory/core/remember_admission.py +0 -161
- package/src/superlocalmemory/core/remember_runtime.py +0 -1190
- package/src/superlocalmemory/core/remote_mode.py +0 -214
- package/src/superlocalmemory/core/reranker_worker.py +0 -338
- package/src/superlocalmemory/core/safe_fs.py +0 -108
- package/src/superlocalmemory/core/scale_autopromote.py +0 -196
- package/src/superlocalmemory/core/scale_engine.py +0 -915
- package/src/superlocalmemory/core/score_contract.py +0 -82
- package/src/superlocalmemory/core/security_primitives.py +0 -672
- package/src/superlocalmemory/core/session_identity.py +0 -98
- package/src/superlocalmemory/core/shadow_router.py +0 -319
- package/src/superlocalmemory/core/slm_disabled.py +0 -85
- package/src/superlocalmemory/core/status_contract.py +0 -108
- package/src/superlocalmemory/core/store_pipeline.py +0 -1404
- package/src/superlocalmemory/core/summarizer.py +0 -200
- package/src/superlocalmemory/core/tier_manager.py +0 -461
- package/src/superlocalmemory/core/topic_signature.py +0 -156
- package/src/superlocalmemory/core/transactions/__init__.py +0 -78
- package/src/superlocalmemory/core/transactions/concrete_owners.py +0 -604
- package/src/superlocalmemory/core/transactions/erasure.py +0 -825
- package/src/superlocalmemory/core/transactions/manifest.py +0 -255
- package/src/superlocalmemory/core/transactions/manifest_key.py +0 -155
- package/src/superlocalmemory/core/transactions/obligations.py +0 -272
- package/src/superlocalmemory/core/transactions/owners.py +0 -114
- package/src/superlocalmemory/core/transactions/reconciler.py +0 -285
- package/src/superlocalmemory/core/transactions/service.py +0 -330
- package/src/superlocalmemory/core/worker_pool.py +0 -377
- package/src/superlocalmemory/core/working_memory.py +0 -288
- package/src/superlocalmemory/dynamics/__init__.py +0 -0
- package/src/superlocalmemory/dynamics/activation_guided_quantization.py +0 -374
- package/src/superlocalmemory/dynamics/eap_scheduler.py +0 -294
- package/src/superlocalmemory/dynamics/ebbinghaus_langevin_coupling.py +0 -171
- package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +0 -227
- package/src/superlocalmemory/encoding/__init__.py +0 -0
- package/src/superlocalmemory/encoding/auto_linker.py +0 -308
- package/src/superlocalmemory/encoding/cognitive_consolidator.py +0 -899
- package/src/superlocalmemory/encoding/consolidator.py +0 -472
- package/src/superlocalmemory/encoding/context_generator.py +0 -175
- package/src/superlocalmemory/encoding/emotional.py +0 -189
- package/src/superlocalmemory/encoding/entity_reflexion.py +0 -200
- package/src/superlocalmemory/encoding/entity_resolver.py +0 -687
- package/src/superlocalmemory/encoding/entropy_gate.py +0 -101
- package/src/superlocalmemory/encoding/fact_extractor.py +0 -877
- package/src/superlocalmemory/encoding/foresight.py +0 -93
- package/src/superlocalmemory/encoding/graph_builder.py +0 -346
- package/src/superlocalmemory/encoding/observation_builder.py +0 -177
- package/src/superlocalmemory/encoding/prospective_markers.py +0 -262
- package/src/superlocalmemory/encoding/scene_builder.py +0 -410
- package/src/superlocalmemory/encoding/signal_inference.py +0 -90
- package/src/superlocalmemory/encoding/temporal_parser.py +0 -432
- package/src/superlocalmemory/encoding/temporal_validator.py +0 -572
- package/src/superlocalmemory/encoding/type_router.py +0 -237
- package/src/superlocalmemory/evaluation/__init__.py +0 -13
- package/src/superlocalmemory/evaluation/calibration.py +0 -308
- package/src/superlocalmemory/evolution/__init__.py +0 -29
- package/src/superlocalmemory/evolution/blind_verifier.py +0 -122
- package/src/superlocalmemory/evolution/budget.py +0 -356
- package/src/superlocalmemory/evolution/evolution_store.py +0 -619
- package/src/superlocalmemory/evolution/llm_dispatch.py +0 -559
- package/src/superlocalmemory/evolution/model_selection.py +0 -175
- package/src/superlocalmemory/evolution/mutation_generator.py +0 -226
- package/src/superlocalmemory/evolution/skill_activator.py +0 -270
- package/src/superlocalmemory/evolution/skill_evolver.py +0 -928
- package/src/superlocalmemory/evolution/triggers.py +0 -376
- package/src/superlocalmemory/evolution/types.py +0 -114
- package/src/superlocalmemory/graph/__init__.py +0 -9
- package/src/superlocalmemory/graph/cozo_adjacency.py +0 -122
- package/src/superlocalmemory/graph/cozo_backend.py +0 -751
- package/src/superlocalmemory/hooks/__init__.py +0 -3
- package/src/superlocalmemory/hooks/_outcome_common.py +0 -523
- package/src/superlocalmemory/hooks/adapter_base.py +0 -347
- package/src/superlocalmemory/hooks/antigravity_adapter.py +0 -171
- package/src/superlocalmemory/hooks/auto_capture.py +0 -133
- package/src/superlocalmemory/hooks/auto_invoker.py +0 -521
- package/src/superlocalmemory/hooks/auto_parameterize.py +0 -147
- package/src/superlocalmemory/hooks/auto_recall.py +0 -191
- package/src/superlocalmemory/hooks/auto_recall_hook.py +0 -251
- package/src/superlocalmemory/hooks/before_web_hook.py +0 -131
- package/src/superlocalmemory/hooks/claude_code_hooks.py +0 -637
- package/src/superlocalmemory/hooks/codex_assets.py +0 -251
- package/src/superlocalmemory/hooks/codex_hooks.py +0 -186
- package/src/superlocalmemory/hooks/context_payload.py +0 -311
- package/src/superlocalmemory/hooks/copilot_adapter.py +0 -208
- package/src/superlocalmemory/hooks/cross_platform_connector.py +0 -90
- package/src/superlocalmemory/hooks/cursor_adapter.py +0 -173
- package/src/superlocalmemory/hooks/hook_daemon.py +0 -295
- package/src/superlocalmemory/hooks/hook_handlers.py +0 -822
- package/src/superlocalmemory/hooks/ide_connector.py +0 -246
- package/src/superlocalmemory/hooks/memory_protocol.py +0 -158
- package/src/superlocalmemory/hooks/portable_kit.py +0 -755
- package/src/superlocalmemory/hooks/post_tool_async_hook.py +0 -183
- package/src/superlocalmemory/hooks/post_tool_outcome_hook.py +0 -351
- package/src/superlocalmemory/hooks/prewarm_auth.py +0 -187
- package/src/superlocalmemory/hooks/rules_engine.py +0 -99
- package/src/superlocalmemory/hooks/session_registry.py +0 -330
- package/src/superlocalmemory/hooks/stop_outcome_hook.py +0 -138
- package/src/superlocalmemory/hooks/sync_loop.py +0 -114
- package/src/superlocalmemory/hooks/topic_shift_hook.py +0 -302
- package/src/superlocalmemory/hooks/user_prompt_hook.py +0 -131
- package/src/superlocalmemory/hooks/user_prompt_rehash_hook.py +0 -210
- package/src/superlocalmemory/infra/__init__.py +0 -3
- package/src/superlocalmemory/infra/auth_middleware.py +0 -145
- package/src/superlocalmemory/infra/backup.py +0 -974
- package/src/superlocalmemory/infra/backup_obligations.py +0 -423
- package/src/superlocalmemory/infra/cache_manager.py +0 -267
- package/src/superlocalmemory/infra/cloud_backup.py +0 -788
- package/src/superlocalmemory/infra/daemon_identity.py +0 -300
- package/src/superlocalmemory/infra/data_root.py +0 -238
- package/src/superlocalmemory/infra/event_bus.py +0 -637
- package/src/superlocalmemory/infra/local_diagnostics.py +0 -327
- package/src/superlocalmemory/infra/pid_manager.py +0 -193
- package/src/superlocalmemory/infra/process_identity.py +0 -180
- package/src/superlocalmemory/infra/process_reaper.py +0 -624
- package/src/superlocalmemory/infra/rate_limiter.py +0 -228
- package/src/superlocalmemory/infra/self_heal.py +0 -401
- package/src/superlocalmemory/infra/version_integrity.py +0 -229
- package/src/superlocalmemory/ingestion/__init__.py +0 -13
- package/src/superlocalmemory/ingestion/adapter_manager.py +0 -255
- package/src/superlocalmemory/ingestion/base_adapter.py +0 -171
- package/src/superlocalmemory/ingestion/calendar_adapter.py +0 -349
- package/src/superlocalmemory/ingestion/credentials.py +0 -125
- package/src/superlocalmemory/ingestion/gmail_adapter.py +0 -378
- package/src/superlocalmemory/ingestion/parsers.py +0 -100
- package/src/superlocalmemory/ingestion/transcript_adapter.py +0 -161
- package/src/superlocalmemory/integrations/__init__.py +0 -1
- package/src/superlocalmemory/integrations/bounded_loops_mcp.py +0 -431
- package/src/superlocalmemory/integrations/bounded_loops_v051.py +0 -236
- package/src/superlocalmemory/learning/__init__.py +0 -0
- package/src/superlocalmemory/learning/adaptive.py +0 -172
- package/src/superlocalmemory/learning/arm_catalog.py +0 -97
- package/src/superlocalmemory/learning/assertion_miner.py +0 -403
- package/src/superlocalmemory/learning/bandit.py +0 -654
- package/src/superlocalmemory/learning/bandit_cache.py +0 -131
- package/src/superlocalmemory/learning/behavioral.py +0 -542
- package/src/superlocalmemory/learning/bootstrap.py +0 -298
- package/src/superlocalmemory/learning/consolidation_cycle.py +0 -398
- package/src/superlocalmemory/learning/consolidation_quantization_worker.py +0 -115
- package/src/superlocalmemory/learning/consolidation_worker.py +0 -261
- package/src/superlocalmemory/learning/cross_project.py +0 -408
- package/src/superlocalmemory/learning/database.py +0 -698
- package/src/superlocalmemory/learning/dedup_hnsw.py +0 -413
- package/src/superlocalmemory/learning/engagement.py +0 -487
- package/src/superlocalmemory/learning/engagement_features.py +0 -279
- package/src/superlocalmemory/learning/ensemble.py +0 -309
- package/src/superlocalmemory/learning/entity_compiler.py +0 -356
- package/src/superlocalmemory/learning/fact_outcome_joins.py +0 -207
- package/src/superlocalmemory/learning/features.py +0 -138
- package/src/superlocalmemory/learning/feedback.py +0 -724
- package/src/superlocalmemory/learning/forgetting_scheduler.py +0 -375
- package/src/superlocalmemory/learning/hnsw_dedup.py +0 -69
- package/src/superlocalmemory/learning/labeler.py +0 -85
- package/src/superlocalmemory/learning/legacy_migration.py +0 -316
- package/src/superlocalmemory/learning/lightgbm_subprocess.py +0 -236
- package/src/superlocalmemory/learning/memory_merge.py +0 -175
- package/src/superlocalmemory/learning/model_cache.py +0 -267
- package/src/superlocalmemory/learning/model_rollback.py +0 -281
- package/src/superlocalmemory/learning/outcome_queue.py +0 -306
- package/src/superlocalmemory/learning/outcomes.py +0 -286
- package/src/superlocalmemory/learning/pattern_miner.py +0 -465
- package/src/superlocalmemory/learning/pattern_miner_constants.py +0 -90
- package/src/superlocalmemory/learning/pcos.py +0 -291
- package/src/superlocalmemory/learning/project_context.py +0 -366
- package/src/superlocalmemory/learning/propensity.py +0 -131
- package/src/superlocalmemory/learning/ranker.py +0 -300
- package/src/superlocalmemory/learning/ranker_common.py +0 -163
- package/src/superlocalmemory/learning/ranker_retrain_legacy.py +0 -210
- package/src/superlocalmemory/learning/ranker_retrain_online.py +0 -423
- package/src/superlocalmemory/learning/reward.py +0 -888
- package/src/superlocalmemory/learning/reward_archive.py +0 -223
- package/src/superlocalmemory/learning/reward_boost.py +0 -211
- package/src/superlocalmemory/learning/reward_from_outcomes.py +0 -365
- package/src/superlocalmemory/learning/reward_model.py +0 -144
- package/src/superlocalmemory/learning/reward_proxy.py +0 -578
- package/src/superlocalmemory/learning/shadow_test.py +0 -524
- package/src/superlocalmemory/learning/signal_kinds.py +0 -79
- package/src/superlocalmemory/learning/signal_worker.py +0 -268
- package/src/superlocalmemory/learning/signals.py +0 -646
- package/src/superlocalmemory/learning/skill_performance_miner.py +0 -422
- package/src/superlocalmemory/learning/source_quality.py +0 -828
- package/src/superlocalmemory/learning/trigram_index.py +0 -548
- package/src/superlocalmemory/learning/workflows.py +0 -309
- package/src/superlocalmemory/llm/__init__.py +0 -0
- package/src/superlocalmemory/llm/backbone.py +0 -364
- package/src/superlocalmemory/loops/__init__.py +0 -58
- package/src/superlocalmemory/loops/budget.py +0 -58
- package/src/superlocalmemory/loops/engine.py +0 -174
- package/src/superlocalmemory/loops/ledger.py +0 -298
- package/src/superlocalmemory/loops/models.py +0 -152
- package/src/superlocalmemory/loops/rules.py +0 -52
- package/src/superlocalmemory/math/__init__.py +0 -0
- package/src/superlocalmemory/math/ebbinghaus.py +0 -352
- package/src/superlocalmemory/math/fisher.py +0 -356
- package/src/superlocalmemory/math/fisher_quantized.py +0 -255
- package/src/superlocalmemory/math/hopfield.py +0 -282
- package/src/superlocalmemory/math/langevin.py +0 -411
- package/src/superlocalmemory/math/polar_quant.py +0 -414
- package/src/superlocalmemory/math/qjl.py +0 -115
- package/src/superlocalmemory/math/sheaf.py +0 -261
- package/src/superlocalmemory/math/turbo_quant.py +0 -318
- package/src/superlocalmemory/mcp/__init__.py +0 -0
- package/src/superlocalmemory/mcp/_daemon_proxy.py +0 -203
- package/src/superlocalmemory/mcp/_pool_adapter.py +0 -181
- package/src/superlocalmemory/mcp/_stdin_guard.py +0 -60
- package/src/superlocalmemory/mcp/agent_context.py +0 -115
- package/src/superlocalmemory/mcp/cli_fallback.py +0 -602
- package/src/superlocalmemory/mcp/http_transport.py +0 -85
- package/src/superlocalmemory/mcp/profiles.py +0 -154
- package/src/superlocalmemory/mcp/resources.py +0 -281
- package/src/superlocalmemory/mcp/server.py +0 -480
- package/src/superlocalmemory/mcp/session_binding.py +0 -98
- package/src/superlocalmemory/mcp/shared.py +0 -112
- package/src/superlocalmemory/mcp/tools.py +0 -18
- package/src/superlocalmemory/mcp/tools_active.py +0 -958
- package/src/superlocalmemory/mcp/tools_brain.py +0 -298
- package/src/superlocalmemory/mcp/tools_code_graph.py +0 -1717
- package/src/superlocalmemory/mcp/tools_context.py +0 -239
- package/src/superlocalmemory/mcp/tools_core.py +0 -1112
- package/src/superlocalmemory/mcp/tools_evolution.py +0 -343
- package/src/superlocalmemory/mcp/tools_learning.py +0 -393
- package/src/superlocalmemory/mcp/tools_loops.py +0 -345
- package/src/superlocalmemory/mcp/tools_mesh.py +0 -429
- package/src/superlocalmemory/mcp/tools_ops.py +0 -115
- package/src/superlocalmemory/mcp/tools_optimize.py +0 -322
- package/src/superlocalmemory/mcp/tools_summaries.py +0 -147
- package/src/superlocalmemory/mcp/tools_v28.py +0 -292
- package/src/superlocalmemory/mcp/tools_v3.py +0 -398
- package/src/superlocalmemory/mcp/tools_v33.py +0 -507
- package/src/superlocalmemory/mesh/__init__.py +0 -12
- package/src/superlocalmemory/mesh/broker.py +0 -812
- package/src/superlocalmemory/mesh/broker_security.py +0 -470
- package/src/superlocalmemory/mesh/discovery.py +0 -365
- package/src/superlocalmemory/mesh/lock_protocol.py +0 -313
- package/src/superlocalmemory/mesh/node_identity.py +0 -97
- package/src/superlocalmemory/mesh/outbox_remote.py +0 -429
- package/src/superlocalmemory/mesh/remote_sync.py +0 -829
- package/src/superlocalmemory/mesh/state_sync.py +0 -286
- package/src/superlocalmemory/migrations/__init__.py +0 -5
- package/src/superlocalmemory/migrations/v3_4_25_to_v3_4_26.py +0 -144
- package/src/superlocalmemory/optimize/NOTICE +0 -6
- package/src/superlocalmemory/optimize/__init__.py +0 -0
- package/src/superlocalmemory/optimize/adapters/__init__.py +0 -68
- package/src/superlocalmemory/optimize/adapters/_agent_registry.py +0 -120
- package/src/superlocalmemory/optimize/adapters/anthropic_adapter.py +0 -112
- package/src/superlocalmemory/optimize/adapters/openai_adapter.py +0 -122
- package/src/superlocalmemory/optimize/adapters/wrap.py +0 -228
- package/src/superlocalmemory/optimize/cache/__init__.py +0 -31
- package/src/superlocalmemory/optimize/cache/boundary_store.py +0 -488
- package/src/superlocalmemory/optimize/cache/centroid_store.py +0 -199
- package/src/superlocalmemory/optimize/cache/context_key.py +0 -67
- package/src/superlocalmemory/optimize/cache/exact.py +0 -88
- package/src/superlocalmemory/optimize/cache/invalidation.py +0 -36
- package/src/superlocalmemory/optimize/cache/key_builder.py +0 -111
- package/src/superlocalmemory/optimize/cache/manager.py +0 -737
- package/src/superlocalmemory/optimize/cache/semantic.py +0 -637
- package/src/superlocalmemory/optimize/cache/stampede.py +0 -50
- package/src/superlocalmemory/optimize/compress/__init__.py +0 -17
- package/src/superlocalmemory/optimize/compress/align.py +0 -159
- package/src/superlocalmemory/optimize/compress/ccr.py +0 -116
- package/src/superlocalmemory/optimize/compress/prose_llmlingua.py +0 -71
- package/src/superlocalmemory/optimize/compress/router.py +0 -667
- package/src/superlocalmemory/optimize/config/__init__.py +0 -56
- package/src/superlocalmemory/optimize/config/defaults.py +0 -43
- package/src/superlocalmemory/optimize/config/schema.py +0 -327
- package/src/superlocalmemory/optimize/config/store.py +0 -270
- package/src/superlocalmemory/optimize/metrics/__init__.py +0 -8
- package/src/superlocalmemory/optimize/metrics/counters.py +0 -155
- package/src/superlocalmemory/optimize/metrics/estimator.py +0 -87
- package/src/superlocalmemory/optimize/metrics/exporters.py +0 -77
- package/src/superlocalmemory/optimize/metrics/persistence.py +0 -115
- package/src/superlocalmemory/optimize/proxy/__init__.py +0 -28
- package/src/superlocalmemory/optimize/proxy/_helpers.py +0 -730
- package/src/superlocalmemory/optimize/proxy/anthropic_surface.py +0 -375
- package/src/superlocalmemory/optimize/proxy/capture.py +0 -550
- package/src/superlocalmemory/optimize/proxy/gemini_surface.py +0 -528
- package/src/superlocalmemory/optimize/proxy/lifecycle.py +0 -126
- package/src/superlocalmemory/optimize/proxy/openai_surface.py +0 -465
- package/src/superlocalmemory/optimize/proxy/server.py +0 -199
- package/src/superlocalmemory/optimize/proxy/vertex_surface.py +0 -246
- package/src/superlocalmemory/optimize/storage/__init__.py +0 -0
- package/src/superlocalmemory/optimize/storage/db.py +0 -1185
- package/src/superlocalmemory/optimize/storage/schema.py +0 -205
- package/src/superlocalmemory/parameterization/__init__.py +0 -47
- package/src/superlocalmemory/parameterization/cross_project.py +0 -12
- package/src/superlocalmemory/parameterization/pattern_extractor.py +0 -584
- package/src/superlocalmemory/parameterization/pii_filter.py +0 -106
- package/src/superlocalmemory/parameterization/prompt_injector.py +0 -219
- package/src/superlocalmemory/parameterization/prompt_lifecycle.py +0 -281
- package/src/superlocalmemory/parameterization/soft_prompt_generator.py +0 -542
- package/src/superlocalmemory/parameterization/workflow_miner.py +0 -17
- package/src/superlocalmemory/reliability/__init__.py +0 -45
- package/src/superlocalmemory/reliability/join_liveness.py +0 -301
- package/src/superlocalmemory/reliability/prior_distance.py +0 -243
- package/src/superlocalmemory/retrieval/__init__.py +0 -0
- package/src/superlocalmemory/retrieval/agentic.py +0 -367
- package/src/superlocalmemory/retrieval/ann_index.py +0 -235
- package/src/superlocalmemory/retrieval/bm25_channel.py +0 -451
- package/src/superlocalmemory/retrieval/bridge_discovery.py +0 -253
- package/src/superlocalmemory/retrieval/channel_registry.py +0 -154
- package/src/superlocalmemory/retrieval/channel_status.py +0 -117
- package/src/superlocalmemory/retrieval/engine.py +0 -1615
- package/src/superlocalmemory/retrieval/entity_channel.py +0 -994
- package/src/superlocalmemory/retrieval/forgetting_filter.py +0 -160
- package/src/superlocalmemory/retrieval/fusion.py +0 -81
- package/src/superlocalmemory/retrieval/graph_adjacency.py +0 -219
- package/src/superlocalmemory/retrieval/hopfield_channel.py +0 -465
- package/src/superlocalmemory/retrieval/profile_channel.py +0 -105
- package/src/superlocalmemory/retrieval/quantization_aware_search.py +0 -147
- package/src/superlocalmemory/retrieval/remote_reranker.py +0 -758
- package/src/superlocalmemory/retrieval/reranker.py +0 -674
- package/src/superlocalmemory/retrieval/scope_policy.py +0 -126
- package/src/superlocalmemory/retrieval/semantic_channel.py +0 -638
- package/src/superlocalmemory/retrieval/spreading.py +0 -288
- package/src/superlocalmemory/retrieval/spreading_activation.py +0 -616
- package/src/superlocalmemory/retrieval/strategy.py +0 -248
- package/src/superlocalmemory/retrieval/temporal_channel.py +0 -433
- package/src/superlocalmemory/retrieval/temporal_frame.py +0 -102
- package/src/superlocalmemory/retrieval/temporal_utils.py +0 -122
- package/src/superlocalmemory/retrieval/temporal_validity_filter.py +0 -499
- package/src/superlocalmemory/retrieval/time_window.py +0 -181
- package/src/superlocalmemory/retrieval/vector_store.py +0 -863
- package/src/superlocalmemory/server/__init__.py +0 -1
- package/src/superlocalmemory/server/api.py +0 -310
- package/src/superlocalmemory/server/asset_versions.py +0 -171
- package/src/superlocalmemory/server/bandit_loops.py +0 -158
- package/src/superlocalmemory/server/config_file.py +0 -90
- package/src/superlocalmemory/server/consolidation_runner.py +0 -140
- package/src/superlocalmemory/server/egress_policy.py +0 -258
- package/src/superlocalmemory/server/loopback.py +0 -85
- package/src/superlocalmemory/server/middleware/__init__.py +0 -11
- package/src/superlocalmemory/server/middleware/security_headers.py +0 -144
- package/src/superlocalmemory/server/origin.py +0 -55
- package/src/superlocalmemory/server/profile_runtime.py +0 -515
- package/src/superlocalmemory/server/rbac_enforce.py +0 -194
- package/src/superlocalmemory/server/recall_health.py +0 -343
- package/src/superlocalmemory/server/recall_serializer.py +0 -320
- package/src/superlocalmemory/server/route_mutations.py +0 -104
- package/src/superlocalmemory/server/routes/__init__.py +0 -4
- package/src/superlocalmemory/server/routes/abstraction.py +0 -314
- package/src/superlocalmemory/server/routes/adapters.py +0 -63
- package/src/superlocalmemory/server/routes/agents.py +0 -303
- package/src/superlocalmemory/server/routes/backup.py +0 -869
- package/src/superlocalmemory/server/routes/behavioral.py +0 -659
- package/src/superlocalmemory/server/routes/brain.py +0 -1892
- package/src/superlocalmemory/server/routes/chat.py +0 -393
- package/src/superlocalmemory/server/routes/compliance.py +0 -533
- package/src/superlocalmemory/server/routes/config_api.py +0 -703
- package/src/superlocalmemory/server/routes/data_io.py +0 -329
- package/src/superlocalmemory/server/routes/entity.py +0 -237
- package/src/superlocalmemory/server/routes/events.py +0 -214
- package/src/superlocalmemory/server/routes/evolution.py +0 -510
- package/src/superlocalmemory/server/routes/helpers.py +0 -499
- package/src/superlocalmemory/server/routes/ingest.py +0 -137
- package/src/superlocalmemory/server/routes/insights.py +0 -366
- package/src/superlocalmemory/server/routes/learning.py +0 -834
- package/src/superlocalmemory/server/routes/learning_telemetry.py +0 -154
- package/src/superlocalmemory/server/routes/lifecycle.py +0 -184
- package/src/superlocalmemory/server/routes/memories.py +0 -1661
- package/src/superlocalmemory/server/routes/mesh.py +0 -517
- package/src/superlocalmemory/server/routes/mesh_lock.py +0 -54
- package/src/superlocalmemory/server/routes/mesh_state.py +0 -63
- package/src/superlocalmemory/server/routes/optimize.py +0 -197
- package/src/superlocalmemory/server/routes/prewarm.py +0 -173
- package/src/superlocalmemory/server/routes/profiles.py +0 -292
- package/src/superlocalmemory/server/routes/ratelimit.py +0 -132
- package/src/superlocalmemory/server/routes/rbac.py +0 -366
- package/src/superlocalmemory/server/routes/stats.py +0 -385
- package/src/superlocalmemory/server/routes/tiers.py +0 -222
- package/src/superlocalmemory/server/routes/timeline.py +0 -258
- package/src/superlocalmemory/server/routes/token.py +0 -90
- package/src/superlocalmemory/server/routes/v3_api.py +0 -3023
- package/src/superlocalmemory/server/routes/ws.py +0 -171
- package/src/superlocalmemory/server/security_middleware.py +0 -89
- package/src/superlocalmemory/server/ui.py +0 -354
- package/src/superlocalmemory/server/unified_daemon.py +0 -6326
- package/src/superlocalmemory/server/write_identity.py +0 -195
- package/src/superlocalmemory/storage/__init__.py +0 -0
- package/src/superlocalmemory/storage/_migration_internals.py +0 -638
- package/src/superlocalmemory/storage/_schema_version.py +0 -174
- package/src/superlocalmemory/storage/access_log.py +0 -170
- package/src/superlocalmemory/storage/admission_codec.py +0 -129
- package/src/superlocalmemory/storage/admission_journal.py +0 -843
- package/src/superlocalmemory/storage/agent_experience.py +0 -546
- package/src/superlocalmemory/storage/backup.py +0 -531
- package/src/superlocalmemory/storage/correction_cases.py +0 -670
- package/src/superlocalmemory/storage/database.py +0 -3180
- package/src/superlocalmemory/storage/deferred_writes.py +0 -209
- package/src/superlocalmemory/storage/embedding_codec.py +0 -200
- package/src/superlocalmemory/storage/embedding_migrator.py +0 -672
- package/src/superlocalmemory/storage/erasure_fence.py +0 -45
- package/src/superlocalmemory/storage/execution_learning.py +0 -285
- package/src/superlocalmemory/storage/external_evidence.py +0 -359
- package/src/superlocalmemory/storage/generation_fence.py +0 -63
- package/src/superlocalmemory/storage/lineage_retention.py +0 -236
- package/src/superlocalmemory/storage/logical_edges.py +0 -86
- package/src/superlocalmemory/storage/memory_write.py +0 -115
- package/src/superlocalmemory/storage/migration_runner.py +0 -895
- package/src/superlocalmemory/storage/migration_v33.py +0 -140
- package/src/superlocalmemory/storage/migrations/M001_add_signal_features_columns.py +0 -67
- package/src/superlocalmemory/storage/migrations/M002_model_state_history.py +0 -107
- package/src/superlocalmemory/storage/migrations/M003_migration_log.py +0 -38
- package/src/superlocalmemory/storage/migrations/M004_cross_platform_sync_log.py +0 -46
- package/src/superlocalmemory/storage/migrations/M005_bandit_tables.py +0 -75
- package/src/superlocalmemory/storage/migrations/M006_action_outcomes_reward.py +0 -75
- package/src/superlocalmemory/storage/migrations/M007_pending_outcomes.py +0 -63
- package/src/superlocalmemory/storage/migrations/M009_model_lineage.py +0 -94
- package/src/superlocalmemory/storage/migrations/M010_evolution_config.py +0 -80
- package/src/superlocalmemory/storage/migrations/M011_archive_and_merge.py +0 -87
- package/src/superlocalmemory/storage/migrations/M012_shadow_observations.py +0 -72
- package/src/superlocalmemory/storage/migrations/M013_bi_temporal_columns.py +0 -55
- package/src/superlocalmemory/storage/migrations/M014_v345_scale_ready.py +0 -45
- package/src/superlocalmemory/storage/migrations/M015_add_pinned_column.py +0 -58
- package/src/superlocalmemory/storage/migrations/M016_add_scope_support.py +0 -120
- package/src/superlocalmemory/storage/migrations/M017_ccq_scope_column.py +0 -79
- package/src/superlocalmemory/storage/migrations/M018_ingestion_operations.py +0 -120
- package/src/superlocalmemory/storage/migrations/M019_derivation_lineage.py +0 -54
- package/src/superlocalmemory/storage/migrations/M020_model_state_integrity.py +0 -52
- package/src/superlocalmemory/storage/migrations/M021_ingestion_log_profile.py +0 -108
- package/src/superlocalmemory/storage/migrations/M022_entity_aliases_profile.py +0 -86
- package/src/superlocalmemory/storage/migrations/M023_mesh_profile_isolation.py +0 -194
- package/src/superlocalmemory/storage/migrations/M024_rbac_users_roles.py +0 -87
- package/src/superlocalmemory/storage/migrations/M025_perf_indexes.py +0 -90
- package/src/superlocalmemory/storage/migrations/M026_rbac_memberships_fk.py +0 -136
- package/src/superlocalmemory/storage/migrations/M027_transferable_patterns_profile.py +0 -163
- package/src/superlocalmemory/storage/migrations/M028_fact_entity_associations.py +0 -305
- package/src/superlocalmemory/storage/migrations/M029_behavioral_history_indexes.py +0 -137
- package/src/superlocalmemory/storage/migrations/M030_entity_explorer_indexes.py +0 -93
- package/src/superlocalmemory/storage/migrations/M031_dead_letter_operations.py +0 -80
- package/src/superlocalmemory/storage/migrations/M032_write_coordinator_admission.py +0 -188
- package/src/superlocalmemory/storage/migrations/M033_projection_transactions.py +0 -148
- package/src/superlocalmemory/storage/migrations/M034_obligation_integrity.py +0 -58
- package/src/superlocalmemory/storage/migrations/M035_erasure_receipts.py +0 -113
- package/src/superlocalmemory/storage/migrations/M036_vector_row_map.py +0 -107
- package/src/superlocalmemory/storage/migrations/M037_manifest_hmac_version.py +0 -162
- package/src/superlocalmemory/storage/migrations/M038_learning_feedback_channel.py +0 -77
- package/src/superlocalmemory/storage/migrations/M039_scene_fact_members.py +0 -137
- package/src/superlocalmemory/storage/migrations/M040_agent_experience_receipts.py +0 -254
- package/src/superlocalmemory/storage/migrations/M041_external_evidence_receipts.py +0 -189
- package/src/superlocalmemory/storage/migrations/M042_correction_case_ledger.py +0 -245
- package/src/superlocalmemory/storage/migrations/M043_quarantine_display_summaries.py +0 -512
- package/src/superlocalmemory/storage/migrations/M044_play_carries_its_own_evidence.py +0 -127
- package/src/superlocalmemory/storage/migrations/M045_fact_outcome_score.py +0 -158
- package/src/superlocalmemory/storage/migrations/M046_prospective_memory_has_its_own_name.py +0 -620
- package/src/superlocalmemory/storage/migrations/M047_fisher_vectors_are_stored_like_every_other_vector.py +0 -306
- package/src/superlocalmemory/storage/migrations/M048_upcoming_holds_only_what_is_upcoming.py +0 -229
- package/src/superlocalmemory/storage/migrations/M049_a_schema_version_marker_is_one_row.py +0 -201
- package/src/superlocalmemory/storage/migrations/M050_execution_learning_v2.py +0 -70
- package/src/superlocalmemory/storage/migrations/__init__.py +0 -103
- package/src/superlocalmemory/storage/migrations.py +0 -333
- package/src/superlocalmemory/storage/models.py +0 -500
- package/src/superlocalmemory/storage/projection_outbox.py +0 -346
- package/src/superlocalmemory/storage/quantized_store.py +0 -280
- package/src/superlocalmemory/storage/read_connection.py +0 -115
- package/src/superlocalmemory/storage/retention_policy.py +0 -860
- package/src/superlocalmemory/storage/schema.py +0 -1108
- package/src/superlocalmemory/storage/schema_code_graph.py +0 -282
- package/src/superlocalmemory/storage/schema_v32.py +0 -382
- package/src/superlocalmemory/storage/schema_v3410.py +0 -159
- package/src/superlocalmemory/storage/schema_v3411.py +0 -149
- package/src/superlocalmemory/storage/schema_v343.py +0 -315
- package/src/superlocalmemory/storage/schema_v345.py +0 -109
- package/src/superlocalmemory/storage/schema_v347.py +0 -140
- package/src/superlocalmemory/storage/sqlite_vectors.py +0 -169
- package/src/superlocalmemory/storage/v2_migrator.py +0 -466
- package/src/superlocalmemory/storage/write_coordinator.py +0 -949
- package/src/superlocalmemory/storage/write_lock.py +0 -88
- package/src/superlocalmemory/summaries/__init__.py +0 -37
- package/src/superlocalmemory/summaries/base.py +0 -267
- package/src/superlocalmemory/summaries/daily_reflection.py +0 -340
- package/src/superlocalmemory/summaries/non_answer.py +0 -223
- package/src/superlocalmemory/summaries/project_work_log.py +0 -440
- package/src/superlocalmemory/summaries/session_summary.py +0 -311
- package/src/superlocalmemory/trust/__init__.py +0 -0
- package/src/superlocalmemory/trust/gate.py +0 -171
- package/src/superlocalmemory/trust/provenance.py +0 -124
- package/src/superlocalmemory/trust/scorer.py +0 -413
- package/src/superlocalmemory/trust/signals.py +0 -153
- package/src/superlocalmemory/ui/assets/slm-icon-white.svg +0 -64
- package/src/superlocalmemory/ui/assets/slm-icon.svg +0 -36
- package/src/superlocalmemory/ui/css/brain.css +0 -409
- package/src/superlocalmemory/ui/css/design-system.css +0 -696
- package/src/superlocalmemory/ui/css/legacy-dashboard.css +0 -663
- package/src/superlocalmemory/ui/css/neural-glass.css +0 -1599
- package/src/superlocalmemory/ui/css/od-bridge.css +0 -158
- package/src/superlocalmemory/ui/favicon.svg +0 -36
- package/src/superlocalmemory/ui/index.html +0 -1638
- package/src/superlocalmemory/ui/js/agents.js +0 -192
- package/src/superlocalmemory/ui/js/auto-settings.js +0 -624
- package/src/superlocalmemory/ui/js/brain.js +0 -1400
- package/src/superlocalmemory/ui/js/clusters.js +0 -326
- package/src/superlocalmemory/ui/js/compliance.js +0 -307
- package/src/superlocalmemory/ui/js/core.js +0 -566
- package/src/superlocalmemory/ui/js/dashboard.js +0 -503
- package/src/superlocalmemory/ui/js/event-delegation.js +0 -113
- package/src/superlocalmemory/ui/js/events.js +0 -178
- package/src/superlocalmemory/ui/js/fact-detail.js +0 -142
- package/src/superlocalmemory/ui/js/feedback.js +0 -339
- package/src/superlocalmemory/ui/js/graph-event-bus.js +0 -83
- package/src/superlocalmemory/ui/js/graph-filters.js +0 -220
- package/src/superlocalmemory/ui/js/graph-ui.js +0 -214
- package/src/superlocalmemory/ui/js/ide-status.js +0 -115
- package/src/superlocalmemory/ui/js/init.js +0 -54
- package/src/superlocalmemory/ui/js/knowledge-graph.js +0 -945
- package/src/superlocalmemory/ui/js/lifecycle.js +0 -387
- package/src/superlocalmemory/ui/js/math-health.js +0 -114
- package/src/superlocalmemory/ui/js/memories.js +0 -394
- package/src/superlocalmemory/ui/js/memory-chat.js +0 -371
- package/src/superlocalmemory/ui/js/memory-timeline.js +0 -265
- package/src/superlocalmemory/ui/js/modal.js +0 -733
- package/src/superlocalmemory/ui/js/ng-entities.js +0 -298
- package/src/superlocalmemory/ui/js/ng-health.js +0 -208
- package/src/superlocalmemory/ui/js/ng-ingestion.js +0 -203
- package/src/superlocalmemory/ui/js/ng-mesh.js +0 -374
- package/src/superlocalmemory/ui/js/ng-shell.js +0 -524
- package/src/superlocalmemory/ui/js/ng-skills.js +0 -663
- package/src/superlocalmemory/ui/js/od-agents.js +0 -588
- package/src/superlocalmemory/ui/js/od-auth-gate.js +0 -257
- package/src/superlocalmemory/ui/js/od-backup.js +0 -878
- package/src/superlocalmemory/ui/js/od-boundedloops.js +0 -324
- package/src/superlocalmemory/ui/js/od-brain.js +0 -1095
- package/src/superlocalmemory/ui/js/od-compliance-ext.js +0 -301
- package/src/superlocalmemory/ui/js/od-components.js +0 -147
- package/src/superlocalmemory/ui/js/od-entities.js +0 -622
- package/src/superlocalmemory/ui/js/od-graph.js +0 -776
- package/src/superlocalmemory/ui/js/od-health.js +0 -579
- package/src/superlocalmemory/ui/js/od-mcp.js +0 -508
- package/src/superlocalmemory/ui/js/od-memories.js +0 -1499
- package/src/superlocalmemory/ui/js/od-mesh.js +0 -645
- package/src/superlocalmemory/ui/js/od-operations.js +0 -1268
- package/src/superlocalmemory/ui/js/od-ops-health.js +0 -417
- package/src/superlocalmemory/ui/js/od-optimize.js +0 -828
- package/src/superlocalmemory/ui/js/od-settings.js +0 -1275
- package/src/superlocalmemory/ui/js/od-shell.js +0 -819
- package/src/superlocalmemory/ui/js/od-skills.js +0 -600
- package/src/superlocalmemory/ui/js/od-team.js +0 -265
- package/src/superlocalmemory/ui/js/optimize.js +0 -191
- package/src/superlocalmemory/ui/js/profiles.js +0 -362
- package/src/superlocalmemory/ui/js/quick-actions.js +0 -334
- package/src/superlocalmemory/ui/js/recall-lab.js +0 -373
- package/src/superlocalmemory/ui/js/search.js +0 -86
- package/src/superlocalmemory/ui/js/settings.js +0 -556
- package/src/superlocalmemory/ui/js/timeline.js +0 -62
- package/src/superlocalmemory/ui/js/trust-dashboard.js +0 -225
- package/src/superlocalmemory/ui/vendor/bootstrap-icons/bootstrap-icons.css +0 -2018
- package/src/superlocalmemory/ui/vendor/bootstrap-icons/fonts/bootstrap-icons.woff +0 -0
- package/src/superlocalmemory/ui/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2 +0 -0
- package/src/superlocalmemory/ui/vendor/bootstrap.bundle.min.js +0 -7
- package/src/superlocalmemory/ui/vendor/bootstrap.min.css +0 -6
- package/src/superlocalmemory/ui/vendor/d3.v7.min.js +0 -2
- package/src/superlocalmemory/ui/vendor/graphology-library.min.js +0 -2
- package/src/superlocalmemory/ui/vendor/graphology.umd.min.js +0 -2
- package/src/superlocalmemory/ui/vendor/inter-ui/inter-variable.min.css +0 -8
- package/src/superlocalmemory/ui/vendor/inter-ui/variable/InterVariable-Italic.woff2 +0 -0
- package/src/superlocalmemory/ui/vendor/inter-ui/variable/InterVariable.woff2 +0 -0
- package/src/superlocalmemory/ui/vendor/sigma.min.js +0 -1
- package/src/superlocalmemory/vector/__init__.py +0 -9
- package/src/superlocalmemory/vector/lancedb_backend.py +0 -366
|
@@ -1,1337 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
2
|
-
# Licensed under AGPL-3.0-or-later - see LICENSE file
|
|
3
|
-
# Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
|
|
4
|
-
|
|
5
|
-
"""Recall pipeline — extracted free functions for MemoryEngine.recall().
|
|
6
|
-
|
|
7
|
-
Direction: engine.py imports this module. This module NEVER imports engine.py.
|
|
8
|
-
|
|
9
|
-
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
from __future__ import annotations
|
|
13
|
-
|
|
14
|
-
import hashlib
|
|
15
|
-
import hmac
|
|
16
|
-
import logging
|
|
17
|
-
from dataclasses import replace
|
|
18
|
-
|
|
19
|
-
from superlocalmemory.core.session_identity import is_conversation
|
|
20
|
-
import sqlite3
|
|
21
|
-
from pathlib import Path
|
|
22
|
-
from typing import TYPE_CHECKING, Any
|
|
23
|
-
|
|
24
|
-
if TYPE_CHECKING:
|
|
25
|
-
import os
|
|
26
|
-
|
|
27
|
-
from superlocalmemory.core.config import SLMConfig
|
|
28
|
-
from superlocalmemory.core.hooks import HookRegistry
|
|
29
|
-
from superlocalmemory.storage.database import DatabaseManager
|
|
30
|
-
|
|
31
|
-
from superlocalmemory.core.security_primitives import ensure_install_token
|
|
32
|
-
from superlocalmemory.storage.models import Mode, RecallResponse
|
|
33
|
-
|
|
34
|
-
logger = logging.getLogger(__name__)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
# ---------------------------------------------------------------------------
|
|
38
|
-
# LLD-00 §3 — HMAC fact-id markers (P0.4, SEC-C-01 fix)
|
|
39
|
-
# ---------------------------------------------------------------------------
|
|
40
|
-
#
|
|
41
|
-
# Every fact surfaced in a recall response is tagged with
|
|
42
|
-
# slm:fact:<fact_id>:<hmac8>
|
|
43
|
-
# where hmac8 is the first 8 hex chars of HMAC-SHA256(install_token, fact_id).
|
|
44
|
-
#
|
|
45
|
-
# post_tool_outcome_hook (LLD-09) scans only for this prefix and validates
|
|
46
|
-
# the HMAC. Unverified markers are ignored — this closes the tool-output
|
|
47
|
-
# injection attack where attacker-controlled output could forge engagement
|
|
48
|
-
# signals by spelling a known fact_id.
|
|
49
|
-
|
|
50
|
-
_HMAC_MARKER_PREFIX = "slm:fact:"
|
|
51
|
-
_HMAC_LEN = 8
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def _emit_marker(fact_id: str) -> str:
|
|
55
|
-
"""Tag ``fact_id`` with its HMAC so downstream hooks can validate.
|
|
56
|
-
|
|
57
|
-
Deterministic per install: a given (install_token, fact_id) pair always
|
|
58
|
-
produces the same marker. Token rotation invalidates old markers.
|
|
59
|
-
"""
|
|
60
|
-
token = ensure_install_token()
|
|
61
|
-
digest = hmac.new(
|
|
62
|
-
token.encode("utf-8"), fact_id.encode("utf-8"), hashlib.sha256
|
|
63
|
-
).hexdigest()[:_HMAC_LEN]
|
|
64
|
-
return f"{_HMAC_MARKER_PREFIX}{fact_id}:{digest}"
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def _validate_marker(marker: str) -> str | None:
|
|
68
|
-
"""Return ``fact_id`` if ``marker`` is a valid HMAC marker, else None.
|
|
69
|
-
|
|
70
|
-
Uses constant-time compare. Never raises.
|
|
71
|
-
"""
|
|
72
|
-
if not isinstance(marker, str) or not marker.startswith(_HMAC_MARKER_PREFIX):
|
|
73
|
-
return None
|
|
74
|
-
rest = marker[len(_HMAC_MARKER_PREFIX):]
|
|
75
|
-
fact_id, sep, presented = rest.rpartition(":")
|
|
76
|
-
if not sep or not fact_id or len(presented) != _HMAC_LEN:
|
|
77
|
-
return None
|
|
78
|
-
try:
|
|
79
|
-
token = ensure_install_token()
|
|
80
|
-
except Exception: # pragma: no cover — install-token I/O failure
|
|
81
|
-
return None
|
|
82
|
-
expected = hmac.new(
|
|
83
|
-
token.encode("utf-8"), fact_id.encode("utf-8"), hashlib.sha256
|
|
84
|
-
).hexdigest()[:_HMAC_LEN]
|
|
85
|
-
if hmac.compare_digest(presented, expected):
|
|
86
|
-
return fact_id
|
|
87
|
-
return None
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def _apply_markers_to_response(response: RecallResponse) -> None:
|
|
91
|
-
"""Populate ``result.marker`` on every result in ``response``, in place.
|
|
92
|
-
|
|
93
|
-
Called as the last step of :func:`run_recall` before returning. Empty
|
|
94
|
-
responses pass through untouched.
|
|
95
|
-
|
|
96
|
-
# L-P-06: audit flagged ``dataclasses.replace`` as a cheaper path.
|
|
97
|
-
# Verified: ``RecallResult`` is NOT frozen, so the direct in-place
|
|
98
|
-
# attribute assignment below is the O(1) mutation path — no dataclass
|
|
99
|
-
# reconstruction happens. ``replace`` would ALLOCATE a fresh instance
|
|
100
|
-
# per result (strictly slower). Keep the in-place mutation.
|
|
101
|
-
"""
|
|
102
|
-
for r in response.results:
|
|
103
|
-
r.marker = _emit_marker(r.fact.fact_id)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
def _preserve_exact_lexical_evidence(
|
|
107
|
-
response: RecallResponse,
|
|
108
|
-
query: str,
|
|
109
|
-
) -> None:
|
|
110
|
-
"""Keep a deterministic exact BM25 hit ahead of learned refinements.
|
|
111
|
-
|
|
112
|
-
Adaptive and bandit ranking are valuable for ambiguous candidates, but
|
|
113
|
-
they must not demote a fact containing the caller's exact query behind
|
|
114
|
-
semantically similar noise. This guard runs after every learned layer and
|
|
115
|
-
changes only ordering; it does not introduce or bypass evidence.
|
|
116
|
-
"""
|
|
117
|
-
normalized_query = " ".join(query.casefold().split())
|
|
118
|
-
if len(normalized_query) < 3 or len(response.results) < 2:
|
|
119
|
-
return
|
|
120
|
-
exact = [
|
|
121
|
-
result
|
|
122
|
-
for result in response.results
|
|
123
|
-
if (
|
|
124
|
-
float((result.channel_scores or {}).get("bm25", 0.0) or 0.0) > 0.0
|
|
125
|
-
and normalized_query
|
|
126
|
-
in " ".join(result.fact.content.casefold().split())
|
|
127
|
-
)
|
|
128
|
-
]
|
|
129
|
-
if not exact:
|
|
130
|
-
return
|
|
131
|
-
strongest = max(
|
|
132
|
-
exact,
|
|
133
|
-
key=lambda result: float(
|
|
134
|
-
(result.channel_scores or {}).get("bm25", 0.0) or 0.0,
|
|
135
|
-
),
|
|
136
|
-
)
|
|
137
|
-
if response.results[0] is strongest:
|
|
138
|
-
return
|
|
139
|
-
response.results = [
|
|
140
|
-
strongest,
|
|
141
|
-
*(result for result in response.results if result is not strongest),
|
|
142
|
-
]
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
# ---------------------------------------------------------------------------
|
|
146
|
-
# Stage 8 SB-1 — feed shadow_router from recall-settled signals.
|
|
147
|
-
#
|
|
148
|
-
# LLD-10 Track A.3 needs live-recall A/B observations to feed ShadowTest
|
|
149
|
-
# (pre-promotion) and ModelRollback (post-promotion). The ndcg_at_10
|
|
150
|
-
# signal materialises when ``EngagementRewardModel.finalize_outcome``
|
|
151
|
-
# settles a row — that is the natural call site for this helper.
|
|
152
|
-
#
|
|
153
|
-
# This is a THIN wrapper over ``core.shadow_router.get_shadow_router``
|
|
154
|
-
# so the finalize-outcome path does not need to import shadow_router
|
|
155
|
-
# directly. Fail-soft on every error — recall pipeline integrity comes
|
|
156
|
-
# first.
|
|
157
|
-
# ---------------------------------------------------------------------------
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
def feed_recall_settled(
|
|
161
|
-
*,
|
|
162
|
-
memory_db: str,
|
|
163
|
-
learning_db: str,
|
|
164
|
-
profile_id: str,
|
|
165
|
-
query_id: str,
|
|
166
|
-
ndcg_at_10: float,
|
|
167
|
-
) -> None:
|
|
168
|
-
"""Route a settled recall's NDCG@10 into the shadow router.
|
|
169
|
-
|
|
170
|
-
The arm is recomputed from ``query_id`` so callers don't need to
|
|
171
|
-
persist arm assignment anywhere — the router's determinism
|
|
172
|
-
guarantees the same arm decision at settle-time that was used at
|
|
173
|
-
recall-time.
|
|
174
|
-
|
|
175
|
-
Called from ``EngagementRewardModel.finalize_outcome`` (LLD-08 §4.2)
|
|
176
|
-
after the reward row is committed. Cheap on the hot path: one
|
|
177
|
-
singleton-cache read + one paired-list append.
|
|
178
|
-
"""
|
|
179
|
-
try:
|
|
180
|
-
from superlocalmemory.core import shadow_router as _sr
|
|
181
|
-
router = _sr.get_shadow_router(
|
|
182
|
-
memory_db=memory_db,
|
|
183
|
-
learning_db=learning_db,
|
|
184
|
-
profile_id=profile_id,
|
|
185
|
-
)
|
|
186
|
-
arm = router.route_query(query_id)
|
|
187
|
-
router.on_recall_settled(
|
|
188
|
-
query_id=query_id, arm=arm, ndcg_at_10=float(ndcg_at_10),
|
|
189
|
-
)
|
|
190
|
-
except Exception as exc: # pragma: no cover — defence in depth
|
|
191
|
-
logger.debug("feed_recall_settled error: %s", exc)
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
# ---------------------------------------------------------------------------
|
|
195
|
-
# V3.3.16: Module-level singletons for recall hot-path objects.
|
|
196
|
-
# Prevents creating new BehavioralTracker / ForgettingScheduler per recall
|
|
197
|
-
# (304 recalls = 304 objects that fragment pymalloc arenas → 25GB).
|
|
198
|
-
# ---------------------------------------------------------------------------
|
|
199
|
-
|
|
200
|
-
_behavioral_tracker_cache: dict[int, object] = {}
|
|
201
|
-
_forgetting_scheduler_cache: dict[int, object] = {}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
def _get_behavioral_tracker(db: Any) -> Any:
|
|
205
|
-
"""Get or create a cached BehavioralTracker for this DB instance."""
|
|
206
|
-
key = id(db)
|
|
207
|
-
if key not in _behavioral_tracker_cache:
|
|
208
|
-
from superlocalmemory.learning.behavioral import BehavioralTracker
|
|
209
|
-
_behavioral_tracker_cache[key] = BehavioralTracker(db)
|
|
210
|
-
return _behavioral_tracker_cache[key]
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
def _get_forgetting_scheduler(db: Any, config: Any) -> Any:
|
|
214
|
-
"""Get or create a cached ForgettingScheduler for this DB instance."""
|
|
215
|
-
key = id(db)
|
|
216
|
-
if key not in _forgetting_scheduler_cache:
|
|
217
|
-
from superlocalmemory.learning.forgetting_scheduler import ForgettingScheduler
|
|
218
|
-
from superlocalmemory.math.ebbinghaus import EbbinghausCurve
|
|
219
|
-
ebbinghaus = EbbinghausCurve(config.forgetting)
|
|
220
|
-
_forgetting_scheduler_cache[key] = ForgettingScheduler(db, ebbinghaus, config.forgetting)
|
|
221
|
-
return _forgetting_scheduler_cache[key]
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
def release_recall_resources(db: Any) -> None:
|
|
225
|
-
"""Release process-level recall singletons owned by a closing database."""
|
|
226
|
-
key = id(db)
|
|
227
|
-
_behavioral_tracker_cache.pop(key, None)
|
|
228
|
-
_forgetting_scheduler_cache.pop(key, None)
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
def _behavioral_entities(results: list[Any], limit: int = 20) -> list[str]:
|
|
232
|
-
"""Read canonical entity IDs from the typed retrieval-result contract.
|
|
233
|
-
|
|
234
|
-
``RetrievalResult`` is a dataclass whose entity evidence lives on
|
|
235
|
-
``result.fact.canonical_entities``. Treating it as a SQLite mapping silently
|
|
236
|
-
discarded every entity and disabled behavioral entity learning.
|
|
237
|
-
"""
|
|
238
|
-
entities: list[str] = []
|
|
239
|
-
seen: set[str] = set()
|
|
240
|
-
for result in results:
|
|
241
|
-
fact = getattr(result, "fact", None)
|
|
242
|
-
for entity_id in getattr(fact, "canonical_entities", ()) or ():
|
|
243
|
-
if not isinstance(entity_id, str) or not entity_id or entity_id in seen:
|
|
244
|
-
continue
|
|
245
|
-
seen.add(entity_id)
|
|
246
|
-
entities.append(entity_id)
|
|
247
|
-
if len(entities) >= limit:
|
|
248
|
-
return entities
|
|
249
|
-
return entities
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
# ---------------------------------------------------------------------------
|
|
253
|
-
# S8-ARC-04 (v3.4.22): unified ranking entry point.
|
|
254
|
-
# ---------------------------------------------------------------------------
|
|
255
|
-
|
|
256
|
-
_RANKING_MODES: frozenset[str] = frozenset({"off", "v1", "v2", "v2-ensemble"})
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
from superlocalmemory.learning.signal_kinds import FEEDBACK_ONLY_SQL
|
|
260
|
-
|
|
261
|
-
class _ReadOnlyLearningView:
|
|
262
|
-
"""Minimal learning-model reader that cannot initialise or mutate a DB."""
|
|
263
|
-
|
|
264
|
-
def __init__(self, db_path: Path) -> None:
|
|
265
|
-
self._db_path = db_path.resolve()
|
|
266
|
-
|
|
267
|
-
def _connection(self) -> sqlite3.Connection:
|
|
268
|
-
connection = sqlite3.connect(
|
|
269
|
-
f"{self._db_path.as_uri()}?mode=ro",
|
|
270
|
-
uri=True,
|
|
271
|
-
timeout=0.25,
|
|
272
|
-
)
|
|
273
|
-
connection.row_factory = sqlite3.Row
|
|
274
|
-
connection.execute("PRAGMA query_only=ON")
|
|
275
|
-
connection.execute("PRAGMA busy_timeout=250")
|
|
276
|
-
return connection
|
|
277
|
-
|
|
278
|
-
def count_signals(self, profile_id: str) -> int:
|
|
279
|
-
"""Count FEEDBACK rows only — an exposure is not feedback.
|
|
280
|
-
|
|
281
|
-
Must agree with ``LearningDatabase.count_signals``: a phase resolved
|
|
282
|
-
from one of these is compared against a threshold resolved from the
|
|
283
|
-
other, so a difference between them is a phase that flickers. Both
|
|
284
|
-
share ``FEEDBACK_ONLY_SQL``; a test asserts neither carries its own
|
|
285
|
-
copy of the predicate.
|
|
286
|
-
"""
|
|
287
|
-
connection = self._connection()
|
|
288
|
-
try:
|
|
289
|
-
row = connection.execute(
|
|
290
|
-
"SELECT COUNT(*) AS count FROM learning_signals "
|
|
291
|
-
f"WHERE profile_id = ?{FEEDBACK_ONLY_SQL}",
|
|
292
|
-
(profile_id,),
|
|
293
|
-
).fetchone()
|
|
294
|
-
return int(row["count"]) if row else 0
|
|
295
|
-
finally:
|
|
296
|
-
connection.close()
|
|
297
|
-
|
|
298
|
-
def count_feedback(self, profile_id: str) -> int:
|
|
299
|
-
"""Count legacy feedback without running schema initialization.
|
|
300
|
-
|
|
301
|
-
Reports the raw ``learning_feedback`` table, which the dashboard
|
|
302
|
-
surfaces as ``legacy_feedback_rows`` alongside a pending-migration
|
|
303
|
-
card. Do NOT gate a ranking phase on this — use ``count_signals``,
|
|
304
|
-
which is the counter every other surface resolves its phase from
|
|
305
|
-
(issue #106).
|
|
306
|
-
"""
|
|
307
|
-
connection = self._connection()
|
|
308
|
-
try:
|
|
309
|
-
row = connection.execute(
|
|
310
|
-
"SELECT COUNT(*) AS count FROM learning_feedback "
|
|
311
|
-
"WHERE profile_id = ?",
|
|
312
|
-
(profile_id,),
|
|
313
|
-
).fetchone()
|
|
314
|
-
return int(row["count"]) if row else 0
|
|
315
|
-
finally:
|
|
316
|
-
connection.close()
|
|
317
|
-
|
|
318
|
-
def load_active_model(self, profile_id: str) -> dict[str, Any] | None:
|
|
319
|
-
connection = self._connection()
|
|
320
|
-
try:
|
|
321
|
-
row = connection.execute(
|
|
322
|
-
"SELECT state_bytes, bytes_sha256, feature_names, trained_at, "
|
|
323
|
-
"model_version FROM learning_model_state "
|
|
324
|
-
"WHERE profile_id = ? AND is_active = 1 LIMIT 1",
|
|
325
|
-
(profile_id,),
|
|
326
|
-
).fetchone()
|
|
327
|
-
if row is None:
|
|
328
|
-
return None
|
|
329
|
-
return {
|
|
330
|
-
"state_bytes": bytes(row["state_bytes"]),
|
|
331
|
-
"bytes_sha256": row["bytes_sha256"],
|
|
332
|
-
"feature_names": row["feature_names"],
|
|
333
|
-
"trained_at": row["trained_at"],
|
|
334
|
-
"model_version": row["model_version"],
|
|
335
|
-
}
|
|
336
|
-
finally:
|
|
337
|
-
connection.close()
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
def _resolve_ranking_mode(env: "dict[str, str] | os._Environ[str]") -> str:
|
|
341
|
-
"""Map the ``SLM_RANKING`` env var to a canonical mode.
|
|
342
|
-
|
|
343
|
-
``SLM_RANKING`` is an explicit operator policy, and the absence of one
|
|
344
|
-
means ``off``: an upgrade must not start reordering results on its own.
|
|
345
|
-
|
|
346
|
-
Fixing the settlement path was necessary for the ensemble to be worth
|
|
347
|
-
enabling, but it is not sufficient. Settlement decides what an arm learns
|
|
348
|
-
*after* a query; the channel weights come from sampling that arm's
|
|
349
|
-
posterior *before* it. Until arms have posteriors shaped by real
|
|
350
|
-
observations, that sample is drawn from the prior, so two identical
|
|
351
|
-
queries can be weighted differently for no reason the store can justify —
|
|
352
|
-
variance with nothing bought by it. A memory system's answers should move
|
|
353
|
-
because the evidence moved.
|
|
354
|
-
|
|
355
|
-
So the ensemble stays opt-in until arms carry settled rewards. Set
|
|
356
|
-
``SLM_RANKING=v2-ensemble`` to enable it, or ``v1``/``v2`` for the
|
|
357
|
-
narrower passes.
|
|
358
|
-
"""
|
|
359
|
-
raw = (env.get("SLM_RANKING", "") or "").strip().lower()
|
|
360
|
-
if raw in _RANKING_MODES:
|
|
361
|
-
return raw
|
|
362
|
-
return "off"
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
def apply_ranking(
|
|
366
|
-
response: "RecallResponse",
|
|
367
|
-
query: str,
|
|
368
|
-
profile_id: str,
|
|
369
|
-
query_id: str,
|
|
370
|
-
*,
|
|
371
|
-
config: Any = None,
|
|
372
|
-
pipeline_version: str = "v2-ensemble",
|
|
373
|
-
record_signals: bool = False,
|
|
374
|
-
record_plays: bool = True,
|
|
375
|
-
memory_db_path: Any = None,
|
|
376
|
-
play_sink: dict | None = None,
|
|
377
|
-
) -> "RecallResponse":
|
|
378
|
-
"""Run the ranking pipeline at the requested version.
|
|
379
|
-
|
|
380
|
-
Modes:
|
|
381
|
-
- ``off``: identity — no ranking passes run at all.
|
|
382
|
-
- ``v1``: v3.1 Active-Memory adaptive rerank only.
|
|
383
|
-
- ``v2``: v1 + v3.4.22 lambdarank rerank + signal enqueue.
|
|
384
|
-
- ``v2-ensemble`` (default): v2 + v3.4.22 contextual-bandit ensemble.
|
|
385
|
-
|
|
386
|
-
Each underlying pass is already defensive (catches its own exceptions),
|
|
387
|
-
so this wrapper adds an outer try/except to guarantee the caller
|
|
388
|
-
always gets a response back. Previously three separate call sites in
|
|
389
|
-
run_recall chained these; collapsing keeps precedence explicit.
|
|
390
|
-
"""
|
|
391
|
-
if pipeline_version == "off":
|
|
392
|
-
return response
|
|
393
|
-
try:
|
|
394
|
-
response = apply_adaptive_ranking(response, query, profile_id,
|
|
395
|
-
config=config)
|
|
396
|
-
except Exception as exc: # pragma: no cover — defensive
|
|
397
|
-
logger.debug("apply_ranking v1 step skipped: %s", exc)
|
|
398
|
-
if pipeline_version == "v1":
|
|
399
|
-
return response
|
|
400
|
-
try:
|
|
401
|
-
response = apply_v2_adaptive_ranking(
|
|
402
|
-
response, query, profile_id, query_id,
|
|
403
|
-
)
|
|
404
|
-
except Exception as exc: # pragma: no cover — defensive
|
|
405
|
-
logger.debug("apply_ranking v2 step skipped: %s", exc)
|
|
406
|
-
if pipeline_version == "v2":
|
|
407
|
-
return response
|
|
408
|
-
try:
|
|
409
|
-
response = apply_v2_bandit_ensemble(
|
|
410
|
-
response, query, profile_id, query_id,
|
|
411
|
-
record_signals=record_signals,
|
|
412
|
-
record_plays=record_plays,
|
|
413
|
-
memory_db_path=memory_db_path,
|
|
414
|
-
play_sink=play_sink,
|
|
415
|
-
)
|
|
416
|
-
except Exception as exc: # pragma: no cover — defensive
|
|
417
|
-
logger.debug("apply_ranking ensemble step skipped: %s", exc)
|
|
418
|
-
return response
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
# ---------------------------------------------------------------------------
|
|
422
|
-
# apply_adaptive_ranking (was MemoryEngine._apply_adaptive_ranking)
|
|
423
|
-
# ---------------------------------------------------------------------------
|
|
424
|
-
|
|
425
|
-
def apply_adaptive_ranking(
|
|
426
|
-
response: RecallResponse,
|
|
427
|
-
query: str,
|
|
428
|
-
pid: str,
|
|
429
|
-
*,
|
|
430
|
-
config: SLMConfig,
|
|
431
|
-
) -> RecallResponse:
|
|
432
|
-
"""Apply adaptive re-ranking if enough learning signals exist.
|
|
433
|
-
|
|
434
|
-
Phase 1 (< 50 signals): returns response unchanged (backward compat).
|
|
435
|
-
Phase 2 (50+): heuristic boosts from recency, access count, trust.
|
|
436
|
-
Phase 3 (200+): LightGBM ML-based reranking.
|
|
437
|
-
"""
|
|
438
|
-
from superlocalmemory.infra.data_root import state_path
|
|
439
|
-
learning_db = state_path("learning.db")
|
|
440
|
-
if not learning_db.exists():
|
|
441
|
-
return response
|
|
442
|
-
|
|
443
|
-
# issue #106: count the CANONICAL store, not the legacy one. The
|
|
444
|
-
# dashboard's Living Brain panel and ranker-phase card both resolve their
|
|
445
|
-
# phase from ``learning_signals``; this gate read ``learning_feedback``,
|
|
446
|
-
# so the phase a user was shown and the phase that actually ranked their
|
|
447
|
-
# results were computed from different tables and could disagree without
|
|
448
|
-
# limit. ``learning_feedback`` rows reach this counter through
|
|
449
|
-
# ``legacy_migration``, which copies them forward.
|
|
450
|
-
try:
|
|
451
|
-
signal_count = _ReadOnlyLearningView(learning_db).count_signals(pid)
|
|
452
|
-
except sqlite3.Error:
|
|
453
|
-
# A pre-learning database may not have this optional table yet.
|
|
454
|
-
# Recall remains a query and cannot create it on demand.
|
|
455
|
-
return response
|
|
456
|
-
|
|
457
|
-
from superlocalmemory.learning.ranker import (
|
|
458
|
-
PHASE_2_THRESHOLD,
|
|
459
|
-
AdaptiveRanker,
|
|
460
|
-
)
|
|
461
|
-
|
|
462
|
-
# Thresholds come from ``learning.ranker`` — the same constants the
|
|
463
|
-
# dashboard gates on. Duplicating the literals here is how the two
|
|
464
|
-
# surfaces drifted apart in the first place.
|
|
465
|
-
if signal_count < PHASE_2_THRESHOLD:
|
|
466
|
-
return response # Phase 1: no change
|
|
467
|
-
|
|
468
|
-
ranker = AdaptiveRanker(signal_count=signal_count)
|
|
469
|
-
|
|
470
|
-
from datetime import UTC
|
|
471
|
-
from datetime import datetime as _dt
|
|
472
|
-
_now = _dt.now(UTC)
|
|
473
|
-
|
|
474
|
-
result_dicts = []
|
|
475
|
-
for r in response.results:
|
|
476
|
-
_age = 0.0
|
|
477
|
-
_created = getattr(r.fact, "created_at", None)
|
|
478
|
-
if _created:
|
|
479
|
-
try:
|
|
480
|
-
_age = max(0.0, (_now - _dt.fromisoformat(
|
|
481
|
-
_created.replace("Z", "+00:00")
|
|
482
|
-
)).total_seconds() / 86400.0)
|
|
483
|
-
except (ValueError, TypeError):
|
|
484
|
-
pass
|
|
485
|
-
result_dicts.append({
|
|
486
|
-
"score": r.score,
|
|
487
|
-
"cross_encoder_score": r.score,
|
|
488
|
-
"trust_score": r.trust_score,
|
|
489
|
-
"channel_scores": r.channel_scores or {},
|
|
490
|
-
"fact": {
|
|
491
|
-
"age_days": _age,
|
|
492
|
-
"access_count": r.fact.access_count,
|
|
493
|
-
},
|
|
494
|
-
"_original": r,
|
|
495
|
-
})
|
|
496
|
-
|
|
497
|
-
query_context = {"query_type": response.query_type}
|
|
498
|
-
reranked = ranker.rerank(result_dicts, query_context)
|
|
499
|
-
|
|
500
|
-
# Rebuild response with new ordering
|
|
501
|
-
for item in reranked:
|
|
502
|
-
original = item.get("_original")
|
|
503
|
-
if original is not None and item.get("_adaptive_score") is not None:
|
|
504
|
-
original.ranking_score = float(item["_adaptive_score"])
|
|
505
|
-
new_results = [d["_original"] for d in reranked]
|
|
506
|
-
|
|
507
|
-
return RecallResponse(
|
|
508
|
-
query=response.query,
|
|
509
|
-
mode=response.mode,
|
|
510
|
-
results=new_results,
|
|
511
|
-
query_type=response.query_type,
|
|
512
|
-
channel_weights=response.channel_weights,
|
|
513
|
-
total_candidates=response.total_candidates,
|
|
514
|
-
retrieval_time_ms=response.retrieval_time_ms,
|
|
515
|
-
# v3.6.6: preserve evidence-floor signal across reranking rebuilds.
|
|
516
|
-
no_confident_match=(len(new_results) == 0) and response.no_confident_match,
|
|
517
|
-
)
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
# ---------------------------------------------------------------------------
|
|
521
|
-
# apply_v2_adaptive_ranking (LLD-02 §4.3)
|
|
522
|
-
# ---------------------------------------------------------------------------
|
|
523
|
-
#
|
|
524
|
-
# Opt-in v3.4.22 path: load active model from learning.db with SHA-256
|
|
525
|
-
# verification, re-rank via native Booster, enqueue signals async. The
|
|
526
|
-
# existing ``apply_adaptive_ranking`` above stays for 3.4.20 callers.
|
|
527
|
-
# ---------------------------------------------------------------------------
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
def apply_v2_adaptive_ranking(
|
|
531
|
-
response: RecallResponse,
|
|
532
|
-
query: str,
|
|
533
|
-
profile_id: str,
|
|
534
|
-
query_id: str,
|
|
535
|
-
*,
|
|
536
|
-
learning_db_path: Any = None,
|
|
537
|
-
) -> RecallResponse:
|
|
538
|
-
"""LLD-02 §4.3 — load verified model, rerank, enqueue signals.
|
|
539
|
-
|
|
540
|
-
Never raises. On any error, returns ``response`` unchanged.
|
|
541
|
-
"""
|
|
542
|
-
try:
|
|
543
|
-
from pathlib import Path as _P
|
|
544
|
-
|
|
545
|
-
from superlocalmemory.infra.data_root import state_path
|
|
546
|
-
from superlocalmemory.learning.model_cache import load_active
|
|
547
|
-
from superlocalmemory.learning.ranker import AdaptiveRanker
|
|
548
|
-
|
|
549
|
-
db_path = (_P(learning_db_path) if learning_db_path
|
|
550
|
-
else state_path("learning.db"))
|
|
551
|
-
if not db_path.exists():
|
|
552
|
-
return response
|
|
553
|
-
|
|
554
|
-
db = _ReadOnlyLearningView(db_path)
|
|
555
|
-
signal_count = db.count_signals(profile_id)
|
|
556
|
-
active = load_active(db, profile_id)
|
|
557
|
-
|
|
558
|
-
ranker = AdaptiveRanker(
|
|
559
|
-
signal_count=signal_count,
|
|
560
|
-
active_model=active,
|
|
561
|
-
)
|
|
562
|
-
|
|
563
|
-
# Build result-dict shape expected by the ranker's rerank() path.
|
|
564
|
-
from datetime import UTC
|
|
565
|
-
from datetime import datetime as _dt
|
|
566
|
-
_now_v2 = _dt.now(UTC)
|
|
567
|
-
|
|
568
|
-
result_dicts: list[dict] = []
|
|
569
|
-
for r in response.results:
|
|
570
|
-
_age_v2 = 0.0
|
|
571
|
-
_created_v2 = getattr(r.fact, "created_at", None)
|
|
572
|
-
if _created_v2:
|
|
573
|
-
try:
|
|
574
|
-
_age_v2 = max(0.0, (_now_v2 - _dt.fromisoformat(
|
|
575
|
-
_created_v2.replace("Z", "+00:00")
|
|
576
|
-
)).total_seconds() / 86400.0)
|
|
577
|
-
except (ValueError, TypeError):
|
|
578
|
-
pass
|
|
579
|
-
result_dicts.append({
|
|
580
|
-
"fact_id": r.fact.fact_id,
|
|
581
|
-
"score": r.score,
|
|
582
|
-
"cross_encoder_score": r.score,
|
|
583
|
-
"trust_score": r.trust_score,
|
|
584
|
-
"channel_scores": r.channel_scores or {},
|
|
585
|
-
"fact": {
|
|
586
|
-
"age_days": _age_v2,
|
|
587
|
-
"access_count": r.fact.access_count,
|
|
588
|
-
},
|
|
589
|
-
"_original": r,
|
|
590
|
-
})
|
|
591
|
-
|
|
592
|
-
query_context = {
|
|
593
|
-
"query_type": response.query_type,
|
|
594
|
-
"profile_id": profile_id,
|
|
595
|
-
}
|
|
596
|
-
reranked_dicts = ranker.rerank(result_dicts, query_context)
|
|
597
|
-
for item in reranked_dicts:
|
|
598
|
-
original = item.get("_original")
|
|
599
|
-
if original is not None and item.get("_adaptive_score") is not None:
|
|
600
|
-
original.ranking_score = float(item["_adaptive_score"])
|
|
601
|
-
new_results = [d["_original"] for d in reranked_dicts
|
|
602
|
-
if "_original" in d]
|
|
603
|
-
|
|
604
|
-
# S8-SK-04 fix: signal enqueue is OWNED by ``apply_v2_bandit_ensemble``
|
|
605
|
-
# (see below), not this function. Previously both emitted a batch
|
|
606
|
-
# under the same query_id which doubled ``learning_signals`` and
|
|
607
|
-
# tripped the phase-transition threshold at half the intended
|
|
608
|
-
# signal count. This function now just re-ranks; the ensemble path
|
|
609
|
-
# is the single source of signal events.
|
|
610
|
-
|
|
611
|
-
return RecallResponse(
|
|
612
|
-
query=response.query,
|
|
613
|
-
mode=response.mode,
|
|
614
|
-
results=new_results,
|
|
615
|
-
query_type=response.query_type,
|
|
616
|
-
channel_weights=response.channel_weights,
|
|
617
|
-
total_candidates=response.total_candidates,
|
|
618
|
-
retrieval_time_ms=response.retrieval_time_ms,
|
|
619
|
-
# v3.6.6: preserve evidence-floor signal across reranking rebuilds.
|
|
620
|
-
no_confident_match=(len(new_results) == 0) and response.no_confident_match,
|
|
621
|
-
)
|
|
622
|
-
except Exception as exc: # pragma: no cover — defensive
|
|
623
|
-
logger.debug("apply_v2_adaptive_ranking skipped: %s", exc)
|
|
624
|
-
return response
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
# ---------------------------------------------------------------------------
|
|
628
|
-
# apply_v2_bandit_ensemble (LLD-03 §5.5)
|
|
629
|
-
# ---------------------------------------------------------------------------
|
|
630
|
-
#
|
|
631
|
-
# Contextual Thompson bandit chooses channel weights. If an LGBM model is
|
|
632
|
-
# active, a D8-blended ensemble re-ranks the reweighted candidates. Never
|
|
633
|
-
# raises; honours ``SLM_BANDIT_DISABLED=1`` as a kill switch.
|
|
634
|
-
# ---------------------------------------------------------------------------
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
def _rank_key(result) -> tuple[float, str]:
|
|
638
|
-
"""Descending ordering key for the score-bias passes.
|
|
639
|
-
|
|
640
|
-
Falls back to ``score`` exactly as the writes do. Falling back to 0.0
|
|
641
|
-
instead — which is what a bare ``ranking_score or 0.0`` does — sorts every
|
|
642
|
-
result that no bias touched as if it scored nothing, collapsing the whole
|
|
643
|
-
un-adjusted tail to the bottom in fact_id order and discarding the ordering
|
|
644
|
-
retrieval worked for. ``ensemble_rerank`` populates ``ranking_score`` on
|
|
645
|
-
every candidate, so that only bites when the ensemble is disabled or has
|
|
646
|
-
fallen back — which is precisely when a bias must still behave.
|
|
647
|
-
|
|
648
|
-
One function rather than the same expression at each call site: two passes
|
|
649
|
-
that sort by different keys silently overwrite each other's ordering.
|
|
650
|
-
|
|
651
|
-
``is None`` rather than a falsy test, because 0.0 is a real score. A
|
|
652
|
-
cross-encoder that judges a candidate irrelevant returns exactly that, and
|
|
653
|
-
``x or y`` would then discard the model's verdict and rank the candidate on
|
|
654
|
-
its retrieval score instead — quietly promoting the thing the model just
|
|
655
|
-
ruled out.
|
|
656
|
-
"""
|
|
657
|
-
utility = result.ranking_score
|
|
658
|
-
if utility is None:
|
|
659
|
-
utility = result.score
|
|
660
|
-
if utility is None:
|
|
661
|
-
utility = 0.0
|
|
662
|
-
return (-float(utility), result.fact.fact_id)
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
def _apply_outcome_bonus(
|
|
666
|
-
results: list, profile_id: str, memory_db_path: Any = None,
|
|
667
|
-
) -> list:
|
|
668
|
-
"""Nudge ranking by whether each memory has demonstrably helped before.
|
|
669
|
-
|
|
670
|
-
Bounded to ``pcos.MAX_BONUS`` (0.15) and weighted by how often the fact has
|
|
671
|
-
actually settled, so history breaks ties between similar-looking memories
|
|
672
|
-
and cannot drag a poor match to the top.
|
|
673
|
-
|
|
674
|
-
Order is recomputed here rather than left to the caller: a bonus that does
|
|
675
|
-
not reorder anything is a number nobody reads, which is the failure mode
|
|
676
|
-
this whole wave exists to fix.
|
|
677
|
-
|
|
678
|
-
Never raises. On a store where M045 has not run, ``fetch_scores`` returns
|
|
679
|
-
``{}`` and this is the identity function.
|
|
680
|
-
"""
|
|
681
|
-
if not results:
|
|
682
|
-
return results
|
|
683
|
-
try:
|
|
684
|
-
import sqlite3 as _sq
|
|
685
|
-
|
|
686
|
-
from superlocalmemory.infra.data_root import state_path
|
|
687
|
-
from superlocalmemory.learning.pcos import (
|
|
688
|
-
RECENT_TOPS,
|
|
689
|
-
bonus_for,
|
|
690
|
-
fetch_scores,
|
|
691
|
-
)
|
|
692
|
-
|
|
693
|
-
ids = [r.fact.fact_id for r in results if getattr(r, "fact", None)]
|
|
694
|
-
if not ids:
|
|
695
|
-
return results
|
|
696
|
-
# The store the RECALL used, not whichever one the data-root default
|
|
697
|
-
# resolves to. Hardcoding state_path("memory.db") worked on a default
|
|
698
|
-
# install and silently read another store's scores — or nothing at all —
|
|
699
|
-
# anywhere the engine was pointed elsewhere: a second data root, a test
|
|
700
|
-
# store, a config whose db_path diverges. Both fail modes are silent,
|
|
701
|
-
# because this function is fail-open by design.
|
|
702
|
-
db_file = Path(memory_db_path) if memory_db_path else state_path("memory.db")
|
|
703
|
-
conn = _sq.connect(f"file:{db_file}?mode=ro", uri=True, timeout=0.5)
|
|
704
|
-
try:
|
|
705
|
-
scores = fetch_scores(conn, profile_id, ids)
|
|
706
|
-
finally:
|
|
707
|
-
conn.close()
|
|
708
|
-
if not scores:
|
|
709
|
-
return results
|
|
710
|
-
|
|
711
|
-
adjusted = []
|
|
712
|
-
for r in results:
|
|
713
|
-
entry = scores.get(r.fact.fact_id)
|
|
714
|
-
if not entry:
|
|
715
|
-
adjusted.append(r)
|
|
716
|
-
continue
|
|
717
|
-
# A fact that has been winning first place lately stops EARNING the
|
|
718
|
-
# bonus. It keeps everything retrieval gave it — the alternative
|
|
719
|
-
# considered was a 10x demotion, which would bury a memory whose
|
|
720
|
-
# only offence is having been useful.
|
|
721
|
-
if RECENT_TOPS.capped(profile_id, r.fact.fact_id):
|
|
722
|
-
adjusted.append(r)
|
|
723
|
-
continue
|
|
724
|
-
delta = bonus_for(*entry)
|
|
725
|
-
if delta == 0.0:
|
|
726
|
-
adjusted.append(r)
|
|
727
|
-
continue
|
|
728
|
-
adjusted.append(replace(
|
|
729
|
-
r,
|
|
730
|
-
ranking_score=float(
|
|
731
|
-
r.ranking_score if r.ranking_score is not None else r.score
|
|
732
|
-
) + delta,
|
|
733
|
-
))
|
|
734
|
-
adjusted.sort(key=_rank_key)
|
|
735
|
-
if adjusted:
|
|
736
|
-
RECENT_TOPS.record_top(profile_id, adjusted[0].fact.fact_id)
|
|
737
|
-
return adjusted
|
|
738
|
-
except Exception as exc: # pragma: no cover — advisory, never fatal
|
|
739
|
-
logger.debug("outcome bonus skipped: %s", exc)
|
|
740
|
-
return results
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
#: Largest lift a memory can earn purely from having been in the last few
|
|
744
|
-
#: turns. Additive on ``ranking_score``, deliberately not a multiplier.
|
|
745
|
-
#:
|
|
746
|
-
#: A multiplier was the first design and it is scale-dependent, which makes the
|
|
747
|
-
#: same constant mean different things on two installs. Measured: with a
|
|
748
|
-
#: cross-encoder loaded, ranking scores land near [0, 1] and 1.5x is worth about
|
|
749
|
-
#: +0.45; with no reranker configured, raw fusion scores top out at
|
|
750
|
-
#: ``n_channels/(rrf_k+1)`` — 0.08 at k=60 — and the same 1.5x is worth +0.03.
|
|
751
|
-
#: One constant, a fifteen-fold difference in effect, decided by installed
|
|
752
|
-
#: optional components. An additive cap behaves the same everywhere.
|
|
753
|
-
#:
|
|
754
|
-
#: 0.20 sits just above the ceiling on the outcome bonus (0.15), so continuity
|
|
755
|
-
#: of attention outranks historical usefulness when the two disagree. Both are
|
|
756
|
-
#: bounded, so neither can drag a genuinely poor match to the top.
|
|
757
|
-
WM_MAX_BONUS = 0.20
|
|
758
|
-
|
|
759
|
-
#: Everything the ranking passes may add to one result, summed.
|
|
760
|
-
#:
|
|
761
|
-
#: Each pass caps its own contribution, which says nothing about the total —
|
|
762
|
-
#: and the total is what decides whether a genuinely better match can be
|
|
763
|
-
#: displaced. Two passes at 0.15 and 0.20 already reach 0.35; a third added
|
|
764
|
-
#: later would raise the ceiling with nothing to notice.
|
|
765
|
-
#:
|
|
766
|
-
#: Stated here so the invariant is one number rather than an emergent property
|
|
767
|
-
#: of however many passes exist, and asserted by test: a result ahead by more
|
|
768
|
-
#: than this cannot be overtaken by bias alone, and the sum of the individual
|
|
769
|
-
#: caps must not exceed it.
|
|
770
|
-
MAX_TOTAL_BIAS = 0.35
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
def _apply_working_memory_bias(
|
|
774
|
-
results: list, profile_id: str, session_id: str,
|
|
775
|
-
) -> list:
|
|
776
|
-
"""Lift memories this session was just looking at.
|
|
777
|
-
|
|
778
|
-
Retrieval still runs in full and this changes nothing about what was
|
|
779
|
-
retrieved — it reorders what came back. A memory absent from the working
|
|
780
|
-
set is untouched, so an empty working set is exactly the identity function
|
|
781
|
-
and the first turn of every session behaves as before.
|
|
782
|
-
|
|
783
|
-
Reads only in-process state: no query, no connection, no file. Deliberately
|
|
784
|
-
does not create a working set — a bias that ran on every recall would
|
|
785
|
-
otherwise register an entry for every session id that ever appears.
|
|
786
|
-
|
|
787
|
-
Never raises. Attention is advisory; a recall that cannot be biased is
|
|
788
|
-
still a correct recall.
|
|
789
|
-
|
|
790
|
-
WHAT OUTRANKS WHAT
|
|
791
|
-
------------------
|
|
792
|
-
This is not the last word on the order. An exact lexical hit is promoted to
|
|
793
|
-
the top after every learned layer, including this one, so the full precedence
|
|
794
|
-
is:
|
|
795
|
-
|
|
796
|
-
exact lexical match > continuity > demonstrated usefulness > retrieval
|
|
797
|
-
|
|
798
|
-
That ordering is deliberate — a memory containing the caller's exact words
|
|
799
|
-
should not sit behind a merely similar one — but it means a boosted memory
|
|
800
|
-
can be moved back off position 1 by a guard that runs later. Two independent
|
|
801
|
-
reviewers raised it as a bug, which is what an undocumented precedence looks
|
|
802
|
-
like from outside, so it is written down here rather than left to be
|
|
803
|
-
rediscovered.
|
|
804
|
-
"""
|
|
805
|
-
if not results or not is_conversation(session_id, profile_id):
|
|
806
|
-
return results
|
|
807
|
-
try:
|
|
808
|
-
from superlocalmemory.core.working_memory import peek
|
|
809
|
-
|
|
810
|
-
wm = peek(profile_id, session_id)
|
|
811
|
-
if wm is None:
|
|
812
|
-
return results
|
|
813
|
-
held = wm.boost_set()
|
|
814
|
-
if not held:
|
|
815
|
-
return results
|
|
816
|
-
|
|
817
|
-
adjusted = []
|
|
818
|
-
for r in results:
|
|
819
|
-
fact = getattr(r, "fact", None)
|
|
820
|
-
if fact is None or fact.fact_id not in held:
|
|
821
|
-
adjusted.append(r)
|
|
822
|
-
continue
|
|
823
|
-
adjusted.append(replace(
|
|
824
|
-
r,
|
|
825
|
-
ranking_score=float(
|
|
826
|
-
r.ranking_score if r.ranking_score is not None else r.score
|
|
827
|
-
) + WM_MAX_BONUS,
|
|
828
|
-
))
|
|
829
|
-
# Same ordering key as the outcome bonus, so the two compose instead of
|
|
830
|
-
# one silently overwriting the other's sort.
|
|
831
|
-
adjusted.sort(key=_rank_key)
|
|
832
|
-
return adjusted
|
|
833
|
-
except Exception as exc: # pragma: no cover — advisory, never fatal
|
|
834
|
-
logger.debug("working-memory bias skipped: %s", exc)
|
|
835
|
-
return results
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
def _resettle_shown_after_bias(
|
|
839
|
-
play_sink: dict, profile_id: str, results: list, shown_before: list[str],
|
|
840
|
-
) -> None:
|
|
841
|
-
"""Correct the play's evidence when the bias changed what was shown.
|
|
842
|
-
|
|
843
|
-
A play is settled later by asking whether anything downstream referenced one
|
|
844
|
-
of the memories this query surfaced. That question is answered against a
|
|
845
|
-
stored list, and the list was written before the continuity bias ran — so a
|
|
846
|
-
memory the bias lifted into the answer was absent from it, and an outcome
|
|
847
|
-
citing that memory settled nothing.
|
|
848
|
-
|
|
849
|
-
Rewritten only when the set actually differs, which is the uncommon case: a
|
|
850
|
-
cold session, or a warm one whose held memories were already at the top,
|
|
851
|
-
costs nothing. Never raises — a play whose evidence cannot be corrected
|
|
852
|
-
settles on the old list, which is the behaviour before this existed.
|
|
853
|
-
"""
|
|
854
|
-
play_id = play_sink.get("play_id")
|
|
855
|
-
learning_db = play_sink.get("learning_db")
|
|
856
|
-
if not play_id or not learning_db:
|
|
857
|
-
return
|
|
858
|
-
try:
|
|
859
|
-
from superlocalmemory.core.working_memory import ADMIT_TOP_N
|
|
860
|
-
from superlocalmemory.learning.bandit import ContextualBandit
|
|
861
|
-
|
|
862
|
-
shown_now = [
|
|
863
|
-
r.fact.fact_id for r in results[:ADMIT_TOP_N]
|
|
864
|
-
if getattr(r, "fact", None) is not None
|
|
865
|
-
]
|
|
866
|
-
if set(shown_now) == set(shown_before):
|
|
867
|
-
return
|
|
868
|
-
ContextualBandit(Path(learning_db), profile_id).record_shown(
|
|
869
|
-
play_id, shown_now,
|
|
870
|
-
)
|
|
871
|
-
except Exception as exc: # pragma: no cover — advisory
|
|
872
|
-
logger.debug("shown-set correction skipped: %s", exc)
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
def _admit_to_working_memory(
|
|
876
|
-
results: list, profile_id: str, session_id: str,
|
|
877
|
-
) -> None:
|
|
878
|
-
"""Remember what this answer showed, so the next turn is not cold.
|
|
879
|
-
|
|
880
|
-
Runs last, on the order the caller will actually see. Admitting the
|
|
881
|
-
pre-rerank order would teach the session something it was never shown.
|
|
882
|
-
"""
|
|
883
|
-
if not results or not is_conversation(session_id, profile_id):
|
|
884
|
-
return
|
|
885
|
-
try:
|
|
886
|
-
from superlocalmemory.core.working_memory import ADMIT_TOP_N, get_or_create
|
|
887
|
-
|
|
888
|
-
shown = [
|
|
889
|
-
r.fact.fact_id for r in results[:ADMIT_TOP_N]
|
|
890
|
-
if getattr(r, "fact", None) is not None and r.fact.fact_id
|
|
891
|
-
]
|
|
892
|
-
if shown:
|
|
893
|
-
get_or_create(profile_id, session_id).admit(shown)
|
|
894
|
-
except Exception as exc: # pragma: no cover — advisory, never fatal
|
|
895
|
-
logger.debug("working-memory admit skipped: %s", exc)
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
def apply_v2_bandit_ensemble(
|
|
899
|
-
response: RecallResponse,
|
|
900
|
-
query: str,
|
|
901
|
-
profile_id: str,
|
|
902
|
-
query_id: str,
|
|
903
|
-
*,
|
|
904
|
-
learning_db_path: Any = None,
|
|
905
|
-
record_signals: bool = False,
|
|
906
|
-
record_plays: bool = True,
|
|
907
|
-
memory_db_path: Any = None,
|
|
908
|
-
play_sink: dict | None = None,
|
|
909
|
-
) -> RecallResponse:
|
|
910
|
-
"""Apply contextual bandit + optional LGBM ensemble rerank. Safe on error.
|
|
911
|
-
|
|
912
|
-
``play_sink``, when given, receives the play this call recorded and the store
|
|
913
|
-
it went to. The caller needs both because the answer is not final here: a
|
|
914
|
-
continuity bias runs afterwards and can change which memories end up in
|
|
915
|
-
front of the user, and the play's evidence has to describe what was actually
|
|
916
|
-
shown. Caller-owned rather than returned on the response, matching how the
|
|
917
|
-
retrieval engine hands back its dropped-channel set.
|
|
918
|
-
|
|
919
|
-
``record_signals`` and ``record_plays`` are separate because they are
|
|
920
|
-
separate things, and conflating them is what stopped the bandit learning.
|
|
921
|
-
|
|
922
|
-
* A **play** is "the bandit chose arm X for this query". It carries no
|
|
923
|
-
reward — ``bandit_plays.reward`` stays NULL until an authenticated
|
|
924
|
-
outcome settles it. One INSERT.
|
|
925
|
-
* A **signal** is an exposure row per displayed fact, twenty per query. Those
|
|
926
|
-
are what inflated the ranking-phase counter 2,675x, and the enqueue also
|
|
927
|
-
writes canonical/learning state, which is the contention the comment
|
|
928
|
-
below describes.
|
|
929
|
-
|
|
930
|
-
``record_signals=False`` was hardcoded at the only caller on 2026-07-27
|
|
931
|
-
(commit ``cbf7929f``, release 3.8.6). That is the same day
|
|
932
|
-
``MAX(bandit_arms.last_played_at)`` stops. It took the play recording down
|
|
933
|
-
with the exposure enqueue, so nothing was ever written for the reward proxy
|
|
934
|
-
to settle, and 165 arms have sat at alpha == beta ever since.
|
|
935
|
-
|
|
936
|
-
Recording a play is not feedback. It is the ticket the settlement path
|
|
937
|
-
later resolves against evidence, and without it there is no learning loop
|
|
938
|
-
at all.
|
|
939
|
-
"""
|
|
940
|
-
import os as _os
|
|
941
|
-
|
|
942
|
-
if _os.environ.get("SLM_BANDIT_DISABLED", "0") == "1":
|
|
943
|
-
return response
|
|
944
|
-
if not response.results:
|
|
945
|
-
return response
|
|
946
|
-
|
|
947
|
-
try:
|
|
948
|
-
from pathlib import Path as _P
|
|
949
|
-
|
|
950
|
-
from superlocalmemory.infra.data_root import state_path
|
|
951
|
-
from superlocalmemory.learning.bandit import ContextualBandit
|
|
952
|
-
from superlocalmemory.learning.ensemble import (
|
|
953
|
-
choose_ensemble,
|
|
954
|
-
ensemble_rerank,
|
|
955
|
-
)
|
|
956
|
-
from superlocalmemory.learning.signals import (
|
|
957
|
-
SignalBatch,
|
|
958
|
-
SignalCandidate,
|
|
959
|
-
enqueue,
|
|
960
|
-
)
|
|
961
|
-
from superlocalmemory.retrieval.engine import apply_channel_weights
|
|
962
|
-
|
|
963
|
-
db_path = (_P(learning_db_path) if learning_db_path
|
|
964
|
-
else state_path("learning.db"))
|
|
965
|
-
if not db_path.exists():
|
|
966
|
-
return response
|
|
967
|
-
|
|
968
|
-
# --- 1. bandit.choose ---------------------------------------------
|
|
969
|
-
entity_count = 0
|
|
970
|
-
# Use query_context hints if available on the engine — cheap fallback.
|
|
971
|
-
bandit = ContextualBandit(db_path, profile_id)
|
|
972
|
-
context = {
|
|
973
|
-
"query_type": response.query_type,
|
|
974
|
-
"entity_count": entity_count,
|
|
975
|
-
}
|
|
976
|
-
# Record the play unless explicitly told not to. ``choose_readonly``
|
|
977
|
-
# samples the same arm from a read-only snapshot and returns
|
|
978
|
-
# ``play_id=None``, so taking that branch means this query can never be
|
|
979
|
-
# settled and the arm can never move off its prior.
|
|
980
|
-
choice = (
|
|
981
|
-
bandit.choose(context, query_id)
|
|
982
|
-
if record_plays
|
|
983
|
-
else bandit.choose_readonly(context)
|
|
984
|
-
)
|
|
985
|
-
|
|
986
|
-
# --- 2. apply channel weights -------------------------------------
|
|
987
|
-
weighted = apply_channel_weights(list(response.results), choice.weights)
|
|
988
|
-
|
|
989
|
-
# --- 3. choose ensemble + load model (optional) -------------------
|
|
990
|
-
active_model = None
|
|
991
|
-
signal_count = 0
|
|
992
|
-
try:
|
|
993
|
-
from superlocalmemory.learning.model_cache import load_active
|
|
994
|
-
db = _ReadOnlyLearningView(db_path)
|
|
995
|
-
signal_count = db.count_signals(profile_id)
|
|
996
|
-
active_model = load_active(db, profile_id)
|
|
997
|
-
except Exception as exc:
|
|
998
|
-
logger.debug("v2 bandit: model/signal load skipped: %s", exc)
|
|
999
|
-
|
|
1000
|
-
weights = choose_ensemble(signal_count, active_model)
|
|
1001
|
-
|
|
1002
|
-
# --- 4. ensemble rerank -------------------------------------------
|
|
1003
|
-
query_context = {
|
|
1004
|
-
"query_type": response.query_type,
|
|
1005
|
-
"profile_id": profile_id,
|
|
1006
|
-
"query_id": query_id,
|
|
1007
|
-
"bandit_play_id": choice.play_id,
|
|
1008
|
-
}
|
|
1009
|
-
try:
|
|
1010
|
-
final_results = ensemble_rerank(
|
|
1011
|
-
weighted, choice, active_model, weights, query_context,
|
|
1012
|
-
)
|
|
1013
|
-
except Exception as exc:
|
|
1014
|
-
logger.debug("v2 bandit ensemble_rerank skipped: %s", exc)
|
|
1015
|
-
final_results = weighted
|
|
1016
|
-
|
|
1017
|
-
# --- 4b. outcome bonus -------------------------------------------
|
|
1018
|
-
# Applied AFTER the model score, never as a model feature. The wave
|
|
1019
|
-
# plan proposed adding "outcome_score" to FEATURE_NAMES for inference
|
|
1020
|
-
# and excluding it from training; that is a shape mismatch —
|
|
1021
|
-
# booster.predict needs the columns the model was trained on, and
|
|
1022
|
-
# features.py asserts len(FEATURE_NAMES) == FEATURE_DIM == 20 against a
|
|
1023
|
-
# live 20-feature model. Applying it here also makes it
|
|
1024
|
-
# true by construction: the model cannot learn from a signal it
|
|
1025
|
-
# never sees, so there is no self-reinforcing loop to exclude.
|
|
1026
|
-
final_results = _apply_outcome_bonus(
|
|
1027
|
-
final_results, profile_id, memory_db_path,
|
|
1028
|
-
)
|
|
1029
|
-
|
|
1030
|
-
# Give the play its evidence: which memories this query actually
|
|
1031
|
-
# surfaced, so the reward proxy can settle it from a downstream
|
|
1032
|
-
# reference instead of falling through to the neutral default. Written
|
|
1033
|
-
# after the rerank because that is when the shown order is final.
|
|
1034
|
-
if choice.play_id:
|
|
1035
|
-
try:
|
|
1036
|
-
bandit.record_shown(
|
|
1037
|
-
choice.play_id, [r.fact.fact_id for r in final_results[:5]],
|
|
1038
|
-
)
|
|
1039
|
-
except Exception as exc: # pragma: no cover — never break a recall
|
|
1040
|
-
logger.debug("v2 bandit record_shown skipped: %s", exc)
|
|
1041
|
-
if play_sink is not None:
|
|
1042
|
-
# So the caller can correct this record if the order changes
|
|
1043
|
-
# after this function returns.
|
|
1044
|
-
play_sink["play_id"] = choice.play_id
|
|
1045
|
-
play_sink["learning_db"] = str(db_path)
|
|
1046
|
-
|
|
1047
|
-
# Recall is a query. Implicit learning signals are deliberately
|
|
1048
|
-
# disabled on this path: even a non-blocking enqueue eventually writes
|
|
1049
|
-
# canonical/learning state and turns dashboard polling into contention.
|
|
1050
|
-
# An explicit feedback command owns durable learning signals instead.
|
|
1051
|
-
# NOTE: this gates the twenty-row exposure enqueue ONLY. The play above
|
|
1052
|
-
# is recorded regardless — see this function's docstring.
|
|
1053
|
-
if record_signals:
|
|
1054
|
-
try:
|
|
1055
|
-
top20 = final_results[:20]
|
|
1056
|
-
candidates = tuple(
|
|
1057
|
-
SignalCandidate(
|
|
1058
|
-
fact_id=r.fact.fact_id,
|
|
1059
|
-
channel_scores=dict(r.channel_scores or {}),
|
|
1060
|
-
cross_encoder_score=None,
|
|
1061
|
-
result_dict={"fact_id": r.fact.fact_id,
|
|
1062
|
-
"score": r.score},
|
|
1063
|
-
)
|
|
1064
|
-
for r in top20
|
|
1065
|
-
)
|
|
1066
|
-
enqueue(SignalBatch(
|
|
1067
|
-
profile_id=profile_id,
|
|
1068
|
-
query_id=query_id,
|
|
1069
|
-
query_text=query,
|
|
1070
|
-
candidates=candidates,
|
|
1071
|
-
query_context=query_context,
|
|
1072
|
-
))
|
|
1073
|
-
except Exception as exc:
|
|
1074
|
-
logger.debug("v2 bandit signal enqueue skipped: %s", exc)
|
|
1075
|
-
|
|
1076
|
-
return RecallResponse(
|
|
1077
|
-
query=response.query,
|
|
1078
|
-
mode=response.mode,
|
|
1079
|
-
results=final_results,
|
|
1080
|
-
query_type=response.query_type,
|
|
1081
|
-
channel_weights=response.channel_weights,
|
|
1082
|
-
total_candidates=response.total_candidates,
|
|
1083
|
-
retrieval_time_ms=response.retrieval_time_ms,
|
|
1084
|
-
# v3.6.6: preserve evidence-floor signal across ensemble rebuilds.
|
|
1085
|
-
no_confident_match=(len(final_results) == 0) and response.no_confident_match,
|
|
1086
|
-
)
|
|
1087
|
-
except Exception as exc: # pragma: no cover — defensive top-level
|
|
1088
|
-
logger.debug("apply_v2_bandit_ensemble skipped: %s", exc)
|
|
1089
|
-
return response
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
# ---------------------------------------------------------------------------
|
|
1093
|
-
# run_recall (was MemoryEngine.recall)
|
|
1094
|
-
# ---------------------------------------------------------------------------
|
|
1095
|
-
|
|
1096
|
-
def resolve_hot_path_fast(fast: bool | None, config: "SLMConfig") -> bool:
|
|
1097
|
-
"""Resolve the recall ``fast`` flag when a caller leaves it unset (None).
|
|
1098
|
-
|
|
1099
|
-
v3.8.2 client-driven agentic: the agent hot path (CLI / MCP / plugins) is
|
|
1100
|
-
consumed by a frontier LLM (Claude Code, Copilot, Codex, …) that reformulates
|
|
1101
|
-
multi-hop / low-confidence queries far better than the local Ollama model.
|
|
1102
|
-
So an unset ``fast`` defaults to True — skip the internal agentic round and
|
|
1103
|
-
let the calling LLM drive refinement — whenever ``retrieval.client_driven_agentic``
|
|
1104
|
-
is on (the ship default). An explicit ``True``/``False`` from the caller
|
|
1105
|
-
always wins (the dashboard search path passes ``True`` for a snappy list;
|
|
1106
|
-
a no-smart-client deployment can pass ``False``). Env override
|
|
1107
|
-
``SLM_HOT_PATH_INTERNAL_AGENTIC=1`` forces internal-agentic-on globally.
|
|
1108
|
-
|
|
1109
|
-
This is the single resolution point: every recall path (HTTP, MCP, CLI,
|
|
1110
|
-
in-process adapter) funnels through ``run_recall`` and calls this, so the
|
|
1111
|
-
client-driven default is consistent everywhere by construction.
|
|
1112
|
-
"""
|
|
1113
|
-
if fast is not None:
|
|
1114
|
-
return bool(fast)
|
|
1115
|
-
import os
|
|
1116
|
-
rc = getattr(config, "retrieval", None)
|
|
1117
|
-
client_driven = bool(getattr(rc, "client_driven_agentic", True))
|
|
1118
|
-
if os.environ.get("SLM_HOT_PATH_INTERNAL_AGENTIC") == "1":
|
|
1119
|
-
client_driven = False
|
|
1120
|
-
return client_driven
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
def run_recall(
|
|
1124
|
-
query: str,
|
|
1125
|
-
profile_id: str,
|
|
1126
|
-
mode: Mode | None = None,
|
|
1127
|
-
limit: int = 20,
|
|
1128
|
-
agent_id: str = "unknown",
|
|
1129
|
-
session_id: str | None = None,
|
|
1130
|
-
*,
|
|
1131
|
-
config: SLMConfig,
|
|
1132
|
-
retrieval_engine: Any,
|
|
1133
|
-
trust_scorer: Any,
|
|
1134
|
-
embedder: Any,
|
|
1135
|
-
db: DatabaseManager,
|
|
1136
|
-
llm: Any,
|
|
1137
|
-
hooks: HookRegistry,
|
|
1138
|
-
access_log: Any = None,
|
|
1139
|
-
auto_linker: Any = None,
|
|
1140
|
-
fast: bool | None = None,
|
|
1141
|
-
include_global: bool = False,
|
|
1142
|
-
include_shared: bool = False,
|
|
1143
|
-
window: str | tuple[str, str] | None = None,
|
|
1144
|
-
as_of: str | None = None,
|
|
1145
|
-
known_as_of: str | None = None,
|
|
1146
|
-
valid_at: str | None = None,
|
|
1147
|
-
include_unknown: bool = False,
|
|
1148
|
-
) -> RecallResponse:
|
|
1149
|
-
"""Recall relevant facts for a query.
|
|
1150
|
-
|
|
1151
|
-
Multi-scope: ``include_global`` / ``include_shared`` control which
|
|
1152
|
-
scopes participate in retrieval (passed through to retrieval engine).
|
|
1153
|
-
|
|
1154
|
-
Pipeline: retrieval -> agentic sufficiency (if configured) -> post-recall updates.
|
|
1155
|
-
|
|
1156
|
-
``fast=True`` skips the internal agentic verification round while retaining
|
|
1157
|
-
the six local retrieval channels + reranker. ``fast=None`` (unset) resolves
|
|
1158
|
-
to the client-driven-agentic default (see ``resolve_hot_path_fast``): the
|
|
1159
|
-
agent hot path skips the internal round and delegates refinement to the
|
|
1160
|
-
calling LLM. ``fast=False`` forces the internal agentic round.
|
|
1161
|
-
|
|
1162
|
-
``as_of``: Optional ISO 8601 datetime string for point-in-time time-travel
|
|
1163
|
-
recall. When set, the bi-temporal validity filter demotes facts that were
|
|
1164
|
-
not yet valid or had already expired at that point. Default ``None`` leaves
|
|
1165
|
-
all existing behaviour unchanged.
|
|
1166
|
-
"""
|
|
1167
|
-
m = mode or config.mode
|
|
1168
|
-
|
|
1169
|
-
# v3.8.2: resolve the client-driven-agentic default when a caller left
|
|
1170
|
-
# ``fast`` unset (None). After this line ``fast`` is a concrete bool, so
|
|
1171
|
-
# the agentic gate below (``if not fast``) behaves identically for every
|
|
1172
|
-
# entry point that funnels through here.
|
|
1173
|
-
fast = resolve_hot_path_fast(fast, config)
|
|
1174
|
-
|
|
1175
|
-
# v3.5.0 diagnostic: per-stage recall timing under SLM_RECALL_TIMING=1.
|
|
1176
|
-
# Zero overhead when the env var is unset. Permanent observability hook.
|
|
1177
|
-
import os as _os_t
|
|
1178
|
-
import time as _time_t
|
|
1179
|
-
_timing = bool(_os_t.environ.get("SLM_RECALL_TIMING"))
|
|
1180
|
-
_t0 = _time_t.monotonic()
|
|
1181
|
-
|
|
1182
|
-
def _mark(_label: str) -> None:
|
|
1183
|
-
if _timing:
|
|
1184
|
-
logger.warning("[RECALL-TIMING] %-22s %.0f ms",
|
|
1185
|
-
_label, (_time_t.monotonic() - _t0) * 1000.0)
|
|
1186
|
-
|
|
1187
|
-
# The interactive path must retain the complete local retrieval contract.
|
|
1188
|
-
# Only agentic verification can invoke an unbounded model round.
|
|
1189
|
-
extra_disabled = None
|
|
1190
|
-
response = retrieval_engine.recall(
|
|
1191
|
-
query, profile_id, m, limit,
|
|
1192
|
-
extra_disabled_channels=extra_disabled,
|
|
1193
|
-
include_global=include_global,
|
|
1194
|
-
include_shared=include_shared,
|
|
1195
|
-
window=window,
|
|
1196
|
-
as_of=as_of,
|
|
1197
|
-
known_as_of=known_as_of,
|
|
1198
|
-
valid_at=valid_at,
|
|
1199
|
-
include_unknown=include_unknown,
|
|
1200
|
-
)
|
|
1201
|
-
_mark("retrieval(chan+rerank)")
|
|
1202
|
-
|
|
1203
|
-
_mark("pre-agentic")
|
|
1204
|
-
# Agentic sufficiency verification
|
|
1205
|
-
# V3.3.19: Only trigger for multi_hop queries in Mode A (rule-based).
|
|
1206
|
-
# Single-hop/factual/temporal queries get WORSE with decomposition —
|
|
1207
|
-
# sub-query noise dilutes precision. Mode C (LLM) can trigger broadly.
|
|
1208
|
-
agentic_rounds = config.retrieval.agentic_max_rounds
|
|
1209
|
-
if not fast and agentic_rounds > 0 and response.results:
|
|
1210
|
-
max_score = max((r.score for r in response.results), default=0.0)
|
|
1211
|
-
has_llm = llm is not None and getattr(llm, "is_available", False)
|
|
1212
|
-
should_trigger = (
|
|
1213
|
-
response.query_type == "multi_hop"
|
|
1214
|
-
or (has_llm and max_score < config.retrieval.agentic_confidence_threshold)
|
|
1215
|
-
or (has_llm and len(response.results) < 3)
|
|
1216
|
-
)
|
|
1217
|
-
if should_trigger:
|
|
1218
|
-
try:
|
|
1219
|
-
from superlocalmemory.retrieval.agentic import AgenticRetriever
|
|
1220
|
-
agentic = AgenticRetriever(
|
|
1221
|
-
confidence_threshold=config.retrieval.agentic_confidence_threshold,
|
|
1222
|
-
db=db,
|
|
1223
|
-
)
|
|
1224
|
-
enhanced_facts = agentic.retrieve(
|
|
1225
|
-
query=query, profile_id=profile_id,
|
|
1226
|
-
retrieval_engine=retrieval_engine,
|
|
1227
|
-
llm=llm,
|
|
1228
|
-
top_k=limit,
|
|
1229
|
-
query_type=response.query_type,
|
|
1230
|
-
)
|
|
1231
|
-
# Replace response results with enhanced facts if we got more
|
|
1232
|
-
if len(enhanced_facts) > len(response.results):
|
|
1233
|
-
from superlocalmemory.storage.models import RetrievalResult
|
|
1234
|
-
enhanced_results = []
|
|
1235
|
-
for i, f in enumerate(enhanced_facts):
|
|
1236
|
-
# Look up real trust score for agentic results
|
|
1237
|
-
fact_trust = 0.5
|
|
1238
|
-
if trust_scorer:
|
|
1239
|
-
try:
|
|
1240
|
-
fact_trust = trust_scorer.get_fact_trust(
|
|
1241
|
-
f.fact_id, profile_id,
|
|
1242
|
-
)
|
|
1243
|
-
except Exception:
|
|
1244
|
-
pass
|
|
1245
|
-
enhanced_results.append(RetrievalResult(
|
|
1246
|
-
fact=f, score=1.0 / (i + 1),
|
|
1247
|
-
channel_scores={"agentic": 1.0},
|
|
1248
|
-
confidence=f.confidence,
|
|
1249
|
-
relevance_score=1.0 / (i + 1),
|
|
1250
|
-
ranking_score=None,
|
|
1251
|
-
memory_confidence=f.confidence,
|
|
1252
|
-
evidence_chain=["agentic_round_2"],
|
|
1253
|
-
trust_score=fact_trust,
|
|
1254
|
-
))
|
|
1255
|
-
response = RecallResponse(
|
|
1256
|
-
query=query, mode=m, results=enhanced_results[:limit],
|
|
1257
|
-
query_type=response.query_type,
|
|
1258
|
-
channel_weights=response.channel_weights,
|
|
1259
|
-
total_candidates=response.total_candidates + len(enhanced_facts),
|
|
1260
|
-
retrieval_time_ms=response.retrieval_time_ms,
|
|
1261
|
-
# v3.6.6: agentic round-2 may add facts; recompute flag.
|
|
1262
|
-
no_confident_match=(len(enhanced_results[:limit]) == 0)
|
|
1263
|
-
and response.no_confident_match,
|
|
1264
|
-
)
|
|
1265
|
-
except Exception as exc:
|
|
1266
|
-
logger.debug("Agentic sufficiency skipped: %s", exc)
|
|
1267
|
-
|
|
1268
|
-
_mark("agentic")
|
|
1269
|
-
# S8-ARC-04 (v3.4.22): unified ranking entry point. Single env-var
|
|
1270
|
-
# (SLM_RANKING=off|v1|v2|v2-ensemble) controls the pipeline. Legacy
|
|
1271
|
-
# SLM_V2_PIPELINE_DISABLED + SLM_BANDIT_DISABLED still honoured for
|
|
1272
|
-
# one-release back-compat. Identity when no active model.
|
|
1273
|
-
try:
|
|
1274
|
-
import os as _os
|
|
1275
|
-
import uuid as _uuid
|
|
1276
|
-
query_id = _uuid.uuid4().hex
|
|
1277
|
-
mode = _resolve_ranking_mode(_os.environ)
|
|
1278
|
-
play_sink: dict = {}
|
|
1279
|
-
response = apply_ranking(
|
|
1280
|
-
response, query, profile_id, query_id,
|
|
1281
|
-
config=config, pipeline_version=mode, record_signals=False,
|
|
1282
|
-
# the store THIS recall read from, so the outcome bonus
|
|
1283
|
-
# cannot resolve a different one
|
|
1284
|
-
memory_db_path=getattr(db, "db_path", None),
|
|
1285
|
-
play_sink=play_sink,
|
|
1286
|
-
)
|
|
1287
|
-
except Exception as exc:
|
|
1288
|
-
logger.debug("Ranking pipeline skipped: %s", exc)
|
|
1289
|
-
|
|
1290
|
-
# Continuity of attention, applied after every learned layer and outside
|
|
1291
|
-
# the ranking-version switch: a session's recent memories bias this answer
|
|
1292
|
-
# whether or not the bandit and the model are enabled, because they are
|
|
1293
|
-
# unrelated features and coupling them would make one disappear with the
|
|
1294
|
-
# other. In-process only — no query is added to the recall path.
|
|
1295
|
-
# Only an id that names a conversation drives continuity. The fronts invent
|
|
1296
|
-
# one per request (HTTP) or per client (MCP) for bookkeeping, and neither is
|
|
1297
|
-
# a conversation: the first would register a working set per dashboard
|
|
1298
|
-
# search and evict real ones, the second would pool unrelated clients into
|
|
1299
|
-
# a shared set. See core.session_identity.
|
|
1300
|
-
_sid = session_id if is_conversation(session_id, profile_id) else ""
|
|
1301
|
-
if _sid and response.results:
|
|
1302
|
-
from superlocalmemory.core.working_memory import ADMIT_TOP_N as _TOP
|
|
1303
|
-
|
|
1304
|
-
_shown_before = [
|
|
1305
|
-
r.fact.fact_id for r in response.results[:_TOP]
|
|
1306
|
-
if getattr(r, "fact", None) is not None
|
|
1307
|
-
]
|
|
1308
|
-
response.results = _apply_working_memory_bias(
|
|
1309
|
-
response.results, profile_id, _sid,
|
|
1310
|
-
)
|
|
1311
|
-
# The play was recorded with the order as it stood before the line
|
|
1312
|
-
# above. If that order changed, its evidence now describes an answer
|
|
1313
|
-
# nobody saw.
|
|
1314
|
-
_resettle_shown_after_bias(
|
|
1315
|
-
play_sink, profile_id, response.results, _shown_before,
|
|
1316
|
-
)
|
|
1317
|
-
|
|
1318
|
-
_preserve_exact_lexical_evidence(response, query)
|
|
1319
|
-
_mark("learning+ranking")
|
|
1320
|
-
# Deliberately no trust, Fisher, retention, lifecycle, popularity, or graph
|
|
1321
|
-
# mutation here. Those state transitions require a separately authenticated
|
|
1322
|
-
# positive/negative outcome; merely returning a result is an exposure.
|
|
1323
|
-
|
|
1324
|
-
from superlocalmemory.core.score_contract import finalize_score_contract
|
|
1325
|
-
finalize_score_contract(response)
|
|
1326
|
-
|
|
1327
|
-
# LLD-00 §3 — stamp HMAC markers on every result so post_tool_outcome_hook
|
|
1328
|
-
# can validate fact_ids observed in downstream tool output.
|
|
1329
|
-
_apply_markers_to_response(response)
|
|
1330
|
-
|
|
1331
|
-
# Last, on the order the caller receives, so the next turn of this session
|
|
1332
|
-
# starts from what was actually shown.
|
|
1333
|
-
if _sid:
|
|
1334
|
-
_admit_to_working_memory(response.results, profile_id, _sid)
|
|
1335
|
-
|
|
1336
|
-
_mark("TOTAL(fisher+trust+markers)")
|
|
1337
|
-
return response
|