pip install ragleap-vectorstores library. To install and run ragleap-core (AI employees, channels, Docker), see docs.ragleap.com; the project home is ragleap.com.ragleap-vectorstores
Pluggable vector backends beyond ragleap-rag core's six — install only the client SDK you actually need, not the whole ecosystem.
Install
Each backend's real client SDK is an optional extra — installing the bare package alone pulls in no heavy dependencies beyond ragleap-rag itself.
pip install ragleap-vectorstores[chroma] # or, with uv uv add ragleap-vectorstores[chroma] pip install ragleap-vectorstores[lancedb] # or, with uv uv add ragleap-vectorstores[lancedb] pip install ragleap-vectorstores[redis] # or, with uv uv add ragleap-vectorstores[redis] pip install ragleap-vectorstores[upstash] # or, with uv uv add ragleap-vectorstores[upstash]
Quick start
database_url= is still required even when using one of these backends for vectors — ragleap-rag's conversation memory is always Postgres-backed regardless of which vector_backend is chosen; only vector storage moves. Swap ChromaBackend for LanceDBBackend(uri="./lancedb_data") to use LanceDB instead, or RedisBackend(redis_url="redis://localhost:6379/0") for Redis (requires a real Redis Stack instance, or plain Redis + the RediSearch module), or UpstashBackend(url="...", token="...") for Upstash Vector (requires an already-created dense-type index — see the Upstash console) — same interface, same rest of the setup.
from ragleap_vectorstores import ChromaBackend
from ragleap import RagLeap, ProviderConfig, EmbeddingConfig
backend = ChromaBackend(persist_directory="./chroma_data")
rag = RagLeap(
database_url="postgresql://...", # still required — conversation memory
vector_backend=backend, # vectors go to Chroma instead
primary=ProviderConfig(provider="gemini", api_key="..."),
embedder=EmbeddingConfig(provider="gemini", api_key="..."),
)
rag.init_schema()
rag.ingest("report.pdf")
answer = rag.ask("What were Q3's key findings?")
Full public API surface
Confirmed via direct source reading, not inspect().
from ragleap_vectorstores import VectorBackend, ChromaBackend, LanceDBBackend, RedisBackend, UpstashBackend, OpenSearchBackend, __version__
ChromaBackend — implements VectorBackend
ChromaBackend(persist_directory, collection_name="ragleap")PersistentClient — no server required. persist_directory is required; both Chroma's on-disk index and a small document-registry SQLite sidecar live there.init_schema(dimensions)insert_document / insert_chunksearch_dense(embedding, top_k, metadata_filter=None)metadata_filters are automatically wrapped in Chroma's required $and form.search_sparse / search_hybridVectorBackend default (empty / dense-only) rather than claiming unimplemented capability.supports_sparse()False — see Known limitations below.list_documents / delete_document / get_document_filenameLanceDBBackend — implements VectorBackend
LanceDBBackend(uri, table_name="ragleap")uri is required; both LanceDB's own on-disk table and a small document-registry SQLite sidecar live there.init_schema(dimensions)open_table ValueError-on-missing behavior, or creates one from an explicit pyarrow schema if it doesn't exist yet.insert_document / insert_chunkinsert_chunk uses real upsert semantics via merge_insert() — re-inserting the same id updates the row in place rather than duplicating it.search_dense(embedding, top_k, metadata_filter=None).metric("cosine") set explicitly — LanceDB defaults to L2 otherwise). Multi-key metadata_filters combine directly with native SQL AND, unlike Chroma's $and requirement.search_sparse / search_hybridVectorBackend default (empty / dense-only) rather than claiming unimplemented capability.supports_sparse()False — see Known limitations below.list_documents / delete_document / get_document_filenameRedisBackend — implements VectorBackend
RedisBackend(redis_url, index_name="ragleap_idx", key_prefix="ragleap:chunk:")redis_url is required.init_schema(dimensions)MODULE LIST for the search module and raises a clear RuntimeError immediately if it's missing, rather than failing confusingly later at query time. Creates a RediSearch HNSW vector index (cosine distance) if one doesn't already exist, or reuses it via ft().info().insert_document / insert_chunksearch_dense(embedding, top_k, metadata_filter=None)metadata_filter only supports narrowing by document_id (a declared TAG field) — RediSearch requires fields to be predeclared in the schema, unlike Chroma's flexible where=. Other filter keys are accepted but ignored rather than raising.search_sparse / search_hybridVectorBackend default (empty / dense-only) rather than claiming unimplemented capability.supports_sparse()False — see Known limitations below.list_documents / delete_document / get_document_filenameUpstashBackend — implements VectorBackend
UpstashBackend(url, token, namespace="")url and token are required.init_schema(dimensions)info() and raises a clear RuntimeError on mismatch (wrong dimension, or a hybrid/sparse-type index) rather than failing confusingly on the first insert.insert_document / insert_chunksearch_dense(embedding, top_k, metadata_filter=None)filter= string. Unlike Redis, arbitrary multi-key metadata_filters work natively — no predeclared schema needed, and multi-key filters combine with AND without Chroma's $and wrapping.search_sparse / search_hybridVectorBackend default (empty / dense-only) rather than claiming unimplemented capability.supports_sparse()False — see Known limitations below.list_documents / delete_document / get_document_filenameprefix= parameter on range()/delete().OpenSearchBackend — implements VectorBackend
OpenSearchBackend(opensearch_url, index_name="ragleap_idx", documents_index_name=None, use_ssl=False, verify_certs=False, http_auth=None, **client_kwargs)opensearch_url is required (for example http://localhost:9200). The client SDK is imported lazily, so a missing opensearch extra raises a clear ImportError at construction.init_schema(dimensions)lucene engine with cosinesimil space. Idempotent: reusing an existing index with a matching dimension just reuses it, while a mismatched dimension raises a clear RuntimeError.insert_document / insert_chunk<document_id>:<chunk_index>, so re-inserting the same chunk overwrites it rather than duplicating. The document registry is a second real OpenSearch index (<index_name>_documents), not a SQLite sidecar. Writes use refresh=True for immediate read-after-write consistency, trading indexing throughput.search_dense(embedding, top_k, metadata_filter=None)metadata_filter supports arbitrary term filters over mapped fields through bool.filter — every key is applied, so a nonexistent field narrows results to zero rather than being silently ignored.search_sparse / search_hybridVectorBackend default (empty / dense-only).supports_sparse()False — see Known limitations below.list_documents / delete_document / get_document_filenamedelete_document() issues a delete_by_query on the chunks index plus a direct delete on the registry index — two requests, not one atomic operation.Known limitations
Drawn from live testing against the real installed chromadb==1.5.9, lancedb==0.37.1, redis-py==7.3.0, upstash-vector==0.8.0, and opensearch-py==3.2.0 packages, plus a real Redis Stack instance, a real Upstash Vector index, and a real OpenSearch 3.8.0 instance — not from documentation alone.
Applies to all backends
- No native sparse/keyword search.
supports_sparse()honestly reportsFalsefor all five — none has a tested full-text/keyword search path in this release, sosearch_hybrid()falls back to dense-only. database_url=is still required. Conversation memory is always Postgres-backed inragleap-rag, regardless of which vector backend is chosen — these backends only replace vector storage.
Chroma-specific
where=filters need exactly one top-level operator. A plain multi-key metadata filter dict raisesValueErroragainst real chromadb —ChromaBackendwraps multi-key filters in$andautomatically, but this is a real constraint worth knowing if you're debugging filter behavior directly against chromadb.- Dependency footprint.
chromadb's own transitive dependencies are non-trivial (kubernetes client libs, OpenTelemetry, uvicorn extras) — correctly gated behind the optional[chroma]extra, but worth knowing before installing into a size-sensitive environment.
LanceDB-specific
- Default metric is L2, not cosine.
LanceDBBackendsets.metric("cosine")explicitly on every query to avoid this — worth knowing if you're querying LanceDB directly outside this backend. - LanceDB does support full-text search via tantivy (
fts_columns=), but that surface wasn't implemented or tested in this release.
Redis-specific
- Requires a real running server. Unlike Chroma/LanceDB, there's no embedded/local mode — plain Redis has no vector search at all.
init_schema()checksMODULE LISTand raises a clear error if thesearchmodule (RediSearch/Redis Stack) isn't loaded. - Metadata filtering is limited to
document_id. RediSearch requires fields to be predeclared in the index schema up front — unlike Chroma's flexiblewhere=, arbitrary metadata keys can't be filtered on without a schema change. - TAG field values must be escaped. An un-escaped UUID-style
document_id(containing hyphens) inside a TAG query silently returns zero matches — RediSearch parses hyphens as query syntax, not literal characters. Handled automatically, but worth knowing if querying Redis directly. - Redis does support full-text search via TextField/BM25 scorers, but that surface wasn't implemented or tested in this release.
Upstash-specific
- No local mode at all. Unlike every other backend here (including self-hostable Redis), there is no way to run Upstash Vector locally — it's a managed cloud service only, and the index must already exist, created via the Upstash console.
- An index can be "dense" or "hybrid" at creation time. A hybrid-type index rejects dense-only upserts with a confusing error — live-verified.
init_schema()checks this viainfo()and fails clearly up front instead. - Writes are eventually consistent. A short delay after
insert_chunk()may be needed before asearch_dense()reliably sees it — live-verified in this package's own test suite, which waits ~1-1.5s after seeding data. - Upstash does support real hybrid dense+sparse search, but that requires a hybrid-type index and sparse embeddings this package doesn't generate, so it's honestly reported as unsupported.
OpenSearch-specific
- Requires a real running instance. There is no embedded/local mode, unlike Chroma/LanceDB.
- The
nmslibk-NN engine is rejected on OpenSearch 3.0+. Most tutorials use it; attempting it raisesmapper_parsing_exceptionat index creation — live-verified. This backend usesengine: "lucene"instead. - The k-NN score is not raw cosine similarity. With
cosinesimiland theluceneengine, the score is(1 + cosine_similarity) / 2, mapping [-1, 1] onto [0, 1] — derived from real numbers (near-identical vectors scored 0.9969, orthogonal vectors 0.5552) and used directly assimilarity_score. - No bulk-insert path yet.
insert_chunk()indexes one document per request — fine for moderate chunk counts, not optimized for very large ingests. - Tested locally only. The 16 OpenSearch tests ran against OpenSearch 3.8.0 in Docker on a local Windows machine (Python 3.14,
opensearch-py==3.2.0); they skip whenOPENSEARCH_TEST_URLis unset and have not been run in CI or on Linux. - OpenSearch does support real BM25/hybrid search, but that surface wasn't implemented in this release, so it's honestly reported as unsupported.
Fixed in v0.5.1
- REAL BUG in v0.5.0, on Python 3.10 only.
OpenSearchBackend.insert_document()useddatetime.UTC, which only exists on Python 3.11+, so it raisedAttributeErroron Python 3.10 - despite this package declaring support for 3.10. Reproduced with the published v0.5.0 wheel; fixed in v0.5.1 by switching todatetime.timezone.utc, which works on every supported version and produces the same timestamp. Python 3.11+ was never affected. - The deprecated
datetime.utcnow()was also removed from the Chroma, LanceDB, Redis and Upstash backends in v0.5.1, with the identical naive-UTC timestamp string preserved - stored data andlist_documents()output are unchanged.