ann_router.backends package
Submodules
Module contents
Backend adapters, one module per ANN engine.
Each module here wraps a single vector-search engine behind the shared
ann_router.base.ANNIndex contract. Only ann_router.backends.exact
has no optional dependency; every other module defers its heavy import so that
import ann_router (and importing this package) never fails because an engine
is not installed. Use ann_router.registry to look adapters up by name.
Author: Warith Harchaoui <warith.harchaoui@deraison.ai>
- class ann_router.backends.AnnoyIndex(dim, metric='cosine', **kwargs)[source]
Bases:
ANNIndexAnnoy random-projection-forest index: build once, then read-only.
Because Annoy has no external-id concept, this adapter keeps its own
position -> idtable sosearchreturns the caller’s ids. External ids must therefore be supplied atbuildtime and are fixed thereafter.- Parameters:
dim (int) – Embedding dimensionality.
metric ({"cosine", "l2", "ip"}, optional) – Distance metric. Defaults to
"cosine"(Annoy “angular”).n_trees (int, optional) – Number of projection trees (more == better recall, larger index). Defaults to 50.
search_k (int, optional) – Nodes inspected at query time (
-1== Annoy’sn_trees * kdefault).kwargs (object)
Examples
>>> AnnoyIndex.capabilities().supports_remove False
- add(vectors)[source]
Not supported — Annoy is frozen after
build().- Parameters:
vectors (numpy.ndarray) – Shape
(m, dim). Unused — always raises.- Raises:
NotSupported – Always; the forest is frozen after
build().- Return type:
None
- add_with_ids(vectors, ids)[source]
Not supported — Annoy is frozen after
build().- Parameters:
vectors (numpy.ndarray) – Shape
(m, dim). Unused — always raises.ids (numpy.ndarray) – Shape
(m,). Unused — always raises.
- Raises:
NotSupported – Always; the forest is frozen after
build().- Return type:
None
- build(vectors, ids=None)[source]
Build and freeze the projection forest.
- Parameters:
vectors (numpy.ndarray) – Shape
(n, dim).ids (numpy.ndarray, optional) – Shape
(n,); defaults torange(n).
- Returns:
self.- Return type:
- classmethod capabilities()[source]
Return the Annoy capability descriptor (frozen: no add/remove).
- Return type:
- classmethod is_available()[source]
Return
Trueif annoy is importable.Examples
>>> isinstance(AnnoyIndex.is_available(), bool) True
- Return type:
- load(path)[source]
Memory-map a forest written by
save().- Parameters:
- Returns:
self, populated from disk.- Return type:
- remove(ids)[source]
Not supported — Annoy cannot delete; rebuild without the ids.
- Parameters:
ids (numpy.ndarray) – Shape
(m,). Unused — always raises.- Raises:
NotSupported – Always; Annoy has no delete operation.
- Return type:
None
- save(path)[source]
Persist the forest and the id table.
The Annoy file itself has no room for external ids, so the id table is written alongside as
<path>.ids.npy.- Parameters:
path (str) – Destination file path.
- Return type:
None
- class ann_router.backends.ExactIndex(dim, metric='cosine', **kwargs)[source]
Bases:
ANNIndexExact k-NN by full matrix multiplication over the stored corpus.
The corpus is kept as a contiguous float32 matrix; for cosine/inner-product metrics search is a single
queries @ corpus.Tfollowed by a top-k partition, and for L2 it is the standard||q||^2 - 2 q·x + ||x||^2expansion. All operations are exact, so this class defines “truth” for the recall benchmarks.- Parameters:
Examples
>>> rng = np.random.default_rng(0) >>> vecs = rng.standard_normal((100, 16)).astype(np.float32) >>> idx = ExactIndex(dim=16).build(vecs) >>> ids, dists = idx.search(vecs[:1], k=1) >>> int(ids[0, 0]) # nearest neighbour of a point is itself 0
- add(vectors)[source]
Append vectors with the next contiguous ids.
- Parameters:
vectors (numpy.ndarray) – Shape
(m, dim).- Return type:
None
Examples
>>> idx = ExactIndex(dim=4).build(np.ones((2, 4), dtype=np.float32)) >>> idx.add(np.ones((3, 4), dtype=np.float32)) >>> idx.size 5
- add_with_ids(vectors, ids)[source]
Append vectors with explicit ids.
- Parameters:
vectors (numpy.ndarray) – Shape
(m, dim).ids (numpy.ndarray) – Shape
(m,).
- Return type:
None
Examples
>>> idx = ExactIndex(dim=4).build(np.ones((2, 4), dtype=np.float32)) >>> idx.add_with_ids(np.zeros((1, 4), dtype=np.float32), np.array([99])) >>> idx.size 3
- build(vectors, ids=None)[source]
Store the initial corpus.
- Parameters:
vectors (numpy.ndarray) – Shape
(n, dim).ids (numpy.ndarray, optional) – Shape
(n,); defaults torange(n).
- Returns:
self.- Return type:
Examples
>>> idx = ExactIndex(dim=4).build(np.ones((5, 4), dtype=np.float32)) >>> idx.search(np.ones((1, 4), dtype=np.float32), k=3)[0].shape (1, 3)
- classmethod capabilities()[source]
Return the exact backend’s capability descriptor.
- Returns:
Exact, mutable, in-memory, no GPU, no metadata filter.
- Return type:
Examples
>>> ExactIndex.capabilities().approximate False
- classmethod is_available()[source]
Return
True— numpy is a core dependency, so exact always works.- Returns:
Always
True.- Return type:
Examples
>>> ExactIndex.is_available() True
- load(path)[source]
Load a corpus previously written by
save().- Parameters:
path (str) – Source
.npzpath.- Returns:
self, populated from disk.- Return type:
- remove(ids)[source]
Delete vectors by id (true deletion — the rows are dropped).
- Parameters:
ids (numpy.ndarray) – Ids to remove.
- Return type:
None
Examples
>>> idx = ExactIndex(dim=4).build(np.ones((3, 4), dtype=np.float32)) >>> idx.remove(np.array([1])) >>> idx.size 2
- save(path)[source]
Persist the corpus and ids to a
.npzfile.- Parameters:
path (str) – Destination path (
.npzappended by numpy if absent).- Return type:
None
Examples
>>> import tempfile, os >>> idx = ExactIndex(dim=4).build(np.ones((3, 4), dtype=np.float32)) >>> p = os.path.join(tempfile.mkdtemp(), "exact.npz") >>> idx.save(p); os.path.exists(p) or os.path.exists(p + ".npz") True
- search(queries, k)[source]
Return exact top-
kneighbours per query.- Parameters:
queries (numpy.ndarray) – Shape
(q, dim).k (int) – Neighbours per query.
- Returns:
ids (numpy.ndarray) – Shape
(q, k)neighbour ids (-1where fewer thankexist).distances (numpy.ndarray) – Shape
(q, k)distances (similarity for cosine/ip, euclidean^2 for l2).
- Return type:
tuple[ndarray, ndarray]
Examples
>>> rng = np.random.default_rng(1) >>> vecs = rng.standard_normal((50, 8)).astype(np.float32) >>> ids, _ = ExactIndex(dim=8).build(vecs).search(vecs[:3], k=5) >>> ids.shape (3, 5)
- class ann_router.backends.FaissIndex(dim, metric='cosine', **kwargs)[source]
Bases:
ANNIndexFAISS IVF(-PQ) index with id mapping and auto-sized coarse quantiser.
- Parameters:
dim (int) – Embedding dimensionality.
metric ({"cosine", "l2", "ip"}, optional) – Distance metric. Defaults to
"cosine".nlist (int, optional) – Number of IVF cells. Defaults to
auto(~sqrt(n)*4, clamped).nprobe (int, optional) – Cells probed at query time (recall/latency trade). Defaults to 16.
use_pq (bool or "auto", optional) – Enable Product Quantisation.
"auto"(default) turns it on abovepq_thresholdvectors.m (int, optional) – PQ sub-quantiser count (must divide
dim). Defaults to a divisor neardim/2.kwargs (object)
Examples
>>> FaissIndex.capabilities().name 'faiss'
- add(vectors)[source]
Append vectors with the next contiguous ids.
- Parameters:
vectors (numpy.ndarray) – Shape
(m, dim).- Return type:
None
- add_with_ids(vectors, ids)[source]
Append vectors with explicit ids (index must already be trained).
- Parameters:
vectors (numpy.ndarray) – Shape
(m, dim).ids (numpy.ndarray) – Shape
(m,)integer ids.
- Raises:
NotSupported – If called before
build()(IVF needs training first).- Return type:
None
- build(vectors, ids=None)[source]
Train and populate the IVF(-PQ) index.
- Parameters:
vectors (numpy.ndarray) – Shape
(n, dim).ids (numpy.ndarray, optional) – Shape
(n,); defaults torange(n).
- Returns:
self.- Return type:
- classmethod capabilities()[source]
Return the FAISS capability descriptor (GPU-capable, add/remove ok).
- Return type:
- classmethod is_available()[source]
Return
Trueif faiss is importable.Examples
>>> isinstance(FaissIndex.is_available(), bool) True
- Return type:
- load(path)[source]
Load an index written by
save().- Parameters:
- Returns:
self, populated from disk.- Return type:
- remove(ids)[source]
Remove vectors by id via the id map.
- Parameters:
ids (numpy.ndarray) – Shape
(m,)integer ids to drop.- Return type:
None
- save(path)[source]
Persist via faiss.write_index.
- Parameters:
path (str) – Destination file path.
- Return type:
None
- class ann_router.backends.HNSWIndex(dim, metric='cosine', **kwargs)[source]
Bases:
ANNIndexhnswlib-backed graph index tuned for high recall on a fixed corpus.
Build knobs (
M,ef_construction) and the query knob (ef) are passed through and default to values that hit ~0.95+ recall on typical 768-d embeddings. The index is grown tomax_elementslazily and doubled on overflow so streamingaddstill works within the “stable corpus” caveat.- Parameters:
dim (int) – Embedding dimensionality.
metric ({"cosine", "l2", "ip"}, optional) – Distance metric. Defaults to
"cosine".M (int, optional) – Graph out-degree. Defaults to 16.
ef_construction (int, optional) – Build-time search width. Defaults to 200.
ef (int, optional) – Query-time search width (recall/latency trade). Defaults to 64.
kwargs (object)
Examples
>>> HNSWIndex.capabilities().name 'hnsw'
- add(vectors)[source]
Append vectors with the next contiguous ids.
- Parameters:
vectors (numpy.ndarray) – Shape
(m, dim).- Return type:
None
- add_with_ids(vectors, ids)[source]
Append vectors with explicit ids, growing capacity if needed.
- Parameters:
vectors (numpy.ndarray) – Shape
(m, dim).ids (numpy.ndarray) – Shape
(m,)integer ids.
- Return type:
None
- build(vectors, ids=None)[source]
Build the graph from an initial corpus.
- Parameters:
vectors (numpy.ndarray) – Shape
(n, dim).ids (numpy.ndarray, optional) – Shape
(n,); defaults torange(n).
- Returns:
self.- Return type:
- classmethod capabilities()[source]
Return the HNSW capability descriptor (remove is tombstone-only).
- Return type:
- classmethod is_available()[source]
Return
Trueif hnswlib is importable.Examples
>>> isinstance(HNSWIndex.is_available(), bool) True
- Return type:
- remove(ids)[source]
Tombstone the given ids (graph is not reclaimed — rebuild for that).
- Parameters:
ids (numpy.ndarray) – Shape
(m,)integer ids to tombstone.- Return type:
None
- save(path)[source]
Persist the graph via hnswlib’s native serialiser.
- Parameters:
path (str) – Destination file path.
- Return type:
None
- class ann_router.backends.PgVectorIndex(dim, metric='cosine', **kwargs)[source]
Bases:
ANNIndexA single vectors table in PostgreSQL, indexed with pgvector HNSW.
Requires a DSN (
dsn=kwarg or theANN_ROUTER_PG_DSNenv var). Points live in a(id bigint, embedding vector(dim), payload jsonb)table so the SQLWHEREpath can filter onpayload.- Parameters:
Examples
>>> PgVectorIndex.capabilities().persistent True
- add(vectors)[source]
Append vectors with ids continuing past the current max.
- Parameters:
vectors (numpy.ndarray) – Shape
(m, dim).- Return type:
None
- add_with_ids(vectors, ids)[source]
Append vectors with explicit ids.
- Parameters:
vectors (numpy.ndarray) – Shape
(m, dim).ids (numpy.ndarray) – Shape
(m,)integer ids.
- Return type:
None
- build(vectors, ids=None, payloads=None)[source]
(Re)create the table, insert the corpus, and build the HNSW index.
- Parameters:
- Returns:
self.- Return type:
- classmethod capabilities()[source]
Return the pgvector capability descriptor (persistent + filterable).
- Return type:
- classmethod is_available()[source]
Return
Trueif psycopg + pgvector import (a live DSN is still needed).Examples
>>> isinstance(PgVectorIndex.is_available(), bool) True
- Return type:
- load(path)[source]
Reconnect to the existing table (
pathmay override the DSN).- Parameters:
path (str) – A DSN to reconnect with, or falsy to reuse the constructor’s DSN.
- Returns:
self, reconnected.- Return type:
- remove(ids)[source]
Delete rows by id.
- Parameters:
ids (numpy.ndarray) – Shape
(m,)integer ids to drop.- Return type:
None
- save(path)[source]
No-op — the table lives in PostgreSQL and is already durable.
- Parameters:
path (str) – Unused — accepted only to satisfy the shared interface.
- Return type:
None
Notes
Persistence is the database’s job; there is nothing to serialise. Use the same DSN + table to
load()the index in another process.
- search_filter(queries, k, where=None)[source]
Return top-
kneighbours, optionally filtered by apayloadclause.- Parameters:
- Returns:
ids (numpy.ndarray) – Shape
(q, k)(-1pads short rows).distances (numpy.ndarray) – Shape
(q, k)distances under the metric operator.
- Return type:
tuple[ndarray, ndarray]
- class ann_router.backends.QdrantIndex(dim, metric='cosine', **kwargs)[source]
Bases:
ANNIndexQdrant collection wrapper (embedded by default) with payload filtering.
- Parameters:
dim (int) – Embedding dimensionality.
metric ({"cosine", "l2", "ip"}, optional) – Distance metric. Defaults to
"cosine".location (str, optional) –
":memory:"(default, embedded), a directory path (embedded on-disk), or a URL for a remote server.collection (str, optional) – Collection name. Defaults to
"ann_router".kwargs (object)
Examples
>>> QdrantIndex.capabilities().supports_filter True
- add(vectors)[source]
Append vectors with the next contiguous ids.
- Parameters:
vectors (numpy.ndarray) – Shape
(m, dim).- Return type:
None
- add_with_ids(vectors, ids)[source]
Append vectors with explicit ids.
- Parameters:
vectors (numpy.ndarray) – Shape
(m, dim).ids (numpy.ndarray) – Shape
(m,)integer ids.
- Return type:
None
- build(vectors, ids=None, payloads=None)[source]
Create the collection and upsert the initial corpus.
- Parameters:
- Return type:
- classmethod capabilities()[source]
Return the Qdrant capability descriptor (persistent + filterable).
- Return type:
- classmethod is_available()[source]
Return
Trueif qdrant-client is importable.Examples
>>> isinstance(QdrantIndex.is_available(), bool) True
- Return type:
- load(path)[source]
Reconnect to an on-disk collection at
path.- Parameters:
path (str) – The on-disk
locationto reconnect to.- Returns:
self, reconnected.- Return type:
- remove(ids)[source]
Delete points by id.
- Parameters:
ids (numpy.ndarray) – Shape
(m,)integer ids to drop.- Return type:
None
- save(path)[source]
No-op for embedded on-disk / remote collections (already persistent).
- Parameters:
path (str) – Unused — accepted only to satisfy the shared interface.
- Return type:
None
Notes
Qdrant persists itself when
locationis a directory or a server URL; the:memory:client is ephemeral by design. Pointsaveat a directorylocationinstead of calling this for durability.
- search_filter(queries, k, where=None)[source]
Return top-
kneighbours, optionally restricted by a payload filter.- Parameters:
- Returns:
ids (numpy.ndarray) – Shape
(q, k)(-1pads short rows when a filter is strict).distances (numpy.ndarray) – Shape
(q, k)scores.
- Return type:
tuple[ndarray, ndarray]
- class ann_router.backends.TurboVecIndex(dim, metric='cosine', **kwargs)[source]
Bases:
ANNIndexturbovec
IdMapIndex: mutable, quantised, id-native.turbovec is id-native (
add_with_ids/remove(id)) and returns(distances, ids)fromsearch— this adapter flips that to the package’s(ids, distances)order. Vectors are normalised for cosine so the quantiser’s inner product matches cosine similarity.- Parameters:
dim (int) – Embedding dimensionality.
metric ({"cosine", "l2", "ip"}, optional) – Distance metric. Defaults to
"cosine". turbovec is inner-product / cosine oriented; L2 is approximated on normalised vectors.bit_width (int, optional) – TurboQuant bit width (2 or 4). Defaults to 4 — the recall/size sweet spot measured in the roitelet study.
kwargs (object)
Examples
>>> TurboVecIndex.capabilities().supports_remove True
- add(vectors)[source]
Append vectors with the next contiguous ids.
- Parameters:
vectors (numpy.ndarray) – Shape
(m, dim).- Return type:
None
- add_with_ids(vectors, ids)[source]
Append vectors with explicit ids (O(1), no rebuild).
- Parameters:
vectors (numpy.ndarray) – Shape
(m, dim).ids (numpy.ndarray) – Shape
(m,)integer ids.
- Return type:
None
- build(vectors, ids=None)[source]
Create the index and insert the initial corpus.
- Parameters:
vectors (numpy.ndarray) – Shape
(n, dim).ids (numpy.ndarray, optional) – Shape
(n,); defaults torange(n).
- Returns:
self.- Return type:
- classmethod capabilities()[source]
Return the turbovec capability descriptor (fully mutable).
- Return type:
- classmethod is_available()[source]
Return
Trueif turbovec is importable.Examples
>>> isinstance(TurboVecIndex.is_available(), bool) True
- Return type:
- load(path)[source]
Load an index written by
save().- Parameters:
- Returns:
self, populated from disk.- Return type:
- remove(ids)[source]
Delete vectors by id — O(1) each, no structural degradation.
- Parameters:
ids (numpy.ndarray) – Shape
(m,)integer ids to drop.- Return type:
None
- save(path)[source]
Persist via turbovec’s native
write.- Parameters:
path (str) – Destination file path.
- Return type:
None