memorymaster 3.14.0__tar.gz → 3.15.1__tar.gz

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 (364) hide show
  1. {memorymaster-3.14.0/memorymaster.egg-info → memorymaster-3.15.1}/PKG-INFO +17 -1
  2. {memorymaster-3.14.0 → memorymaster-3.15.1}/README.md +14 -0
  3. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/_storage_lifecycle.py +24 -2
  4. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/_storage_read.py +5 -0
  5. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/_storage_schema.py +2 -1
  6. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/_storage_sources.py +17 -1
  7. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/_storage_write_claims.py +24 -3
  8. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/config.py +27 -0
  9. memorymaster-3.15.1/memorymaster/llm_rerank.py +216 -0
  10. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/llm_steward.py +5 -2
  11. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/operator.py +10 -10
  12. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/postgres_store.py +46 -32
  13. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/retrieval.py +31 -2
  14. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/scheduler.py +9 -0
  15. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/service.py +33 -14
  16. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/storage.py +19 -8
  17. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/vault_linter.py +2 -1
  18. {memorymaster-3.14.0 → memorymaster-3.15.1/memorymaster.egg-info}/PKG-INFO +17 -1
  19. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster.egg-info/SOURCES.txt +2 -1
  20. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster.egg-info/requires.txt +2 -0
  21. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster.egg-info/top_level.txt +1 -0
  22. {memorymaster-3.14.0 → memorymaster-3.15.1}/pyproject.toml +10 -2
  23. memorymaster-3.15.1/tests/bench_longmemeval.py +921 -0
  24. memorymaster-3.15.1/tests/test_storage_parity.py +287 -0
  25. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_wiki_freshness.py +1 -1
  26. memorymaster-3.14.0/scripts/run_longmemeval.py +0 -997
  27. memorymaster-3.14.0/tests/test_storage_parity.py +0 -67
  28. {memorymaster-3.14.0 → memorymaster-3.15.1}/LICENSE +0 -0
  29. {memorymaster-3.14.0 → memorymaster-3.15.1}/artifacts/bm25-per-field-eval-harness.py +0 -0
  30. {memorymaster-3.14.0 → memorymaster-3.15.1}/benchmarks/longmemeval_runner.py +0 -0
  31. {memorymaster-3.14.0 → memorymaster-3.15.1}/benchmarks/longmemeval_vector_runner.py +0 -0
  32. {memorymaster-3.14.0 → memorymaster-3.15.1}/benchmarks/perf_smoke.py +0 -0
  33. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/__init__.py +0 -0
  34. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/__main__.py +0 -0
  35. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/_storage_shared.py +0 -0
  36. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/access_control.py +0 -0
  37. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/action_exporters.py +0 -0
  38. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/action_extractor.py +0 -0
  39. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/atlas_claim_extractor.py +0 -0
  40. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/atlas_contract.py +0 -0
  41. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/auto_extractor.py +0 -0
  42. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/auto_resolver.py +0 -0
  43. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/candidate_dedupe.py +0 -0
  44. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/claim_edges.py +0 -0
  45. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/claim_verifier.py +0 -0
  46. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/cli.py +0 -0
  47. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/cli_handlers_basic.py +0 -0
  48. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/cli_handlers_curation.py +0 -0
  49. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/cli_helpers.py +0 -0
  50. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/closets.py +0 -0
  51. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/config_templates/claude-md-append.md +0 -0
  52. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/config_templates/codex-agents-md-append.md +0 -0
  53. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/config_templates/hooks/memorymaster-auto-ingest.py +0 -0
  54. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/config_templates/hooks/memorymaster-classify.py +0 -0
  55. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/config_templates/hooks/memorymaster-dream-sync.py +0 -0
  56. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/config_templates/hooks/memorymaster-precompact.py +0 -0
  57. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/config_templates/hooks/memorymaster-recall.py +0 -0
  58. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/config_templates/hooks/memorymaster-session-start.py +0 -0
  59. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/config_templates/hooks/memorymaster-steward-cycle.py +0 -0
  60. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/config_templates/hooks/memorymaster-validate-wiki.py +0 -0
  61. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/conflict_resolver.py +0 -0
  62. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/connectors/__init__.py +0 -0
  63. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/connectors/whatsapp.py +0 -0
  64. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/context_hook.py +0 -0
  65. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/context_optimizer.py +0 -0
  66. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/daily_notes.py +0 -0
  67. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/dashboard.py +0 -0
  68. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/db_merge.py +0 -0
  69. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/dream_bridge.py +0 -0
  70. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/embeddings.py +0 -0
  71. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/entity_extractor.py +0 -0
  72. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/entity_graph.py +0 -0
  73. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/entity_registry.py +0 -0
  74. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/federated_graphify.py +0 -0
  75. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/feedback.py +0 -0
  76. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/graph_store.py +0 -0
  77. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/hook_log.py +0 -0
  78. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/jobs/__init__.py +0 -0
  79. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/jobs/calibration.py +0 -0
  80. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/jobs/compact_summaries.py +0 -0
  81. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/jobs/compactor.py +0 -0
  82. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/jobs/decay.py +0 -0
  83. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/jobs/dedup.py +0 -0
  84. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/jobs/deterministic.py +0 -0
  85. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/jobs/entity_graph_export.py +0 -0
  86. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/jobs/extractor.py +0 -0
  87. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/jobs/staleness.py +0 -0
  88. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/jobs/validator.py +0 -0
  89. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/key_rotator.py +0 -0
  90. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/lifecycle.py +0 -0
  91. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/llm_provider.py +0 -0
  92. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/mcp_server.py +0 -0
  93. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/mcp_usage.py +0 -0
  94. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/media_processing.py +0 -0
  95. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/media_providers.py +0 -0
  96. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/metrics_exporter.py +0 -0
  97. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/models.py +0 -0
  98. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/observability.py +0 -0
  99. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/operator_queue.py +0 -0
  100. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/plugins.py +0 -0
  101. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/policy.py +0 -0
  102. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/qdrant_backend.py +0 -0
  103. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/qdrant_recall_fallback.py +0 -0
  104. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/qmd_bridge.py +0 -0
  105. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/query_classifier.py +0 -0
  106. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/query_expansion.py +0 -0
  107. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/recall_fusion.py +0 -0
  108. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/recall_tokenizer.py +0 -0
  109. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/retry.py +0 -0
  110. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/review.py +0 -0
  111. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/rl_trainer.py +0 -0
  112. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/schema.py +0 -0
  113. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/schema.sql +0 -0
  114. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/schema_postgres.sql +0 -0
  115. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/scope_utils.py +0 -0
  116. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/security.py +0 -0
  117. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/session_tracker.py +0 -0
  118. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/setup_hooks.py +0 -0
  119. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/skill_evolver.py +0 -0
  120. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/snapshot.py +0 -0
  121. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/steward.py +0 -0
  122. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/steward_classifier.py +0 -0
  123. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/steward_features.py +0 -0
  124. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/store_factory.py +0 -0
  125. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/transcript_miner.py +0 -0
  126. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/turn_schema.py +0 -0
  127. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/vault_bases.py +0 -0
  128. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/vault_curator.py +0 -0
  129. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/vault_exporter.py +0 -0
  130. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/vault_log.py +0 -0
  131. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/vault_query_capture.py +0 -0
  132. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/vault_synthesis.py +0 -0
  133. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/verbatim_recall.py +0 -0
  134. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/verbatim_store.py +0 -0
  135. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/webhook.py +0 -0
  136. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/wiki_engine.py +0 -0
  137. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/wiki_freshness.py +0 -0
  138. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/wiki_similarity.py +0 -0
  139. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/wiki_suggest.py +0 -0
  140. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster/wiki_validate.py +0 -0
  141. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster.egg-info/dependency_links.txt +0 -0
  142. {memorymaster-3.14.0 → memorymaster-3.15.1}/memorymaster.egg-info/entry_points.txt +0 -0
  143. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/agg_recall_latency.py +0 -0
  144. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/alert_operator_metrics.py +0 -0
  145. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/audit_dedupe_precision.py +0 -0
  146. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/autoresearch_daemon.py +0 -0
  147. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/backfill_entity_extraction.py +0 -0
  148. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/backfill_graph_store.py +0 -0
  149. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/backfill_stop_hook_citations.py +0 -0
  150. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/backtest_steward_classifier.py +0 -0
  151. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/build_steward_training_set.py +0 -0
  152. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/check_hook_template_drift.py +0 -0
  153. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/claude_to_turns.py +0 -0
  154. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/codex_live_to_turns.py +0 -0
  155. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/compaction_edge_cases.py +0 -0
  156. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/compaction_trace_report.py +0 -0
  157. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/compaction_trace_validate.py +0 -0
  158. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/confusion_matrix_eval.py +0 -0
  159. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/conversation_importer.py +0 -0
  160. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/conversation_to_turns.py +0 -0
  161. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/e2e_operator.py +0 -0
  162. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/email_live_to_turns.py +0 -0
  163. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/eval_bm25_sweep.py +0 -0
  164. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/eval_classify_f1.py +0 -0
  165. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/eval_memorymaster.py +0 -0
  166. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/eval_recall_precision_at_5.py +0 -0
  167. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/eval_recall_quality.py +0 -0
  168. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/eval_steward_pareto.py +0 -0
  169. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/eval_verbatim_recall.py +0 -0
  170. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/expand_recall_eval.py +0 -0
  171. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/generate_drill_signoff.py +0 -0
  172. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/git_to_turns.py +0 -0
  173. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/github_live_to_turns.py +0 -0
  174. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/gitnexus_to_claims.py +0 -0
  175. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/grid_recall_weights.py +0 -0
  176. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/index_claims_to_qdrant.py +0 -0
  177. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/ingest_planning_docs.py +0 -0
  178. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/jira_live_to_turns.py +0 -0
  179. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/label_prompts_with_judge.py +0 -0
  180. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/llm_benchmark.py +0 -0
  181. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/measure_dedupe_thresholds.py +0 -0
  182. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/merge_scope_variants.py +0 -0
  183. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/messages_to_turns.py +0 -0
  184. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/operator_metrics.py +0 -0
  185. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/precompute_candidates.py +0 -0
  186. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/recurring_incident_drill.py +0 -0
  187. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/release_readiness.py +0 -0
  188. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/run_codex_autologger.py +0 -0
  189. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/run_incident_drill.py +0 -0
  190. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/scheduled_ingest.py +0 -0
  191. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/setup-hooks.py +0 -0
  192. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/slack_live_to_turns.py +0 -0
  193. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/sync_hook_templates.py +0 -0
  194. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/tickets_to_turns.py +0 -0
  195. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/train_steward_classifier.py +0 -0
  196. {memorymaster-3.14.0 → memorymaster-3.15.1}/scripts/webhook_to_turns.py +0 -0
  197. {memorymaster-3.14.0 → memorymaster-3.15.1}/setup.cfg +0 -0
  198. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/conftest.py +0 -0
  199. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/integration/test_extract_llm_ollama_live.py +0 -0
  200. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_access_control.py +0 -0
  201. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_action_exporters.py +0 -0
  202. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_action_extractor.py +0 -0
  203. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_atlas_claim_extractor.py +0 -0
  204. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_atlas_contract.py +0 -0
  205. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_atlas_source_schema.py +0 -0
  206. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_auto_extractor.py +0 -0
  207. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_auto_ingest_hook_citations.py +0 -0
  208. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_auto_ingest_hook_schema.py +0 -0
  209. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_auto_resolver.py +0 -0
  210. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_auto_validate.py +0 -0
  211. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_bm25_per_field.py +0 -0
  212. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_calibration.py +0 -0
  213. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_calibration_priors_applied.py +0 -0
  214. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_candidate_dedupe.py +0 -0
  215. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_claim_edges.py +0 -0
  216. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_claim_links.py +0 -0
  217. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_claim_type_ranking.py +0 -0
  218. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_classify_hook_f1.py +0 -0
  219. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_classify_hook_latency.py +0 -0
  220. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_claude_to_turns.py +0 -0
  221. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_cli_dry_run.py +0 -0
  222. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_cli_json_flag.py +0 -0
  223. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_cli_ready.py +0 -0
  224. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_cli_review_queue.py +0 -0
  225. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_cli_subcommands.py +0 -0
  226. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_closets.py +0 -0
  227. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_closets_recall_integration.py +0 -0
  228. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_compact_summaries.py +0 -0
  229. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_compact_summaries_sensitivity.py +0 -0
  230. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_compaction_trace.py +0 -0
  231. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_compactor_artifact_order.py +0 -0
  232. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_config.py +0 -0
  233. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_conflict_resolver.py +0 -0
  234. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_confusion_matrix_eval.py +0 -0
  235. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_connection_retry.py +0 -0
  236. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_connectors.py +0 -0
  237. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_context_hook.py +0 -0
  238. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_context_optimizer.py +0 -0
  239. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_context_optimizer_provider.py +0 -0
  240. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_conversation_to_turns.py +0 -0
  241. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_dashboard.py +0 -0
  242. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_dashboard_coverage.py +0 -0
  243. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_dashboard_latency.py +0 -0
  244. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_dashboard_lineage.py +0 -0
  245. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_dashboard_review_queue.py +0 -0
  246. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_db_merge_confidence_conflict.py +0 -0
  247. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_db_merge_coverage_v2.py +0 -0
  248. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_decay_coverage.py +0 -0
  249. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_decay_respects_pinned.py +0 -0
  250. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_dedup.py +0 -0
  251. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_dedup_cli.py +0 -0
  252. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_dedup_conflict_disambiguation.py +0 -0
  253. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_deterministic_predicates.py +0 -0
  254. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_dream_bridge_coverage_v2.py +0 -0
  255. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_dream_bridge_sensitivity.py +0 -0
  256. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_embeddings_coverage.py +0 -0
  257. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_entity_extractor.py +0 -0
  258. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_entity_extractor_llm.py +0 -0
  259. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_entity_graph.py +0 -0
  260. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_entity_graph_export.py +0 -0
  261. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_entity_new_kinds.py +0 -0
  262. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_entity_regex_v3.py +0 -0
  263. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_entity_registry.py +0 -0
  264. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_eval_harness.py +0 -0
  265. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_events_schema.py +0 -0
  266. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_extract_llm_ollama.py +0 -0
  267. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_federated_graphify_mcp.py +0 -0
  268. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_federated_query_safety.py +0 -0
  269. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_feedback.py +0 -0
  270. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_fts5_search.py +0 -0
  271. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_graph_distance.py +0 -0
  272. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_graph_store.py +0 -0
  273. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_handler_regressions.py +0 -0
  274. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_hook_env_isolation.py +0 -0
  275. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_human_id.py +0 -0
  276. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_incident_drill_runner.py +0 -0
  277. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_integration_workflows.py +0 -0
  278. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_key_rotator.py +0 -0
  279. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_lifecycle.py +0 -0
  280. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_lifecycle_supersede_invariant.py +0 -0
  281. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_llm_fallback.py +0 -0
  282. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_llm_provider_claude_cli.py +0 -0
  283. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_llm_provider_key_rotation.py +0 -0
  284. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_llm_steward_coverage.py +0 -0
  285. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_llm_steward_key_rotation.py +0 -0
  286. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_mcp_filter_bypass.py +0 -0
  287. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_mcp_helpers.py +0 -0
  288. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_mcp_rate_limit.py +0 -0
  289. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_mcp_server_validation.py +0 -0
  290. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_mcp_usage.py +0 -0
  291. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_media_processing.py +0 -0
  292. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_meta_decisions.py +0 -0
  293. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_metrics_exporter.py +0 -0
  294. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_observability.py +0 -0
  295. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_obsidian_mind_patterns.py +0 -0
  296. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_operator.py +0 -0
  297. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_operator_queue.py +0 -0
  298. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_perf_smoke_config.py +0 -0
  299. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_plugins.py +0 -0
  300. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_policy_coverage.py +0 -0
  301. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_policy_mode_env.py +0 -0
  302. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_postgres_parity.py +0 -0
  303. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_qdrant_backend.py +0 -0
  304. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_qmd_bridge.py +0 -0
  305. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_query_classifier.py +0 -0
  306. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_query_expansion.py +0 -0
  307. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_recall_entity_fanout.py +0 -0
  308. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_recall_fusion.py +0 -0
  309. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_recall_latency.py +0 -0
  310. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_recall_precision_at_5.py +0 -0
  311. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_recall_tokenizer.py +0 -0
  312. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_recall_vector_fallback.py +0 -0
  313. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_reliability_hardening.py +0 -0
  314. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_resolvers_concurrent_supersede.py +0 -0
  315. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_retrieval_profile.py +0 -0
  316. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_review.py +0 -0
  317. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_rl_trainer.py +0 -0
  318. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_rrf_auto_gate.py +0 -0
  319. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_scheduler.py +0 -0
  320. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_schema.py +0 -0
  321. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_scope_boost.py +0 -0
  322. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_scope_utils.py +0 -0
  323. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_security_access.py +0 -0
  324. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_security_patterns.py +0 -0
  325. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_sensitivity_filter_adversarial.py +0 -0
  326. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_sensitivity_filter_adversarial_v2.py +0 -0
  327. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_sensitivity_filter_t07.py +0 -0
  328. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_service_coverage.py +0 -0
  329. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_session_tracker.py +0 -0
  330. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_snapshot.py +0 -0
  331. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_snapshot_roundtrip.py +0 -0
  332. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_sqlite_core.py +0 -0
  333. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_staleness.py +0 -0
  334. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_stealth_mode.py +0 -0
  335. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_steward.py +0 -0
  336. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_steward_classifier.py +0 -0
  337. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_steward_features.py +0 -0
  338. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_steward_features_v3.py +0 -0
  339. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_steward_resolution_parity.py +0 -0
  340. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_store_factory.py +0 -0
  341. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_tenant_isolation.py +0 -0
  342. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_turn_schema.py +0 -0
  343. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_two_pass_recall.py +0 -0
  344. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_v311_fixes.py +0 -0
  345. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_v313_e2e.py +0 -0
  346. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_v313_run_cycle_dedupe.py +0 -0
  347. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_v390_e2e.py +0 -0
  348. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_v391_strict_warnings.py +0 -0
  349. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_vault_exporter.py +0 -0
  350. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_vault_linter_orphan.py +0 -0
  351. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_vector_search.py +0 -0
  352. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_verbatim_dedup.py +0 -0
  353. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_verbatim_recall.py +0 -0
  354. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_verbatim_store.py +0 -0
  355. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_verbatim_store_qdrant.py +0 -0
  356. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_webhook.py +0 -0
  357. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_whatsapp_importer.py +0 -0
  358. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_wiki_autopromote.py +0 -0
  359. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_wiki_binding.py +0 -0
  360. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_wiki_engine_idempotency.py +0 -0
  361. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_wiki_explored_and_contradictions.py +0 -0
  362. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_wiki_similarity_multiscope.py +0 -0
  363. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_wiki_suggest.py +0 -0
  364. {memorymaster-3.14.0 → memorymaster-3.15.1}/tests/test_wiki_validate_cli.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memorymaster
