ann_router.base module
The common ANN index interface every backend implements.
The router’s whole value proposition is that eight very different vector-search engines — from a pure-numpy brute-force scan to Qdrant — can be driven through one small surface. This module defines that surface:
Capabilities— a static descriptor of what a backend can do (remove? filter? persist? needs a GPU?), so the router can reason about a backend without importing its (possibly absent) dependency.ANNIndex— the abstract base class withbuild/add/add_with_ids/remove/search/save/load.NotSupportedandBackendUnavailable— the two honest failure modes: an operation a backend genuinely cannot do (Annoy removes), versus a backend whose optional dependency is not installed.
Consumes: ann_router.spec (metric names).
Produces: the base classes every module in ann_router.backends subclasses.
Author: Warith Harchaoui <warith.harchaoui@deraison.ai>
- class ann_router.base.ANNIndex(dim, metric='cosine', **kwargs)[source]
Bases:
ABCAbstract base class for every ANN backend adapter.
Subclasses wrap one engine behind a uniform surface so the router — and downstream code — can build, query, mutate, and persist an index without knowing which engine is underneath. Not every backend supports every operation; those raise
NotSupportedand advertise the limitation viacapabilities().- Parameters:
dim (int) – Embedding dimensionality the index is built for.
metric ({"cosine", "l2", "ip"}, optional) – Distance metric. Defaults to
"cosine".**kwargs – Backend-specific build parameters (e.g. HNSW
M).
Notes
Concrete constructors must not import their heavy dependency at module import time — only inside methods (or a lazily-called checker) — so that
import ann_routerstays cheap and dependency-free.- abstractmethod add(vectors)[source]
Append vectors, assigning them the next contiguous ids.
- Parameters:
vectors (numpy.ndarray) – Shape
(m, dim).- Raises:
NotSupported – If the backend is frozen after
build(e.g. Annoy).- Return type:
None
- abstractmethod 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.
- Raises:
NotSupported – If the backend cannot map external ids or is frozen.
- Return type:
None
- abstractmethod build(vectors, ids=None)[source]
Build the index from an initial batch of vectors.
- Parameters:
vectors (numpy.ndarray) – Shape
(n, dim), dtype float32 (coerced if needed).ids (numpy.ndarray, optional) – Shape
(n,)integer ids. Defaults torange(n).
- Returns:
self, so calls can be chained.- Return type:
- abstractmethod classmethod capabilities()[source]
Return the static capability descriptor for this backend.
- Returns:
What the backend can do — readable without importing its dependency.
- Return type:
- abstractmethod classmethod is_available()[source]
Return
Trueif the backend’s dependency is importable.- Returns:
Truewhen the engine can actually be used on this machine.- Return type:
- abstractmethod remove(ids)[source]
Delete vectors by id.
- Parameters:
ids (numpy.ndarray) – Shape
(m,)integer ids to drop.- Raises:
NotSupported – If the backend cannot delete (e.g. Annoy) or only tombstones.
- Return type:
None
- abstractmethod save(path)[source]
Persist the index to
path.- Parameters:
path (str) – Destination file (or directory) path.
- Raises:
NotSupported – If the backend cannot serialise itself.
- Return type:
None
Bases:
ImportErrorRaised when a backend’s optional dependency is not installed.
Importing
ann_routermust never fail becausefaissorturbovecare absent, so backends defer their heavy imports. When a caller actually tries to use an uninstalled backend, this actionable error names thepip installextra that fixes it.Examples
>>> raise BackendUnavailable("faiss not installed. Run: pip install 'ann-router[faiss]'") Traceback (most recent call last): ... ann_router.base.BackendUnavailable: faiss not installed. Run: pip install 'ann-router[faiss]'
- class ann_router.base.Capabilities(name, supports_add, supports_remove, supports_filter, persistent, needs_gpu, approximate, metrics, pip_extra)[source]
Bases:
objectStatic description of what a backend can and cannot do.
The router reads this without importing the backend’s dependency, so it can rank engines even when their libraries are absent. Each flag maps to a routing consequence documented inline.
- Parameters:
name (str) – The backend identifier (matches
ann_router.spec.BackendName).supports_add (bool) – Can accept vectors after the initial
build(streaming inserts).supports_remove (bool) – Can delete vectors by id without a full rebuild.
supports_filter (bool) – Can restrict a search by structured metadata / payload.
persistent (bool) – Naturally survives process restarts (a database) as opposed to needing an explicit
save/loadround-trip.needs_gpu (bool) – Requires a GPU to be worthwhile (or at all).
approximate (bool) – Returns approximate neighbours (vs. exact ground truth).
metrics (tuple of str) – The distance metrics the backend supports.
pip_extra (str) – The optional-dependency extra that installs it (empty for
exact).
Examples
>>> cap = Capabilities(name="exact", supports_add=True, supports_remove=True, ... supports_filter=False, persistent=False, needs_gpu=False, ... approximate=False, metrics=("cosine", "l2", "ip"), pip_extra="") >>> cap.approximate False
- exception ann_router.base.NotSupported[source]
Bases:
RuntimeErrorRaised when a backend genuinely cannot perform a requested operation.
This is distinct from “not yet implemented”: it flags a fundamental limitation of the engine (e.g. Annoy is frozen after
buildand cannot add or remove). The router uses the capability descriptor to avoid routing a dynamic workload to such a backend, but the guard here is the last line of defence for callers who instantiate a backend directly.Examples
>>> raise NotSupported("annoy: remove() unsupported (frozen index)") Traceback (most recent call last): ... ann_router.base.NotSupported: annoy: remove() unsupported (frozen index)