superlocalmemory 2.8.5 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (434) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +9 -1
  3. package/NOTICE +63 -0
  4. package/README.md +165 -480
  5. package/bin/slm +17 -449
  6. package/bin/slm-npm +2 -2
  7. package/bin/slm.bat +4 -2
  8. package/conftest.py +5 -0
  9. package/docs/api-reference.md +284 -0
  10. package/docs/architecture.md +149 -0
  11. package/docs/auto-memory.md +150 -0
  12. package/docs/cli-reference.md +276 -0
  13. package/docs/compliance.md +191 -0
  14. package/docs/configuration.md +182 -0
  15. package/docs/getting-started.md +102 -0
  16. package/docs/ide-setup.md +261 -0
  17. package/docs/mcp-tools.md +220 -0
  18. package/docs/migration-from-v2.md +170 -0
  19. package/docs/profiles.md +173 -0
  20. package/docs/troubleshooting.md +310 -0
  21. package/{configs → ide/configs}/antigravity-mcp.json +3 -3
  22. package/ide/configs/chatgpt-desktop-mcp.json +16 -0
  23. package/{configs → ide/configs}/claude-desktop-mcp.json +3 -3
  24. package/{configs → ide/configs}/codex-mcp.toml +4 -4
  25. package/{configs → ide/configs}/continue-mcp.yaml +4 -3
  26. package/{configs → ide/configs}/continue-skills.yaml +6 -6
  27. package/ide/configs/cursor-mcp.json +15 -0
  28. package/{configs → ide/configs}/gemini-cli-mcp.json +2 -2
  29. package/{configs → ide/configs}/jetbrains-mcp.json +2 -2
  30. package/{configs → ide/configs}/opencode-mcp.json +2 -2
  31. package/{configs → ide/configs}/perplexity-mcp.json +2 -2
  32. package/{configs → ide/configs}/vscode-copilot-mcp.json +2 -2
  33. package/{configs → ide/configs}/windsurf-mcp.json +3 -3
  34. package/{configs → ide/configs}/zed-mcp.json +2 -2
  35. package/{hooks → ide/hooks}/context-hook.js +9 -20
  36. package/ide/hooks/memory-list-skill.js +70 -0
  37. package/ide/hooks/memory-profile-skill.js +101 -0
  38. package/ide/hooks/memory-recall-skill.js +62 -0
  39. package/ide/hooks/memory-remember-skill.js +68 -0
  40. package/ide/hooks/memory-reset-skill.js +160 -0
  41. package/{hooks → ide/hooks}/post-recall-hook.js +2 -2
  42. package/ide/integrations/langchain/README.md +106 -0
  43. package/ide/integrations/langchain/langchain_superlocalmemory/__init__.py +9 -0
  44. package/ide/integrations/langchain/langchain_superlocalmemory/chat_message_history.py +201 -0
  45. package/ide/integrations/langchain/pyproject.toml +38 -0
  46. package/{src/learning → ide/integrations/langchain}/tests/__init__.py +1 -0
  47. package/ide/integrations/langchain/tests/test_chat_message_history.py +215 -0
  48. package/ide/integrations/langchain/tests/test_security.py +117 -0
  49. package/ide/integrations/llamaindex/README.md +81 -0
  50. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/__init__.py +9 -0
  51. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/base.py +316 -0
  52. package/ide/integrations/llamaindex/pyproject.toml +43 -0
  53. package/{src/lifecycle → ide/integrations/llamaindex}/tests/__init__.py +1 -2
  54. package/ide/integrations/llamaindex/tests/test_chat_store.py +294 -0
  55. package/ide/integrations/llamaindex/tests/test_security.py +241 -0
  56. package/{skills → ide/skills}/slm-build-graph/SKILL.md +6 -6
  57. package/{skills → ide/skills}/slm-list-recent/SKILL.md +5 -5
  58. package/{skills → ide/skills}/slm-recall/SKILL.md +5 -5
  59. package/{skills → ide/skills}/slm-remember/SKILL.md +6 -6
  60. package/{skills → ide/skills}/slm-show-patterns/SKILL.md +7 -7
  61. package/{skills → ide/skills}/slm-status/SKILL.md +9 -9
  62. package/{skills → ide/skills}/slm-switch-profile/SKILL.md +9 -9
  63. package/package.json +13 -22
  64. package/pyproject.toml +85 -0
  65. package/scripts/build-dmg.sh +417 -0
  66. package/scripts/install-skills.ps1 +334 -0
  67. package/{install.ps1 → scripts/install.ps1} +36 -4
  68. package/{install.sh → scripts/install.sh} +14 -13
  69. package/scripts/postinstall.js +2 -2
  70. package/scripts/start-dashboard.ps1 +52 -0
  71. package/scripts/start-dashboard.sh +41 -0
  72. package/scripts/sync-wiki.ps1 +127 -0
  73. package/scripts/sync-wiki.sh +82 -0
  74. package/scripts/test-dmg.sh +161 -0
  75. package/scripts/test-npm-package.ps1 +252 -0
  76. package/scripts/test-npm-package.sh +207 -0
  77. package/scripts/verify-install.ps1 +294 -0
  78. package/scripts/verify-install.sh +266 -0
  79. package/src/superlocalmemory/__init__.py +0 -0
  80. package/src/superlocalmemory/attribution/__init__.py +9 -0
  81. package/src/superlocalmemory/attribution/mathematical_dna.py +235 -0
  82. package/src/superlocalmemory/attribution/signer.py +153 -0
  83. package/src/superlocalmemory/attribution/watermark.py +189 -0
  84. package/src/superlocalmemory/cli/__init__.py +5 -0
  85. package/src/superlocalmemory/cli/commands.py +245 -0
  86. package/src/superlocalmemory/cli/main.py +89 -0
  87. package/src/superlocalmemory/cli/migrate_cmd.py +55 -0
  88. package/src/superlocalmemory/cli/post_install.py +99 -0
  89. package/src/superlocalmemory/cli/setup_wizard.py +129 -0
  90. package/src/superlocalmemory/compliance/__init__.py +0 -0
  91. package/src/superlocalmemory/compliance/abac.py +204 -0
  92. package/src/superlocalmemory/compliance/audit.py +314 -0
  93. package/src/superlocalmemory/compliance/eu_ai_act.py +131 -0
  94. package/src/superlocalmemory/compliance/gdpr.py +294 -0
  95. package/src/superlocalmemory/compliance/lifecycle.py +158 -0
  96. package/src/superlocalmemory/compliance/retention.py +232 -0
  97. package/src/superlocalmemory/compliance/scheduler.py +148 -0
  98. package/src/superlocalmemory/core/__init__.py +0 -0
  99. package/src/superlocalmemory/core/config.py +391 -0
  100. package/src/superlocalmemory/core/embeddings.py +293 -0
  101. package/src/superlocalmemory/core/engine.py +701 -0
  102. package/src/superlocalmemory/core/hooks.py +65 -0
  103. package/src/superlocalmemory/core/maintenance.py +172 -0
  104. package/src/superlocalmemory/core/modes.py +140 -0
  105. package/src/superlocalmemory/core/profiles.py +234 -0
  106. package/src/superlocalmemory/core/registry.py +117 -0
  107. package/src/superlocalmemory/dynamics/__init__.py +0 -0
  108. package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +223 -0
  109. package/src/superlocalmemory/encoding/__init__.py +0 -0
  110. package/src/superlocalmemory/encoding/consolidator.py +485 -0
  111. package/src/superlocalmemory/encoding/emotional.py +125 -0
  112. package/src/superlocalmemory/encoding/entity_resolver.py +525 -0
  113. package/src/superlocalmemory/encoding/entropy_gate.py +104 -0
  114. package/src/superlocalmemory/encoding/fact_extractor.py +775 -0
  115. package/src/superlocalmemory/encoding/foresight.py +91 -0
  116. package/src/superlocalmemory/encoding/graph_builder.py +302 -0
  117. package/src/superlocalmemory/encoding/observation_builder.py +160 -0
  118. package/src/superlocalmemory/encoding/scene_builder.py +183 -0
  119. package/src/superlocalmemory/encoding/signal_inference.py +90 -0
  120. package/src/superlocalmemory/encoding/temporal_parser.py +426 -0
  121. package/src/superlocalmemory/encoding/type_router.py +235 -0
  122. package/src/superlocalmemory/hooks/__init__.py +3 -0
  123. package/src/superlocalmemory/hooks/auto_capture.py +111 -0
  124. package/src/superlocalmemory/hooks/auto_recall.py +93 -0
  125. package/src/superlocalmemory/hooks/ide_connector.py +204 -0
  126. package/src/superlocalmemory/hooks/rules_engine.py +99 -0
  127. package/src/superlocalmemory/infra/__init__.py +3 -0
  128. package/src/superlocalmemory/infra/auth_middleware.py +82 -0
  129. package/src/superlocalmemory/infra/backup.py +317 -0
  130. package/src/superlocalmemory/infra/cache_manager.py +267 -0
  131. package/src/superlocalmemory/infra/event_bus.py +381 -0
  132. package/src/superlocalmemory/infra/rate_limiter.py +135 -0
  133. package/src/{webhook_dispatcher.py → superlocalmemory/infra/webhook_dispatcher.py} +104 -101
  134. package/src/superlocalmemory/learning/__init__.py +0 -0
  135. package/src/superlocalmemory/learning/adaptive.py +172 -0
  136. package/src/superlocalmemory/learning/behavioral.py +490 -0
  137. package/src/superlocalmemory/learning/behavioral_listener.py +94 -0
  138. package/src/superlocalmemory/learning/bootstrap.py +298 -0
  139. package/src/superlocalmemory/learning/cross_project.py +399 -0
  140. package/src/superlocalmemory/learning/database.py +376 -0
  141. package/src/superlocalmemory/learning/engagement.py +323 -0
  142. package/src/superlocalmemory/learning/features.py +138 -0
  143. package/src/superlocalmemory/learning/feedback.py +316 -0
  144. package/src/superlocalmemory/learning/outcomes.py +255 -0
  145. package/src/superlocalmemory/learning/project_context.py +366 -0
  146. package/src/superlocalmemory/learning/ranker.py +155 -0
  147. package/src/superlocalmemory/learning/source_quality.py +303 -0
  148. package/src/superlocalmemory/learning/workflows.py +309 -0
  149. package/src/superlocalmemory/llm/__init__.py +0 -0
  150. package/src/superlocalmemory/llm/backbone.py +316 -0
  151. package/src/superlocalmemory/math/__init__.py +0 -0
  152. package/src/superlocalmemory/math/fisher.py +356 -0
  153. package/src/superlocalmemory/math/langevin.py +398 -0
  154. package/src/superlocalmemory/math/sheaf.py +257 -0
  155. package/src/superlocalmemory/mcp/__init__.py +0 -0
  156. package/src/superlocalmemory/mcp/resources.py +245 -0
  157. package/src/superlocalmemory/mcp/server.py +61 -0
  158. package/src/superlocalmemory/mcp/tools.py +18 -0
  159. package/src/superlocalmemory/mcp/tools_core.py +305 -0
  160. package/src/superlocalmemory/mcp/tools_v28.py +223 -0
  161. package/src/superlocalmemory/mcp/tools_v3.py +286 -0
  162. package/src/superlocalmemory/retrieval/__init__.py +0 -0
  163. package/src/superlocalmemory/retrieval/agentic.py +295 -0
  164. package/src/superlocalmemory/retrieval/ann_index.py +223 -0
  165. package/src/superlocalmemory/retrieval/bm25_channel.py +185 -0
  166. package/src/superlocalmemory/retrieval/bridge_discovery.py +170 -0
  167. package/src/superlocalmemory/retrieval/engine.py +390 -0
  168. package/src/superlocalmemory/retrieval/entity_channel.py +179 -0
  169. package/src/superlocalmemory/retrieval/fusion.py +78 -0
  170. package/src/superlocalmemory/retrieval/profile_channel.py +105 -0
  171. package/src/superlocalmemory/retrieval/reranker.py +154 -0
  172. package/src/superlocalmemory/retrieval/semantic_channel.py +232 -0
  173. package/src/superlocalmemory/retrieval/strategy.py +96 -0
  174. package/src/superlocalmemory/retrieval/temporal_channel.py +175 -0
  175. package/src/superlocalmemory/server/__init__.py +1 -0
  176. package/src/superlocalmemory/server/api.py +248 -0
  177. package/src/superlocalmemory/server/routes/__init__.py +4 -0
  178. package/src/superlocalmemory/server/routes/agents.py +107 -0
  179. package/src/superlocalmemory/server/routes/backup.py +91 -0
  180. package/src/superlocalmemory/server/routes/behavioral.py +127 -0
  181. package/src/superlocalmemory/server/routes/compliance.py +160 -0
  182. package/src/superlocalmemory/server/routes/data_io.py +188 -0
  183. package/src/superlocalmemory/server/routes/events.py +183 -0
  184. package/src/superlocalmemory/server/routes/helpers.py +85 -0
  185. package/src/superlocalmemory/server/routes/learning.py +273 -0
  186. package/src/superlocalmemory/server/routes/lifecycle.py +116 -0
  187. package/src/superlocalmemory/server/routes/memories.py +399 -0
  188. package/src/superlocalmemory/server/routes/profiles.py +219 -0
  189. package/src/superlocalmemory/server/routes/stats.py +346 -0
  190. package/src/superlocalmemory/server/routes/v3_api.py +365 -0
  191. package/src/superlocalmemory/server/routes/ws.py +82 -0
  192. package/src/superlocalmemory/server/security_middleware.py +57 -0
  193. package/src/superlocalmemory/server/ui.py +245 -0
  194. package/src/superlocalmemory/storage/__init__.py +0 -0
  195. package/src/superlocalmemory/storage/access_control.py +182 -0
  196. package/src/superlocalmemory/storage/database.py +594 -0
  197. package/src/superlocalmemory/storage/migrations.py +303 -0
  198. package/src/superlocalmemory/storage/models.py +406 -0
  199. package/src/superlocalmemory/storage/schema.py +726 -0
  200. package/src/superlocalmemory/storage/v2_migrator.py +317 -0
  201. package/src/superlocalmemory/trust/__init__.py +0 -0
  202. package/src/superlocalmemory/trust/gate.py +130 -0
  203. package/src/superlocalmemory/trust/provenance.py +124 -0
  204. package/src/superlocalmemory/trust/scorer.py +347 -0
  205. package/src/superlocalmemory/trust/signals.py +153 -0
  206. package/ui/index.html +278 -5
  207. package/ui/js/auto-settings.js +70 -0
  208. package/ui/js/dashboard.js +90 -0
  209. package/ui/js/fact-detail.js +92 -0
  210. package/ui/js/feedback.js +2 -2
  211. package/ui/js/ide-status.js +102 -0
  212. package/ui/js/math-health.js +98 -0
  213. package/ui/js/recall-lab.js +127 -0
  214. package/ui/js/settings.js +2 -2
  215. package/ui/js/trust-dashboard.js +73 -0
  216. package/api_server.py +0 -724
  217. package/bin/aider-smart +0 -72
  218. package/bin/superlocalmemoryv2-learning +0 -4
  219. package/bin/superlocalmemoryv2-list +0 -3
  220. package/bin/superlocalmemoryv2-patterns +0 -4
  221. package/bin/superlocalmemoryv2-profile +0 -3
  222. package/bin/superlocalmemoryv2-recall +0 -3
  223. package/bin/superlocalmemoryv2-remember +0 -3
  224. package/bin/superlocalmemoryv2-reset +0 -3
  225. package/bin/superlocalmemoryv2-status +0 -3
  226. package/configs/chatgpt-desktop-mcp.json +0 -16
  227. package/configs/cursor-mcp.json +0 -15
  228. package/docs/SECURITY-QUICK-REFERENCE.md +0 -214
  229. package/hooks/memory-list-skill.js +0 -139
  230. package/hooks/memory-profile-skill.js +0 -273
  231. package/hooks/memory-recall-skill.js +0 -114
  232. package/hooks/memory-remember-skill.js +0 -127
  233. package/hooks/memory-reset-skill.js +0 -274
  234. package/mcp_server.py +0 -1800
  235. package/requirements-core.txt +0 -22
  236. package/requirements-learning.txt +0 -12
  237. package/requirements.txt +0 -12
  238. package/src/agent_registry.py +0 -411
  239. package/src/auth_middleware.py +0 -61
  240. package/src/auto_backup.py +0 -459
  241. package/src/behavioral/__init__.py +0 -49
  242. package/src/behavioral/behavioral_listener.py +0 -203
  243. package/src/behavioral/behavioral_patterns.py +0 -275
  244. package/src/behavioral/cross_project_transfer.py +0 -206
  245. package/src/behavioral/outcome_inference.py +0 -194
  246. package/src/behavioral/outcome_tracker.py +0 -193
  247. package/src/behavioral/tests/__init__.py +0 -4
  248. package/src/behavioral/tests/test_behavioral_integration.py +0 -108
  249. package/src/behavioral/tests/test_behavioral_patterns.py +0 -150
  250. package/src/behavioral/tests/test_cross_project_transfer.py +0 -142
  251. package/src/behavioral/tests/test_mcp_behavioral.py +0 -139
  252. package/src/behavioral/tests/test_mcp_report_outcome.py +0 -117
  253. package/src/behavioral/tests/test_outcome_inference.py +0 -107
  254. package/src/behavioral/tests/test_outcome_tracker.py +0 -96
  255. package/src/cache_manager.py +0 -518
  256. package/src/compliance/__init__.py +0 -48
  257. package/src/compliance/abac_engine.py +0 -149
  258. package/src/compliance/abac_middleware.py +0 -116
  259. package/src/compliance/audit_db.py +0 -215
  260. package/src/compliance/audit_logger.py +0 -148
  261. package/src/compliance/retention_manager.py +0 -289
  262. package/src/compliance/retention_scheduler.py +0 -186
  263. package/src/compliance/tests/__init__.py +0 -4
  264. package/src/compliance/tests/test_abac_enforcement.py +0 -95
  265. package/src/compliance/tests/test_abac_engine.py +0 -124
  266. package/src/compliance/tests/test_abac_mcp_integration.py +0 -118
  267. package/src/compliance/tests/test_audit_db.py +0 -123
  268. package/src/compliance/tests/test_audit_logger.py +0 -98
  269. package/src/compliance/tests/test_mcp_audit.py +0 -128
  270. package/src/compliance/tests/test_mcp_retention_policy.py +0 -125
  271. package/src/compliance/tests/test_retention_manager.py +0 -131
  272. package/src/compliance/tests/test_retention_scheduler.py +0 -99
  273. package/src/compression/__init__.py +0 -25
  274. package/src/compression/cli.py +0 -150
  275. package/src/compression/cold_storage.py +0 -217
  276. package/src/compression/config.py +0 -72
  277. package/src/compression/orchestrator.py +0 -133
  278. package/src/compression/tier2_compressor.py +0 -228
  279. package/src/compression/tier3_compressor.py +0 -153
  280. package/src/compression/tier_classifier.py +0 -148
  281. package/src/db_connection_manager.py +0 -536
  282. package/src/embedding_engine.py +0 -63
  283. package/src/embeddings/__init__.py +0 -47
  284. package/src/embeddings/cache.py +0 -70
  285. package/src/embeddings/cli.py +0 -113
  286. package/src/embeddings/constants.py +0 -47
  287. package/src/embeddings/database.py +0 -91
  288. package/src/embeddings/engine.py +0 -247
  289. package/src/embeddings/model_loader.py +0 -145
  290. package/src/event_bus.py +0 -562
  291. package/src/graph/__init__.py +0 -36
  292. package/src/graph/build_helpers.py +0 -74
  293. package/src/graph/cli.py +0 -87
  294. package/src/graph/cluster_builder.py +0 -188
  295. package/src/graph/cluster_summary.py +0 -148
  296. package/src/graph/constants.py +0 -47
  297. package/src/graph/edge_builder.py +0 -162
  298. package/src/graph/entity_extractor.py +0 -95
  299. package/src/graph/graph_core.py +0 -226
  300. package/src/graph/graph_search.py +0 -231
  301. package/src/graph/hierarchical.py +0 -207
  302. package/src/graph/schema.py +0 -99
  303. package/src/graph_engine.py +0 -52
  304. package/src/hnsw_index.py +0 -628
  305. package/src/hybrid_search.py +0 -46
  306. package/src/learning/__init__.py +0 -217
  307. package/src/learning/adaptive_ranker.py +0 -682
  308. package/src/learning/bootstrap/__init__.py +0 -69
  309. package/src/learning/bootstrap/constants.py +0 -93
  310. package/src/learning/bootstrap/db_queries.py +0 -316
  311. package/src/learning/bootstrap/sampling.py +0 -82
  312. package/src/learning/bootstrap/text_utils.py +0 -71
  313. package/src/learning/cross_project_aggregator.py +0 -857
  314. package/src/learning/db/__init__.py +0 -40
  315. package/src/learning/db/constants.py +0 -44
  316. package/src/learning/db/schema.py +0 -279
  317. package/src/learning/engagement_tracker.py +0 -628
  318. package/src/learning/feature_extractor.py +0 -708
  319. package/src/learning/feedback_collector.py +0 -806
  320. package/src/learning/learning_db.py +0 -915
  321. package/src/learning/project_context_manager.py +0 -572
  322. package/src/learning/ranking/__init__.py +0 -33
  323. package/src/learning/ranking/constants.py +0 -84
  324. package/src/learning/ranking/helpers.py +0 -278
  325. package/src/learning/source_quality_scorer.py +0 -676
  326. package/src/learning/synthetic_bootstrap.py +0 -755
  327. package/src/learning/tests/test_adaptive_ranker.py +0 -325
  328. package/src/learning/tests/test_adaptive_ranker_v28.py +0 -60
  329. package/src/learning/tests/test_aggregator.py +0 -306
  330. package/src/learning/tests/test_auto_retrain_v28.py +0 -35
  331. package/src/learning/tests/test_e2e_ranking_v28.py +0 -82
  332. package/src/learning/tests/test_feature_extractor_v28.py +0 -93
  333. package/src/learning/tests/test_feedback_collector.py +0 -294
  334. package/src/learning/tests/test_learning_db.py +0 -602
  335. package/src/learning/tests/test_learning_db_v28.py +0 -110
  336. package/src/learning/tests/test_learning_init_v28.py +0 -48
  337. package/src/learning/tests/test_outcome_signals.py +0 -48
  338. package/src/learning/tests/test_project_context.py +0 -292
  339. package/src/learning/tests/test_schema_migration.py +0 -319
  340. package/src/learning/tests/test_signal_inference.py +0 -397
  341. package/src/learning/tests/test_source_quality.py +0 -351
  342. package/src/learning/tests/test_synthetic_bootstrap.py +0 -429
  343. package/src/learning/tests/test_workflow_miner.py +0 -318
  344. package/src/learning/workflow_pattern_miner.py +0 -655
  345. package/src/lifecycle/__init__.py +0 -54
  346. package/src/lifecycle/bounded_growth.py +0 -239
  347. package/src/lifecycle/compaction_engine.py +0 -226
  348. package/src/lifecycle/lifecycle_engine.py +0 -355
  349. package/src/lifecycle/lifecycle_evaluator.py +0 -257
  350. package/src/lifecycle/lifecycle_scheduler.py +0 -130
  351. package/src/lifecycle/retention_policy.py +0 -285
  352. package/src/lifecycle/tests/test_bounded_growth.py +0 -193
  353. package/src/lifecycle/tests/test_compaction.py +0 -179
  354. package/src/lifecycle/tests/test_lifecycle_engine.py +0 -137
  355. package/src/lifecycle/tests/test_lifecycle_evaluation.py +0 -177
  356. package/src/lifecycle/tests/test_lifecycle_scheduler.py +0 -127
  357. package/src/lifecycle/tests/test_lifecycle_search.py +0 -109
  358. package/src/lifecycle/tests/test_mcp_compact.py +0 -149
  359. package/src/lifecycle/tests/test_mcp_lifecycle_status.py +0 -114
  360. package/src/lifecycle/tests/test_retention_policy.py +0 -162
  361. package/src/mcp_tools_v28.py +0 -281
  362. package/src/memory/__init__.py +0 -36
  363. package/src/memory/cli.py +0 -205
  364. package/src/memory/constants.py +0 -39
  365. package/src/memory/helpers.py +0 -28
  366. package/src/memory/schema.py +0 -166
  367. package/src/memory-profiles.py +0 -595
  368. package/src/memory-reset.py +0 -491
  369. package/src/memory_compression.py +0 -989
  370. package/src/memory_store_v2.py +0 -1155
  371. package/src/migrate_v1_to_v2.py +0 -629
  372. package/src/pattern_learner.py +0 -34
  373. package/src/patterns/__init__.py +0 -24
  374. package/src/patterns/analyzers.py +0 -251
  375. package/src/patterns/learner.py +0 -271
  376. package/src/patterns/scoring.py +0 -171
  377. package/src/patterns/store.py +0 -225
  378. package/src/patterns/terminology.py +0 -140
  379. package/src/provenance_tracker.py +0 -312
  380. package/src/qualixar_attribution.py +0 -139
  381. package/src/qualixar_watermark.py +0 -78
  382. package/src/query_optimizer.py +0 -511
  383. package/src/rate_limiter.py +0 -83
  384. package/src/search/__init__.py +0 -20
  385. package/src/search/cli.py +0 -77
  386. package/src/search/constants.py +0 -26
  387. package/src/search/engine.py +0 -241
  388. package/src/search/fusion.py +0 -122
  389. package/src/search/index_loader.py +0 -114
  390. package/src/search/methods.py +0 -162
  391. package/src/search_engine_v2.py +0 -401
  392. package/src/setup_validator.py +0 -482
  393. package/src/subscription_manager.py +0 -391
  394. package/src/tree/__init__.py +0 -59
  395. package/src/tree/builder.py +0 -185
  396. package/src/tree/nodes.py +0 -202
  397. package/src/tree/queries.py +0 -257
  398. package/src/tree/schema.py +0 -80
  399. package/src/tree_manager.py +0 -19
  400. package/src/trust/__init__.py +0 -45
  401. package/src/trust/constants.py +0 -66
  402. package/src/trust/queries.py +0 -157
  403. package/src/trust/schema.py +0 -95
  404. package/src/trust/scorer.py +0 -299
  405. package/src/trust/signals.py +0 -95
  406. package/src/trust_scorer.py +0 -44
  407. package/ui/app.js +0 -1588
  408. package/ui/js/graph-cytoscape-monolithic-backup.js +0 -1168
  409. package/ui/js/graph-cytoscape.js +0 -1168
  410. package/ui/js/graph-d3-backup.js +0 -32
  411. package/ui/js/graph.js +0 -32
  412. package/ui_server.py +0 -266
  413. /package/docs/{ACCESSIBILITY.md → v2-archive/ACCESSIBILITY.md} +0 -0
  414. /package/docs/{ARCHITECTURE.md → v2-archive/ARCHITECTURE.md} +0 -0
  415. /package/docs/{CLI-COMMANDS-REFERENCE.md → v2-archive/CLI-COMMANDS-REFERENCE.md} +0 -0
  416. /package/docs/{COMPRESSION-README.md → v2-archive/COMPRESSION-README.md} +0 -0
  417. /package/docs/{FRAMEWORK-INTEGRATIONS.md → v2-archive/FRAMEWORK-INTEGRATIONS.md} +0 -0
  418. /package/docs/{MCP-MANUAL-SETUP.md → v2-archive/MCP-MANUAL-SETUP.md} +0 -0
  419. /package/docs/{MCP-TROUBLESHOOTING.md → v2-archive/MCP-TROUBLESHOOTING.md} +0 -0
  420. /package/docs/{PATTERN-LEARNING.md → v2-archive/PATTERN-LEARNING.md} +0 -0
  421. /package/docs/{PROFILES-GUIDE.md → v2-archive/PROFILES-GUIDE.md} +0 -0
  422. /package/docs/{RESET-GUIDE.md → v2-archive/RESET-GUIDE.md} +0 -0
  423. /package/docs/{SEARCH-ENGINE-V2.2.0.md → v2-archive/SEARCH-ENGINE-V2.2.0.md} +0 -0
  424. /package/docs/{SEARCH-INTEGRATION-GUIDE.md → v2-archive/SEARCH-INTEGRATION-GUIDE.md} +0 -0
  425. /package/docs/{UI-SERVER.md → v2-archive/UI-SERVER.md} +0 -0
  426. /package/docs/{UNIVERSAL-INTEGRATION.md → v2-archive/UNIVERSAL-INTEGRATION.md} +0 -0
  427. /package/docs/{V2.2.0-OPTIONAL-SEARCH.md → v2-archive/V2.2.0-OPTIONAL-SEARCH.md} +0 -0
  428. /package/docs/{WINDOWS-INSTALL-README.txt → v2-archive/WINDOWS-INSTALL-README.txt} +0 -0
  429. /package/docs/{WINDOWS-POST-INSTALL.txt → v2-archive/WINDOWS-POST-INSTALL.txt} +0 -0
  430. /package/docs/{example_graph_usage.py → v2-archive/example_graph_usage.py} +0 -0
  431. /package/{completions → ide/completions}/slm.bash +0 -0
  432. /package/{completions → ide/completions}/slm.zsh +0 -0
  433. /package/{configs → ide/configs}/cody-commands.json +0 -0
  434. /package/{install-skills.sh → scripts/install-skills.sh} +0 -0
