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
@@ -0,0 +1,179 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under the MIT License - see LICENSE file
3
+ # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
+
5
+ """SuperLocalMemory V3 — Entity Graph Channel with Spreading Activation.
6
+
7
+ SA-RAG pattern: entities from query -> canonical lookup -> graph traversal
8
+ with decay. Handles BOTH uppercase and lowercase entity mentions.
9
+
10
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
11
+ License: MIT
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import json
16
+ import logging
17
+ import re
18
+ from collections import defaultdict
19
+ from typing import TYPE_CHECKING
20
+
21
+ if TYPE_CHECKING:
22
+ from superlocalmemory.encoding.entity_resolver import EntityResolver
23
+ from superlocalmemory.storage.database import DatabaseManager
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+ _PROPER_NOUN_RE = re.compile(r"\b[A-Z][a-z]{1,}\b")
28
+
29
+ _ENTITY_STOP: frozenset[str] = frozenset({
30
+ # Expanded stop list for query entity extraction
31
+ "what", "when", "where", "who", "which", "how", "does", "did",
32
+ "the", "that", "this", "there", "then", "than", "they", "them",
33
+ "have", "has", "had", "been", "being", "about", "after", "before",
34
+ "from", "into", "with", "some", "other", "would", "could", "should",
35
+ "will", "because", "also", "just", "like", "know", "think",
36
+ "feel", "want", "need", "make", "take", "give", "tell", "said",
37
+ "wow", "gonna", "got", "by", "thanks", "thank", "hey", "hi",
38
+ "hello", "bye", "good", "great", "nice", "cool", "right",
39
+ "let", "can", "might", "much", "many", "more", "most",
40
+ "something", "anything", "everything", "nothing", "someone",
41
+ "it", "my", "your", "our", "their", "me", "you", "we", "us",
42
+ "do", "if", "or", "no", "to", "at", "on", "in", "so",
43
+ "go", "come", "see", "look", "say", "ask", "try", "keep",
44
+ "yes", "yeah", "sure", "okay", "ok", "really", "actually",
45
+ "maybe", "well", "still", "even", "very",
46
+ })
47
+
48
+
49
+ def extract_query_entities(query: str) -> list[str]:
50
+ """Extract entity candidates from query (handles both cases).
51
+
52
+ Strategy: find proper nouns in original + title-cased text,
53
+ plus quoted phrases. Deduplicates case-insensitively.
54
+ """
55
+ candidates: list[str] = []
56
+ seen: set[str] = set()
57
+
58
+ def _add(name: str) -> None:
59
+ lo = name.lower()
60
+ if lo not in seen and lo not in _ENTITY_STOP and len(name) >= 2:
61
+ seen.add(lo)
62
+ candidates.append(name)
63
+
64
+ for m in _PROPER_NOUN_RE.finditer(query):
65
+ _add(m.group(0))
66
+ for m in _PROPER_NOUN_RE.finditer(query.title()):
67
+ _add(m.group(0))
68
+ for m in re.finditer(r'"([^"]+)"', query):
69
+ _add(m.group(1).strip())
70
+
71
+ return candidates
72
+
73
+
74
+ class EntityGraphChannel:
75
+ """Entity-based retrieval with spreading activation (SA-RAG)."""
76
+
77
+ def __init__(
78
+ self, db: DatabaseManager,
79
+ entity_resolver: EntityResolver | None = None,
80
+ decay: float = 0.7, activation_threshold: float = 0.1,
81
+ max_hops: int = 3,
82
+ ) -> None:
83
+ self._db = db
84
+ self._resolver = entity_resolver
85
+ self._decay = decay
86
+ self._threshold = activation_threshold
87
+ self._max_hops = max_hops
88
+
89
+ def search(self, query: str, profile_id: str, top_k: int = 50) -> list[tuple[str, float]]:
90
+ """Search via entity graph with spreading activation."""
91
+ raw_entities = extract_query_entities(query)
92
+ if not raw_entities:
93
+ return []
94
+
95
+ canonical_ids = self._resolve_entities(raw_entities, profile_id)
96
+ if not canonical_ids:
97
+ return []
98
+
99
+ # Seed activation from direct entity-linked facts
100
+ activation: dict[str, float] = defaultdict(float)
101
+ visited_entities: set[str] = set(canonical_ids)
102
+
103
+ for eid in canonical_ids:
104
+ for fact in self._db.get_facts_by_entity(eid, profile_id):
105
+ activation[fact.fact_id] = max(activation[fact.fact_id], 1.0)
106
+
107
+ # Spreading activation through graph edges
108
+ frontier = set(activation.keys())
109
+ for hop in range(1, self._max_hops):
110
+ hop_decay = self._decay ** hop
111
+ if hop_decay < self._threshold:
112
+ break
113
+ next_frontier: set[str] = set()
114
+
115
+ for fid in frontier:
116
+ for edge in self._db.get_edges_for_node(fid, profile_id):
117
+ neighbor = edge.target_id if edge.source_id == fid else edge.source_id
118
+ propagated = activation[fid] * self._decay
119
+ if propagated >= self._threshold and propagated > activation.get(neighbor, 0.0):
120
+ activation[neighbor] = propagated
121
+ next_frontier.add(neighbor)
122
+
123
+ # Discover new entities from activated facts -> get their facts
124
+ new_eids = self._discover_entities(frontier, profile_id, visited_entities)
125
+ for eid in new_eids:
126
+ visited_entities.add(eid)
127
+ for fact in self._db.get_facts_by_entity(eid, profile_id):
128
+ if hop_decay > activation.get(fact.fact_id, 0.0):
129
+ activation[fact.fact_id] = hop_decay
130
+ next_frontier.add(fact.fact_id)
131
+
132
+ frontier = next_frontier
133
+ if not frontier:
134
+ break
135
+
136
+ results = [(fid, sc) for fid, sc in activation.items() if sc >= self._threshold]
137
+ results.sort(key=lambda x: x[1], reverse=True)
138
+ return results[:top_k]
139
+
140
+ def _resolve_entities(self, raw: list[str], profile_id: str) -> list[str]:
141
+ """Resolve raw names to canonical entity IDs."""
142
+ ids: list[str] = []
143
+ seen: set[str] = set()
144
+ if self._resolver is not None:
145
+ for eid in self._resolver.resolve(raw, profile_id).values():
146
+ if eid not in seen:
147
+ seen.add(eid)
148
+ ids.append(eid)
149
+ else:
150
+ for name in raw:
151
+ ent = self._db.get_entity_by_name(name, profile_id)
152
+ if ent and ent.entity_id not in seen:
153
+ seen.add(ent.entity_id)
154
+ ids.append(ent.entity_id)
155
+ return ids
156
+
157
+ def _discover_entities(
158
+ self, fact_ids: set[str], profile_id: str, visited: set[str],
159
+ ) -> list[str]:
160
+ """Find new canonical entity IDs referenced by a set of facts."""
161
+ new: list[str] = []
162
+ seen = set(visited)
163
+ for fid in fact_ids:
164
+ rows = self._db.execute(
165
+ "SELECT canonical_entities_json FROM atomic_facts WHERE fact_id = ?", (fid,),
166
+ )
167
+ if not rows:
168
+ continue
169
+ raw = dict(rows[0]).get("canonical_entities_json")
170
+ if not raw:
171
+ continue
172
+ try:
173
+ for eid in json.loads(raw):
174
+ if eid not in seen:
175
+ seen.add(eid)
176
+ new.append(eid)
177
+ except (ValueError, TypeError):
178
+ continue
179
+ return new
@@ -0,0 +1,78 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under the MIT License - see LICENSE file
3
+ # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
+
5
+ """SuperLocalMemory V3 — Weighted Reciprocal Rank Fusion.
6
+
7
+ Single-pass RRF with k=60 for diverse retrieval (D116).
8
+ V1 had triple re-fusion which destroyed rankings — fixed in V2.
9
+
10
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
11
+ License: MIT
12
+ """
13
+ from __future__ import annotations
14
+
15
+ from dataclasses import dataclass, field
16
+
17
+
18
+ @dataclass(frozen=True)
19
+ class FusionResult:
20
+ """Single fused result with per-channel provenance."""
21
+ fact_id: str
22
+ fused_score: float
23
+ channel_ranks: dict[str, int] = field(default_factory=dict)
24
+ channel_scores: dict[str, float] = field(default_factory=dict)
25
+
26
+
27
+ def weighted_rrf(
28
+ channels: dict[str, list[tuple[str, float]]],
29
+ weights: dict[str, float],
30
+ k: int = 60,
31
+ max_rank_penalty: int = 1000,
32
+ ) -> list[FusionResult]:
33
+ """Fuse ranked lists via Weighted Reciprocal Rank Fusion.
34
+
35
+ Args:
36
+ channels: channel_name -> [(fact_id, score)] sorted desc.
37
+ weights: channel_name -> weight multiplier.
38
+ k: RRF smoothing constant (60 for diverse retrieval, D116).
39
+ max_rank_penalty: Rank assigned to absent documents.
40
+
41
+ Returns:
42
+ FusionResult list sorted by fused_score descending.
43
+ """
44
+ if k <= 0:
45
+ raise ValueError(f"k must be positive, got {k}")
46
+
47
+ rank_maps: dict[str, dict[str, int]] = {}
48
+ score_maps: dict[str, dict[str, float]] = {}
49
+
50
+ for ch, ranked in channels.items():
51
+ ranks: dict[str, int] = {}
52
+ scores: dict[str, float] = {}
53
+ for i, (fid, sc) in enumerate(ranked):
54
+ ranks[fid] = i + 1
55
+ scores[fid] = sc
56
+ rank_maps[ch] = ranks
57
+ score_maps[ch] = scores
58
+
59
+ all_ids: set[str] = set()
60
+ for ranked in channels.values():
61
+ for fid, _ in ranked:
62
+ all_ids.add(fid)
63
+
64
+ results: list[FusionResult] = []
65
+ for fid in all_ids:
66
+ fused = 0.0
67
+ ch_ranks: dict[str, int] = {}
68
+ ch_scores: dict[str, float] = {}
69
+ for ch in channels:
70
+ w = weights.get(ch, 1.0)
71
+ rank = rank_maps[ch].get(fid, max_rank_penalty)
72
+ ch_ranks[ch] = rank
73
+ ch_scores[ch] = score_maps[ch].get(fid, 0.0)
74
+ fused += w / (k + rank)
75
+ results.append(FusionResult(fid, fused, ch_ranks, ch_scores))
76
+
77
+ results.sort(key=lambda r: r.fused_score, reverse=True)
78
+ return results
@@ -0,0 +1,105 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under the MIT License - see LICENSE file
3
+ # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
+
5
+ """SuperLocalMemory V3 — Profile Channel (Entity-Profile Retrieval).
6
+
7
+ Returns fact IDs from entity profiles — enables direct answers for
8
+ "What does Alice do?" style queries without full embedding search.
9
+
10
+ This is a SHORTCUT channel: if the query mentions a known entity,
11
+ the profile's accumulated fact IDs are injected directly into the
12
+ retrieval pool with high scores.
13
+
14
+ Competitor reference: EverMemOS profile synthesis (~+15-20% SH).
15
+
16
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
17
+ License: MIT
18
+ """
19
+ from __future__ import annotations
20
+
21
+ import logging
22
+ import re
23
+ from typing import TYPE_CHECKING
24
+
25
+ if TYPE_CHECKING:
26
+ from superlocalmemory.storage.database import DatabaseManager
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+ # Pattern for extracting potential entity names (capitalized multi-word)
31
+ _ENTITY_PATTERN = re.compile(r"\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+)*\b")
32
+
33
+ # Common sentence starters to exclude from entity extraction
34
+ _SENTENCE_STARTERS = frozenset({
35
+ "What", "Where", "Who", "Which", "How", "When", "Does", "Did",
36
+ "Can", "Could", "Would", "Should", "Are", "Is", "Was", "Were",
37
+ "Has", "Have", "The", "Tell", "Please", "Do", "Why",
38
+ })
39
+
40
+
41
+ class ProfileChannel:
42
+ """Entity-profile-based retrieval for direct entity queries.
43
+
44
+ If the query mentions a known entity (by canonical name or alias),
45
+ the entity's profile fact IDs are returned with high base score.
46
+
47
+ Usage::
48
+
49
+ channel = ProfileChannel(db)
50
+ results = channel.search("What is Alice's job?", "default")
51
+ # returns [(fact_id_1, 0.95), (fact_id_2, 0.95), ...]
52
+ """
53
+
54
+ def __init__(self, db: DatabaseManager) -> None:
55
+ self._db = db
56
+
57
+ def search(
58
+ self,
59
+ query: str,
60
+ profile_id: str,
61
+ top_k: int = 10,
62
+ ) -> list[tuple[str, float]]:
63
+ """Search entity profiles for matching facts.
64
+
65
+ Args:
66
+ query: User query text.
67
+ profile_id: Scope to this profile.
68
+ top_k: Maximum results to return.
69
+
70
+ Returns:
71
+ List of (fact_id, score) sorted by score descending.
72
+ """
73
+ entities = self._extract_entity_names(query)
74
+ if not entities:
75
+ return []
76
+
77
+ results: list[tuple[str, float]] = []
78
+ seen: set[str] = set()
79
+
80
+ for name in entities:
81
+ entity = self._db.get_entity_by_name(name, profile_id)
82
+ if not entity:
83
+ continue
84
+
85
+ profiles = self._db.get_entity_profiles_by_entity(
86
+ entity.entity_id, profile_id,
87
+ )
88
+ for p in profiles:
89
+ for fid in p.fact_ids:
90
+ if fid not in seen:
91
+ seen.add(fid)
92
+ results.append((fid, 0.95))
93
+
94
+ results.sort(key=lambda x: x[1], reverse=True)
95
+ return results[:top_k]
96
+
97
+ @staticmethod
98
+ def _extract_entity_names(query: str) -> list[str]:
99
+ """Extract potential entity names from query text.
100
+
101
+ Returns capitalized words/phrases that aren't common
102
+ sentence starters.
103
+ """
104
+ matches = _ENTITY_PATTERN.findall(query)
105
+ return [m for m in matches if m not in _SENTENCE_STARTERS]
@@ -0,0 +1,154 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under the MIT License - see LICENSE file
3
+ # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
+
5
+ """SuperLocalMemory V3 — Cross-Encoder Reranker.
6
+
7
+ Scores (query, fact) pairs through a cross-encoder in a single forward
8
+ pass. Lazy model loading, thread-safe via lock.
9
+
10
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
11
+ License: MIT
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import logging
17
+ import threading
18
+ from typing import Any
19
+
20
+ from superlocalmemory.storage.models import AtomicFact
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ class CrossEncoderReranker:
26
+ """Rerank candidate facts using a local cross-encoder model.
27
+
28
+ When the model is unavailable (missing package, download failure,
29
+ offline environment), falls back to returning candidates in their
30
+ original score order — never crashes.
31
+
32
+ Args:
33
+ model_name: HuggingFace cross-encoder model identifier.
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ model_name: str = "BAAI/bge-reranker-v2-m3",
39
+ ) -> None:
40
+ self._model_name = model_name
41
+ self._model: Any = None
42
+ self._loaded = False
43
+ self._lock = threading.Lock()
44
+
45
+ # ------------------------------------------------------------------
46
+ # Lazy loading
47
+ # ------------------------------------------------------------------
48
+
49
+ def _ensure_model(self) -> None:
50
+ """Load cross-encoder on first use (thread-safe)."""
51
+ if self._loaded:
52
+ return
53
+
54
+ with self._lock:
55
+ if self._loaded:
56
+ return # Double-check after acquiring lock
57
+ try:
58
+ from sentence_transformers import CrossEncoder
59
+
60
+ self._model = CrossEncoder(self._model_name)
61
+ logger.info("Cross-encoder loaded: %s", self._model_name)
62
+ except ImportError:
63
+ logger.warning(
64
+ "sentence-transformers not installed; "
65
+ "cross-encoder reranking disabled"
66
+ )
67
+ except OSError as exc:
68
+ logger.warning(
69
+ "Failed to load cross-encoder %s: %s",
70
+ self._model_name,
71
+ exc,
72
+ )
73
+ finally:
74
+ self._loaded = True
75
+
76
+ # ------------------------------------------------------------------
77
+ # Public API
78
+ # ------------------------------------------------------------------
79
+
80
+ def rerank(
81
+ self,
82
+ query: str,
83
+ candidates: list[tuple[AtomicFact, float]],
84
+ top_k: int = 10,
85
+ ) -> list[tuple[AtomicFact, float]]:
86
+ """Rerank candidates by cross-encoder relevance.
87
+
88
+ Each (query, fact.content) pair is scored in a single forward
89
+ pass. Results are returned sorted by cross-encoder score.
90
+
91
+ When the model is unavailable, returns candidates sorted by
92
+ their existing score (graceful fallback).
93
+
94
+ Args:
95
+ query: User query text.
96
+ candidates: List of (AtomicFact, score) tuples from the
97
+ fusion stage.
98
+ top_k: Maximum results to return.
99
+
100
+ Returns:
101
+ Top-k (AtomicFact, cross_encoder_score) tuples, sorted
102
+ descending by cross-encoder score.
103
+ """
104
+ if not candidates:
105
+ return []
106
+
107
+ self._ensure_model()
108
+
109
+ if self._model is None:
110
+ # Fallback: keep existing score order
111
+ sorted_cands = sorted(
112
+ candidates, key=lambda x: x[1], reverse=True
113
+ )
114
+ return sorted_cands[:top_k]
115
+
116
+ # Build (query, document) pairs for batch scoring
117
+ pairs: list[tuple[str, str]] = [
118
+ (query, fact.content) for fact, _ in candidates
119
+ ]
120
+
121
+ scores = self._model.predict(pairs)
122
+
123
+ scored: list[tuple[AtomicFact, float]] = [
124
+ (fact, float(score))
125
+ for (fact, _), score in zip(candidates, scores)
126
+ ]
127
+
128
+ scored.sort(key=lambda x: x[1], reverse=True)
129
+ return scored[:top_k]
130
+
131
+ def score_pair(self, query: str, document: str) -> float:
132
+ """Score a single (query, document) pair.
133
+
134
+ Args:
135
+ query: Query text.
136
+ document: Document text.
137
+
138
+ Returns:
139
+ Relevance score (higher = more relevant). 0.0 if model
140
+ is unavailable.
141
+ """
142
+ self._ensure_model()
143
+
144
+ if self._model is None:
145
+ return 0.0
146
+
147
+ scores = self._model.predict([(query, document)])
148
+ return float(scores[0])
149
+
150
+ @property
151
+ def is_available(self) -> bool:
152
+ """Whether the cross-encoder model is loaded and ready."""
153
+ self._ensure_model()
154
+ return self._model is not None