llama-index-vector-stores-rabitqlib
A LlamaIndex vector store backed by RaBitQ-quantized HNSW (rabitqlib.HnswIndex). It's a local, in-process index, a drop-in for SimpleVectorStore or FaissVectorStore, and stores compressed vectors (default 7 bits per dimension).
Status: v0.1, not yet published to PyPI.
Install
pip install -e . # from this folder; pulls llama-index-core and rabitqlib>=0.8.3
Quickstart
from llama_index.core import StorageContext, VectorStoreIndex, load_index_from_storage
from llama_index.vector_stores.rabitqlib import RabitqHnswVectorStore
vector_store = RabitqHnswVectorStore(dim=1536) # your embedding model's dimension
storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex.from_documents(documents, storage_context=storage_context)
nodes = index.as_retriever(similarity_top_k=5).retrieve("my question")
index.delete_ref_doc("some-doc-id", delete_from_docstore=True)
# Persist and reload. The vector store has to be loaded explicitly, because
# StorageContext.from_defaults(persist_dir=...) alone would load SimpleVectorStore.
index.storage_context.persist(persist_dir="./storage")
vector_store = RabitqHnswVectorStore.from_persist_dir("./storage")
storage_context = StorageContext.from_defaults(
vector_store=vector_store, persist_dir="./storage"
)
index = load_index_from_storage(storage_context)
examples/quickstart.py is a runnable offline version that uses a toy embedding and no LLM. It passes min_train_size=1 so its three documents go into the HNSW index. With the default of 1024, a store that small is answered by an exact scan of the buffer and never reaches RaBitQ (see "Training is deferred" below).
Options
| Option | Default | Meaning |
|---|---|---|
dim |
required | Embedding dimension, 64 to 65536. |
metric |
"cosine" |
"cosine" (normalizes vectors, then uses inner product), "ip", or "l2". |
nbits |
7 | RaBitQ bits per dimension, 1 to 9. Fewer bits means less memory and lower recall. |
M, ef_construction |
16, 200 | HNSW graph parameters. |
num_clusters |
16 | RaBitQ centroids, trained once. |
seed |
100 | Graph, rotation and clustering seed. |
ef |
max(k, 64) |
Search breadth; always raised to at least similarity_top_k. Can be changed at any time (store.ef = 128). |
num_threads |
library default | Threads for add, train and search; 0 means all CPUs. With more than one thread the graph is built in a nondeterministic order, so results can differ slightly between runs. Use 1 for a reproducible graph. |
min_train_size |
1024 | Embeddings buffered before the index is trained (see below). |
train_vectors / centroids |
none | Train up front instead of buffering. Centroids are used as given. |
How it works
- Text lives in LlamaIndex's docstore (
stores_text = False), as with FAISS. The store keeps only embeddings plus a node-ID ↔ index-ID map and a ref-doc ↔ node map. - Training is deferred. RaBitQ trains its centroids once, on the first vectors it gets, and can't retrain afterwards. Training on a handful of vectors would hurt every later query. So until
min_train_sizeembeddings have arrived they're kept in a float32 buffer and searched exactly. When the threshold is reached, the whole buffer is added to a newHnswIndexin one call, which trains on it. Passtrain_vectors=orcentroids=to skip the buffer. - Adds are batched. Each
add(nodes)makes oneHnswIndex.addcall. Re-adding an existing node ID replaces its embedding. - Scores are higher-is-better, computed from RaBitQ distance estimates. They're close to exact at
nbits=7, but not exact:cosine: cosine similarity, clipped to [-1, 1];ip: inner product (unbounded);l2:1 / (1 + squared_l2), in (0, 1].
- Persistence.
persist(path)writes JSON metadata topath(for exampledefault__vector_store.json). Alongside it gopath.hnsw(the index),path.buffer.npy(vectors still buffered) orpath.train.npy(training data not yet used). The JSON has aformat_version. Only the local filesystem is supported. - Thread safety. Queries and
persistrun concurrently with each other, though twopersistcalls run one at a time. Add, delete and clear are exclusive. Waiting writers don't block new queries, so a continuous stream of overlapping queries can hold an add back indefinitely. That's unlikely with RAG-style traffic.
Limitations (v0.1)
- No metadata filters. A query with
filters, ordelete_nodes(filters=...), raisesValueError, and so does a query restricted todoc_ids. Only the default query mode is supported, so no MMR, hybrid or sparse search. node_idson a query is ignored, as inFaissVectorStore, soindex.as_retriever(node_ids=subset)searches every node.as_retriever()always passes the node IDs it knew about when it was created. Honouring that list would break long-lived retrievers after an insert, and would cost O(n) per query.- Deletes don't reclaim memory. Deleted nodes are excluded from results but stay in storage. To reclaim it, rebuild from your documents.
- Accuracy is bounded by quantization. Recall can plateau below 1.0 however large
efis (see below). Raisenbitsfor higher accuracy. - No compaction or retraining. Centroids are fixed after the first training. If your data drifts a lot, rebuild.
Measured recall
Run with examples/recall.py. Setup: 10,000 base and 200 query vectors, dim 128, drawn from 50 Gaussian clusters (seed 0); default M=16, ef_construction=200, num_clusters=16; graph built with num_threads=1, so runs are reproducible; top-10 compared against an exact NumPy scan. Machine: 96-core Linux, rabitqlib 0.8.3, llama-index-core 0.14.25. With multi-threaded builds, expect differences of about ±0.002.
| metric | nbits | ef=64 | ef=128 | ef=256 |
|---|---|---|---|---|
| cosine | 4 | 0.7335 | 0.7335 | 0.7335 |
| cosine | 7 | 0.9510 | 0.9510 | 0.9510 |
| l2 | 4 | 0.7685 | 0.7680 | 0.7680 |
| l2 | 7 | 0.9680 | 0.9685 | 0.9685 |
On this dataset, recall is limited by the quantized distance estimates, not by graph search: calling rabitqlib.HnswIndex directly gives the same numbers, and they level off from about ef=64. HnswIndex has no exact re-ranking step, so raising ef can't push recall past this ceiling; only a higher nbits can. This is one synthetic dataset, so measure on your own embeddings.
Development
python -m pytest --cov=llama_index.vector_stores.rabitqlib
ruff check . && ruff format --check .
mypy llama_index tests
License
Apache-2.0, the same as RaBitQ-Library.
Metadata
Release files for llama-index-vector-stores-rabitqlib 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| llama_index_vector_stores_rabitqlib-0.1.0.tar.gz | 14.6 kB | Without provenance | Details for llama_index_vector_stores_rabitqlib-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| llama_index_vector_stores_rabitqlib-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for llama_index_vector_stores_rabitqlib-0.1.0-py3-none-any.whl |
Total release size: 30.4 kB
Release files / llama_index_vector_stores_rabitqlib-0.1.0.tar.gz
| Download URL | llama_index_vector_stores_rabitqlib-0.1.0.tar.gz |
|---|---|
| Size | 14.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0ce6e205a90efd54594df320429914de70e46a135e79fe46b4c86fe2180175b1
|
|
BLAKE2b-256 checksum How to use checksums |
3b78470d50bc1e45b218253a492a1c1d19da6ae1edeff7f79427b7472af6eb6a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.0rc1
|
Release files / llama_index_vector_stores_rabitqlib-0.1.0-py3-none-any.whl
| Download URL | llama_index_vector_stores_rabitqlib-0.1.0-py3-none-any.whl |
|---|---|
| Size | 15.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bdbe8817bccfe435f1b4ed58d4d882054caf329b2f7811e8ea69af9e943fd8f6
|
|
BLAKE2b-256 checksum How to use checksums |
6299841305baf260b15ad3f3798bc235efba48ce2d09112be907defe39fc75af
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.0rc1
|
Security for llama-index-vector-stores-rabitqlib 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.