wavemind 2.2.5__tar.gz → 2.2.6__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.5 → wavemind-2.2.6}/PKG-INFO +24 -8
- {wavemind-2.2.5 → wavemind-2.2.6}/README.md +23 -7
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/scale_readiness_benchmark.py +79 -2
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/scale_readiness_results.json +19 -8
- {wavemind-2.2.5 → wavemind-2.2.6}/docker-compose.yml +1 -1
- {wavemind-2.2.5 → wavemind-2.2.6}/pyproject.toml +1 -1
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_replication.py +88 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_scale_readiness_benchmark.py +4 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/__init__.py +3 -1
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/replication.py +230 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind.egg-info/PKG-INFO +24 -8
- {wavemind-2.2.5 → wavemind-2.2.6}/CONTRIBUTING.md +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/Dockerfile +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/LICENSE +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/MANIFEST.in +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/SECURITY.md +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/SUPPORT.md +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/BENCHMARK_LEADERBOARD.md +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/BENCHMARK_REPORT.md +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/agent_memory_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/agent_memory_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/ann_index_curve_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/ann_index_curve_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/benchmark_matrix_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/benchmark_registry.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/dynamic_memory_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/dynamic_memory_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/field_memory_dynamics_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/field_memory_dynamics_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/locomo_evidence_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/locomo_memory_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/locomo_sentence_evidence_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/long_memory_evidence_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/long_memory_evidence_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/longmemeval_answer_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/longmemeval_answer_extractive_20_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/longmemeval_answer_qwen25_0_5b_50_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/longmemeval_answer_qwen25_1_5b_50_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/longmemeval_evidence_50_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/longmemeval_evidence_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/longmemeval_memory_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/memory_competitor_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/memory_competitor_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/nomiracl_russian_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/nomiracl_russian_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/open_retrieval_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/open_retrieval_scifact_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/production_index_profile_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/production_load_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/production_load_qdrant_100k_tuned_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/production_load_qdrant_1m_ef_sweep_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/production_load_qdrant_1m_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/production_load_qdrant_1m_tuned_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/production_load_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/render_benchmark_charts.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/render_benchmark_leaderboard.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/render_benchmark_report.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/ru_sentences_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/benchmarks/wavemind_capacity_results.json +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/docs/BENCHMARK_BRIEF.md +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/docs/CHROMA_MIGRATION.md +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/docs/DEMO_SCRIPT.md +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/docs/LAUNCH_KIT.md +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/docs/OBSERVABILITY.md +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/docs/PROJECT_BOARD.md +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/docs/RELEASE.md +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/docs/ROADMAP.md +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/docs/RU_LAUNCH_POSTS.md +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/docs/USE_CASES.md +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/docs/assets/benchmark-summary.svg +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/docs/assets/wavemind-demo.gif +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/docs/assets/wavemind-social-card.svg +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/examples/agent_with_memory.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/examples/chroma_migration.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/examples/customer_support_memory.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/examples/demo.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/examples/dynamic_memory_demo.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/examples/framework_integrations.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/examples/langchain_memory.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/examples/llamaindex_retriever.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/examples/observability/README.md +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/examples/observability/docker-compose.yml +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/examples/observability/otel-collector.yaml +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/examples/observability/prometheus-alerts.yml +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/examples/observability/prometheus.yml +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/examples/production-index-profile/README.md +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/examples/production-index-profile/docker-compose.yml +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/examples/research_notebook_memory.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/examples/sharded_memory.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/install.bat +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/install.sh +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/requirements-optional.txt +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/requirements.txt +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/setup.cfg +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_agent_memory_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_ann_index_curve_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_api.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_api_process_persistence.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_benchmark_brief.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_benchmark_charts.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_benchmark_leaderboard.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_benchmark_registry.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_benchmark_report.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_chroma_migration_example.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_cli_smoke.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_cluster.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_core_persistence.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_dynamic_memory_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_examples.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_field_graph.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_field_graph_integration.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_field_memory_dynamics_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_framework_adapters.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_import_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_indexes_encoders.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_jobs.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_langchain_integration.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_locomo_memory_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_long_memory_evidence_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_longmemeval_answer_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_longmemeval_memory_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_memory_competitor_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_multimodal.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_nomiracl_russian_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_observability.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_observability_docs.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_open_retrieval_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_packaging_files.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_postgres_storage.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_production_index_profile.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_production_load_benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_scale_plan.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_semantic_and_latency.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/tests/test_sharding.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/__main__.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/api.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/benchmark.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/cli.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/cluster.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/core.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/encoders.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/field_graph.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/importers.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/indexes.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/integrations/__init__.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/integrations/autogen.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/integrations/crewai.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/integrations/langchain.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/integrations/langgraph.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/integrations/llamaindex.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/jobs.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/multimodal.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/observability.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/scale.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/sharding.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/storage.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind/studio.py +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind.egg-info/SOURCES.txt +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind.egg-info/dependency_links.txt +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind.egg-info/entry_points.txt +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/wavemind.egg-info/requires.txt +0 -0
- {wavemind-2.2.5 → wavemind-2.2.6}/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.6
|
|
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,13 +543,15 @@ 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, tombstone repair deleted `1` stale record, p99 query-after-loss `1.
|
|
547
|
-
|
|
|
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.33 ms`. |
|
|
547
|
+
| Active-active delta sync | 2 regions, bidirectional convergence `true`, stale import suppressed after delete `true`, tombstone convergence `true`, sync `120.19 ms`. |
|
|
548
|
+
| Structured payloads | image/audio/table/event retrieval, precision@1 `1.000`, p99 `0.62 ms`. |
|
|
548
549
|
|
|
549
550
|
This profile validates routing, quorum-replicated runtime behavior, cache
|
|
550
|
-
behavior, and structured payload handling.
|
|
551
|
-
Real 100k, 1M, and 10M latency claims should
|
|
552
|
-
FAISS/Qdrant/pgvector load tests on production-like
|
|
551
|
+
behavior, active-active namespace delta sync, and structured payload handling.
|
|
552
|
+
It is not a 10M-vector load test. Real 100k, 1M, and 10M latency claims should
|
|
553
|
+
come from service-backed FAISS/Qdrant/pgvector load tests on production-like
|
|
554
|
+
hardware.
|
|
553
555
|
|
|
554
556
|
Cluster placement planning:
|
|
555
557
|
|
|
@@ -1041,7 +1043,7 @@ Current read:
|
|
|
1041
1043
|
| 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
1044
|
| 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
1045
|
| 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, missing-record repair, tombstone repair, and structured payload precision@1 `1.000`. | This proves routing, cache, payload,
|
|
1046
|
+
| 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, active-active delta sync, and structured payload precision@1 `1.000`. | This proves routing, cache, payload, replicated-runtime, and namespace-delta foundations. It is not a 10M-vector latency claim; real 10M latency still needs service-backed load tests on larger hardware. |
|
|
1045
1047
|
| 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
1048
|
| 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
1049
|
|
|
@@ -1060,7 +1062,7 @@ Current read:
|
|
|
1060
1062
|
| Production index profile | Docker-backed 50000-vector profile for persisted FAISS, Qdrant service, and PostgreSQL/pgvector HNSW. | implemented | FAISS / Qdrant service / pgvector | Keep service-mode candidate generation above `0.95` recall@10 and below 10 ms average query latency at 50000 vectors. |
|
|
1061
1063
|
| Production load profile | 100k and 1M service-backed candidate-index checks with p95/p99 latency. | implemented | Qdrant service / pgvector HNSW / FAISS persisted | Keep 100k at recall@10 `1.000`; push 1M p99 below 100 ms with recall@10 >= 0.95. |
|
|
1062
1064
|
| Qdrant 1M HNSW ef sweep | One 1M Qdrant collection queried with multiple `hnsw_ef` values. | implemented | Qdrant service | Repeat with 100+ queries and collection-level HNSW build parameters before claiming a stable 1M SLO. |
|
|
1063
|
-
| Scale readiness profile | Cluster placement, node/zone-loss simulation, quorum report, replicated runtime, hot-cache behavior, and structured/multimodal payload retrieval. | implemented | Mem0 / Zep / LangGraph persistent memory / GraphRAG target adapters | Keep quorum replication and repair green while adding larger service-backed 10M load tests. |
|
|
1065
|
+
| Scale readiness profile | Cluster placement, node/zone-loss simulation, quorum report, replicated runtime, active-active delta sync, hot-cache behavior, and structured/multimodal payload retrieval. | implemented | Mem0 / Zep / LangGraph persistent memory / GraphRAG target adapters | Keep quorum replication, namespace-delta sync, and repair green while adding larger service-backed 10M load tests. |
|
|
1064
1066
|
| Memory competitor adapter profile | Dynamic-memory scenario wired for external memory frameworks. | implemented | Mem0 / Zep / LangGraph persistent memory | Report real competitor results only when their packages/services are explicitly configured. |
|
|
1065
1067
|
| [BEIR](https://github.com/beir-cellar/beir) | Standard zero-shot information retrieval quality. | planned | Chroma / Qdrant / FAISS | Stay within 0.02 `nDCG@10` on identical embeddings. |
|
|
1066
1068
|
| [MTEB Retrieval](https://github.com/embeddings-benchmark/mteb) | Separates encoder quality from retrieval-store quality. | planned | Chroma / Qdrant / FAISS | Prove WaveMind does not reduce same-embedding retrieval quality. |
|
|
@@ -1220,6 +1222,20 @@ production foundation for namespace-level HA and eventual-consistency behavior;
|
|
|
1220
1222
|
for full consensus across independent network services, deploy WaveMind with
|
|
1221
1223
|
Postgres/Qdrant/ops-layer replication.
|
|
1222
1224
|
|
|
1225
|
+
For multi-region active-active experiments, export and import namespace deltas:
|
|
1226
|
+
|
|
1227
|
+
```python
|
|
1228
|
+
region_a.remember("Tenant A billing preference.", namespace="tenant:a")
|
|
1229
|
+
delta = region_a.export_namespace_delta("tenant:a")
|
|
1230
|
+
region_b.import_namespace_delta(delta)
|
|
1231
|
+
|
|
1232
|
+
region_a.forget(text="Tenant A billing preference.", namespace="tenant:a")
|
|
1233
|
+
region_b.import_namespace_delta(region_a.export_namespace_delta("tenant:a"))
|
|
1234
|
+
```
|
|
1235
|
+
|
|
1236
|
+
The delta contains active records plus tombstones. Import is idempotent and
|
|
1237
|
+
tombstone-aware, so a stale region export cannot resurrect a deleted memory.
|
|
1238
|
+
|
|
1223
1239
|
Checked-in official LoCoMo retrieval result:
|
|
1224
1240
|
|
|
1225
1241
|
10 conversations, 5882 memory turns, 1977 evidence-labeled questions,
|
|
@@ -490,13 +490,15 @@ 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, tombstone repair deleted `1` stale record, p99 query-after-loss `1.
|
|
494
|
-
|
|
|
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.33 ms`. |
|
|
494
|
+
| Active-active delta sync | 2 regions, bidirectional convergence `true`, stale import suppressed after delete `true`, tombstone convergence `true`, sync `120.19 ms`. |
|
|
495
|
+
| Structured payloads | image/audio/table/event retrieval, precision@1 `1.000`, p99 `0.62 ms`. |
|
|
495
496
|
|
|
496
497
|
This profile validates routing, quorum-replicated runtime behavior, cache
|
|
497
|
-
behavior, and structured payload handling.
|
|
498
|
-
Real 100k, 1M, and 10M latency claims should
|
|
499
|
-
FAISS/Qdrant/pgvector load tests on production-like
|
|
498
|
+
behavior, active-active namespace delta sync, and structured payload handling.
|
|
499
|
+
It is not a 10M-vector load test. Real 100k, 1M, and 10M latency claims should
|
|
500
|
+
come from service-backed FAISS/Qdrant/pgvector load tests on production-like
|
|
501
|
+
hardware.
|
|
500
502
|
|
|
501
503
|
Cluster placement planning:
|
|
502
504
|
|
|
@@ -988,7 +990,7 @@ Current read:
|
|
|
988
990
|
| 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
991
|
| 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
992
|
| 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, missing-record repair, tombstone repair, and structured payload precision@1 `1.000`. | This proves routing, cache, payload,
|
|
993
|
+
| 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, active-active delta sync, and structured payload precision@1 `1.000`. | This proves routing, cache, payload, replicated-runtime, and namespace-delta foundations. It is not a 10M-vector latency claim; real 10M latency still needs service-backed load tests on larger hardware. |
|
|
992
994
|
| 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
995
|
| 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
996
|
|
|
@@ -1007,7 +1009,7 @@ Current read:
|
|
|
1007
1009
|
| Production index profile | Docker-backed 50000-vector profile for persisted FAISS, Qdrant service, and PostgreSQL/pgvector HNSW. | implemented | FAISS / Qdrant service / pgvector | Keep service-mode candidate generation above `0.95` recall@10 and below 10 ms average query latency at 50000 vectors. |
|
|
1008
1010
|
| Production load profile | 100k and 1M service-backed candidate-index checks with p95/p99 latency. | implemented | Qdrant service / pgvector HNSW / FAISS persisted | Keep 100k at recall@10 `1.000`; push 1M p99 below 100 ms with recall@10 >= 0.95. |
|
|
1009
1011
|
| Qdrant 1M HNSW ef sweep | One 1M Qdrant collection queried with multiple `hnsw_ef` values. | implemented | Qdrant service | Repeat with 100+ queries and collection-level HNSW build parameters before claiming a stable 1M SLO. |
|
|
1010
|
-
| Scale readiness profile | Cluster placement, node/zone-loss simulation, quorum report, replicated runtime, hot-cache behavior, and structured/multimodal payload retrieval. | implemented | Mem0 / Zep / LangGraph persistent memory / GraphRAG target adapters | Keep quorum replication and repair green while adding larger service-backed 10M load tests. |
|
|
1012
|
+
| Scale readiness profile | Cluster placement, node/zone-loss simulation, quorum report, replicated runtime, active-active delta sync, hot-cache behavior, and structured/multimodal payload retrieval. | implemented | Mem0 / Zep / LangGraph persistent memory / GraphRAG target adapters | Keep quorum replication, namespace-delta sync, and repair green while adding larger service-backed 10M load tests. |
|
|
1011
1013
|
| Memory competitor adapter profile | Dynamic-memory scenario wired for external memory frameworks. | implemented | Mem0 / Zep / LangGraph persistent memory | Report real competitor results only when their packages/services are explicitly configured. |
|
|
1012
1014
|
| [BEIR](https://github.com/beir-cellar/beir) | Standard zero-shot information retrieval quality. | planned | Chroma / Qdrant / FAISS | Stay within 0.02 `nDCG@10` on identical embeddings. |
|
|
1013
1015
|
| [MTEB Retrieval](https://github.com/embeddings-benchmark/mteb) | Separates encoder quality from retrieval-store quality. | planned | Chroma / Qdrant / FAISS | Prove WaveMind does not reduce same-embedding retrieval quality. |
|
|
@@ -1167,6 +1169,20 @@ production foundation for namespace-level HA and eventual-consistency behavior;
|
|
|
1167
1169
|
for full consensus across independent network services, deploy WaveMind with
|
|
1168
1170
|
Postgres/Qdrant/ops-layer replication.
|
|
1169
1171
|
|
|
1172
|
+
For multi-region active-active experiments, export and import namespace deltas:
|
|
1173
|
+
|
|
1174
|
+
```python
|
|
1175
|
+
region_a.remember("Tenant A billing preference.", namespace="tenant:a")
|
|
1176
|
+
delta = region_a.export_namespace_delta("tenant:a")
|
|
1177
|
+
region_b.import_namespace_delta(delta)
|
|
1178
|
+
|
|
1179
|
+
region_a.forget(text="Tenant A billing preference.", namespace="tenant:a")
|
|
1180
|
+
region_b.import_namespace_delta(region_a.export_namespace_delta("tenant:a"))
|
|
1181
|
+
```
|
|
1182
|
+
|
|
1183
|
+
The delta contains active records plus tombstones. Import is idempotent and
|
|
1184
|
+
tombstone-aware, so a stale region export cannot resurrect a deleted memory.
|
|
1185
|
+
|
|
1170
1186
|
Checked-in official LoCoMo retrieval result:
|
|
1171
1187
|
|
|
1172
1188
|
10 conversations, 5882 memory turns, 1977 evidence-labeled questions,
|
|
@@ -238,6 +238,79 @@ def run_replication_runtime_profile() -> dict[str, object]:
|
|
|
238
238
|
memory.close()
|
|
239
239
|
|
|
240
240
|
|
|
241
|
+
def run_active_active_delta_profile() -> dict[str, object]:
|
|
242
|
+
with tempfile.TemporaryDirectory() as directory:
|
|
243
|
+
kwargs = {
|
|
244
|
+
"replication_factor": 3,
|
|
245
|
+
"width": 16,
|
|
246
|
+
"height": 16,
|
|
247
|
+
"layers": 1,
|
|
248
|
+
"encoder": HashingTextEncoder(vector_dim=64),
|
|
249
|
+
}
|
|
250
|
+
region_a = ReplicatedWaveMind(
|
|
251
|
+
root_path=Path(directory) / "region-a",
|
|
252
|
+
nodes=[
|
|
253
|
+
{"id": "region-a-1", "address": "127.0.0.1:8101", "zone": "zone-a"},
|
|
254
|
+
{"id": "region-a-2", "address": "127.0.0.1:8102", "zone": "zone-b"},
|
|
255
|
+
{"id": "region-a-3", "address": "127.0.0.1:8103", "zone": "zone-c"},
|
|
256
|
+
],
|
|
257
|
+
**kwargs,
|
|
258
|
+
)
|
|
259
|
+
region_b = ReplicatedWaveMind(
|
|
260
|
+
root_path=Path(directory) / "region-b",
|
|
261
|
+
nodes=[
|
|
262
|
+
{"id": "region-b-1", "address": "127.0.0.1:8201", "zone": "zone-a"},
|
|
263
|
+
{"id": "region-b-2", "address": "127.0.0.1:8202", "zone": "zone-b"},
|
|
264
|
+
{"id": "region-b-3", "address": "127.0.0.1:8203", "zone": "zone-c"},
|
|
265
|
+
],
|
|
266
|
+
**kwargs,
|
|
267
|
+
)
|
|
268
|
+
try:
|
|
269
|
+
namespace = "tenant:active-active"
|
|
270
|
+
region_a.remember("region a billing preference", namespace=namespace)
|
|
271
|
+
region_b.remember("region b support preference", namespace=namespace)
|
|
272
|
+
sync_started = time.perf_counter()
|
|
273
|
+
import_b = region_b.import_namespace_delta(
|
|
274
|
+
region_a.export_namespace_delta(namespace)
|
|
275
|
+
)
|
|
276
|
+
import_a = region_a.import_namespace_delta(
|
|
277
|
+
region_b.export_namespace_delta(namespace)
|
|
278
|
+
)
|
|
279
|
+
sync_ms = (time.perf_counter() - sync_started) * 1000.0
|
|
280
|
+
converged = (
|
|
281
|
+
region_a.query("support preference", namespace=namespace, top_k=1)
|
|
282
|
+
and region_b.query("billing preference", namespace=namespace, top_k=1)
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
stale_delta = region_b.export_namespace_delta(namespace)
|
|
286
|
+
region_a.forget(text="region a billing preference", namespace=namespace)
|
|
287
|
+
region_a.import_namespace_delta(stale_delta)
|
|
288
|
+
suppressed_stale_import = all(
|
|
289
|
+
result.text != "region a billing preference"
|
|
290
|
+
for result in region_a.query("billing preference", namespace=namespace, top_k=3)
|
|
291
|
+
)
|
|
292
|
+
tombstone_delta = region_a.export_namespace_delta(namespace)
|
|
293
|
+
tombstone_report = region_b.import_namespace_delta(tombstone_delta)
|
|
294
|
+
tombstone_converged = all(
|
|
295
|
+
result.text != "region a billing preference"
|
|
296
|
+
for result in region_b.query("billing preference", namespace=namespace, top_k=3)
|
|
297
|
+
)
|
|
298
|
+
return {
|
|
299
|
+
"engine": "WaveMind active-active delta sync",
|
|
300
|
+
"regions": 2,
|
|
301
|
+
"replication_factor_per_region": 3,
|
|
302
|
+
"records_imported": import_a.imported_records + import_b.imported_records,
|
|
303
|
+
"converged_after_bidirectional_sync": bool(converged),
|
|
304
|
+
"sync_ms": sync_ms,
|
|
305
|
+
"suppressed_stale_import_after_delete": suppressed_stale_import,
|
|
306
|
+
"tombstone_deleted_records": tombstone_report.deleted_records,
|
|
307
|
+
"tombstone_converged": tombstone_converged,
|
|
308
|
+
}
|
|
309
|
+
finally:
|
|
310
|
+
region_a.close()
|
|
311
|
+
region_b.close()
|
|
312
|
+
|
|
313
|
+
|
|
241
314
|
def run_multimodal_profile() -> dict[str, object]:
|
|
242
315
|
with tempfile.TemporaryDirectory() as directory:
|
|
243
316
|
memory = WaveMind(
|
|
@@ -325,6 +398,7 @@ def run_benchmark(
|
|
|
325
398
|
),
|
|
326
399
|
run_cache_profile(queries=cache_queries, capacity=cache_capacity),
|
|
327
400
|
run_replication_runtime_profile(),
|
|
401
|
+
run_active_active_delta_profile(),
|
|
328
402
|
run_multimodal_profile(),
|
|
329
403
|
]
|
|
330
404
|
return {
|
|
@@ -337,8 +411,8 @@ def run_benchmark(
|
|
|
337
411
|
"description": (
|
|
338
412
|
"Deterministic scale-readiness profile for cluster placement, "
|
|
339
413
|
"node/zone loss simulation, quorum-replicated runtime behavior, "
|
|
340
|
-
"hot-cache behavior, and structured
|
|
341
|
-
"not a 10M-vector database load test."
|
|
414
|
+
"active-active delta sync, hot-cache behavior, and structured "
|
|
415
|
+
"payload retrieval. This is not a 10M-vector database load test."
|
|
342
416
|
),
|
|
343
417
|
},
|
|
344
418
|
"results": results,
|
|
@@ -379,6 +453,9 @@ def main() -> int:
|
|
|
379
453
|
print(f"| replicated runtime | recalled_after_node_loss | {result['recalled_after_node_loss']} |")
|
|
380
454
|
print(f"| replicated runtime | repair_copied_records | {result['repair_copied_records']} |")
|
|
381
455
|
print(f"| replicated runtime | tombstone_repair_deleted_records | {result['tombstone_repair_deleted_records']} |")
|
|
456
|
+
elif result["engine"] == "WaveMind active-active delta sync":
|
|
457
|
+
print(f"| active-active delta | converged | {result['converged_after_bidirectional_sync']} |")
|
|
458
|
+
print(f"| active-active delta | tombstone_converged | {result['tombstone_converged']} |")
|
|
382
459
|
else:
|
|
383
460
|
print(f"| structured payloads | precision@1 | {result['precision_at_1']:.3f} |")
|
|
384
461
|
print(f"\nWrote {args.output}")
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"namespace_count": 4096,
|
|
6
6
|
"node_count": 4,
|
|
7
7
|
"replication_factor": 2,
|
|
8
|
-
"description": "Deterministic scale-readiness profile for cluster placement, node/zone loss simulation, quorum-replicated runtime behavior, hot-cache behavior, and structured payload retrieval. This is not a 10M-vector database load test."
|
|
8
|
+
"description": "Deterministic scale-readiness profile for cluster placement, node/zone loss simulation, quorum-replicated runtime behavior, active-active delta sync, hot-cache behavior, and structured payload retrieval. This is not a 10M-vector database load test."
|
|
9
9
|
},
|
|
10
10
|
"results": [
|
|
11
11
|
{
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"namespaces": 4096,
|
|
15
15
|
"nodes": 4,
|
|
16
16
|
"replication_factor": 2,
|
|
17
|
-
"placement_ms":
|
|
17
|
+
"placement_ms": 55.97210000269115,
|
|
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.001386649877531454,
|
|
36
|
+
"p99_lookup_ms": 0.0026999623514711857
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
39
|
"engine": "WaveMind replicated runtime",
|
|
@@ -47,8 +47,19 @@
|
|
|
47
47
|
"tombstone_suppressed_before_repair": true,
|
|
48
48
|
"tombstone_suppressed_after_repair": true,
|
|
49
49
|
"tombstone_repair_deleted_records": 1,
|
|
50
|
-
"avg_query_after_loss_ms": 1.
|
|
51
|
-
"p99_query_after_loss_ms": 1.
|
|
50
|
+
"avg_query_after_loss_ms": 1.3324000174179673,
|
|
51
|
+
"p99_query_after_loss_ms": 1.3324000174179673
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"engine": "WaveMind active-active delta sync",
|
|
55
|
+
"regions": 2,
|
|
56
|
+
"replication_factor_per_region": 3,
|
|
57
|
+
"records_imported": 6,
|
|
58
|
+
"converged_after_bidirectional_sync": true,
|
|
59
|
+
"sync_ms": 120.19369995687157,
|
|
60
|
+
"suppressed_stale_import_after_delete": true,
|
|
61
|
+
"tombstone_deleted_records": 3,
|
|
62
|
+
"tombstone_converged": true
|
|
52
63
|
},
|
|
53
64
|
{
|
|
54
65
|
"engine": "WaveMind structured payloads",
|
|
@@ -60,8 +71,8 @@
|
|
|
60
71
|
],
|
|
61
72
|
"queries": 4,
|
|
62
73
|
"precision_at_1": 1.0,
|
|
63
|
-
"avg_latency_ms": 0.
|
|
64
|
-
"p99_latency_ms": 0.
|
|
74
|
+
"avg_latency_ms": 0.4274749953765422,
|
|
75
|
+
"p99_latency_ms": 0.6231999723240733
|
|
65
76
|
}
|
|
66
77
|
]
|
|
67
78
|
}
|
|
@@ -24,6 +24,22 @@ def _cluster(tmp_path, **kwargs):
|
|
|
24
24
|
)
|
|
25
25
|
|
|
26
26
|
|
|
27
|
+
def _region(tmp_path, name: str, **kwargs):
|
|
28
|
+
return ReplicatedWaveMind(
|
|
29
|
+
root_path=tmp_path / name,
|
|
30
|
+
nodes=[
|
|
31
|
+
{"id": f"{name}-a", "address": "127.0.0.1:8101", "zone": "zone-a"},
|
|
32
|
+
{"id": f"{name}-b", "address": "127.0.0.1:8102", "zone": "zone-b"},
|
|
33
|
+
{"id": f"{name}-c", "address": "127.0.0.1:8103", "zone": "zone-c"},
|
|
34
|
+
],
|
|
35
|
+
width=16,
|
|
36
|
+
height=16,
|
|
37
|
+
layers=1,
|
|
38
|
+
encoder=HashingTextEncoder(vector_dim=64),
|
|
39
|
+
**kwargs,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
27
43
|
def test_replicated_wavemind_writes_to_all_replicas_and_reads_after_node_loss(tmp_path):
|
|
28
44
|
memory = _cluster(tmp_path, replication_factor=3)
|
|
29
45
|
try:
|
|
@@ -189,6 +205,78 @@ def test_replicated_wavemind_stores_stable_replica_metadata(tmp_path):
|
|
|
189
205
|
memory.close()
|
|
190
206
|
|
|
191
207
|
|
|
208
|
+
def test_replicated_wavemind_namespace_delta_converges_two_regions(tmp_path):
|
|
209
|
+
region_a = _region(tmp_path, "region-a", replication_factor=3)
|
|
210
|
+
region_b = _region(tmp_path, "region-b", replication_factor=3)
|
|
211
|
+
try:
|
|
212
|
+
namespace = "tenant:active-active"
|
|
213
|
+
region_a.remember("region a remembers billing preference", namespace=namespace)
|
|
214
|
+
region_b.remember("region b remembers support preference", namespace=namespace)
|
|
215
|
+
|
|
216
|
+
report_b = region_b.import_namespace_delta(region_a.export_namespace_delta(namespace))
|
|
217
|
+
report_a = region_a.import_namespace_delta(region_b.export_namespace_delta(namespace))
|
|
218
|
+
|
|
219
|
+
assert report_b.imported_records == 3
|
|
220
|
+
assert report_a.imported_records == 3
|
|
221
|
+
assert region_a.query("support preference", namespace=namespace, top_k=1)[0].text == (
|
|
222
|
+
"region b remembers support preference"
|
|
223
|
+
)
|
|
224
|
+
assert region_b.query("billing preference", namespace=namespace, top_k=1)[0].text == (
|
|
225
|
+
"region a remembers billing preference"
|
|
226
|
+
)
|
|
227
|
+
finally:
|
|
228
|
+
region_a.close()
|
|
229
|
+
region_b.close()
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def test_replicated_wavemind_delta_import_is_idempotent(tmp_path):
|
|
233
|
+
region_a = _region(tmp_path, "region-a", replication_factor=3)
|
|
234
|
+
region_b = _region(tmp_path, "region-b", replication_factor=3)
|
|
235
|
+
try:
|
|
236
|
+
namespace = "tenant:idempotent"
|
|
237
|
+
region_a.remember("idempotent active active memory", namespace=namespace)
|
|
238
|
+
delta = region_a.export_namespace_delta(namespace)
|
|
239
|
+
|
|
240
|
+
first = region_b.import_namespace_delta(delta)
|
|
241
|
+
second = region_b.import_namespace_delta(delta)
|
|
242
|
+
placement = region_b.placement(namespace)
|
|
243
|
+
|
|
244
|
+
assert first.imported_records == 3
|
|
245
|
+
assert second.imported_records == 0
|
|
246
|
+
assert second.skipped_records == 3
|
|
247
|
+
for node_id in placement.replicas:
|
|
248
|
+
records = region_b._mind(node_id).store.list(namespace=namespace)
|
|
249
|
+
assert [record.text for record in records] == ["idempotent active active memory"]
|
|
250
|
+
finally:
|
|
251
|
+
region_a.close()
|
|
252
|
+
region_b.close()
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def test_replicated_wavemind_tombstone_delta_beats_stale_record_delta(tmp_path):
|
|
256
|
+
region_a = _region(tmp_path, "region-a", replication_factor=3)
|
|
257
|
+
region_b = _region(tmp_path, "region-b", replication_factor=3)
|
|
258
|
+
try:
|
|
259
|
+
namespace = "tenant:tombstone-delta"
|
|
260
|
+
region_a.remember("delete wins over stale region export", namespace=namespace)
|
|
261
|
+
region_b.import_namespace_delta(region_a.export_namespace_delta(namespace))
|
|
262
|
+
stale_delta = region_b.export_namespace_delta(namespace)
|
|
263
|
+
|
|
264
|
+
region_a.forget(text="delete wins over stale region export", namespace=namespace)
|
|
265
|
+
region_a.import_namespace_delta(stale_delta)
|
|
266
|
+
|
|
267
|
+
assert region_a.query("stale region export", namespace=namespace, top_k=1) == []
|
|
268
|
+
|
|
269
|
+
tombstone_delta = region_a.export_namespace_delta(namespace)
|
|
270
|
+
report = region_b.import_namespace_delta(tombstone_delta)
|
|
271
|
+
|
|
272
|
+
assert report.deleted_records == 3
|
|
273
|
+
assert report.imported_tombstones == 3
|
|
274
|
+
assert region_b.query("stale region export", namespace=namespace, top_k=1) == []
|
|
275
|
+
finally:
|
|
276
|
+
region_a.close()
|
|
277
|
+
region_b.close()
|
|
278
|
+
|
|
279
|
+
|
|
192
280
|
def test_replicated_wavemind_rejects_global_db_path(tmp_path):
|
|
193
281
|
with pytest.raises(ValueError, match="db_path"):
|
|
194
282
|
ReplicatedWaveMind(
|
|
@@ -21,5 +21,9 @@ def test_scale_readiness_benchmark_covers_cluster_cache_and_payloads():
|
|
|
21
21
|
assert results["WaveMind replicated runtime"]["tombstone_suppressed_before_repair"] is True
|
|
22
22
|
assert results["WaveMind replicated runtime"]["tombstone_suppressed_after_repair"] is True
|
|
23
23
|
assert results["WaveMind replicated runtime"]["tombstone_repair_deleted_records"] == 1
|
|
24
|
+
assert results["WaveMind active-active delta sync"]["converged_after_bidirectional_sync"] is True
|
|
25
|
+
assert results["WaveMind active-active delta sync"]["suppressed_stale_import_after_delete"] is True
|
|
26
|
+
assert results["WaveMind active-active delta sync"]["tombstone_converged"] is True
|
|
27
|
+
assert results["WaveMind active-active delta sync"]["tombstone_deleted_records"] == 3
|
|
24
28
|
assert results["WaveMind structured payloads"]["precision_at_1"] == 1.0
|
|
25
29
|
assert payload["scenario"]["simulated_memories"] == 100_000
|
|
@@ -20,6 +20,7 @@ from .multimodal import (
|
|
|
20
20
|
)
|
|
21
21
|
from .replication import (
|
|
22
22
|
ReadQuorumError,
|
|
23
|
+
ReplicatedDeltaImportReport,
|
|
23
24
|
ReplicatedRepairReport,
|
|
24
25
|
ReplicatedWaveMind,
|
|
25
26
|
ReplicatedWriteResult,
|
|
@@ -36,7 +37,7 @@ from .storage import (
|
|
|
36
37
|
create_memory_store,
|
|
37
38
|
)
|
|
38
39
|
|
|
39
|
-
__version__ = "2.2.
|
|
40
|
+
__version__ = "2.2.6"
|
|
40
41
|
|
|
41
42
|
__all__ = [
|
|
42
43
|
"FieldProjector",
|
|
@@ -55,6 +56,7 @@ __all__ = [
|
|
|
55
56
|
"QueryResult",
|
|
56
57
|
"ReadQuorumError",
|
|
57
58
|
"RedisHotMemoryCache",
|
|
59
|
+
"ReplicatedDeltaImportReport",
|
|
58
60
|
"ReplicatedRepairReport",
|
|
59
61
|
"ReplicatedWaveMind",
|
|
60
62
|
"ReplicatedWriteResult",
|