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.
- {wavemind-2.2.4 → wavemind-2.2.5}/PKG-INFO +10 -9
- {wavemind-2.2.4 → wavemind-2.2.5}/README.md +9 -8
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/scale_readiness_benchmark.py +39 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/scale_readiness_results.json +10 -7
- {wavemind-2.2.4 → wavemind-2.2.5}/docker-compose.yml +1 -1
- {wavemind-2.2.4 → wavemind-2.2.5}/pyproject.toml +1 -1
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_replication.py +92 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_scale_readiness_benchmark.py +3 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/__init__.py +1 -1
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/replication.py +247 -19
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind.egg-info/PKG-INFO +10 -9
- {wavemind-2.2.4 → wavemind-2.2.5}/CONTRIBUTING.md +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/Dockerfile +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/LICENSE +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/MANIFEST.in +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/SECURITY.md +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/SUPPORT.md +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/BENCHMARK_LEADERBOARD.md +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/BENCHMARK_REPORT.md +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/agent_memory_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/agent_memory_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/ann_index_curve_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/ann_index_curve_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/benchmark_matrix_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/benchmark_registry.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/dynamic_memory_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/dynamic_memory_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/field_memory_dynamics_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/field_memory_dynamics_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/locomo_evidence_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/locomo_memory_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/locomo_sentence_evidence_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/long_memory_evidence_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/long_memory_evidence_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/longmemeval_answer_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/longmemeval_answer_extractive_20_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/longmemeval_answer_qwen25_0_5b_50_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/longmemeval_answer_qwen25_1_5b_50_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/longmemeval_evidence_50_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/longmemeval_evidence_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/longmemeval_memory_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/memory_competitor_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/memory_competitor_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/nomiracl_russian_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/nomiracl_russian_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/open_retrieval_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/open_retrieval_scifact_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/production_index_profile_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/production_load_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/production_load_qdrant_100k_tuned_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/production_load_qdrant_1m_ef_sweep_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/production_load_qdrant_1m_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/production_load_qdrant_1m_tuned_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/production_load_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/render_benchmark_charts.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/render_benchmark_leaderboard.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/render_benchmark_report.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/ru_sentences_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/benchmarks/wavemind_capacity_results.json +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/docs/BENCHMARK_BRIEF.md +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/docs/CHROMA_MIGRATION.md +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/docs/DEMO_SCRIPT.md +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/docs/LAUNCH_KIT.md +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/docs/OBSERVABILITY.md +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/docs/PROJECT_BOARD.md +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/docs/RELEASE.md +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/docs/ROADMAP.md +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/docs/RU_LAUNCH_POSTS.md +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/docs/USE_CASES.md +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/docs/assets/benchmark-summary.svg +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/docs/assets/wavemind-demo.gif +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/docs/assets/wavemind-social-card.svg +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/examples/agent_with_memory.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/examples/chroma_migration.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/examples/customer_support_memory.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/examples/demo.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/examples/dynamic_memory_demo.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/examples/framework_integrations.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/examples/langchain_memory.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/examples/llamaindex_retriever.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/examples/observability/README.md +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/examples/observability/docker-compose.yml +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/examples/observability/otel-collector.yaml +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/examples/observability/prometheus-alerts.yml +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/examples/observability/prometheus.yml +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/examples/production-index-profile/README.md +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/examples/production-index-profile/docker-compose.yml +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/examples/research_notebook_memory.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/examples/sharded_memory.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/install.bat +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/install.sh +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/requirements-optional.txt +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/requirements.txt +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/setup.cfg +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_agent_memory_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_ann_index_curve_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_api.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_api_process_persistence.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_benchmark_brief.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_benchmark_charts.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_benchmark_leaderboard.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_benchmark_registry.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_benchmark_report.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_chroma_migration_example.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_cli_smoke.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_cluster.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_core_persistence.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_dynamic_memory_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_examples.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_field_graph.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_field_graph_integration.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_field_memory_dynamics_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_framework_adapters.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_import_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_indexes_encoders.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_jobs.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_langchain_integration.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_locomo_memory_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_long_memory_evidence_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_longmemeval_answer_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_longmemeval_memory_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_memory_competitor_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_multimodal.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_nomiracl_russian_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_observability.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_observability_docs.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_open_retrieval_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_packaging_files.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_postgres_storage.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_production_index_profile.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_production_load_benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_scale_plan.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_semantic_and_latency.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/tests/test_sharding.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/__main__.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/api.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/benchmark.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/cli.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/cluster.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/core.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/encoders.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/field_graph.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/importers.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/indexes.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/integrations/__init__.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/integrations/autogen.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/integrations/crewai.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/integrations/langchain.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/integrations/langgraph.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/integrations/llamaindex.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/jobs.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/multimodal.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/observability.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/scale.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/sharding.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/storage.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind/studio.py +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind.egg-info/SOURCES.txt +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind.egg-info/dependency_links.txt +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind.egg-info/entry_points.txt +0 -0
- {wavemind-2.2.4 → wavemind-2.2.5}/wavemind.egg-info/requires.txt +0 -0
- {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.
|
|
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.
|
|
547
|
-
| Structured payloads | image/audio/table/event retrieval, precision@1 `1.000`, p99 `0.
|
|
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,
|
|
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,
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
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.
|
|
494
|
-
| Structured payloads | image/audio/table/event retrieval, precision@1 `1.000`, p99 `0.
|
|
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,
|
|
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,
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
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":
|
|
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.
|
|
36
|
-
"p99_lookup_ms": 0.
|
|
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
|
-
"
|
|
48
|
-
"
|
|
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.
|
|
61
|
-
"p99_latency_ms": 0.
|
|
63
|
+
"avg_latency_ms": 0.47089999134186655,
|
|
64
|
+
"p99_latency_ms": 0.7531000301241875
|
|
62
65
|
}
|
|
63
66
|
]
|
|
64
67
|
}
|
|
@@ -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
|