wavemind 2.2.4__tar.gz → 2.2.5__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 (162) hide show
  1. {wavemind-2.2.4 → wavemind-2.2.5}/PKG-INFO +10 -9
  2. {wavemind-2.2.4 → wavemind-2.2.5}/README.md +9 -8
  3. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/scale_readiness_benchmark.py +39 -0
  4. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/scale_readiness_results.json +10 -7
  5. {wavemind-2.2.4 → wavemind-2.2.5}/docker-compose.yml +1 -1
  6. {wavemind-2.2.4 → wavemind-2.2.5}/pyproject.toml +1 -1
  7. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_replication.py +92 -0
  8. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_scale_readiness_benchmark.py +3 -0
  9. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/__init__.py +1 -1
  10. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/replication.py +247 -19
  11. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind.egg-info/PKG-INFO +10 -9
  12. {wavemind-2.2.4 → wavemind-2.2.5}/CONTRIBUTING.md +0 -0
  13. {wavemind-2.2.4 → wavemind-2.2.5}/Dockerfile +0 -0
  14. {wavemind-2.2.4 → wavemind-2.2.5}/LICENSE +0 -0
  15. {wavemind-2.2.4 → wavemind-2.2.5}/MANIFEST.in +0 -0
  16. {wavemind-2.2.4 → wavemind-2.2.5}/SECURITY.md +0 -0
  17. {wavemind-2.2.4 → wavemind-2.2.5}/SUPPORT.md +0 -0
  18. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/BENCHMARK_LEADERBOARD.md +0 -0
  19. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/BENCHMARK_REPORT.md +0 -0
  20. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/agent_memory_benchmark.py +0 -0
  21. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/agent_memory_results.json +0 -0
  22. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/ann_index_curve_benchmark.py +0 -0
  23. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/ann_index_curve_results.json +0 -0
  24. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/benchmark_matrix_results.json +0 -0
  25. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/benchmark_registry.py +0 -0
  26. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/dynamic_memory_benchmark.py +0 -0
  27. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/dynamic_memory_results.json +0 -0
  28. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/field_memory_dynamics_benchmark.py +0 -0
  29. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/field_memory_dynamics_results.json +0 -0
  30. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/locomo_evidence_results.json +0 -0
  31. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/locomo_memory_benchmark.py +0 -0
  32. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/locomo_sentence_evidence_results.json +0 -0
  33. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/long_memory_evidence_benchmark.py +0 -0
  34. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/long_memory_evidence_results.json +0 -0
  35. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/longmemeval_answer_benchmark.py +0 -0
  36. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/longmemeval_answer_extractive_20_results.json +0 -0
  37. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/longmemeval_answer_qwen25_0_5b_50_results.json +0 -0
  38. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/longmemeval_answer_qwen25_1_5b_50_results.json +0 -0
  39. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/longmemeval_evidence_50_results.json +0 -0
  40. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/longmemeval_evidence_results.json +0 -0
  41. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/longmemeval_memory_benchmark.py +0 -0
  42. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/memory_competitor_benchmark.py +0 -0
  43. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/memory_competitor_results.json +0 -0
  44. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/nomiracl_russian_benchmark.py +0 -0
  45. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/nomiracl_russian_results.json +0 -0
  46. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/open_retrieval_benchmark.py +0 -0
  47. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/open_retrieval_scifact_results.json +0 -0
  48. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/production_index_profile_results.json +0 -0
  49. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/production_load_benchmark.py +0 -0
  50. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/production_load_qdrant_100k_tuned_results.json +0 -0
  51. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/production_load_qdrant_1m_ef_sweep_results.json +0 -0
  52. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/production_load_qdrant_1m_results.json +0 -0
  53. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/production_load_qdrant_1m_tuned_results.json +0 -0
  54. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/production_load_results.json +0 -0
  55. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/render_benchmark_charts.py +0 -0
  56. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/render_benchmark_leaderboard.py +0 -0
  57. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/render_benchmark_report.py +0 -0
  58. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/ru_sentences_benchmark.py +0 -0
  59. {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/wavemind_capacity_results.json +0 -0
  60. {wavemind-2.2.4 → wavemind-2.2.5}/docs/BENCHMARK_BRIEF.md +0 -0
  61. {wavemind-2.2.4 → wavemind-2.2.5}/docs/CHROMA_MIGRATION.md +0 -0
  62. {wavemind-2.2.4 → wavemind-2.2.5}/docs/DEMO_SCRIPT.md +0 -0
  63. {wavemind-2.2.4 → wavemind-2.2.5}/docs/LAUNCH_KIT.md +0 -0
  64. {wavemind-2.2.4 → wavemind-2.2.5}/docs/OBSERVABILITY.md +0 -0
  65. {wavemind-2.2.4 → wavemind-2.2.5}/docs/PROJECT_BOARD.md +0 -0
  66. {wavemind-2.2.4 → wavemind-2.2.5}/docs/RELEASE.md +0 -0
  67. {wavemind-2.2.4 → wavemind-2.2.5}/docs/ROADMAP.md +0 -0
  68. {wavemind-2.2.4 → wavemind-2.2.5}/docs/RU_LAUNCH_POSTS.md +0 -0
  69. {wavemind-2.2.4 → wavemind-2.2.5}/docs/USE_CASES.md +0 -0
  70. {wavemind-2.2.4 → wavemind-2.2.5}/docs/assets/benchmark-summary.svg +0 -0
  71. {wavemind-2.2.4 → wavemind-2.2.5}/docs/assets/wavemind-demo.gif +0 -0
  72. {wavemind-2.2.4 → wavemind-2.2.5}/docs/assets/wavemind-social-card.svg +0 -0
  73. {wavemind-2.2.4 → wavemind-2.2.5}/examples/agent_with_memory.py +0 -0
  74. {wavemind-2.2.4 → wavemind-2.2.5}/examples/chroma_migration.py +0 -0
  75. {wavemind-2.2.4 → wavemind-2.2.5}/examples/customer_support_memory.py +0 -0
  76. {wavemind-2.2.4 → wavemind-2.2.5}/examples/demo.py +0 -0
  77. {wavemind-2.2.4 → wavemind-2.2.5}/examples/dynamic_memory_demo.py +0 -0
  78. {wavemind-2.2.4 → wavemind-2.2.5}/examples/framework_integrations.py +0 -0
  79. {wavemind-2.2.4 → wavemind-2.2.5}/examples/langchain_memory.py +0 -0
  80. {wavemind-2.2.4 → wavemind-2.2.5}/examples/llamaindex_retriever.py +0 -0
  81. {wavemind-2.2.4 → wavemind-2.2.5}/examples/observability/README.md +0 -0
  82. {wavemind-2.2.4 → wavemind-2.2.5}/examples/observability/docker-compose.yml +0 -0
  83. {wavemind-2.2.4 → wavemind-2.2.5}/examples/observability/otel-collector.yaml +0 -0
  84. {wavemind-2.2.4 → wavemind-2.2.5}/examples/observability/prometheus-alerts.yml +0 -0
  85. {wavemind-2.2.4 → wavemind-2.2.5}/examples/observability/prometheus.yml +0 -0
  86. {wavemind-2.2.4 → wavemind-2.2.5}/examples/production-index-profile/README.md +0 -0
  87. {wavemind-2.2.4 → wavemind-2.2.5}/examples/production-index-profile/docker-compose.yml +0 -0
  88. {wavemind-2.2.4 → wavemind-2.2.5}/examples/research_notebook_memory.py +0 -0
  89. {wavemind-2.2.4 → wavemind-2.2.5}/examples/sharded_memory.py +0 -0
  90. {wavemind-2.2.4 → wavemind-2.2.5}/install.bat +0 -0
  91. {wavemind-2.2.4 → wavemind-2.2.5}/install.sh +0 -0
  92. {wavemind-2.2.4 → wavemind-2.2.5}/requirements-optional.txt +0 -0
  93. {wavemind-2.2.4 → wavemind-2.2.5}/requirements.txt +0 -0
  94. {wavemind-2.2.4 → wavemind-2.2.5}/setup.cfg +0 -0
  95. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_agent_memory_benchmark.py +0 -0
  96. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_ann_index_curve_benchmark.py +0 -0
  97. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_api.py +0 -0
  98. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_api_process_persistence.py +0 -0
  99. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_benchmark_brief.py +0 -0
  100. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_benchmark_charts.py +0 -0
  101. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_benchmark_leaderboard.py +0 -0
  102. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_benchmark_registry.py +0 -0
  103. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_benchmark_report.py +0 -0
  104. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_chroma_migration_example.py +0 -0
  105. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_cli_smoke.py +0 -0
  106. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_cluster.py +0 -0
  107. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_core_persistence.py +0 -0
  108. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_dynamic_memory_benchmark.py +0 -0
  109. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_examples.py +0 -0
  110. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_field_graph.py +0 -0
  111. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_field_graph_integration.py +0 -0
  112. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_field_memory_dynamics_benchmark.py +0 -0
  113. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_framework_adapters.py +0 -0
  114. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_import_benchmark.py +0 -0
  115. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_indexes_encoders.py +0 -0
  116. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_jobs.py +0 -0
  117. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_langchain_integration.py +0 -0
  118. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_locomo_memory_benchmark.py +0 -0
  119. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_long_memory_evidence_benchmark.py +0 -0
  120. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_longmemeval_answer_benchmark.py +0 -0
  121. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_longmemeval_memory_benchmark.py +0 -0
  122. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_memory_competitor_benchmark.py +0 -0
  123. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_multimodal.py +0 -0
  124. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_nomiracl_russian_benchmark.py +0 -0
  125. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_observability.py +0 -0
  126. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_observability_docs.py +0 -0
  127. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_open_retrieval_benchmark.py +0 -0
  128. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_packaging_files.py +0 -0
  129. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_postgres_storage.py +0 -0
  130. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_production_index_profile.py +0 -0
  131. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_production_load_benchmark.py +0 -0
  132. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_scale_plan.py +0 -0
  133. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_semantic_and_latency.py +0 -0
  134. {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_sharding.py +0 -0
  135. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/__main__.py +0 -0
  136. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/api.py +0 -0
  137. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/benchmark.py +0 -0
  138. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/cli.py +0 -0
  139. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/cluster.py +0 -0
  140. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/core.py +0 -0
  141. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/encoders.py +0 -0
  142. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/field_graph.py +0 -0
  143. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/importers.py +0 -0
  144. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/indexes.py +0 -0
  145. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/integrations/__init__.py +0 -0
  146. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/integrations/autogen.py +0 -0
  147. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/integrations/crewai.py +0 -0
  148. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/integrations/langchain.py +0 -0
  149. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/integrations/langgraph.py +0 -0
  150. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/integrations/llamaindex.py +0 -0
  151. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/jobs.py +0 -0
  152. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/multimodal.py +0 -0
  153. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/observability.py +0 -0
  154. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/scale.py +0 -0
  155. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/sharding.py +0 -0
  156. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/storage.py +0 -0
  157. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/studio.py +0 -0
  158. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind.egg-info/SOURCES.txt +0 -0
  159. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind.egg-info/dependency_links.txt +0 -0
  160. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind.egg-info/entry_points.txt +0 -0
  161. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind.egg-info/requires.txt +0 -0
  162. {wavemind-2.2.4 → wavemind-2.2.5}/wavemind.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wavemind
3
- Version: 2.2.4
3
+ Version: 2.2.5
4
4
  Summary: Local-first dynamic memory field with vector search and wave-field re-ranking
5
5
  License-Expression: MIT
6
6
  Project-URL: Homepage, https://github.com/CaspianG/wavemind
@@ -543,8 +543,8 @@ Checked-in result:
543
543
  |---|---:|
544
544
  | Cluster planner | 4096 namespaces, 4 nodes, replication factor 2, node-loss availability `1.000`, zone-loss availability `1.000`, write quorum `2`. |
545
545
  | Hot cache | 2000 lookups, hit rate `0.920`, p99 lookup `0.01 ms`. |
546
- | Replicated runtime | 3 physical WaveMind stores, replication factor 3, write quorum 2, node-loss recall `true`, repair copied `1` missing record, p99 query-after-loss `1.16 ms`. |
547
- | Structured payloads | image/audio/table/event retrieval, precision@1 `1.000`, p99 `0.69 ms`. |
546
+ | Replicated runtime | 3 physical WaveMind stores, replication factor 3, write quorum 2, node-loss recall `true`, repair copied `1` missing record, tombstone repair deleted `1` stale record, p99 query-after-loss `1.44 ms`. |
547
+ | Structured payloads | image/audio/table/event retrieval, precision@1 `1.000`, p99 `0.75 ms`. |
548
548
 
549
549
  This profile validates routing, quorum-replicated runtime behavior, cache
550
550
  behavior, and structured payload handling. It is not a 10M-vector load test.
@@ -1041,7 +1041,7 @@ Current read:
1041
1041
  | LongMemEval 50-query smoke | On the first 50 non-abstention LongMemEval-S questions, WaveMind reaches `evidence_recall@5 0.920`, `precision@1 0.760`, and `MRR@5 0.827`; Chroma/Qdrant static reach `0.600`, `0.260`, and `0.385`. | This is the fast regression profile for checking current changes before rerunning the full LongMemEval profile. WaveMind wins on quality; latency still needs work. |
1042
1042
  | ANN/index curve | At 50000 generated 128-d vectors, NumPy exact keeps `recall@10 1.000` at `6.49 ms`; quantized int8 keeps `0.934` at `24.92 ms`; Annoy is faster at `4.92 ms` but drops to `0.730` recall; Qdrant local keeps `1.000` recall at `43.49 ms`. | Current local scale boundary is clear: quantized search needs kernel work, Annoy needs tuning/FAISS, and Qdrant should be tested in service mode for a fair production comparison. |
1043
1043
  | Production load | At 100000 generated 128-d vectors, service-mode Qdrant reaches `recall@10 1.000`, avg `10.28 ms`, p99 `21.26 ms`. At 1M, tuned Qdrant reaches `recall@10 0.984`, avg `116.80 ms`, p99 `209.28 ms`; an EF sweep finds `recall@10 0.977`, avg `64.76 ms`, p99 `103.77 ms` at `hnsw_ef=2048` on 30 queries. | 100k is production-grade on the tested machine. 1M recall is now strong, but p99 still needs tuning before claiming a stable sub-100 ms SLO. |
1044
- | Scale readiness | Deterministic 1M-memory simulation validates 4096 namespace placements over 4 nodes with replication factor 2, node-loss availability `1.000`, zone-loss availability `1.000`, hot-cache hit rate `0.920`, quorum-replicated runtime recall after node loss, replica repair, and structured payload precision@1 `1.000`. | This proves routing, cache, payload, and replicated-runtime foundations. It is not a 10M-vector latency claim; real 10M latency still needs service-backed load tests on larger hardware. |
1044
+ | Scale readiness | Deterministic 1M-memory simulation validates 4096 namespace placements over 4 nodes with replication factor 2, node-loss availability `1.000`, zone-loss availability `1.000`, hot-cache hit rate `0.920`, quorum-replicated runtime recall after node loss, missing-record repair, tombstone repair, and structured payload precision@1 `1.000`. | This proves routing, cache, payload, and replicated-runtime foundations. It is not a 10M-vector latency claim; real 10M latency still needs service-backed load tests on larger hardware. |
1045
1045
  | Memory competitor adapters | WaveMind reaches `precision@1 0.80`, `precision@3 1.00`, stale suppression `1.00` on the small adapter profile. Mem0, Zep, and LangGraph are listed as skipped unless their real packages/services are configured. | This prevents fake competitor claims. The adapter harness is ready; real Mem0/Zep/LangGraph results still need configured installs. |
1046
1046
  | LongMemEval local answer generation | With the same local Ollama `qwen2.5:1.5b`, WaveMind reaches `exact_match 0.240`, `contains_answer 0.380`, `token_f1 0.333`, and `evidence_recall@5 0.920`; Chroma and Qdrant static both reach `0.120`, `0.160`, `0.170`, and `0.600`. | This is the first checked-in end-to-end answer benchmark against Chroma/Qdrant. It is still a 50-question lightweight smoke run, not a full LongMemEval leaderboard score. |
1047
1047
 
@@ -1213,11 +1213,12 @@ print(memory.query("support replies", namespace="tenant:a", top_k=3))
1213
1213
  memory.close()
1214
1214
  ```
1215
1215
 
1216
- The runtime uses separate durable stores per node, quorum writes, quorum reads,
1217
- merged replica results, and `repair_namespace()` for recovered replicas. It is
1218
- the production foundation for namespace-level HA; for full consensus across
1219
- independent network services, deploy WaveMind with Postgres/Qdrant/ops-layer
1220
- replication.
1216
+ The runtime uses separate durable stores per node, stable replica keys, operation
1217
+ metadata, quorum writes, quorum reads, merged replica results, tombstone-aware
1218
+ delete propagation, and `repair_namespace()` for recovered replicas. It is the
1219
+ production foundation for namespace-level HA and eventual-consistency behavior;
1220
+ for full consensus across independent network services, deploy WaveMind with
1221
+ Postgres/Qdrant/ops-layer replication.
1221
1222
 
1222
1223
  Checked-in official LoCoMo retrieval result:
1223
1224
 
@@ -490,8 +490,8 @@ Checked-in result:
490
490
  |---|---:|
491
491
  | Cluster planner | 4096 namespaces, 4 nodes, replication factor 2, node-loss availability `1.000`, zone-loss availability `1.000`, write quorum `2`. |
492
492
  | Hot cache | 2000 lookups, hit rate `0.920`, p99 lookup `0.01 ms`. |
493
- | Replicated runtime | 3 physical WaveMind stores, replication factor 3, write quorum 2, node-loss recall `true`, repair copied `1` missing record, p99 query-after-loss `1.16 ms`. |
494
- | Structured payloads | image/audio/table/event retrieval, precision@1 `1.000`, p99 `0.69 ms`. |
493
+ | Replicated runtime | 3 physical WaveMind stores, replication factor 3, write quorum 2, node-loss recall `true`, repair copied `1` missing record, tombstone repair deleted `1` stale record, p99 query-after-loss `1.44 ms`. |
494
+ | Structured payloads | image/audio/table/event retrieval, precision@1 `1.000`, p99 `0.75 ms`. |
495
495
 
496
496
  This profile validates routing, quorum-replicated runtime behavior, cache
497
497
  behavior, and structured payload handling. It is not a 10M-vector load test.
@@ -988,7 +988,7 @@ Current read:
988
988
  | LongMemEval 50-query smoke | On the first 50 non-abstention LongMemEval-S questions, WaveMind reaches `evidence_recall@5 0.920`, `precision@1 0.760`, and `MRR@5 0.827`; Chroma/Qdrant static reach `0.600`, `0.260`, and `0.385`. | This is the fast regression profile for checking current changes before rerunning the full LongMemEval profile. WaveMind wins on quality; latency still needs work. |
989
989
  | ANN/index curve | At 50000 generated 128-d vectors, NumPy exact keeps `recall@10 1.000` at `6.49 ms`; quantized int8 keeps `0.934` at `24.92 ms`; Annoy is faster at `4.92 ms` but drops to `0.730` recall; Qdrant local keeps `1.000` recall at `43.49 ms`. | Current local scale boundary is clear: quantized search needs kernel work, Annoy needs tuning/FAISS, and Qdrant should be tested in service mode for a fair production comparison. |
990
990
  | Production load | At 100000 generated 128-d vectors, service-mode Qdrant reaches `recall@10 1.000`, avg `10.28 ms`, p99 `21.26 ms`. At 1M, tuned Qdrant reaches `recall@10 0.984`, avg `116.80 ms`, p99 `209.28 ms`; an EF sweep finds `recall@10 0.977`, avg `64.76 ms`, p99 `103.77 ms` at `hnsw_ef=2048` on 30 queries. | 100k is production-grade on the tested machine. 1M recall is now strong, but p99 still needs tuning before claiming a stable sub-100 ms SLO. |
991
- | Scale readiness | Deterministic 1M-memory simulation validates 4096 namespace placements over 4 nodes with replication factor 2, node-loss availability `1.000`, zone-loss availability `1.000`, hot-cache hit rate `0.920`, quorum-replicated runtime recall after node loss, replica repair, and structured payload precision@1 `1.000`. | This proves routing, cache, payload, and replicated-runtime foundations. It is not a 10M-vector latency claim; real 10M latency still needs service-backed load tests on larger hardware. |
991
+ | Scale readiness | Deterministic 1M-memory simulation validates 4096 namespace placements over 4 nodes with replication factor 2, node-loss availability `1.000`, zone-loss availability `1.000`, hot-cache hit rate `0.920`, quorum-replicated runtime recall after node loss, missing-record repair, tombstone repair, and structured payload precision@1 `1.000`. | This proves routing, cache, payload, and replicated-runtime foundations. It is not a 10M-vector latency claim; real 10M latency still needs service-backed load tests on larger hardware. |
992
992
  | Memory competitor adapters | WaveMind reaches `precision@1 0.80`, `precision@3 1.00`, stale suppression `1.00` on the small adapter profile. Mem0, Zep, and LangGraph are listed as skipped unless their real packages/services are configured. | This prevents fake competitor claims. The adapter harness is ready; real Mem0/Zep/LangGraph results still need configured installs. |
993
993
  | LongMemEval local answer generation | With the same local Ollama `qwen2.5:1.5b`, WaveMind reaches `exact_match 0.240`, `contains_answer 0.380`, `token_f1 0.333`, and `evidence_recall@5 0.920`; Chroma and Qdrant static both reach `0.120`, `0.160`, `0.170`, and `0.600`. | This is the first checked-in end-to-end answer benchmark against Chroma/Qdrant. It is still a 50-question lightweight smoke run, not a full LongMemEval leaderboard score. |
994
994
 
@@ -1160,11 +1160,12 @@ print(memory.query("support replies", namespace="tenant:a", top_k=3))
1160
1160
  memory.close()
1161
1161
  ```
1162
1162
 
1163
- The runtime uses separate durable stores per node, quorum writes, quorum reads,
1164
- merged replica results, and `repair_namespace()` for recovered replicas. It is
1165
- the production foundation for namespace-level HA; for full consensus across
1166
- independent network services, deploy WaveMind with Postgres/Qdrant/ops-layer
1167
- replication.
1163
+ The runtime uses separate durable stores per node, stable replica keys, operation
1164
+ metadata, quorum writes, quorum reads, merged replica results, tombstone-aware
1165
+ delete propagation, and `repair_namespace()` for recovered replicas. It is the
1166
+ production foundation for namespace-level HA and eventual-consistency behavior;
1167
+ for full consensus across independent network services, deploy WaveMind with
1168
+ Postgres/Qdrant/ops-layer replication.
1168
1169
 
1169
1170
  Checked-in official LoCoMo retrieval result:
1170
1171
 
@@ -184,6 +184,41 @@ def run_replication_runtime_profile() -> dict[str, object]:
184
184
  finally:
185
185
  partial.close()
186
186
 
187
+ tombstone = ReplicatedWaveMind(
188
+ root_path=Path(directory) / "tombstone",
189
+ nodes=[
190
+ {"id": "node-a", "address": "127.0.0.1:8101", "zone": "zone-a"},
191
+ {"id": "node-b", "address": "127.0.0.1:8102", "zone": "zone-b"},
192
+ {"id": "node-c", "address": "127.0.0.1:8103", "zone": "zone-c"},
193
+ ],
194
+ replication_factor=3,
195
+ width=16,
196
+ height=16,
197
+ layers=1,
198
+ encoder=HashingTextEncoder(vector_dim=64),
199
+ )
200
+ try:
201
+ tombstone_placement = tombstone.placement(namespace)
202
+ missed_delete = tombstone_placement.replicas[-1]
203
+ tombstone.remember("repair must not resurrect deleted memory", namespace=namespace)
204
+ tombstone.set_node_available(missed_delete, False)
205
+ tombstone.forget(
206
+ text="repair must not resurrect deleted memory",
207
+ namespace=namespace,
208
+ )
209
+ tombstone.set_node_available(missed_delete, True)
210
+ suppressed_before_repair = (
211
+ tombstone.query("resurrect deleted memory", namespace=namespace, top_k=1)
212
+ == []
213
+ )
214
+ tombstone_repair = tombstone.repair_namespace(namespace)
215
+ suppressed_after_repair = (
216
+ tombstone.query("resurrect deleted memory", namespace=namespace, top_k=1)
217
+ == []
218
+ )
219
+ finally:
220
+ tombstone.close()
221
+
187
222
  return {
188
223
  "engine": "WaveMind replicated runtime",
189
224
  "nodes": 3,
@@ -193,6 +228,9 @@ def run_replication_runtime_profile() -> dict[str, object]:
193
228
  "writes": len(write.writes),
194
229
  "recalled_after_node_loss": recalled_after_loss,
195
230
  "repair_copied_records": repair.copied_records,
231
+ "tombstone_suppressed_before_repair": suppressed_before_repair,
232
+ "tombstone_suppressed_after_repair": suppressed_after_repair,
233
+ "tombstone_repair_deleted_records": tombstone_repair.deleted_records,
196
234
  "avg_query_after_loss_ms": statistics.mean(latencies),
197
235
  "p99_query_after_loss_ms": percentile(latencies, 99),
198
236
  }
@@ -340,6 +378,7 @@ def main() -> int:
340
378
  elif result["engine"] == "WaveMind replicated runtime":
341
379
  print(f"| replicated runtime | recalled_after_node_loss | {result['recalled_after_node_loss']} |")
342
380
  print(f"| replicated runtime | repair_copied_records | {result['repair_copied_records']} |")
381
+ print(f"| replicated runtime | tombstone_repair_deleted_records | {result['tombstone_repair_deleted_records']} |")
343
382
  else:
344
383
  print(f"| structured payloads | precision@1 | {result['precision_at_1']:.3f} |")
345
384
  print(f"\nWrote {args.output}")
@@ -14,7 +14,7 @@
14
14
  "namespaces": 4096,
15
15
  "nodes": 4,
16
16
  "replication_factor": 2,
17
- "placement_ms": 62.52979999408126,
17
+ "placement_ms": 58.688499964773655,
18
18
  "max_replica_load": 2413,
19
19
  "min_replica_load": 1728,
20
20
  "replica_load_stdev": 316.54462560593254,
@@ -32,8 +32,8 @@
32
32
  "capacity": 512,
33
33
  "hit_rate": 0.92,
34
34
  "evictions": 0,
35
- "avg_lookup_ms": 0.0015143999480642378,
36
- "p99_lookup_ms": 0.006199989002197981
35
+ "avg_lookup_ms": 0.0016512502334080637,
36
+ "p99_lookup_ms": 0.005799985956400633
37
37
  },
38
38
  {
39
39
  "engine": "WaveMind replicated runtime",
@@ -44,8 +44,11 @@
44
44
  "writes": 3,
45
45
  "recalled_after_node_loss": true,
46
46
  "repair_copied_records": 1,
47
- "avg_query_after_loss_ms": 0.9093000553548336,
48
- "p99_query_after_loss_ms": 0.9093000553548336
47
+ "tombstone_suppressed_before_repair": true,
48
+ "tombstone_suppressed_after_repair": true,
49
+ "tombstone_repair_deleted_records": 1,
50
+ "avg_query_after_loss_ms": 1.435799989849329,
51
+ "p99_query_after_loss_ms": 1.435799989849329
49
52
  },
50
53
  {
51
54
  "engine": "WaveMind structured payloads",
@@ -57,8 +60,8 @@
57
60
  ],
58
61
  "queries": 4,
59
62
  "precision_at_1": 1.0,
60
- "avg_latency_ms": 0.5151500226929784,
61
- "p99_latency_ms": 0.7709000492468476
63
+ "avg_latency_ms": 0.47089999134186655,
64
+ "p99_latency_ms": 0.7531000301241875
62
65
  }