@@ -1,676 +0,0 @@
1
- #!/usr/bin/env python3
2
- # SPDX-License-Identifier: MIT
3
- # Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
4
- """
5
- SourceQualityScorer — Per-source quality learning.
6
-
7
- Learns which memory sources (tools/agents) produce memories that users
8
- actually find useful. If memories from 'mcp:claude-desktop' get positive
9
- feedback (via memory_used) 3x more often than memories from 'cli:terminal',
10
- then Claude Desktop memories receive a quality boost in the adaptive ranker.
11
-
12
- Data Sources:
13
- - memory.db `created_by` column (set by ProvenanceTracker in v2.5)
14
- Values like: 'mcp:claude-desktop', 'mcp:cursor', 'cli:terminal',
15
- 'rest:api', 'user', etc.
16
- - learning.db `ranking_feedback` table (positive signals from FeedbackCollector)
17
- Signal types: 'mcp_used', 'cli_useful', 'dashboard_click'
18
-
19
- Scoring Algorithm (Beta-Binomial Smoothing):
20
- quality_score = (alpha + positive_signals) / (alpha + beta + total_memories)
21
-
22
- With alpha=1, beta=1 (Laplace smoothing / uniform prior):
23
- - Unknown source with 0 feedback: 1/(2+0) = 0.50 (neutral)
24
- - Source with 5 positives out of 10 total: 6/12 = 0.50 (average)
25
- - Source with 8 positives out of 10 total: 9/12 = 0.75 (good)
26
- - Source with 1 positive out of 10 total: 2/12 = 0.17 (poor)
27
-
28
- This naturally handles:
29
- - Cold start: new sources get 0.5 (neutral) until evidence accumulates
30
- - Low sample: smoothing prevents extreme scores from few observations
31
- - Convergence: scores stabilize as evidence grows
32
-
33
- Storage:
34
- Results stored in learning.db `source_quality` table via LearningDB.
35
- The adaptive ranker reads source_quality at query time to boost/penalize
36
- memories based on their source.
37
-
38
- Thread Safety:
39
- - All writes protected by LearningDB's internal write lock
40
- - Reads to memory.db use per-call connections (safe with WAL mode)
41
- - compute_source_scores() is idempotent — safe to call concurrently
42
-
43
- Graceful Degradation:
44
- - If memory.db lacks `created_by` column: all memories grouped as 'unknown'
45
- - If learning.db unavailable: scores computed but not persisted
46
- - If ranking_feedback is empty: all sources get 0.5 (neutral)
47
-
48
- Research Backing:
49
- - Beta-Binomial smoothing: Standard Bayesian approach (matches trust_scorer.py)
50
- - Source reliability learning: ADPMF (IPM 2024) privacy-preserving feedback
51
- - FCS LREC 2024: cold-start handling via smoothing priors
52
- """
53
-
54
- import json
55
- import logging
56
- import sqlite3
57
- import threading
58
- from datetime import datetime
59
- from pathlib import Path
60
- from typing import Dict, List, Optional, Any
61
-
62
- logger = logging.getLogger("superlocalmemory.learning.source_quality")
63
-
64
- # ---------------------------------------------------------------------------
65
- # Import LearningDB (sibling module in src/learning/)
66
- # ---------------------------------------------------------------------------
67
- try:
68
- from .learning_db import LearningDB
69
- except ImportError:
70
- try:
71
- from learning_db import LearningDB
72
- except ImportError:
73
- LearningDB = None
74
- logger.warning(
75
- "LearningDB not available — source quality scores will not persist."
76
- )
77
-
78
- # ---------------------------------------------------------------------------
79
- # Constants
80
- # ---------------------------------------------------------------------------
81
-
82
- MEMORY_DIR = Path.home() / ".claude-memory"
83
- DEFAULT_MEMORY_DB = MEMORY_DIR / "memory.db"
84
-
85
- # Beta-Binomial prior parameters (Laplace smoothing)
86
- ALPHA = 1.0 # Prior successes
87
- BETA = 1.0 # Prior failures
88
-
89
- # Default score for unknown sources (= alpha / (alpha + beta))
90
- DEFAULT_QUALITY_SCORE = ALPHA / (ALPHA + BETA)
91
-
92
- # Minimum total memories from a source before we trust its score
93
- # Below this, the score is blended toward the default
94
- MIN_EVIDENCE_THRESHOLD = 5
95
-
96
- # Positive feedback signal types from ranking_feedback table
97
- POSITIVE_SIGNAL_TYPES = ("mcp_used", "cli_useful", "dashboard_click")
98
-
99
-
100
- class SourceQualityScorer:
101
- """
102
- Learns which memory sources produce higher-quality memories.
103
-
104
- Computes a quality score per source using Beta-Binomial smoothing
105
- over positive feedback signals. Stores results in learning.db for
106
- use by the adaptive ranker.
107
-
108
- Usage:
109
- scorer = SourceQualityScorer()
110
- scores = scorer.compute_source_scores()
111
- # scores = {'mcp:claude-desktop': 0.72, 'cli:terminal': 0.45, ...}
112
-
113
- boost = scorer.get_source_boost(memory_dict)
114
- # boost = 0.72 (for a memory from claude-desktop)
115
- """
116
-
117
- def __init__(
118
- self,
119
- memory_db_path: Optional[Path] = None,
120
- learning_db: Optional[Any] = None,
121
- ):
122
- """
123
- Initialize the source quality scorer.
124
-
125
- Args:
126
- memory_db_path: Path to memory.db (READ-ONLY). Defaults to
127
- ~/.claude-memory/memory.db.
128
- learning_db: A LearningDB instance for reading feedback and
129
- storing scores. If None, one is created.
130
- """
131
- self.memory_db_path = Path(memory_db_path) if memory_db_path else DEFAULT_MEMORY_DB
132
- self._lock = threading.Lock()
133
-
134
- # In-memory cache of source scores (refreshed by compute_source_scores)
135
- self._cached_scores: Dict[str, float] = {}
136
-
137
- # Initialize LearningDB
138
- if learning_db is not None:
139
- self._learning_db = learning_db
140
- elif LearningDB is not None:
141
- try:
142
- self._learning_db = LearningDB.get_instance()
143
- except Exception as e:
144
- logger.error("Failed to initialize LearningDB: %s", e)
145
- self._learning_db = None
146
- else:
147
- self._learning_db = None
148
-
149
- # Pre-load cached scores from learning.db if available
150
- self._load_cached_scores()
151
-
152
- logger.info(
153
- "SourceQualityScorer initialized: memory_db=%s, learning_db=%s, "
154
- "cached_sources=%d",
155
- self.memory_db_path,
156
- "available" if self._learning_db else "unavailable",
157
- len(self._cached_scores),
158
- )
159
-
160
- # ======================================================================
161
- # Core Scoring
162
- # ======================================================================
163
-
164
- def compute_source_scores(self) -> Dict[str, float]:
165
- """
166
- Compute quality scores for all memory sources.
167
-
168
- Workflow:
169
- 1. Get total memories per source from memory.db (created_by column)
170
- 2. Get positive feedback count per source by joining
171
- learning.db ranking_feedback with memory.db memories
172
- 3. Compute Beta-Binomial smoothed score per source
173
- 4. Store results in learning.db source_quality table
174
- 5. Update in-memory cache
175
-
176
- Returns:
177
- Dict mapping source_id -> quality_score (0.0 to 1.0)
178
- """
179
- # Step 1: Count total memories per source from memory.db
180
- source_totals = self._get_memory_counts_by_source()
181
-
182
- if not source_totals:
183
- logger.info("No source data found in memory.db.")
184
- return {}
185
-
186
- # Step 2: Count positive signals per source
187
- source_positives = self._get_positive_signal_counts(
188
- set(source_totals.keys())
189
- )
190
-
191
- # Step 3: Compute Beta-Binomial scores
192
- scores = {}
193
- for source_id, total in source_totals.items():
194
- positives = source_positives.get(source_id, 0)
195
- score = self._beta_binomial_score(positives, total)
196
- scores[source_id] = round(score, 4)
197
-
198
- # Step 4: Store in learning.db
199
- self._store_scores(scores, source_totals, source_positives)
200
-
201
- # Step 5: Update cache
202
- with self._lock:
203
- self._cached_scores = dict(scores)
204
-
205
- logger.info(
206
- "Source quality scores computed for %d sources: %s",
207
- len(scores),
208
- ", ".join(
209
- "%s=%.3f" % (s, sc) for s, sc in sorted(
210
- scores.items(), key=lambda x: -x[1]
211
- )[:5]
212
- ) + ("..." if len(scores) > 5 else ""),
213
- )
214
-
215
- return scores
216
-
217
- def get_source_boost(
218
- self,
219
- memory: dict,
220
- source_scores: Optional[Dict[str, float]] = None,
221
- ) -> float:
222
- """
223
- Get the ranking boost for a memory based on its source quality.
224
-
225
- This is called by the adaptive ranker at query time for each
226
- candidate memory. The boost is a float in [0.0, 1.0] that
227
- represents how trustworthy/useful this source tends to be.
228
-
229
- Args:
230
- memory: A memory dict. Must have 'created_by' key, or will
231
- fall back to DEFAULT_QUALITY_SCORE.
232
- source_scores: Optional pre-computed scores dict. If None,
233
- uses the internal cache. Pass this to avoid
234
- repeated cache reads in a tight loop.
235
-
236
- Returns:
237
- Quality score (0.0 to 1.0). 0.5 for unknown sources.
238
- """
239
- scores = source_scores if source_scores is not None else self._cached_scores
240
-
241
- # Extract source identifier from the memory
242
- source_id = self._extract_source_id(memory)
243
-
244
- if not source_id or source_id not in scores:
245
- return DEFAULT_QUALITY_SCORE
246
-
247
- return scores[source_id]
248
-
249
- def refresh(self):
250
- """
251
- Recompute all source scores.
252
-
253
- Convenience wrapper for compute_source_scores(). Called periodically
254
- by the engagement tracker or on explicit user request.
255
- """
256
- return self.compute_source_scores()
257
-
258
- # ======================================================================
259
- # Data Extraction (memory.db — READ-ONLY)
260
- # ======================================================================
261
-
262
- def _get_memory_counts_by_source(self) -> Dict[str, int]:
263
- """
264
- Count total memories per source from memory.db's `created_by` column.
265
-
266
- Handles the case where the `created_by` column does not exist
267
- (older databases pre-v2.5). In that case, all memories are
268
- grouped under 'unknown'.
269
-
270
- Returns:
271
- Dict mapping source_id -> total memory count.
272
- """
273
- counts: Dict[str, int] = {}
274
-
275
- try:
276
- conn = sqlite3.connect(str(self.memory_db_path), timeout=10)
277
- try:
278
- conn.execute("PRAGMA busy_timeout=5000")
279
- cursor = conn.cursor()
280
-
281
- # Check if created_by column exists
282
- cursor.execute("PRAGMA table_info(memories)")
283
- columns = {row[1] for row in cursor.fetchall()}
284
-
285
- if "created_by" in columns:
286
- cursor.execute("""
287
- SELECT
288
- COALESCE(created_by, 'unknown') AS source,
289
- COUNT(*) AS cnt
290
- FROM memories
291
- GROUP BY source
292
- ORDER BY cnt DESC
293
- """)
294
- for row in cursor.fetchall():
295
- source_id = row[0] if row[0] else "unknown"
296
- counts[source_id] = row[1]
297
- else:
298
- # Column doesn't exist — count all as 'unknown'
299
- cursor.execute("SELECT COUNT(*) FROM memories")
300
- total = cursor.fetchone()[0]
301
- if total > 0:
302
- counts["unknown"] = total
303
- logger.debug(
304
- "created_by column not in memory.db — "
305
- "all %d memories grouped as 'unknown'.",
306
- total,
307
- )
308
- finally:
309
- conn.close()
310
-
311
- except sqlite3.OperationalError as e:
312
- logger.warning("Error reading memory counts by source: %s", e)
313
- except Exception as e:
314
- logger.error("Unexpected error reading memory.db: %s", e)
315
-
316
- return counts
317
-
318
- def _get_positive_signal_counts(
319
- self,
320
- known_sources: set,
321
- ) -> Dict[str, int]:
322
- """
323
- Count positive feedback signals per source.
324
-
325
- Joins learning.db's ranking_feedback (positive signals) with
326
- memory.db's memories (to get created_by) on memory_id.
327
-
328
- This requires reading from BOTH databases. We do a two-step approach:
329
- 1. Get all memory_ids with positive feedback from learning.db
330
- 2. Look up their created_by from memory.db
331
-
332
- This avoids ATTACH DATABASE which can have locking issues.
333
-
334
- Returns:
335
- Dict mapping source_id -> positive signal count.
336
- """
337
- positives: Dict[str, int] = {}
338
-
339
- if self._learning_db is None:
340
- return positives
341
-
342
- # Step 1: Get memory_ids with positive feedback from learning.db
343
- feedback_memory_ids: Dict[int, int] = {} # memory_id -> count
344
-
345
- try:
346
- feedback_rows = self._learning_db.get_feedback_for_training(limit=50000)
347
- for row in feedback_rows:
348
- signal_type = row.get("signal_type", "")
349
- if signal_type in POSITIVE_SIGNAL_TYPES:
350
- mem_id = row.get("memory_id")
351
- if mem_id is not None:
352
- feedback_memory_ids[mem_id] = (
353
- feedback_memory_ids.get(mem_id, 0) + 1
354
- )
355
- except Exception as e:
356
- logger.warning("Could not read feedback from learning.db: %s", e)
357
- return positives
358
-
359
- if not feedback_memory_ids:
360
- return positives
361
-
362
- # Step 2: Look up created_by for each feedback memory_id in memory.db
363
- try:
364
- conn = sqlite3.connect(str(self.memory_db_path), timeout=10)
365
- try:
366
- conn.execute("PRAGMA busy_timeout=5000")
367
- cursor = conn.cursor()
368
-
369
- # Check if created_by column exists
370
- cursor.execute("PRAGMA table_info(memories)")
371
- columns = {row[1] for row in cursor.fetchall()}
372
-
373
- if "created_by" not in columns:
374
- # All positives go to 'unknown'
375
- total_positives = sum(feedback_memory_ids.values())
376
- if total_positives > 0:
377
- positives["unknown"] = total_positives
378
- return positives
379
-
380
- # Batch lookup in chunks to avoid SQLite variable limit
381
- mem_ids = list(feedback_memory_ids.keys())
382
- chunk_size = 500 # SQLite max variables is 999
383
-
384
- for i in range(0, len(mem_ids), chunk_size):
385
- chunk = mem_ids[i:i + chunk_size]
386
- placeholders = ",".join("?" * len(chunk))
387
- cursor.execute(
388
- "SELECT id, COALESCE(created_by, 'unknown') "
389
- "FROM memories WHERE id IN (%s)" % placeholders,
390
- chunk,
391
- )
392
- for row in cursor.fetchall():
393
- mem_id = row[0]
394
- source_id = row[1] if row[1] else "unknown"
395
- count = feedback_memory_ids.get(mem_id, 0)
396
- positives[source_id] = positives.get(source_id, 0) + count
397
- finally:
398
- conn.close()
399
-
400
- except sqlite3.OperationalError as e:
401
- logger.warning("Error looking up memory sources: %s", e)
402
- except Exception as e:
403
- logger.error("Unexpected error in positive signal lookup: %s", e)
404
-
405
- return positives
406
-
407
- # ======================================================================
408
- # Scoring Math
409
- # ======================================================================
410
-
411
- @staticmethod
412
- def _beta_binomial_score(positive_count: int, total_count: int) -> float:
413
- """
414
- Compute Beta-Binomial smoothed quality score.
415
-
416
- Formula: (alpha + positive) / (alpha + beta + total)
417
-
418
- With alpha=1, beta=1 (uniform prior / Laplace smoothing):
419
- - 0 positives, 0 total = 0.50 (neutral)
420
- - 5 positives, 10 total = 0.50
421
- - 8 positives, 10 total = 0.75
422
- - 1 positive, 10 total = 0.17
423
- - 50 positives, 100 total = 0.50
424
-
425
- This converges to the true rate as evidence grows, while being
426
- conservative (pulled toward 0.5) with limited data.
427
-
428
- Args:
429
- positive_count: Number of positive feedback signals.
430
- total_count: Total number of memories from this source.
431
-
432
- Returns:
433
- Quality score in [0.0, 1.0].
434
- """
435
- score = (ALPHA + positive_count) / (ALPHA + BETA + total_count)
436
- return max(0.0, min(1.0, score))
437
-
438
- # ======================================================================
439
- # Storage (learning.db)
440
- # ======================================================================
441
-
442
- def _store_scores(
443
- self,
444
- scores: Dict[str, float],
445
- totals: Dict[str, int],
446
- positives: Dict[str, int],
447
- ):
448
- """
449
- Store computed scores in learning.db's source_quality table.
450
-
451
- Uses LearningDB.update_source_quality() which handles UPSERT
452
- internally with its own write lock.
453
- """
454
- if self._learning_db is None:
455
- logger.debug(
456
- "LearningDB unavailable — scores computed but not stored."
457
- )
458
- return
459
-
460
- stored = 0
461
- for source_id, score in scores.items():
462
- try:
463
- self._learning_db.update_source_quality(
464
- source_id=source_id,
465
- positive_signals=positives.get(source_id, 0),
466
- total_memories=totals.get(source_id, 0),
467
- )
468
- stored += 1
469
- except Exception as e:
470
- logger.error(
471
- "Failed to store score for source '%s': %s",
472
- source_id, e,
473
- )
474
-
475
- logger.debug("Stored %d/%d source quality scores.", stored, len(scores))
476
-
477
- def _load_cached_scores(self):
478
- """
479
- Load source quality scores from learning.db into the in-memory cache.
480
-
481
- Called on initialization so that get_source_boost() works immediately
482
- without requiring a compute_source_scores() call first.
483
- """
484
- if self._learning_db is None:
485
- return
486
-
487
- try:
488
- db_scores = self._learning_db.get_source_scores()
489
- with self._lock:
490
- self._cached_scores = dict(db_scores)
491
- if db_scores:
492
- logger.debug(
493
- "Loaded %d cached source scores from learning.db.",
494
- len(db_scores),
495
- )
496
- except Exception as e:
497
- logger.debug("Could not load cached source scores: %s", e)
498
-
499
- # ======================================================================
500
- # Utility Methods
501
- # ======================================================================
502
-
503
- @staticmethod
504
- def _extract_source_id(memory: dict) -> Optional[str]:
505
- """
506
- Extract the source identifier from a memory dict.
507
-
508
- Checks 'created_by' first (set by ProvenanceTracker), then
509
- falls back to 'source_protocol' if available.
510
-
511
- Args:
512
- memory: A memory dict (from search results or direct DB query).
513
-
514
- Returns:
515
- Source identifier string, or None if not available.
516
- """
517
- # Primary: created_by (e.g., 'mcp:claude-desktop', 'cli:terminal')
518
- source = memory.get("created_by")
519
- if source and source != "user":
520
- return source
521
-
522
- # Fallback: source_protocol (e.g., 'mcp', 'cli', 'rest')
523
- protocol = memory.get("source_protocol")
524
- if protocol:
525
- return protocol
526
-
527
- # Last resort: the 'user' default from provenance_tracker
528
- if source == "user":
529
- return "user"
530
-
531
- return None
532
-
533
- def get_all_scores(self) -> Dict[str, dict]:
534
- """
535
- Get detailed quality information for all tracked sources.
536
-
537
- Returns full details including positive signals, total memories,
538
- and computed score for diagnostic/dashboard display.
539
-
540
- Returns:
541
- Dict mapping source_id -> {quality_score, positive_signals,
542
- total_memories, last_updated}
543
- """
544
- if self._learning_db is None:
545
- # Return from cache with minimal info
546
- with self._lock:
547
- return {
548
- source_id: {
549
- "quality_score": score,
550
- "positive_signals": None,
551
- "total_memories": None,
552
- "last_updated": None,
553
- }
554
- for source_id, score in self._cached_scores.items()
555
- }
556
-
557
- try:
558
- conn = self._learning_db._get_connection()
559
- cursor = conn.cursor()
560
- cursor.execute("""
561
- SELECT source_id, quality_score, positive_signals,
562
- total_memories, last_updated
563
- FROM source_quality
564
- ORDER BY quality_score DESC
565
- """)
566
- results = {}
567
- for row in cursor.fetchall():
568
- results[row["source_id"]] = {
569
- "quality_score": row["quality_score"],
570
- "positive_signals": row["positive_signals"],
571
- "total_memories": row["total_memories"],
572
- "last_updated": row["last_updated"],
573
- }
574
- conn.close()
575
- return results
576
- except Exception as e:
577
- logger.error("Failed to read detailed source scores: %s", e)
578
- return {}
579
-
580
- def get_source_summary(self) -> str:
581
- """
582
- Get a human-readable summary of source quality scores.
583
-
584
- Returns:
585
- Formatted multi-line string for diagnostics or dashboard.
586
- """
587
- all_scores = self.get_all_scores()
588
-
589
- if not all_scores:
590
- return "No source quality data available. Run refresh() first."
591
-
592
- lines = ["Source Quality Scores:", ""]
593
- lines.append(
594
- " %-30s %8s %8s %8s"
595
- % ("Source", "Score", "Positive", "Total")
596
- )
597
- lines.append(" " + "-" * 62)
598
-
599
- for source_id, data in sorted(
600
- all_scores.items(), key=lambda x: -x[1]["quality_score"]
601
- ):
602
- pos = data["positive_signals"]
603
- tot = data["total_memories"]
604
- lines.append(
605
- " %-30s %8.3f %8s %8s"
606
- % (
607
- source_id,
608
- data["quality_score"],
609
- str(pos) if pos is not None else "?",
610
- str(tot) if tot is not None else "?",
611
- )
612
- )
613
-
614
- return "\n".join(lines)
615
-
616
-
617
- # ===========================================================================
618
- # CLI Interface
619
- # ===========================================================================
620
-
621
- if __name__ == "__main__":
622
- import sys as _sys
623
-
624
- logging.basicConfig(
625
- level=logging.INFO,
626
- format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
627
- )
628
-
629
- scorer = SourceQualityScorer()
630
-
631
- if len(_sys.argv) < 2:
632
- print("SourceQualityScorer — Per-Source Quality Learning")
633
- print()
634
- print("Usage:")
635
- print(" python source_quality_scorer.py compute # Compute all source scores")
636
- print(" python source_quality_scorer.py show # Show current scores")
637
- print(" python source_quality_scorer.py summary # Human-readable summary")
638
- _sys.exit(0)
639
-
640
- command = _sys.argv[1]
641
-
642
- if command == "compute":
643
- scores = scorer.compute_source_scores()
644
- if scores:
645
- print("\nComputed quality scores for %d sources:" % len(scores))
646
- for source_id, score in sorted(scores.items(), key=lambda x: -x[1]):
647
- bar = "#" * int(score * 20)
648
- print(" %-30s %.3f [%-20s]" % (source_id, score, bar))
649
- else:
650
- print("No sources found. Add memories with provenance tracking first.")
651
-
652
- elif command == "show":
653
- all_scores = scorer.get_all_scores()
654
- if all_scores:
655
- print("\nStored source quality scores:")
656
- for source_id, data in sorted(
657
- all_scores.items(), key=lambda x: -x[1]["quality_score"]
658
- ):
659
- print(
660
- " %-30s score=%.3f positives=%s total=%s"
661
- % (
662
- source_id,
663
- data["quality_score"],
664
- data["positive_signals"],
665
- data["total_memories"],
666
- )
667
- )
668
- else:
669
- print("No scores stored. Run 'compute' first.")
670
-
671
- elif command == "summary":
672
- print(scorer.get_source_summary())
673
-
674
- else:
675
- print("Unknown command: %s" % command)
676
- _sys.exit(1)