How To Add New Retriever Backend#
This guide explains how to add a new vector database backend to the retriever service.
Design contract#
Each backend owns two modules under src/retriever/backends/<backend_name>/:
backend.pyget_vectordb()check_ready()
filters.pybuild_filters(tags, time_filter, filters, property_name)
The backend registry (src/retriever/backends/registry.py) dynamically loads these callables and advertises backend filter capabilities.
The runtime calls similarity_search_with_score(query, k, fetch_k=..., filter=...) on the object returned by get_vectordb(). For image queries, the runtime calls similarity_search_with_score_by_vector(embedding, k, ...) with a pre-computed embedding vector. The filter keyword varies by backend (filter for most, expr for Milvus).
Step-by-step#
Create a backend folder from template:
cp -r src/retriever/backends/_template src/retriever/backends/<backend_name>
Implement vector store wiring in
backend.py.Required behavior:
Return an object implementing
similarity_search_with_score(query, k, **kwargs)andsimilarity_search_with_score_by_vector(embedding, k, **kwargs).Ensure compatibility with keyword arguments used by the service (
fetch_k,filterorexpr).Perform lazy imports for optional dependencies.
Raise clear
ImportErrormessages when an optional package is missing.Use
@lru_cache(maxsize=1)where appropriate to avoid repeated client initialization.
Implement filter translation in
filters.py.Map these inputs to backend-native filters:
tagstime_filterdynamic
filters(eq,in,gte,lte,between)
Return one of:
dictfor document-style filtersstrfor expression-style filters (for example Milvusexpr)Nonewhen no filters apply
Note: The primary request grammar is
where. Backends currently receive a pushdown-safe subset translated intotags,time_filter, and legacyfilters; unsupported clauses are handled in the service fallback path.Register backend in
src/retriever/backends/registry.py.Add a
BACKEND_REGISTRYentry:"<backend_name>": BackendSpec( backend_module_path="src.retriever.backends.<backend_name>.backend", filters_module_path="src.retriever.backends.<backend_name>.filters", )
Update
BACKEND_PUSHDOWN_OPERATORSinsrc/retriever/backends/registry.pyfor the new backend.Add environment variables in
src/common/settings.py. Include connection and backend-specific tuning fields.Add dependency group in
pyproject.toml. Follow the current pattern usingbackend-<backend_name>groups.Add compose overlay file
docker/compose.<backend_name>.yamlfor local stack bring-up and functional tests.Update backend-aware startup wiring in
setup.shanddocker/Dockerfile.setup.sh: backend validation list, required env checks (validate_required_env_vars), optional--up-with-<backend_name>flow, and any backend-specific.envdefaults.docker/Dockerfile: allowRETRIEVER_BACKEND=<backend_name>in the backend allowlist used during poetry install.
Update API and user-facing docs.
Update the OpenAPI schema in
docs/user-guide/api-docs/openapi.yamlfor any request/response/filter capability changes.Update the user guides that mention supported backends, required environment variables, and startup flows.
Add tests.
Minimum unit coverage:
registry dispatch for new backend
filter translation behavior for supported pushdown operators
readiness path (or controlled mock)
request/schema and service compatibility when applicable
Existing unit examples:
tests/test_backend_factory.pytests/test_filters.pytests/test_schema.pytests/test_service.py
Functional coverage:
Create
tests/functional/test_<backend_name>_filters.pywith abackend_namefixture and callassert_filter_matrix.Add per-backend port configuration in
tests/functional/conftest.py(PORT_MAP).Ensure
docker/compose.<backend_name>.yamlexists and supports seeded test data setup.
Validate:
poetry install --only "main,backend-<backend_name>,dev" --no-root PYTHONPATH=. poetry run pytest -q tests --ignore=tests/functional
Run backend functional checks (manual run):
RUN_FUNCTIONAL_BACKEND_TESTS=1 PYTHONPATH=. poetry run pytest -q tests/functional/test_<backend_name>_filters.py
Implementation checklist#
[ ] backend folder created
Check!Click to mark[ ]
backend.pyimplemented
Check!Click to mark[ ]
filters.pyimplemented
Check!Click to mark[ ] registry updated
Check!Click to mark[ ] settings updated
Check!Click to mark[ ] dependency group updated
Check!Click to mark[ ] compose overlay added (
docker/compose.<backend_name>.yaml)
Check!Click to mark[ ] setup and Docker backend allowlists updated
Check!Click to mark[ ] OpenAPI schema updated (
docs/user-guide/api-docs/openapi.yaml)
Check!Click to mark[ ] tests added/updated
Check!Click to mark[ ] docs updated
Check!Click to mark
Common pitfalls#
Not translating filters to backend-native syntax correctly
Not updating backend pushdown operator capabilities in
registry.pyMissing optional dependency guards
Forgetting to register backend in registry
Missing compose overlay and functional-test port map entry
Forgetting to update
docs/user-guide/api-docs/openapi.yamlafter request/response changesMissing
MULTIMODAL_EMBEDDING_ENDPOINT/EMBEDDINGS_ENDPOINTandEMBEDDING_MODEL_NAMEat runtimeInconsistent score/filter behavior across backends