63
66
  ]
64
67
  }
@@ -4,7 +4,7 @@ services:
4
4
  context: .
5
5
  args:
6
6
  INSTALL_OPTIONAL: "false"
7
- image: wavemind:2.2.4
7
+ image: wavemind:2.2.5
8
8
  restart: unless-stopped
9
9
  environment:
10
10
  WAVEMIND_DB: /data/wavemind.sqlite3
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "wavemind"
7
- version = "2.2.4"
7
+ version = "2.2.5"
8
8
  description = "Local-first dynamic memory field with vector search and wave-field re-ranking"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -97,6 +97,98 @@ def test_replicated_wavemind_repairs_recovered_replica(tmp_path):
97
97
  memory.close()
98
98
 
99
99
 
100
+ def test_replicated_wavemind_repair_does_not_resurrect_forgotten_memory(tmp_path):
101
+ memory = _cluster(tmp_path, replication_factor=3)
102
+ try:
103
+ namespace = "tenant:tombstone"
104
+ memory.remember("stale replicated memory should stay deleted", namespace=namespace)
105
+ placement = memory.placement(namespace)
106
+ missed_delete = placement.replicas[-1]
107
+
108
+ memory.set_node_available(missed_delete, False)
109
+ delete = memory.forget(
110
+ text="stale replicated memory should stay deleted",
111
+ namespace=namespace,
112
+ )
113
+ assert delete.ok
114
+
115
+ memory.set_node_available(missed_delete, True)
116
+ assert memory._mind(missed_delete).store.count(namespace=namespace) == 1
117
+ assert memory.query("stale replicated memory", namespace=namespace, top_k=1) == []
118
+
119
+ report = memory.repair_namespace(namespace)
120
+
121
+ assert report.deleted_records == 1
122
+ assert report.copied_records == 0
123
+ assert report.tombstone_keys == 1
124
+ assert memory._mind(missed_delete).store.count(namespace=namespace) == 0
125
+ assert memory.query("stale replicated memory", namespace=namespace, top_k=1) == []
126
+ finally:
127
+ memory.close()
128
+
129
+
130
+ def test_replicated_wavemind_forget_by_id_deletes_replicas_with_different_local_ids(tmp_path):
131
+ memory = _cluster(tmp_path, replication_factor=3, write_quorum=2)
132
+ try:
133
+ namespace = "tenant:id-delete"
134
+ placement = memory.placement(namespace)
135
+ lagging = next(node_id for node_id in placement.replicas if node_id != placement.primary)
136
+ memory.set_node_available(lagging, False)
137
+ memory.remember("advance ids on two replicas", namespace=namespace)
138
+ memory.set_node_available(lagging, True)
139
+
140
+ write = memory.remember("delete this record by primary id", namespace=namespace)
141
+ lagging_ids = [
142
+ record.id
143
+ for record in memory._mind(lagging).store.list(namespace=namespace)
144
+ if record.text == "delete this record by primary id"
145
+ ]
146
+
147
+ assert write.primary_id is not None
148
+ assert lagging_ids == [1]
149
+ assert write.primary_id != lagging_ids[0]
150
+
151
+ delete = memory.forget(id=write.primary_id, namespace=namespace)
152
+
153
+ assert delete.ok
154
+ assert all(
155
+ result.text != "delete this record by primary id"
156
+ for result in memory.query("delete this record by primary id", namespace=namespace, top_k=3)
157
+ )
158
+ for node_id in placement.replicas:
159
+ texts = [
160
+ record.text
161
+ for record in memory._mind(node_id).store.list(namespace=namespace)
162
+ ]
163
+ assert "delete this record by primary id" not in texts
164
+ finally:
165
+ memory.close()
166
+
167
+
168
+ def test_replicated_wavemind_stores_stable_replica_metadata(tmp_path):
169
+ memory = _cluster(tmp_path, replication_factor=3)
170
+ try:
171
+ namespace = "tenant:metadata"
172
+ write = memory.remember(
173
+ "metadata replicated memory",
174
+ namespace=namespace,
175
+ tags=["profile"],
176
+ metadata={"source": "test"},
177
+ )
178
+ keys = set()
179
+ operation_ids = set()
180
+ for node_id in write.writes:
181
+ records = memory._mind(node_id).store.list(namespace=namespace)
182
+ assert len(records) == 1
183
+ keys.add(records[0].metadata["_wavemind_replica_key"])
184
+ operation_ids.add(records[0].metadata["_wavemind_operation_id"])
185
+
186
+ assert len(keys) == 1
187
+ assert len(operation_ids) == 1
188
+ finally:
189
+ memory.close()
190
+
191
+
100
192
  def test_replicated_wavemind_rejects_global_db_path(tmp_path):
101
193
  with pytest.raises(ValueError, match="db_path"):
102
194
  ReplicatedWaveMind(
@@ -18,5 +18,8 @@ def test_scale_readiness_benchmark_covers_cluster_cache_and_payloads():
18
18
  assert results["WaveMind hot cache"]["hit_rate"] > 0.0
19
19
  assert results["WaveMind replicated runtime"]["recalled_after_node_loss"] is True
20
20
  assert results["WaveMind replicated runtime"]["repair_copied_records"] == 1
21
+ assert results["WaveMind replicated runtime"]["tombstone_suppressed_before_repair"] is True
22
+ assert results["WaveMind replicated runtime"]["tombstone_suppressed_after_repair"] is True
23
+ assert results["WaveMind replicated runtime"]["tombstone_repair_deleted_records"] == 1
21
24
  assert results["WaveMind structured payloads"]["precision_at_1"] == 1.0
22
25
  assert payload["scenario"]["simulated_memories"] == 100_000
@@ -36,7 +36,7 @@ from .storage import (
36
36
  create_memory_store,
37
37
  )
38
38
 
39
- __version__ = "2.2.4"
39
+ __version__ = "2.2.5"
40
40
 
41
41
  __all__ = [
42
42
  "FieldProjector",