3
- Version: 3.14.0
3
+ Version: 3.15.1
4
4
  Summary: Production-grade memory reliability system for AI coding agents. Lifecycle-managed claims with citations, conflict detection, steward governance, and MCP integration.
5
5
  Author: wolverin0
6
6
  License: MIT
@@ -17,6 +17,8 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
17
17
  Requires-Python: >=3.10
18
18
  Description-Content-Type: text/markdown
19
19
  License-File: LICENSE
20
+ Requires-Dist: requests>=2.31
21
+ Requires-Dist: tenacity>=8.2
20
22
  Provides-Extra: postgres
21
23
  Requires-Dist: psycopg[binary]>=3.2; extra == "postgres"
22
24
  Provides-Extra: security
@@ -96,6 +98,20 @@ recent PR status, and sensitivity-filter invariants.
96
98
 
97
99
  Full feature index lives in [`docs/handbook.md`](docs/handbook.md).
98
100
 
101
+ ## Benchmarks
102
+
103
+ **LongMemEval-S (N=500, retrieval-only)** — v3.15.0 now leads the publicly-reported numbers from [agentmemory](https://github.com/rohitg00/agentmemory) on R@5 and MRR, after wiring `sentence-transformers/all-MiniLM-L6-v2` into the bench harness (the v3.14 baseline was unintentionally BM25-only).
104
+
105
+ ![LongMemEval-S benchmark](docs/benchmark-longmemeval.svg)
106
+
107
+ | Metric | v3.14.0 | **v3.15.0** | agentmemory | Δ vs agentmemory |
108
+ |---|---|---|---|---|
109
+ | Recall@5 | 0.894 | **0.966** | 0.952 | **+0.014** ★ |
110
+ | Recall@10 | 0.942 | **0.984** | 0.986 | -0.002 |
111
+ | MRR | 0.799 | **0.902** | 0.882 | **+0.020** ★ |
112
+
113
+ Reproduce: `python tests/bench_longmemeval.py --retrieval-only`. Full methodology, experiment-by-experiment deltas (1 KEEP, 2 REVERT, 3 NULL), and the architectural findings that surfaced along the way live in [`docs/longmemeval-results.md`](docs/longmemeval-results.md) and [`docs/v315-experiments/`](docs/v315-experiments/). QA-accuracy pass (with judge) is deferred until provider quotas allow.
114
+
99
115
  ## Prerequisites
100
116
 
101
117
  **Required (the package won't function without these)**
@@ -52,6 +52,20 @@ recent PR status, and sensitivity-filter invariants.
52
52
 
53
53
  Full feature index lives in [`docs/handbook.md`](docs/handbook.md).
54
54
 
55
+ ## Benchmarks
56
+
57
+ **LongMemEval-S (N=500, retrieval-only)** — v3.15.0 now leads the publicly-reported numbers from [agentmemory](https://github.com/rohitg00/agentmemory) on R@5 and MRR, after wiring `sentence-transformers/all-MiniLM-L6-v2` into the bench harness (the v3.14 baseline was unintentionally BM25-only).
58
+
59
+ ![LongMemEval-S benchmark](docs/benchmark-longmemeval.svg)
60
+
61
+ | Metric | v3.14.0 | **v3.15.0** | agentmemory | Δ vs agentmemory |
62
+ |---|---|---|---|---|
63
+ | Recall@5 | 0.894 | **0.966** | 0.952 | **+0.014** ★ |
64
+ | Recall@10 | 0.942 | **0.984** | 0.986 | -0.002 |
65
+ | MRR | 0.799 | **0.902** | 0.882 | **+0.020** ★ |
66
+
67
+ Reproduce: `python tests/bench_longmemeval.py --retrieval-only`. Full methodology, experiment-by-experiment deltas (1 KEEP, 2 REVERT, 3 NULL), and the architectural findings that surfaced along the way live in [`docs/longmemeval-results.md`](docs/longmemeval-results.md) and [`docs/v315-experiments/`](docs/v315-experiments/). QA-accuracy pass (with judge) is deferred until provider quotas allow.
68
+
55
69
  ## Prerequisites
56
70
 
57
71
  **Required (the package won't function without these)**
@@ -10,6 +10,7 @@ import json
10
10
  import logging
11
11
  import sqlite3
12
12
  from datetime import datetime, timedelta, timezone
13
+ from typing import TYPE_CHECKING, Any
13
14
 
14
15
  from memorymaster.embeddings import EmbeddingProvider, cosine_similarity
15
16
  from memorymaster.models import (
@@ -33,6 +34,27 @@ logger = logging.getLogger(__name__)
33
34
 
34
35
 
35
36
  class _LifecycleMixin:
37
+ if TYPE_CHECKING:
38
+ def connect(self) -> sqlite3.Connection: ...
39
+
40
+ def get_claim(self, claim_id: int, include_citations: bool = True) -> Claim | None: ...
41
+
42
+ def _ensure_event_integrity_schema(self, conn: sqlite3.Connection) -> None: ...
43
+
44
+ def _ensure_embeddings_schema(self, conn: sqlite3.Connection) -> None: ...
45
+
46
+ def _insert_event_row(
47
+ self,
48
+ conn: sqlite3.Connection,
49
+ *,
50
+ claim_id: int | None,
51
+ event_type: str,
52
+ from_status: str | None,
53
+ to_status: str | None,
54
+ details: str | None,
55
+ payload_json: str | None,
56
+ created_at: str,
57
+ ) -> int: ...
36
58
 
37
59
  def apply_status_transition(
38
60
  self,
@@ -167,8 +189,8 @@ class _LifecycleMixin:
167
189
  return 0
168
190
 
169
191
 
170
- def reconcile_integrity(self, *, fix: bool = False, limit: int = 500) -> dict[str, object]:
171
- report: dict[str, object] = {
192
+ def reconcile_integrity(self, *, fix: bool = False, limit: int = 500) -> dict[str, Any]:
193
+ report: dict[str, Any] = {
172
194
  "checked_at": utc_now(),
173
195
  "fix_mode": bool(fix),
174
196
  "issues": {},
@@ -9,6 +9,7 @@ from __future__ import annotations
9
9
  import logging
10
10
  import sqlite3
11
11
  from datetime import datetime, timedelta, timezone
12
+ from typing import TYPE_CHECKING
12
13
 
13
14
  from memorymaster.models import (
14
15
  Citation,
@@ -22,6 +23,10 @@ logger = logging.getLogger(__name__)
22
23
 
23
24
 
24
25
  class _ReadMixin:
26
+ if TYPE_CHECKING:
27
+ def connect(self) -> sqlite3.Connection: ...
28
+
29
+ def init_db(self) -> None: ...
25
30
 
26
31
  def _check_idempotency(self, conn: sqlite3.Connection, idempotency_key: str | None) -> Claim | None:
27
32
  """Check if a claim with this idempotency key already exists. Returns existing claim or None."""
@@ -338,7 +338,8 @@ class _SchemaMixin:
338
338
  predicate,
339
339
  object_value,
340
340
  content='claims',
341
- content_rowid='id'
341
+ content_rowid='id',
342
+ tokenize='porter unicode61'
342
343
  )
343
344
  """
344
345
  )
@@ -8,7 +8,7 @@ from __future__ import annotations
8
8
 
9
9
  import json
10
10
  import sqlite3
11
- from typing import Any
11
+ from typing import TYPE_CHECKING, Any
12
12
 
13
13
  from memorymaster._storage_shared import utc_now
14
14
  from memorymaster.models import (
@@ -51,6 +51,22 @@ def _bounded_confidence(confidence: float | None) -> float | None:
51
51
 
52
52
 
53
53
  class _SourceItemsMixin:
54
+ if TYPE_CHECKING:
55
+ def connect(self) -> sqlite3.Connection: ...
56
+
57
+ def _insert_event_row(
58
+ self,
59
+ conn: sqlite3.Connection,
60
+ *,
61
+ claim_id: int | None,
62
+ event_type: str,
63
+ from_status: str | None,
64
+ to_status: str | None,
65
+ details: str | None,
66
+ payload_json: str | None,
67
+ created_at: str,
68
+ ) -> int: ...
69
+
54
70
  def upsert_external_source(
55
71
  self,
56
72
  *,
@@ -9,21 +9,42 @@ from __future__ import annotations
9
9
  import json
10
10
  import logging
11
11
  import sqlite3
12
+ from typing import TYPE_CHECKING
12
13
 
13
14
  from memorymaster.models import (
14
15
  CitationInput,
15
16
  Claim,
16
17
  validate_event_payload,
17
18
  )
18
-
19
- logger = logging.getLogger(__name__)
20
-
21
19
  from memorymaster._storage_shared import (
22
20
  utc_now,
23
21
  )
24
22
 
23
+ logger = logging.getLogger(__name__)
24
+
25
25
 
26
26
  class _WriteClaimsMixin:
27
+ if TYPE_CHECKING:
28
+ def connect(self) -> sqlite3.Connection: ...
29
+
30
+ def _check_idempotency(self, conn: sqlite3.Connection, idempotency_key: str | None) -> Claim | None: ...
31
+
32
+ def get_claim(self, claim_id: int, include_citations: bool = True) -> Claim | None: ...
33
+
34
+ def _allocate_human_id(self, conn: sqlite3.Connection, subject: str | None, text: str, claim_id: int) -> str: ...
35
+
36
+ def _insert_event_row(
37
+ self,
38
+ conn: sqlite3.Connection,
39
+ *,
40
+ claim_id: int | None,
41
+ event_type: str,
42
+ from_status: str | None,
43
+ to_status: str | None,
44
+ details: str | None,
45
+ payload_json: str | None,
46
+ created_at: str,
47
+ ) -> int: ...
27
48
 
28
49
  def create_claim(
29
50
  self,
@@ -46,6 +46,15 @@ MEMORYMASTER_PINNED_BONUS
46
46
  Score bonus applied to pinned claims during ranking.
47
47
  Default: ``0.03``
48
48
 
49
+ MEMORYMASTER_SESSION_DIVERSITY_CAP
50
+ Maximum ranked results to keep per source session before applying the
51
+ final query limit. Set to ``0`` to disable.
52
+ Default: ``3``
53
+
54
+ MEMORYMASTER_LLM_RERANK
55
+ Enable Gemini cross-encoder reranking over the top retrieval candidates.
56
+ Default: ``0``
57
+
49
58
  MEMORYMASTER_CONFIG_FILE
50
59
  Path to a JSON config file. Keys match attribute names on ``Config``.
51
60
  """
@@ -186,6 +195,8 @@ class Config:
186
195
  stale_threshold: float = 0.35
187
196
  conflict_margin: float = 0.08
188
197
  pinned_bonus: float = 0.03
198
+ session_diversity_cap: int = 3
199
+ llm_rerank: bool = False
189
200
 
190
201
  # --- Initial confidence priors calibrated from validator outcomes ---
191
202
  default_initial_confidence: float = DEFAULT_INITIAL_CONFIDENCE
@@ -330,6 +341,8 @@ def load_config(config_path: str | Path | None = None) -> Config:
330
341
  _apply_env_float(overrides, "MEMORYMASTER_STALE_THRESHOLD", "stale_threshold")
331
342
  _apply_env_float(overrides, "MEMORYMASTER_CONFLICT_MARGIN", "conflict_margin")
332
343
  _apply_env_float(overrides, "MEMORYMASTER_PINNED_BONUS", "pinned_bonus")
344
+ _apply_env_int(overrides, "MEMORYMASTER_SESSION_DIVERSITY_CAP", "session_diversity_cap")
345
+ _apply_env_bool(overrides, "MEMORYMASTER_LLM_RERANK", "llm_rerank")
333
346
 
334
347
  # Filter to only valid Config fields
335
348
  valid_fields = {f.name for f in Config.__dataclass_fields__.values()}
@@ -357,3 +370,17 @@ def _apply_env_float(overrides: dict[str, object], env_var: str, key: str) -> No
357
370
  if not raw:
358
371
  return
359
372
  overrides[key] = float(raw)
373
+
374
+
375
+ def _apply_env_int(overrides: dict[str, object], env_var: str, key: str) -> None:
376
+ raw = os.environ.get(env_var, "").strip()
377
+ if not raw:
378
+ return
379
+ overrides[key] = int(raw)
380
+
381
+
382
+ def _apply_env_bool(overrides: dict[str, object], env_var: str, key: str) -> None:
383
+ raw = os.environ.get(env_var, "").strip().lower()
384
+ if not raw:
385
+ return
386
+ overrides[key] = raw in {"1", "true", "yes", "on"}
@@ -0,0 +1,216 @@
1
+ """LLM cross-encoder reranking for retrieval candidates."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import os
7
+ import threading
8
+ import time
9
+ from collections.abc import Sequence
10
+ from contextlib import contextmanager
11
+ from typing import Any
12
+
13
+ from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_exponential
14
+
15
+ from memorymaster.llm_provider import call_llm, parse_json_response
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ _MAX_SNIPPET_CHARS = 300
20
+ _LAST_CALL_AT = 0.0
21
+ _RATE_LOCK = threading.Lock()
22
+ _CONSECUTIVE_FAILURES = 0
23
+ _DISABLED = False
24
+ _STATS: dict[str, int] = {"attempts": 0, "successes": 0, "failures": 0, "disabled_fallbacks": 0}
25
+ _PROMPT = """Score how relevant each candidate is to the question.
26
+
27
+ Return STRICT JSON only: an array of [candidate_index, relevance_score] pairs.
28
+ candidate_index must match the numbered candidate. relevance_score must be an integer from 0 to 100.
29
+ Do not include prose or markdown."""
30
+
31
+
32
+ class LLMRerankError(RuntimeError):
33
+ """Raised for retryable rerank judge failures."""
34
+
35
+
36
+ @contextmanager
37
+ def _temporary_llm_env(judge_model: str):
38
+ saved_provider = os.environ.get("MEMORYMASTER_LLM_PROVIDER")
39
+ saved_model = os.environ.get("MEMORYMASTER_LLM_MODEL")
40
+ saved_key_file = os.environ.get("MEMORYMASTER_KEY_FILE")
41
+ saved_key_rotation = os.environ.get("MEMORYMASTER_LLM_KEY_ROTATION")
42
+ try:
43
+ from memorymaster.key_rotator import clear_cache
44
+
45
+ clear_cache()
46
+ os.environ["MEMORYMASTER_LLM_PROVIDER"] = "google"
47
+ os.environ["MEMORYMASTER_LLM_MODEL"] = judge_model
48
+ os.environ["MEMORYMASTER_KEY_FILE"] = "__memorymaster_llm_rerank_no_key_file__"
49
+ os.environ["MEMORYMASTER_LLM_KEY_ROTATION"] = "0"
50
+ yield
51
+ finally:
52
+ if saved_provider is None:
53
+ os.environ.pop("MEMORYMASTER_LLM_PROVIDER", None)
54
+ else:
55
+ os.environ["MEMORYMASTER_LLM_PROVIDER"] = saved_provider
56
+ if saved_model is None:
57
+ os.environ.pop("MEMORYMASTER_LLM_MODEL", None)
58
+ else:
59
+ os.environ["MEMORYMASTER_LLM_MODEL"] = saved_model
60
+ if saved_key_file is None:
61
+ os.environ.pop("MEMORYMASTER_KEY_FILE", None)
62
+ else:
63
+ os.environ["MEMORYMASTER_KEY_FILE"] = saved_key_file
64
+ if saved_key_rotation is None:
65
+ os.environ.pop("MEMORYMASTER_LLM_KEY_ROTATION", None)
66
+ else:
67
+ os.environ["MEMORYMASTER_LLM_KEY_ROTATION"] = saved_key_rotation
68
+ clear_cache()
69
+
70
+
71
+ def _min_interval_seconds() -> float:
72
+ raw = os.environ.get("MEMORYMASTER_LLM_RERANK_MIN_INTERVAL_SECONDS", "3.1").strip()
73
+ try:
74
+ return max(0.0, float(raw))
75
+ except ValueError:
76
+ return 3.1
77
+
78
+
79
+ def _max_failures_before_disable() -> int:
80
+ raw = os.environ.get("MEMORYMASTER_LLM_RERANK_MAX_FAILURES", "1").strip()
81
+ try:
82
+ return max(1, int(raw))
83
+ except ValueError:
84
+ return 1
85
+
86
+
87
+ def _pace_judge_call() -> None:
88
+ global _LAST_CALL_AT
89
+ min_interval = _min_interval_seconds()
90
+ if min_interval <= 0:
91
+ return
92
+ with _RATE_LOCK:
93
+ now = time.monotonic()
94
+ sleep_for = max(0.0, min_interval - (now - _LAST_CALL_AT))
95
+ if sleep_for > 0:
96
+ time.sleep(sleep_for)
97
+ _LAST_CALL_AT = time.monotonic()
98
+
99
+
100
+ def _candidate_text(candidate: Any) -> str:
101
+ if isinstance(candidate, str):
102
+ return candidate
103
+ if isinstance(candidate, dict):
104
+ claim = candidate.get("claim")
105
+ if claim is not None:
106
+ return str(getattr(claim, "text", "") or "")
107
+ return str(candidate.get("text", "") or "")
108
+ return str(getattr(candidate, "text", "") or candidate)
109
+
110
+
111
+ def _build_rerank_input(query: str, candidates: Sequence[Any]) -> str:
112
+ lines = [f"Question: {query.strip()}", "", "Candidates:"]
113
+ for idx, candidate in enumerate(candidates, start=1):
114
+ snippet = " ".join(_candidate_text(candidate).split())[:_MAX_SNIPPET_CHARS]
115
+ lines.append(f"{idx}. {snippet}")
116
+ return "\n".join(lines)
117
+
118
+
119
+ @retry(
120
+ stop=stop_after_attempt(3),
121
+ wait=wait_exponential(min=10, max=60),
122
+ retry=retry_if_exception_type(LLMRerankError),
123
+ reraise=True,
124
+ )
125
+ def _call_rerank_judge(query: str, candidates: Sequence[Any], judge_model: str) -> str:
126
+ _pace_judge_call()
127
+ with _temporary_llm_env(judge_model):
128
+ response = call_llm(_PROMPT, _build_rerank_input(query, candidates))
129
+ if not response.strip():
130
+ raise LLMRerankError("empty rerank judge response")
131
+ return response
132
+
133
+
134
+ def _score_entry(entry: Any) -> tuple[int, float] | None:
135
+ if isinstance(entry, dict):
136
+ raw_index = entry.get("candidate_index", entry.get("index"))
137
+ raw_score = entry.get("relevance_score", entry.get("score"))
138
+ elif isinstance(entry, (list, tuple)) and len(entry) >= 2:
139
+ raw_index, raw_score = entry[0], entry[1]
140
+ else:
141
+ return None
142
+ try:
143
+ index = int(raw_index)
144
+ score = float(raw_score)
145
+ except (TypeError, ValueError):
146
+ return None
147
+ return index, max(0.0, min(100.0, score))
148
+
149
+
150
+ def _parse_scores(response: str, candidate_count: int) -> dict[int, float]:
151
+ scores: dict[int, float] = {}
152
+ for entry in parse_json_response(response):
153
+ parsed = _score_entry(entry)
154
+ if parsed is None:
155
+ continue
156
+ index, score = parsed
157
+ if 1 <= index <= candidate_count:
158
+ scores[index - 1] = score
159
+ return scores
160
+
161
+
162
+ def get_rerank_stats() -> dict[str, int]:
163
+ return {**_STATS, "disabled": int(_DISABLED)}
164
+
165
+
166
+ def rerank_temporarily_disabled() -> bool:
167
+ return _DISABLED
168
+
169
+
170
+ def rerank_with_llm(
171
+ query: str,
172
+ candidates: list[Any],
173
+ top_k: int = 5,
174
+ judge_model: str = "gemini-2.5-flash",
175
+ ) -> list[Any]:
176
+ """Rerank candidates with a batched Gemini relevance judge.
177
+
178
+ Any judge failure returns the original ordering truncated to ``top_k``.
179
+ """
180
+ if top_k <= 0:
181
+ return []
182
+ if not query.strip() or not candidates:
183
+ return candidates[:top_k]
184
+
185
+ global _CONSECUTIVE_FAILURES, _DISABLED
186
+ if _DISABLED:
187
+ _STATS["disabled_fallbacks"] += 1
188
+ return candidates[:top_k]
189
+
190
+ _STATS["attempts"] += 1
191
+ try:
192
+ response = _call_rerank_judge(query, candidates, judge_model)
193
+ scores = _parse_scores(response, len(candidates))
194
+ except Exception as exc:
195
+ _STATS["failures"] += 1
196
+ _CONSECUTIVE_FAILURES += 1
197
+ if _CONSECUTIVE_FAILURES >= _max_failures_before_disable():
198
+ _DISABLED = True
199
+ logger.warning("LLM rerank failed; using input order: %s", exc)
200
+ return candidates[:top_k]
201
+
202
+ if not scores:
203
+ _STATS["failures"] += 1
204
+ _CONSECUTIVE_FAILURES += 1
205
+ if _CONSECUTIVE_FAILURES >= _max_failures_before_disable():
206
+ _DISABLED = True
207
+ logger.warning("LLM rerank returned no parseable scores; using input order")
208
+ return candidates[:top_k]
209
+
210
+ _STATS["successes"] += 1
211
+ _CONSECUTIVE_FAILURES = 0
212
+ ranked = sorted(
213
+ enumerate(candidates),
214
+ key=lambda item: (-scores.get(item[0], -1.0), item[0]),
215
+ )
216
+ return [candidate for _, candidate in ranked[:top_k]]
@@ -22,6 +22,7 @@ import time
22
22
  import urllib.request
23
23
  import urllib.error
24
24
  from dataclasses import dataclass, field
25
+ from typing import Any
25
26
 
26
27
  log = logging.getLogger(__name__)
27
28
 
@@ -524,7 +525,7 @@ def run_steward(
524
525
  cooldown_seconds: float = DEFAULT_COOLDOWN_SECONDS,
525
526
  auto_validate: bool = True,
526
527
  workspace_root: str = "",
527
- ) -> dict:
528
+ ) -> dict[str, Any]:
528
529
  """Process candidate claims through LLM extraction and curation.
529
530
 
530
531
  Args:
@@ -570,7 +571,7 @@ def run_steward(
570
571
  dedupe_on = _dedupe_enabled()
571
572
  dedupe_shadow = _dedupe_shadow()
572
573
 
573
- stats = {
574
+ stats: dict[str, Any] = {
574
575
  "total": len(candidates),
575
576
  "confirmed": 0,
576
577
  "archived": 0,
@@ -766,6 +767,8 @@ def run_steward(
766
767
  (text[:200], subj, pred, obj_val, conf, scope),
767
768
  )
768
769
  new_id = cursor.lastrowid
770
+ if new_id is None:
771
+ raise RuntimeError("SQLite did not return a claim id.")
769
772
  conn.execute(
770
773
  "INSERT INTO events (claim_id, event_type, details, created_at) "
771
774
  "VALUES (?, 'transition', ?, datetime('now'))",
@@ -226,12 +226,12 @@ def _extract_generic_claims(raw: str, add_claim) -> None:
226
226
 
227
227
 
228
228
  class HeuristicClaimExtractor:
229
- def extract(self, text: str) -> list[dict[str, object]]:
229
+ def extract(self, text: str) -> list[dict[str, Any]]:
230
230
  raw = text.strip()
231
231
  if not raw:
232
232
  return []
233
233
 
234
- claims: list[dict[str, object]] = []
234
+ claims: list[dict[str, Any]] = []
235
235
  seen: set[tuple[str, str, str]] = set()
236
236
 
237
237
  def add_claim(
@@ -287,7 +287,7 @@ class MemoryOperator:
287
287
  self.extractor = extractor or HeuristicClaimExtractor()
288
288
  self._reconcile_counter = 0
289
289
 
290
- def process_turn(self, turn: TurnInput) -> dict[str, object]:
290
+ def process_turn(self, turn: TurnInput) -> dict[str, Any]:
291
291
  if self.config.progressive_retrieval:
292
292
  tier1 = self.service.query(
293
293
  turn.user_text,
@@ -533,9 +533,9 @@ class MemoryOperator:
533
533
  if not (queue_inbox and queue_inbox == canonical_inbox):
534
534
  return start_offset, read_offset, acked_offset, persisted_seen_events, persisted_processed_events, pending_queue, queue_state_loaded
535
535
 
536
- start_offset = max(0, int(raw_queue_state.get("read_offset", raw_queue_state.get("offset", 0))))
536
+ start_offset = max(0, int(raw_queue_state.get("read_offset") or raw_queue_state.get("offset") or 0))
537
537
  read_offset = start_offset
538
- acked_offset = max(0, int(raw_queue_state.get("acked_offset", raw_queue_state.get("offset", read_offset))))
538
+ acked_offset = max(0, int(raw_queue_state.get("acked_offset") or raw_queue_state.get("offset") or read_offset))
539
539
  persisted_seen_events = max(0, int(raw_queue_state.get("seen_events", 0)))
540
540
  persisted_processed_events = max(0, int(raw_queue_state.get("processed_events", 0)))
541
541
  next_queue_id = max(1, int(raw_queue_state.get("next_queue_id", 1)))
@@ -684,7 +684,7 @@ class MemoryOperator:
684
684
  def _record_turn_processed(
685
685
  self,
686
686
  turn,
687
- summary: dict[str, object],
687
+ summary: dict[str, Any],
688
688
  entry_id: int,
689
689
  entry_offset: int,
690
690
  seen_events: int,
@@ -848,10 +848,10 @@ class MemoryOperator:
848
848
  queue_journal_path = Path(str(self.config.queue_journal_jsonl_path).strip())
849
849
  queue_journal_path.parent.mkdir(parents=True, exist_ok=True)
850
850
 
851
- def emit(event: str, payload: dict[str, object] | None = None) -> None:
851
+ def emit(event: str, payload: dict[str, Any] | None = None) -> None:
852
852
  if log_path is None:
853
853
  return
854
- record = {"ts": _utc_now_iso(), "event": event}
854
+ record: dict[str, Any] = {"ts": _utc_now_iso(), "event": event}
855
855
  if payload:
856
856
  record.update(payload)
857
857
  with log_path.open("a", encoding="utf-8") as out:
@@ -1160,10 +1160,10 @@ class MemoryOperator:
1160
1160
  log_path = Path(self.config.log_jsonl_path)
1161
1161
  log_path.parent.mkdir(parents=True, exist_ok=True)
1162
1162
 
1163
- def emit(event: str, payload: dict[str, object] | None = None) -> None:
1163
+ def emit(event: str, payload: dict[str, Any] | None = None) -> None:
1164
1164
  if log_path is None:
1165
1165
  return
1166
- record = {"ts": _utc_now_iso(), "event": event}
1166
+ record: dict[str, Any] = {"ts": _utc_now_iso(), "event": event}
1167
1167
  if payload:
1168
1168
  record.update(payload)
1169
1169
  with log_path.open("a", encoding="utf-8") as out: