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,812 +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
|
-
"""Fact consolidation — writes a DISPLAY summary, never a memory.
|
|
6
|
-
|
|
7
|
-
Groups warm/cold facts that share an entity and writes one summary per cluster
|
|
8
|
-
into ``consolidated_summaries``, a display-only table. Nothing in the retrieval
|
|
9
|
-
corpus is created, modified or hidden by this module.
|
|
10
|
-
|
|
11
|
-
WHAT CHANGED AND WHY IT HAD TO
|
|
12
|
-
------------------------------
|
|
13
|
-
From v3.6.4 to 4.0.9 this module ended each cluster with a raw
|
|
14
|
-
``INSERT INTO atomic_facts`` carrying ``memory_id=''``, ``importance=0.8`` and
|
|
15
|
-
``entities_json`` holding *every* entity in the cluster. Three consequences, all
|
|
16
|
-
measured on the author's 5,089-fact store:
|
|
17
|
-
|
|
18
|
-
* 1,195 model-written rows entered the retrieval corpus, and because each
|
|
19
|
-
carried its whole cluster's entity list they had more entity links than any
|
|
20
|
-
real memory, so the entity channel ranked them first. Asked "what am I
|
|
21
|
-
working on", the store answered "Unfortunately, there is no information
|
|
22
|
-
available about 'Gateway', 'State', 'Bounded', or 'Claude' in the provided
|
|
23
|
-
text." at ranks 1, 2 and 3.
|
|
24
|
-
* The rows had no ``temporal_events`` at all (0 of 1,195), so they won the
|
|
25
|
-
temporal channel through its ``created_at`` recency fallback as well.
|
|
26
|
-
* Their entity clusters made them eligible for consolidation in turn: 353 of
|
|
27
|
-
them are summaries of summaries. A store summarising its own summaries
|
|
28
|
-
drifts away from what the user actually said, one pass at a time.
|
|
29
|
-
|
|
30
|
-
Bypassing ``DatabaseManager`` also bypassed the constraint that would have
|
|
31
|
-
refused the row outright — ``atomic_facts`` declares
|
|
32
|
-
``FOREIGN KEY (memory_id) REFERENCES memories(memory_id)`` and ``memories`` has
|
|
33
|
-
no ``''`` row, but ``storage/memory_write.py`` sets only ``busy_timeout``, not
|
|
34
|
-
``PRAGMA foreign_keys=ON``.
|
|
35
|
-
|
|
36
|
-
So the boundary this module now keeps is the one v3.6 intended: a summary is a
|
|
37
|
-
*view* of memory, shown on the dashboard and to Mode B/C readers, and it is not
|
|
38
|
-
a thing recall can return. ``community_summaries`` is the precedent.
|
|
39
|
-
|
|
40
|
-
CONTRACT
|
|
41
|
-
--------
|
|
42
|
-
1. NEVER writes to ``atomic_facts`` — not the summary, not the sources.
|
|
43
|
-
2. NEVER archives the source facts. Archiving them made sense only while a
|
|
44
|
-
retrievable summary stood in for them; a display-only summary does not, so
|
|
45
|
-
archiving would replace ten reachable memories with nothing.
|
|
46
|
-
3. Model output is cleaned (``clean_llm_summary``) and then rejected if it is
|
|
47
|
-
a non-answer (``is_non_answer``) — checked before any write, with the
|
|
48
|
-
reason logged.
|
|
49
|
-
4. Only clusters warm/cold facts; never touches 'active' or 'pinned'.
|
|
50
|
-
5. All writes per cluster wrapped in SAVEPOINT for atomicity.
|
|
51
|
-
6. Entity ID LIKE patterns use JSON-boundary quoting to prevent substring
|
|
52
|
-
false positives.
|
|
53
|
-
|
|
54
|
-
Modes: A extractive, B Ollama, C cloud LLM with fallbacks down to extractive.
|
|
55
|
-
|
|
56
|
-
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
57
|
-
"""
|
|
58
|
-
|
|
59
|
-
from __future__ import annotations
|
|
60
|
-
|
|
61
|
-
import hashlib
|
|
62
|
-
import json
|
|
63
|
-
import logging
|
|
64
|
-
import sqlite3
|
|
65
|
-
import uuid
|
|
66
|
-
from datetime import datetime, timezone
|
|
67
|
-
from pathlib import Path
|
|
68
|
-
from typing import TYPE_CHECKING, Union
|
|
69
|
-
|
|
70
|
-
if TYPE_CHECKING:
|
|
71
|
-
from superlocalmemory.storage.database import DatabaseManager
|
|
72
|
-
|
|
73
|
-
from superlocalmemory.summaries.base import clean_llm_summary
|
|
74
|
-
from superlocalmemory.summaries.non_answer import MIN_USEFUL_CHARS, is_non_answer
|
|
75
|
-
|
|
76
|
-
logger = logging.getLogger("superlocalmemory.fact_consolidator")
|
|
77
|
-
|
|
78
|
-
_MAX_CLUSTER_SIZE = 10 # Max facts to merge into one
|
|
79
|
-
_MIN_CLUSTER_SIZE = 3 # Need at least 3 related facts to consolidate
|
|
80
|
-
_MAX_CONSOLIDATED_CHARS = 2000
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def consolidate_facts(
|
|
84
|
-
db_or_path: "Union[DatabaseManager, str, Path]",
|
|
85
|
-
profile_id: str = "default",
|
|
86
|
-
max_clusters: int = 20,
|
|
87
|
-
dry_run: bool = False,
|
|
88
|
-
config: object | None = None,
|
|
89
|
-
) -> dict:
|
|
90
|
-
"""Find and consolidate clusters of related facts.
|
|
91
|
-
|
|
92
|
-
Concurrency fix (v3.8.4 — implements TODO from Fix-A comment):
|
|
93
|
-
When a DatabaseManager is provided the consolidation now uses per-cluster
|
|
94
|
-
short write transactions instead of one long raw_connection() that held
|
|
95
|
-
the write lock across Ollama / Cloud-LLM calls for every cluster after
|
|
96
|
-
the first. The fix:
|
|
97
|
-
|
|
98
|
-
1. Discover clusters with a short memory_read() (no write lock).
|
|
99
|
-
2. For each cluster: fetch fact content (memory_read()), generate the
|
|
100
|
-
summary OUTSIDE any lock (Ollama / Cloud LLM may take 30 s), then
|
|
101
|
-
do the SAVEPOINT write inside a short memory_write() block.
|
|
102
|
-
|
|
103
|
-
SAVEPOINT atomicity per cluster is preserved: _consolidate_cluster still
|
|
104
|
-
uses SAVEPOINT / RELEASE / ROLLBACK TO internally.
|
|
105
|
-
|
|
106
|
-
Mode behavior:
|
|
107
|
-
- Mode A: Extractive only (no LLM). Always available.
|
|
108
|
-
- Mode B: Ollama LLM summarization. Falls back to extractive if Ollama is
|
|
109
|
-
down OR if the model answers with a non-answer.
|
|
110
|
-
- Mode C: Cloud LLM (user's configured provider), then Ollama, then
|
|
111
|
-
extractive, with the same non-answer rejection at each step.
|
|
112
|
-
|
|
113
|
-
Returns stats: consolidated, clusters_found, facts_summarized,
|
|
114
|
-
rejected, errors.
|
|
115
|
-
"""
|
|
116
|
-
from superlocalmemory.storage.database import DatabaseManager
|
|
117
|
-
from superlocalmemory.storage.memory_write import memory_read, memory_write
|
|
118
|
-
|
|
119
|
-
stats: dict = {
|
|
120
|
-
"clusters_found": 0,
|
|
121
|
-
"consolidated": 0,
|
|
122
|
-
# Renamed from facts_archived. Nothing is archived any more, and a key
|
|
123
|
-
# that keeps reporting a count for an action no longer taken is how a
|
|
124
|
-
# behaviour change hides from whoever reads the numbers.
|
|
125
|
-
"facts_summarized": 0,
|
|
126
|
-
# Clusters whose summary was a non-answer and was refused. Counted
|
|
127
|
-
# separately from `errors`: refusing junk is the guard working, not a
|
|
128
|
-
# failure, but a run where every cluster is refused means the model is
|
|
129
|
-
# misbehaving and that has to be visible.
|
|
130
|
-
"rejected": 0,
|
|
131
|
-
# Clusters whose summary already existed over the same sources, so no
|
|
132
|
-
# model call was made. On a settled store this becomes the whole count.
|
|
133
|
-
"unchanged": 0,
|
|
134
|
-
"facts_archived": 0, # retained at 0 for callers that still read it
|
|
135
|
-
"errors": 0,
|
|
136
|
-
"error_detail": "",
|
|
137
|
-
"mode": "a",
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if config:
|
|
141
|
-
mode = getattr(config, 'mode', None)
|
|
142
|
-
if mode:
|
|
143
|
-
mode_str = getattr(mode, 'value', str(mode)).lower()
|
|
144
|
-
stats["mode"] = mode_str
|
|
145
|
-
|
|
146
|
-
if isinstance(db_or_path, DatabaseManager):
|
|
147
|
-
db_path = db_or_path.db_path
|
|
148
|
-
try:
|
|
149
|
-
# Step 1: discover clusters — short read, no write lock.
|
|
150
|
-
with memory_read(db_path) as rconn:
|
|
151
|
-
rconn.row_factory = sqlite3.Row
|
|
152
|
-
clusters = _find_consolidation_clusters(rconn, profile_id, max_clusters)
|
|
153
|
-
stats["clusters_found"] = len(clusters)
|
|
154
|
-
|
|
155
|
-
for entity_id, entity_name, fact_ids in clusters:
|
|
156
|
-
try:
|
|
157
|
-
# Step 2: load fact content for this cluster (read, no write lock).
|
|
158
|
-
placeholders = ",".join("?" * len(fact_ids))
|
|
159
|
-
with memory_read(db_path) as rconn:
|
|
160
|
-
rconn.row_factory = sqlite3.Row
|
|
161
|
-
facts = rconn.execute(
|
|
162
|
-
f"SELECT fact_id, content, confidence, created_at, "
|
|
163
|
-
f"canonical_entities_json, scope, shared_with "
|
|
164
|
-
f"FROM atomic_facts "
|
|
165
|
-
f"WHERE fact_id IN ({placeholders}) ORDER BY created_at",
|
|
166
|
-
fact_ids,
|
|
167
|
-
).fetchall()
|
|
168
|
-
facts = [dict(f) for f in facts]
|
|
169
|
-
|
|
170
|
-
if len(facts) < _MIN_CLUSTER_SIZE:
|
|
171
|
-
continue
|
|
172
|
-
|
|
173
|
-
# Step 2b: has this exact cluster already been summarised?
|
|
174
|
-
#
|
|
175
|
-
# Necessary because 4.0.10 stopped archiving the sources. The
|
|
176
|
-
# old code made a cluster ineligible by archiving it; now the
|
|
177
|
-
# same warm facts are eligible on every maintenance pass, so
|
|
178
|
-
# without this the summarizer is re-run for an unchanged
|
|
179
|
-
# cluster every 30 minutes forever. Measured over five
|
|
180
|
-
# consecutive passes on one cluster before this check:
|
|
181
|
-
# consolidated_summaries converged at 1, but
|
|
182
|
-
# fact_consolidations grew 1, 2, 3, 4, 5 -- and in Mode B/C
|
|
183
|
-
# each of those passes paid for a model call to regenerate
|
|
184
|
-
# text it already had.
|
|
185
|
-
#
|
|
186
|
-
# Checked before generation, not after, because the cost
|
|
187
|
-
# being avoided IS the generation.
|
|
188
|
-
if _already_summarised(db_path, profile_id, entity_id, fact_ids):
|
|
189
|
-
stats["unchanged"] += 1
|
|
190
|
-
continue
|
|
191
|
-
|
|
192
|
-
# Step 3: generate summary OUTSIDE any write lock.
|
|
193
|
-
# Ollama (Mode B) or Cloud LLM (Mode C) may take 30 s here.
|
|
194
|
-
summary, generated_by = _generate_summary(
|
|
195
|
-
entity_name, facts, config,
|
|
196
|
-
)
|
|
197
|
-
if not summary:
|
|
198
|
-
stats["rejected"] += 1
|
|
199
|
-
continue
|
|
200
|
-
|
|
201
|
-
if dry_run:
|
|
202
|
-
stats["consolidated"] += 1
|
|
203
|
-
stats["facts_summarized"] += len(fact_ids)
|
|
204
|
-
continue
|
|
205
|
-
|
|
206
|
-
# Step 4: short per-cluster write — hold lock only for SQL.
|
|
207
|
-
with memory_write(db_path) as conn:
|
|
208
|
-
conn.row_factory = sqlite3.Row
|
|
209
|
-
result = _consolidate_cluster(
|
|
210
|
-
conn, profile_id, entity_id, entity_name,
|
|
211
|
-
fact_ids, dry_run=False, config=None,
|
|
212
|
-
_presummary=summary, _generated_by=generated_by,
|
|
213
|
-
)
|
|
214
|
-
if result:
|
|
215
|
-
stats["consolidated"] += 1
|
|
216
|
-
stats["facts_summarized"] += len(fact_ids)
|
|
217
|
-
else:
|
|
218
|
-
stats["rejected"] += 1
|
|
219
|
-
except Exception as exc:
|
|
220
|
-
logger.warning(
|
|
221
|
-
"Consolidation failed for %s: %s",
|
|
222
|
-
entity_name, exc, exc_info=True,
|
|
223
|
-
)
|
|
224
|
-
stats["errors"] += 1
|
|
225
|
-
|
|
226
|
-
if stats["consolidated"] > 0:
|
|
227
|
-
logger.info(
|
|
228
|
-
"Fact consolidation: %d display summaries written over "
|
|
229
|
-
"%d facts, %d clusters refused",
|
|
230
|
-
stats["consolidated"], stats["facts_summarized"],
|
|
231
|
-
stats["rejected"],
|
|
232
|
-
)
|
|
233
|
-
except Exception as exc:
|
|
234
|
-
logger.error("Fact consolidation failed: %s", exc, exc_info=True)
|
|
235
|
-
stats["errors"] += 1
|
|
236
|
-
stats["error_detail"] = str(exc)
|
|
237
|
-
return stats
|
|
238
|
-
|
|
239
|
-
# Backward-compat: str | Path — open our own connection.
|
|
240
|
-
#
|
|
241
|
-
# Type-checked, not assumed. This branch used to run for ANYTHING that was
|
|
242
|
-
# not a DatabaseManager, stringify it, and hand the result to
|
|
243
|
-
# sqlite3.connect — which creates whatever filename it is given. A test
|
|
244
|
-
# passing a MagicMock therefore had a real 4 KB SQLite file named
|
|
245
|
-
# "<MagicMock id='4422448000'>" written into the repository root, one per
|
|
246
|
-
# test. 42 of them had accumulated, and tests/test_ci_guards/
|
|
247
|
-
# test_no_magicmock_artifacts.py failed after any full-suite run.
|
|
248
|
-
#
|
|
249
|
-
# 4.0.6 is where this started firing: it wired consolidate_facts into
|
|
250
|
-
# run_maintenance, so every caller with a mock config reached this line.
|
|
251
|
-
#
|
|
252
|
-
# Refusing an unusable argument is also right beyond the test symptom —
|
|
253
|
-
# silently creating a database at a nonsense path cannot be what any caller
|
|
254
|
-
# wanted, and it hides the real bug (the caller passed the wrong thing).
|
|
255
|
-
if not isinstance(db_or_path, (str, Path)):
|
|
256
|
-
raise TypeError(
|
|
257
|
-
"consolidate_facts() expects a DatabaseManager, or a str/Path to "
|
|
258
|
-
f"memory.db for backward compatibility; got {type(db_or_path).__name__}. "
|
|
259
|
-
"Passing anything else previously created a database file named after "
|
|
260
|
-
"the object's repr."
|
|
261
|
-
)
|
|
262
|
-
|
|
263
|
-
logger.warning(
|
|
264
|
-
"consolidate_facts: passing a db_path is deprecated — pass a "
|
|
265
|
-
"DatabaseManager instead (Fix A backward-compat shim active)"
|
|
266
|
-
)
|
|
267
|
-
conn = sqlite3.connect(str(db_or_path))
|
|
268
|
-
wal_mode = conn.execute("PRAGMA journal_mode=WAL").fetchone()
|
|
269
|
-
if wal_mode and wal_mode[0] != "wal":
|
|
270
|
-
logger.warning("WAL mode not active, got: %s", wal_mode[0])
|
|
271
|
-
conn.execute("PRAGMA busy_timeout=10000")
|
|
272
|
-
conn.row_factory = sqlite3.Row
|
|
273
|
-
|
|
274
|
-
try:
|
|
275
|
-
_run_consolidation(conn, profile_id, max_clusters, dry_run, config, stats)
|
|
276
|
-
if not dry_run:
|
|
277
|
-
conn.commit()
|
|
278
|
-
except Exception as exc:
|
|
279
|
-
logger.error("Fact consolidation failed: %s", exc, exc_info=True)
|
|
280
|
-
stats["errors"] += 1
|
|
281
|
-
stats["error_detail"] = str(exc)
|
|
282
|
-
finally:
|
|
283
|
-
conn.close()
|
|
284
|
-
|
|
285
|
-
return stats
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
def _run_consolidation(
|
|
289
|
-
conn: sqlite3.Connection,
|
|
290
|
-
profile_id: str,
|
|
291
|
-
max_clusters: int,
|
|
292
|
-
dry_run: bool,
|
|
293
|
-
config: object | None,
|
|
294
|
-
stats: dict,
|
|
295
|
-
) -> None:
|
|
296
|
-
"""Core consolidation logic — connection-agnostic inner function."""
|
|
297
|
-
clusters = _find_consolidation_clusters(conn, profile_id, max_clusters)
|
|
298
|
-
stats["clusters_found"] = len(clusters)
|
|
299
|
-
|
|
300
|
-
for entity_id, entity_name, fact_ids in clusters:
|
|
301
|
-
try:
|
|
302
|
-
result = _consolidate_cluster(
|
|
303
|
-
conn, profile_id, entity_id, entity_name,
|
|
304
|
-
fact_ids, dry_run, config,
|
|
305
|
-
)
|
|
306
|
-
if result:
|
|
307
|
-
stats["consolidated"] += 1
|
|
308
|
-
stats["facts_summarized"] += len(fact_ids)
|
|
309
|
-
else:
|
|
310
|
-
stats["rejected"] += 1
|
|
311
|
-
except Exception as exc:
|
|
312
|
-
logger.warning(
|
|
313
|
-
"Consolidation failed for %s: %s",
|
|
314
|
-
entity_name, exc, exc_info=True,
|
|
315
|
-
)
|
|
316
|
-
stats["errors"] += 1
|
|
317
|
-
|
|
318
|
-
if stats["consolidated"] > 0:
|
|
319
|
-
logger.info(
|
|
320
|
-
"Fact consolidation: %d display summaries written over %d facts",
|
|
321
|
-
stats["consolidated"], stats["facts_summarized"],
|
|
322
|
-
)
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
def _already_summarised(
|
|
326
|
-
db_path: "str | Path",
|
|
327
|
-
profile_id: str,
|
|
328
|
-
entity_id: str,
|
|
329
|
-
fact_ids: list[str],
|
|
330
|
-
) -> bool:
|
|
331
|
-
"""Whether this exact cluster already has a display summary.
|
|
332
|
-
|
|
333
|
-
Compares SORTED source ids, because cluster order comes from an
|
|
334
|
-
``ORDER BY confidence DESC, created_at DESC`` that two equal-confidence
|
|
335
|
-
facts can swap between passes -- comparing the raw lists would report
|
|
336
|
-
"changed" on a cluster that did not.
|
|
337
|
-
|
|
338
|
-
Any error means "not summarised", which costs one redundant generation and
|
|
339
|
-
never skips work that was needed. Read-only.
|
|
340
|
-
"""
|
|
341
|
-
from superlocalmemory.storage.memory_write import memory_read
|
|
342
|
-
|
|
343
|
-
wanted = sorted(fact_ids)
|
|
344
|
-
try:
|
|
345
|
-
with memory_read(db_path) as conn:
|
|
346
|
-
conn.row_factory = sqlite3.Row
|
|
347
|
-
rows = conn.execute(
|
|
348
|
-
"SELECT source_fact_ids FROM consolidated_summaries "
|
|
349
|
-
" WHERE profile_id = ? AND entity_id = ?",
|
|
350
|
-
(profile_id, entity_id),
|
|
351
|
-
).fetchall()
|
|
352
|
-
except Exception as exc: # noqa: BLE001 -- never block consolidation
|
|
353
|
-
logger.debug("cluster-unchanged check skipped: %s", exc)
|
|
354
|
-
return False
|
|
355
|
-
|
|
356
|
-
for row in rows:
|
|
357
|
-
try:
|
|
358
|
-
if sorted(json.loads(row["source_fact_ids"] or "[]")) == wanted:
|
|
359
|
-
return True
|
|
360
|
-
except (json.JSONDecodeError, TypeError):
|
|
361
|
-
continue
|
|
362
|
-
return False
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
def _find_consolidation_clusters(
|
|
366
|
-
conn: sqlite3.Connection,
|
|
367
|
-
profile_id: str,
|
|
368
|
-
max_clusters: int,
|
|
369
|
-
) -> list[tuple[str, str, list[str]]]:
|
|
370
|
-
"""Find entities with clusters of warm/cold facts ready for consolidation.
|
|
371
|
-
|
|
372
|
-
Uses JSON-boundary quoting on entity_id to prevent substring false positives.
|
|
373
|
-
Both outer count and inner fact query are scoped to profile_id.
|
|
374
|
-
"""
|
|
375
|
-
c = conn.cursor()
|
|
376
|
-
|
|
377
|
-
# Find entities with many non-active, non-pinned facts
|
|
378
|
-
# Uses '%" entity_id "%' pattern for JSON boundary matching
|
|
379
|
-
entities = c.execute("""
|
|
380
|
-
SELECT ce.entity_id, ce.canonical_name, COUNT(af.fact_id) as fact_count
|
|
381
|
-
FROM canonical_entities ce
|
|
382
|
-
JOIN atomic_facts af
|
|
383
|
-
ON af.canonical_entities_json LIKE '%"' || ce.entity_id || '"%'
|
|
384
|
-
AND af.profile_id = ?
|
|
385
|
-
WHERE ce.profile_id = ?
|
|
386
|
-
AND af.lifecycle IN ('warm', 'cold')
|
|
387
|
-
AND af.fact_id NOT IN (
|
|
388
|
-
SELECT fact_id FROM pinned_facts WHERE profile_id = ?
|
|
389
|
-
)
|
|
390
|
-
GROUP BY ce.entity_id
|
|
391
|
-
HAVING COUNT(af.fact_id) >= ?
|
|
392
|
-
ORDER BY COUNT(af.fact_id) DESC
|
|
393
|
-
LIMIT ?
|
|
394
|
-
""", (profile_id, profile_id, profile_id, _MIN_CLUSTER_SIZE,
|
|
395
|
-
max_clusters)).fetchall()
|
|
396
|
-
|
|
397
|
-
clusters = []
|
|
398
|
-
for entity in entities:
|
|
399
|
-
eid = entity["entity_id"]
|
|
400
|
-
facts = c.execute("""
|
|
401
|
-
SELECT af.fact_id FROM atomic_facts af
|
|
402
|
-
WHERE af.canonical_entities_json LIKE ?
|
|
403
|
-
AND af.profile_id = ?
|
|
404
|
-
AND af.lifecycle IN ('warm', 'cold')
|
|
405
|
-
AND af.fact_id NOT IN (
|
|
406
|
-
SELECT fact_id FROM pinned_facts WHERE profile_id = ?
|
|
407
|
-
)
|
|
408
|
-
ORDER BY af.confidence DESC, af.created_at DESC
|
|
409
|
-
LIMIT ?
|
|
410
|
-
""", (f'%"{eid}"%', profile_id, profile_id,
|
|
411
|
-
_MAX_CLUSTER_SIZE)).fetchall()
|
|
412
|
-
|
|
413
|
-
fact_ids = [f["fact_id"] for f in facts]
|
|
414
|
-
if len(fact_ids) >= _MIN_CLUSTER_SIZE:
|
|
415
|
-
clusters.append((eid, entity["canonical_name"], fact_ids))
|
|
416
|
-
|
|
417
|
-
return clusters
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
def _consolidate_cluster(
|
|
421
|
-
conn: sqlite3.Connection,
|
|
422
|
-
profile_id: str,
|
|
423
|
-
entity_id: str,
|
|
424
|
-
entity_name: str,
|
|
425
|
-
fact_ids: list[str],
|
|
426
|
-
dry_run: bool,
|
|
427
|
-
config: object | None = None,
|
|
428
|
-
*,
|
|
429
|
-
_presummary: str | None = None,
|
|
430
|
-
_generated_by: str = "extractive",
|
|
431
|
-
) -> dict | None:
|
|
432
|
-
"""Write one display summary for a cluster of facts.
|
|
433
|
-
|
|
434
|
-
Touches ``consolidated_summaries`` and ``fact_consolidations`` and nothing
|
|
435
|
-
else. In particular it does not write, update or archive any row in
|
|
436
|
-
``atomic_facts`` — see the module docstring.
|
|
437
|
-
|
|
438
|
-
All writes are wrapped in a SAVEPOINT for atomicity — if any step fails,
|
|
439
|
-
the entire cluster consolidation is rolled back.
|
|
440
|
-
|
|
441
|
-
_presummary (v3.8.4): when the caller supplies a pre-computed summary
|
|
442
|
-
(generated OUTSIDE the write lock), skip the _generate_summary() call.
|
|
443
|
-
This is the short-lock path used by the DatabaseManager branch of
|
|
444
|
-
consolidate_facts(). The str | Path backward-compat path still calls
|
|
445
|
-
_generate_summary() inline (legacy behaviour, no regression).
|
|
446
|
-
|
|
447
|
-
_generated_by records which mode produced the text ('extractive',
|
|
448
|
-
'ollama', 'cloud'), so a reader can tell a local extractive digest from
|
|
449
|
-
model prose without inspecting the words.
|
|
450
|
-
"""
|
|
451
|
-
c = conn.cursor()
|
|
452
|
-
|
|
453
|
-
# Load fact contents including canonical_entities_json.
|
|
454
|
-
# Even when _presummary is provided we still re-read from the DB inside
|
|
455
|
-
# the write lock so the SAVEPOINT has a fresh, authoritative facts list.
|
|
456
|
-
placeholders = ",".join("?" * len(fact_ids))
|
|
457
|
-
facts = c.execute(
|
|
458
|
-
f"SELECT fact_id, content, confidence, created_at, canonical_entities_json, "
|
|
459
|
-
f"scope, shared_with "
|
|
460
|
-
f"FROM atomic_facts "
|
|
461
|
-
f"WHERE fact_id IN ({placeholders}) ORDER BY created_at",
|
|
462
|
-
fact_ids,
|
|
463
|
-
).fetchall()
|
|
464
|
-
|
|
465
|
-
if len(facts) < _MIN_CLUSTER_SIZE:
|
|
466
|
-
return None
|
|
467
|
-
|
|
468
|
-
if _presummary is not None:
|
|
469
|
-
summary = _presummary
|
|
470
|
-
else:
|
|
471
|
-
# Legacy path (str | Path caller) — may call Ollama with write lock held.
|
|
472
|
-
summary, _generated_by = _generate_summary(entity_name, facts, config)
|
|
473
|
-
if not summary:
|
|
474
|
-
return None
|
|
475
|
-
|
|
476
|
-
# Last line of defence, on BOTH paths. _generate_summary already cleans and
|
|
477
|
-
# vets its own output, but this function is reachable with a caller-supplied
|
|
478
|
-
# _presummary, and "the caller checked" is exactly the assumption that let
|
|
479
|
-
# 1,195 non-answers into the store.
|
|
480
|
-
#
|
|
481
|
-
# Cleaning runs here too, not just judging. A first draft of this guard
|
|
482
|
-
# only judged, and a test supplying "Sure! Here is a concise summary:
|
|
483
|
-
# <real content>" as a _presummary stored the scaffolding verbatim -- the
|
|
484
|
-
# text passed the non-answer check because it does contain a real summary,
|
|
485
|
-
# and nothing had stripped the two sentences in front of it.
|
|
486
|
-
# clean_llm_summary is idempotent, so running it on both paths is free.
|
|
487
|
-
summary = clean_llm_summary(summary)
|
|
488
|
-
_rejected, _why = is_non_answer(summary, min_chars=MIN_USEFUL_CHARS)
|
|
489
|
-
if _rejected:
|
|
490
|
-
logger.info(
|
|
491
|
-
"Consolidation summary for '%s' rejected before write (%s)",
|
|
492
|
-
entity_name, _why,
|
|
493
|
-
)
|
|
494
|
-
return None
|
|
495
|
-
|
|
496
|
-
if dry_run:
|
|
497
|
-
return {"entity": entity_name, "facts": len(facts), "summary_len": len(summary)}
|
|
498
|
-
|
|
499
|
-
# Use SAVEPOINT for atomic multi-step write
|
|
500
|
-
savepoint_name = f"consolidate_{uuid.uuid4().hex[:8]}"
|
|
501
|
-
c.execute(f"SAVEPOINT {savepoint_name}")
|
|
502
|
-
|
|
503
|
-
try:
|
|
504
|
-
new_fact_id = uuid.uuid4().hex[:16]
|
|
505
|
-
now = datetime.now(timezone.utc).isoformat()
|
|
506
|
-
|
|
507
|
-
# v3.6.15 multi-scope: a summary must never be MORE visible than its
|
|
508
|
-
# sources, or it would leak a private fact into a shared/global summary.
|
|
509
|
-
# Preserve scope only when the whole cluster agrees; any mix (or shared
|
|
510
|
-
# facts with differing targets) falls back to 'personal' — the most
|
|
511
|
-
# restrictive scope. All-personal clusters (the common case) are
|
|
512
|
-
# unchanged. shared_with is preserved only for a uniform shared cluster.
|
|
513
|
-
_src_scopes = {(f["scope"] or "personal") for f in facts}
|
|
514
|
-
_src_shared = {f["shared_with"] for f in facts}
|
|
515
|
-
if _src_scopes == {"global"}:
|
|
516
|
-
_sum_scope, _sum_shared = "global", None
|
|
517
|
-
elif _src_scopes == {"shared"} and len(_src_shared) == 1:
|
|
518
|
-
_sum_scope, _sum_shared = "shared", facts[0]["shared_with"]
|
|
519
|
-
else:
|
|
520
|
-
_sum_scope, _sum_shared = "personal", None
|
|
521
|
-
|
|
522
|
-
# The cluster's pooled entity list is deliberately NOT carried onto the
|
|
523
|
-
# summary. Pooling ten facts' entities gave the old row more entity
|
|
524
|
-
# links than any single real memory, which is precisely how these rows
|
|
525
|
-
# came to out-rank the user's own words in the entity channel. The
|
|
526
|
-
# summary is identified by the one entity that seeded the cluster.
|
|
527
|
-
|
|
528
|
-
# The summary goes in the DISPLAY table. Not atomic_facts — see the
|
|
529
|
-
# module docstring for the 1,195 rows that taught us why.
|
|
530
|
-
#
|
|
531
|
-
# UNIQUE (profile_id, entity_id, content) makes a repeated pass
|
|
532
|
-
# idempotent: re-summarising an unchanged cluster refreshes the
|
|
533
|
-
# coverage window instead of accumulating a near-duplicate every time
|
|
534
|
-
# maintenance runs. The old code's reinforce-or-insert dance against
|
|
535
|
-
# atomic_facts existed for the same reason and is no longer needed.
|
|
536
|
-
_dates = [f["created_at"] for f in facts if f["created_at"]]
|
|
537
|
-
c.execute("""
|
|
538
|
-
INSERT INTO consolidated_summaries
|
|
539
|
-
(summary_id, profile_id, entity_id, entity_name, content,
|
|
540
|
-
source_fact_ids, source_count, char_count, generated_by,
|
|
541
|
-
scope, shared_with, source_earliest, source_latest, created_at)
|
|
542
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
543
|
-
ON CONFLICT (profile_id, entity_id, content) DO UPDATE SET
|
|
544
|
-
source_fact_ids = excluded.source_fact_ids,
|
|
545
|
-
source_count = excluded.source_count,
|
|
546
|
-
source_earliest = excluded.source_earliest,
|
|
547
|
-
source_latest = excluded.source_latest,
|
|
548
|
-
created_at = excluded.created_at
|
|
549
|
-
""", (
|
|
550
|
-
new_fact_id, profile_id, entity_id, entity_name, summary,
|
|
551
|
-
json.dumps(fact_ids), len(facts), len(summary),
|
|
552
|
-
_generated_by, _sum_scope, _sum_shared,
|
|
553
|
-
min(_dates) if _dates else None,
|
|
554
|
-
max(_dates) if _dates else None,
|
|
555
|
-
now,
|
|
556
|
-
))
|
|
557
|
-
|
|
558
|
-
# Provenance ledger, kept for the repair pass and for auditability.
|
|
559
|
-
# `strategy` distinguishes a display summary from the retrieval-corpus
|
|
560
|
-
# rows the old path wrote, so 'entity_cluster' remains an exact
|
|
561
|
-
# selector for what has to be quarantined.
|
|
562
|
-
# Deterministic id + INSERT OR IGNORE, so re-running over the same
|
|
563
|
-
# cluster does not append a row. fact_consolidations carries no unique
|
|
564
|
-
# constraint, so a random uuid grew the ledger by one row per
|
|
565
|
-
# maintenance pass per cluster with nothing to show for it -- roughly a
|
|
566
|
-
# thousand rows a day on a store with twenty clusters.
|
|
567
|
-
consolidation_id = hashlib.sha256(
|
|
568
|
-
"\0".join((
|
|
569
|
-
profile_id, new_fact_id, "display_summary",
|
|
570
|
-
*sorted(fact_ids),
|
|
571
|
-
)).encode("utf-8")
|
|
572
|
-
).hexdigest()[:16]
|
|
573
|
-
c.execute("""
|
|
574
|
-
INSERT OR IGNORE INTO fact_consolidations
|
|
575
|
-
(consolidation_id, profile_id, consolidated_fact_id,
|
|
576
|
-
source_fact_ids, strategy, created_at)
|
|
577
|
-
VALUES (?, ?, ?, ?, 'display_summary', ?)
|
|
578
|
-
""", (consolidation_id, profile_id, new_fact_id,
|
|
579
|
-
json.dumps(fact_ids), now))
|
|
580
|
-
|
|
581
|
-
# NO archiving, and NO association_edge deletion.
|
|
582
|
-
#
|
|
583
|
-
# Both were correct while a retrievable summary replaced the facts it
|
|
584
|
-
# merged. A display-only summary replaces nothing, so archiving the
|
|
585
|
-
# sources would take ten reachable memories out of recall and put
|
|
586
|
-
# nothing in their place. Measured cost of the old behaviour on the
|
|
587
|
-
# author's store: 528 genuine memories sitting in retention zone
|
|
588
|
-
# 'archive', every one of them put there by this function and by
|
|
589
|
-
# nothing else, all unreachable in normal recall.
|
|
590
|
-
#
|
|
591
|
-
# This module is now purely additive to the store. A test asserts it:
|
|
592
|
-
# tests/test_core/test_consolidation_writes_no_memories.py
|
|
593
|
-
c.execute(f"RELEASE SAVEPOINT {savepoint_name}")
|
|
594
|
-
|
|
595
|
-
except Exception:
|
|
596
|
-
c.execute(f"ROLLBACK TO SAVEPOINT {savepoint_name}")
|
|
597
|
-
raise
|
|
598
|
-
|
|
599
|
-
logger.info(
|
|
600
|
-
"Display summary for '%s' from %d facts → %s (%d chars, %s)",
|
|
601
|
-
entity_name, len(facts), new_fact_id[:8], len(summary), _generated_by,
|
|
602
|
-
)
|
|
603
|
-
|
|
604
|
-
return {"entity": entity_name, "facts": len(facts), "new_fact_id": new_fact_id}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
def _generate_summary(
|
|
608
|
-
entity_name: str,
|
|
609
|
-
facts: list,
|
|
610
|
-
config: object | None = None,
|
|
611
|
-
) -> tuple[str | None, str]:
|
|
612
|
-
"""Generate a display summary for the user's configured mode.
|
|
613
|
-
|
|
614
|
-
Returns ``(summary_or_None, generated_by)`` where ``generated_by`` is
|
|
615
|
-
'extractive', 'ollama' or 'cloud'. It used to return the text alone, which
|
|
616
|
-
left no way to tell a local digest from model prose after the fact — and
|
|
617
|
-
since only the model paths can produce a non-answer, that distinction is
|
|
618
|
-
what makes a bad batch traceable to its source.
|
|
619
|
-
|
|
620
|
-
Model output goes through two stages before it is offered to the caller:
|
|
621
|
-
|
|
622
|
-
1. ``clean_llm_summary`` strips chat scaffolding *around* the answer.
|
|
623
|
-
This stripper has existed in ``summaries/base.py`` since 3.6 and this
|
|
624
|
-
module referenced it zero times, which is why 20 of the author's
|
|
625
|
-
stored summaries begin "Here is a concise summary paragraph".
|
|
626
|
-
2. ``is_non_answer`` rejects text that is scaffolding *all the way
|
|
627
|
-
through* — a refusal, a request for more input, a report that the
|
|
628
|
-
input was empty. Order matters: step 1 rescues "Here is a summary:
|
|
629
|
-
<real content>", and running step 2 first would discard it.
|
|
630
|
-
|
|
631
|
-
A rejected model summary falls back to extractive, which is derived
|
|
632
|
-
mechanically from the facts and so cannot refuse. Falling back beats
|
|
633
|
-
writing nothing: the user still gets a digest.
|
|
634
|
-
|
|
635
|
-
All modes cap output at _MAX_CONSOLIDATED_CHARS.
|
|
636
|
-
"""
|
|
637
|
-
mode = "a"
|
|
638
|
-
if config:
|
|
639
|
-
m = getattr(config, 'mode', None)
|
|
640
|
-
if m:
|
|
641
|
-
mode = getattr(m, 'value', str(m)).lower()
|
|
642
|
-
|
|
643
|
-
def _vet(
|
|
644
|
-
text: str | None, source: str, *, model_written: bool = True,
|
|
645
|
-
) -> str | None:
|
|
646
|
-
"""Clean, then reject a non-answer. Returns None if unusable."""
|
|
647
|
-
if not text:
|
|
648
|
-
return None
|
|
649
|
-
cleaned = clean_llm_summary(text)
|
|
650
|
-
rejected, why = is_non_answer(
|
|
651
|
-
cleaned, min_chars=MIN_USEFUL_CHARS, model_written=model_written,
|
|
652
|
-
)
|
|
653
|
-
if rejected:
|
|
654
|
-
logger.info(
|
|
655
|
-
"%s summary for '%s' discarded (%s); falling back",
|
|
656
|
-
source, entity_name, why,
|
|
657
|
-
)
|
|
658
|
-
return None
|
|
659
|
-
return cleaned
|
|
660
|
-
|
|
661
|
-
result: str | None = None
|
|
662
|
-
generated_by = "extractive"
|
|
663
|
-
|
|
664
|
-
if mode == "b":
|
|
665
|
-
result = _vet(_summarize_with_ollama(entity_name, facts, config), "Ollama")
|
|
666
|
-
if result:
|
|
667
|
-
generated_by = "ollama"
|
|
668
|
-
elif mode == "c":
|
|
669
|
-
result = _vet(_summarize_with_cloud_llm(entity_name, facts, config), "Cloud LLM")
|
|
670
|
-
if result:
|
|
671
|
-
generated_by = "cloud"
|
|
672
|
-
else:
|
|
673
|
-
result = _vet(_summarize_with_ollama(entity_name, facts, config), "Ollama")
|
|
674
|
-
if result:
|
|
675
|
-
generated_by = "ollama"
|
|
676
|
-
|
|
677
|
-
if not result:
|
|
678
|
-
# Extractive is assembled from the facts' own sentences, so it has no
|
|
679
|
-
# opinion to refuse with — and applying the refusal rules to it would
|
|
680
|
-
# reject a summary because the owner's own memory happened to contain a
|
|
681
|
-
# phrase like "the facts provided". Still checked for the two things
|
|
682
|
-
# that do apply: length, and tool-call markup, which a cluster of
|
|
683
|
-
# markup-carrying facts would otherwise reassemble verbatim.
|
|
684
|
-
result = _vet(
|
|
685
|
-
_summarize_extractive(entity_name, facts), "Extractive",
|
|
686
|
-
model_written=False,
|
|
687
|
-
)
|
|
688
|
-
generated_by = "extractive"
|
|
689
|
-
|
|
690
|
-
# Uniform cap across all modes
|
|
691
|
-
if result and len(result) > _MAX_CONSOLIDATED_CHARS:
|
|
692
|
-
result = result[:_MAX_CONSOLIDATED_CHARS - 3] + "..."
|
|
693
|
-
|
|
694
|
-
return result, generated_by
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
def _summarize_with_ollama(
|
|
698
|
-
entity_name: str,
|
|
699
|
-
facts: list,
|
|
700
|
-
config: object | None = None,
|
|
701
|
-
) -> str | None:
|
|
702
|
-
"""Mode B: Summarize using local Ollama LLM."""
|
|
703
|
-
try:
|
|
704
|
-
import urllib.request
|
|
705
|
-
|
|
706
|
-
api_base = "http://localhost:11434"
|
|
707
|
-
model = "llama3.2"
|
|
708
|
-
timeout = 30
|
|
709
|
-
|
|
710
|
-
if config and hasattr(config, 'llm'):
|
|
711
|
-
api_base = getattr(config.llm, 'api_base', api_base) or api_base
|
|
712
|
-
model = getattr(config.llm, 'model', model) or model
|
|
713
|
-
# v3.6.12 (modeb-4): the LLMConfig field is `timeout_seconds`, not
|
|
714
|
-
# `timeout` — the old read always missed and silently used 30s.
|
|
715
|
-
timeout = getattr(config.llm, 'timeout_seconds', None) or \
|
|
716
|
-
getattr(config.llm, 'timeout', None) or timeout
|
|
717
|
-
|
|
718
|
-
fact_texts = "\n".join(f"- {f['content']}" for f in facts[:_MAX_CLUSTER_SIZE])
|
|
719
|
-
prompt = (
|
|
720
|
-
f"Merge these {len(facts)} facts about '{entity_name}' into ONE concise "
|
|
721
|
-
f"summary paragraph. Keep all key information. Maximum 500 words. "
|
|
722
|
-
f"No preamble.\n\nFacts:\n{fact_texts}"
|
|
723
|
-
)
|
|
724
|
-
|
|
725
|
-
payload = json.dumps({
|
|
726
|
-
"model": model,
|
|
727
|
-
"prompt": prompt,
|
|
728
|
-
"stream": False,
|
|
729
|
-
"options": {"num_predict": 600},
|
|
730
|
-
}).encode()
|
|
731
|
-
|
|
732
|
-
req = urllib.request.Request(
|
|
733
|
-
f"{api_base}/api/generate",
|
|
734
|
-
data=payload,
|
|
735
|
-
headers={"Content-Type": "application/json"},
|
|
736
|
-
)
|
|
737
|
-
resp = urllib.request.urlopen(req, timeout=timeout)
|
|
738
|
-
result = json.loads(resp.read().decode())
|
|
739
|
-
text = result.get("response", "").strip()
|
|
740
|
-
return text if text and len(text) > 50 else None
|
|
741
|
-
except Exception as exc:
|
|
742
|
-
logger.warning("Ollama summarization failed: %s", exc)
|
|
743
|
-
return None
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
def _summarize_with_cloud_llm(
|
|
747
|
-
entity_name: str,
|
|
748
|
-
facts: list,
|
|
749
|
-
config: object | None = None,
|
|
750
|
-
) -> str | None:
|
|
751
|
-
"""Mode C: Summarize using the user's configured cloud LLM provider."""
|
|
752
|
-
if not config or not hasattr(config, 'llm'):
|
|
753
|
-
return None
|
|
754
|
-
|
|
755
|
-
llm_config = config.llm
|
|
756
|
-
provider = getattr(llm_config, 'provider', '')
|
|
757
|
-
if not provider:
|
|
758
|
-
return None
|
|
759
|
-
|
|
760
|
-
try:
|
|
761
|
-
from superlocalmemory.llm.backbone import LLMBackbone
|
|
762
|
-
llm = LLMBackbone(llm_config)
|
|
763
|
-
if not llm.is_available():
|
|
764
|
-
return None
|
|
765
|
-
|
|
766
|
-
fact_texts = "\n".join(f"- {f['content']}" for f in facts[:_MAX_CLUSTER_SIZE])
|
|
767
|
-
prompt = (
|
|
768
|
-
f"Merge these {len(facts)} facts about '{entity_name}' into ONE concise "
|
|
769
|
-
f"summary paragraph. Keep all key information. Maximum 500 words. "
|
|
770
|
-
f"No preamble.\n\nFacts:\n{fact_texts}"
|
|
771
|
-
)
|
|
772
|
-
|
|
773
|
-
response = llm.generate(
|
|
774
|
-
prompt=prompt,
|
|
775
|
-
system="You are a precise fact summarizer. Output only the merged summary.",
|
|
776
|
-
max_tokens=600,
|
|
777
|
-
temperature=0.1,
|
|
778
|
-
)
|
|
779
|
-
text = response.strip() if response else None
|
|
780
|
-
return text if text and len(text) > 50 else None
|
|
781
|
-
except Exception as exc:
|
|
782
|
-
logger.warning("Cloud LLM summarization failed: %s", exc)
|
|
783
|
-
return None
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
def _summarize_extractive(entity_name: str, facts: list) -> str:
|
|
787
|
-
"""Extractive summary — all sentences from all facts, deduped.
|
|
788
|
-
|
|
789
|
-
Includes ALL sentences from each fact (not just the first one)
|
|
790
|
-
to preserve complete information.
|
|
791
|
-
"""
|
|
792
|
-
header = f"{entity_name}: "
|
|
793
|
-
seen = set()
|
|
794
|
-
sentences = []
|
|
795
|
-
|
|
796
|
-
for f in facts:
|
|
797
|
-
content = f["content"]
|
|
798
|
-
# Split on sentence boundaries and include ALL sentences
|
|
799
|
-
raw_sentences = [s.strip() for s in content.split(". ") if s.strip()]
|
|
800
|
-
for sent in raw_sentences:
|
|
801
|
-
if not sent.endswith("."):
|
|
802
|
-
sent += "."
|
|
803
|
-
normalized = sent.lower()
|
|
804
|
-
if normalized not in seen:
|
|
805
|
-
seen.add(normalized)
|
|
806
|
-
sentences.append(sent)
|
|
807
|
-
|
|
808
|
-
body = " ".join(sentences)
|
|
809
|
-
result = header + body
|
|
810
|
-
if len(result) > _MAX_CONSOLIDATED_CHARS:
|
|
811
|
-
result = result[:_MAX_CONSOLIDATED_CHARS - 3] + "..."
|
|
812
|
-
return result
|