Skip to content

feat: add Valkey as RAG vector store backend#2063

Open
daric93 wants to merge 4 commits into
FoundationAgents:mainfrom
daric93:feat/valkey-rag-vector-store
Open

feat: add Valkey as RAG vector store backend#2063
daric93 wants to merge 4 commits into
FoundationAgents:mainfrom
daric93:feat/valkey-rag-vector-store

Conversation

@daric93

@daric93 daric93 commented Jun 12, 2026

Copy link
Copy Markdown

Closes #2062

Summary

Adds Valkey (via the Valkey Search module) as a RAG vector store backend in MetaGPT, following the existing ConfigBasedFactory pattern used by FAISS, Chroma, and Elasticsearch.

Changes

  • Schema (metagpt/rag/schema.py): ValkeyStoreConfig, ValkeyIndexConfig, ValkeyRetrieverConfig
  • Vector store (metagpt/rag/vector_stores/valkey.py): ValkeyVectorStore implementing llama-index BasePydanticVectorStore, backed by valkey-glide FT.CREATE / FT.SEARCH (HNSW + KNN)
  • Retriever (metagpt/rag/retrievers/valkey_retriever.py): ValkeyRetriever extending RAGRetriever
  • Factories: registered Valkey configs in RAGIndexFactory and RetrieverFactory (lazy imports so valkey-glide stays optional)
  • Dependencies (setup.py): valkey-glide>=2.1.0,<3.0.0 added to the rag extra
  • Config example (config/config2.example.yaml): commented Valkey section

Design notes

  • Storage: JSON documents with an HNSW VectorField, COSINE distance, FLOAT32. FLAT algorithm also supported.
  • client_name="metagpt_rag_client" on all connections.
  • Sync-over-async handled by a single persistent event loop on a dedicated thread, so the cached client always runs on its creation loop.
  • Batch insert via asyncio.gather (_BATCH_SIZE=100); raises on partial failure.
  • SCAN loops bounded by _MAX_SCAN_ITERATIONS; drop_index() always cleans orphaned keys.

Testing

  • 54 tests passing (unit + integration) on Python 3.11 in a venv.
  • 14 live integration tests run against a live Valkey container (valkey/valkey-bundle:9.1, Search module) on localhost:6379not skipped.
    • 11 vector store integration tests + 3 retriever integration tests
  • 40 unit tests (mocked valkey-glide client) covering connect/TLS/password/timeout, index creation, batch insert + partial-failure, KNN query, delete, drop-index cleanup, SCAN safety limit, and persistent-loop reuse.
  • black, isort, ruff all clean.
  • Existing FAISS/Chroma/ES/BM25 factory tests still pass — purely additive, no regressions.

How to test manually

# Start Valkey with the Search module
podman run -d --name valkey-test -p 6379:6379 valkey/valkey-bundle:9.1

# Run the suite
ALLOW_OPENAI_API_CALL=0 python -m pytest \
  tests/metagpt/rag/vector_stores/ \
  tests/metagpt/rag/retrievers/test_valkey_retriever.py \
  tests/metagpt/rag/retrievers/test_valkey_retriever_integration.py \
  tests/metagpt/rag/factories/test_index.py \
  tests/metagpt/rag/factories/test_retriever.py \
  -v --timeout=60

Configuration required

New optional valkey section in config2.yaml (see config/config2.example.yaml):

Key Default Description
host localhost Valkey server host
port 6379 Valkey server port
password None Optional auth password
use_tls false TLS for cloud deployments (e.g. ElastiCache)
request_timeout 5000 Request timeout (ms)
index_name metagpt_rag FT.SEARCH index name
prefix metagpt:rag: Key prefix for document storage
vector_dimensions 1536 Embedding dimensionality
distance_metric COSINE COSINE, L2, or IP
vector_algorithm HNSW HNSW or FLAT

Requirements

Requires a Valkey deployment with the Search module (e.g. valkey/valkey-bundle:9.1). Purely additive — no changes to existing backends.

daric93 added 3 commits June 9, 2026 15:09
Integrate Valkey Search module as a RAG vector store backend using the existing ConfigBasedFactory pattern.

- Add ValkeyStoreConfig, ValkeyIndexConfig, ValkeyRetrieverConfig schemas - Implement ValkeyVectorStore (BasePydanticVectorStore) using valkey-glide - Implement ValkeyRetriever extending RAGRetriever - Register Valkey in RAGIndexFactory and RetrieverFactory - Add valkey-glide>=2.1.0,<3.0.0 to rag extras in setup.py - Add Valkey configuration example to config2.example.yaml - Unit + integration tests (54 passing) against live Valkey

Requires Valkey with Search module (valkey-bundle:9.1). Jira: AEA-506

Signed-off-by: Daria Korenieva <daric2612@gmail.com>
- Rewrite ValkeyVectorStore on the synchronous glide_sync client,
  removing the persistent-loop / run_async threading bridge to match
  the other RAG backends (FAISS/Chroma/ES) and eliminate the deadlock risk
- Use native glide methods (delete/ping/scan, glide_json.set) instead of
  custom_command; batch inserts now use an atomic glide Batch (one round-trip)
- Use ft.list() for index existence checks instead of error-string matching
- Fix delete(ref_doc_id) to remove all chunks sharing a source doc id
- Add embedding-dimension validation, FT.SEARCH response-shape guards, and
  warning logs on metadata/score parse fallback
- Redact password in repr (store + ValkeyStoreConfig); warn when password
  is set without TLS
- Type distance_metric/vector_algorithm as Literal so bad values fail at config
- Retriever uses only public store methods and prefers aget_query_embedding
- Remove unused escape helpers; add vector_algorithm to config example;
  add valkey-glide-sync dependency; annotate _create_valkey_retriever return
- Rewrite unit + integration tests for the sync client (31 unit, 15 integration)
Move the seven in-method 'from glide_sync import ...' statements in valkey.py into a single top-level import block, matching how the RAG factories import chromadb/faiss at module top. Optionality is already enforced at the factory boundary (ValkeyVectorStore is lazily imported inside _create_valkey), so per-method imports added no real isolation.

Update unit tests to patch glide_sync symbols on the valkey module namespace (patch.multiple) instead of sys.modules, since the names are now bound at import time.

Tests: 31 unit, 15 integration, 16 factory (62) pass; black/isort/ruff clean.
Signed-off-by: Daria Korenieva <daric2612@gmail.com>
…s The embedding tests assigned a PropertyMock directly to the shared singleton config.llm.proxy without mocker.patch, so pytest-mock never restored it. The leaked PropertyMock then propagated to the global config and caused the autouse llm_mock fixture to pass an invalid proxy to httpx, breaking unrelated tests (e.g. the Valkey RAG suite) with 'Proxy protocol must be either http, https, socks5, or socks5h'. Use mocker.patch.object so the original proxy value is restored on teardown.

Signed-off-by: Daria Korenieva <daric2612@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Add Valkey as a RAG vector store backend

1 participant