vd.base#
Core contracts, data model, and abstract bases for the vd vectorDB facade.
vd is a facade over vector databases. This module is the single source of
truth for the contract every backend adapter satisfies and that all of vd’s
higher-level tooling (search, io, migration, analytics, …) is written against.
The contract, smallest-first:
Document— the unit stored in a collection:id,text,vector,metadata.Collection— aMutableMapping[str, Document]plus asearch()method. This is the one retrieval extension.Client— aMapping[str, Collection]: a live connection to one backend, through which collections are created, fetched, and dropped.AbstractCollection/AbstractClient— adapter-author conveniences. A backend implements a handful of raw primitives; these bases supply everything users actually see (flexible inputs, optional text embedding,egresstransforms, batch helpers, dimension checks) uniformly.
Embedding is deliberately external. vd stores and searches vectors;
turning text into vectors is another package’s job (e.g. ef). A
Client may be handed an optional embedder callable purely as a
convenience, so collection["k"] = "some text" and
collection.search("query text") work. With no embedder configured those
text forms raise EmbeddingRequiredError, and the caller must pass
Document objects (or pre-computed vectors) directly.
Module Attributes
A document may be supplied to batch operations in several flexible shapes. |
|
Distance metrics the facade understands. |
Classes
|
Base class implementing the |
Base class implementing the |
|
|
The async sibling of |
|
The async sibling of |
|
A live connection to one backend: |
|
A collection of documents: |
|
The unit stored in a |
|
A collection that supports efficient batch insertion. |
|
A collection that supports native hybrid (dense + lexical) search. |
|
Marker protocol set on async clients/collections that use a backend's native async SDK rather than the universal |
Exceptions
Raised when a known backend's Python package is not installed. |
|
Raised when text is given but no embedder is configured. |
|
Raised on a write to a static (immutable) index. |
|
Raised when an operation needs a capability the backend lacks. |
|
Raised when a metadata filter uses an operator a backend cannot honor. |
|
Base class for every error |
- class vd.base.AbstractClient(*, embedder=None, **config)[source]#
Bases:
MappingBase class implementing the
Clientcontract for adapters.A
Clientis aMapping[str, Collection]. A backend subclasses this and implementscreate_collection(),get_collection(),delete_collection(), andlist_collections(); the mapping behavior, theget_or_create_collection()convenience, theclientescape hatch, and context-manager support come for free.- Parameters:
- property client: Any#
The raw backend client — a supported, documented escape hatch.
Drop to it for backend-specific operations the facade does not expose. Returns
Nonefor backends with no external client object (e.g. the in-memory backend).
- close()[source]#
Release backend resources. Default no-op; adapters override as needed.
- Return type:
- abstractmethod create_collection(name, *, dimension=None, metric='cosine', **index_config)[source]#
Create a new collection.
- Parameters:
name (
str) – Collection name.dimension (
Optional[int]) – Vector dimension. May beNonefor backends that can infer it from the first written vector; required up front by backends that cannot.metric (
str) – Distance metric:"cosine","dot", or"l2".**index_config – Backend-specific index tuning (HNSW
M/ef, IVFnlist, …). Documented per adapter; never abstracted into a common enum.
- Raises:
ValueError – If a collection of that name already exists.
- Return type:
- abstractmethod delete_collection(name)[source]#
Drop a collection; raise
KeyErrorif absent.- Return type:
- abstractmethod get_collection(name)[source]#
Return an existing collection; raise
KeyErrorif absent.- Return type:
- class vd.base.AbstractCollection[source]#
Bases:
_CollectionPolicy,MutableMappingBase class implementing the
Collectioncontract for adapters.A backend subclasses this and implements the raw primitives below; everything users see is provided here, once, uniformly:
flexible
__setitem__inputs (text / tuple /Document),optional text embedding when a
Documentarrives without a vector,text-query embedding in
search(),central filter validation against
supported_filter_operators,egressresult transforms,batch helpers (
add_documents(),upsert()),eager dimension-mismatch detection.
Subclass responsibilities (raw primitives)#
_write(doc)Upsert one document. Its
vectoris guaranteed non-Noneand dimension-checked._read(key) -> DocumentFetch one document; raise
KeyErrorif absent._drop(key)Delete one document; raise
KeyErrorif absent._keys() -> Iterator[str]Iterate document ids.
_count() -> intNumber of documents.
_query(vector, *, limit, filter, **kwargs) -> Iterable[SearchResult]Raw nearest-neighbor search.
filteris the canonical AST — the adapter translates it. Each result is a dict with at leastid,text,score,metadata.
Optional overrides#
_write_many(docs)Efficient bulk upsert. Defaults to a loop over
_write.native(property)The raw backend collection handle (escape hatch).
- add_documents(documents, *, batch_size=100)[source]#
Add many documents, embedding and writing them in batches.
Each item may be a string, a
(text, ...)tuple, or aDocument(seeDocumentInput). Items without anidget a deterministic auto-generated one.- Return type:
- property native: Any#
The raw backend collection handle — a supported, documented escape hatch.
Use it to reach backend-specific features the facade does not expose, rather than circumventing
vd. ReturnsNoneif the adapter has no distinct native object.
- search(query, *, limit=10, filter=None, egress=None, **kwargs)[source]#
Return the
limitdocuments most similar toquery.- Parameters:
query (
Union[str,list[float]]) – Query text (embedded via the client’sembedder) or a pre-computed query vector.limit (
int) – Maximum number of results.filter (
Optional[dict[str,Any]]) – Metadata filter in the canonicalvddialect (seevd.filters). Validated against this backend’ssupported_filter_operatorsbefore the query runs, so an unsupported operator fails with a clearUnsupportedFilterError.egress (
Optional[Callable[[dict[str,Any]],Any]]) – Transform applied to each result dict before it is yielded.**kwargs – Backend-specific search options, passed through to
_query.
- Yields:
dict –
{"id", "text", "score", "metadata"}— or whateveregressreturns.scoreis a higher-is-better, per-metric canonical similarity (see the “Score semantics” table at the top ofvd.base): cosine in[-1, 1], dot in(-inf, +inf), l2 squashed to(0, 1]. Adapters whose backend returns a native combined-ranking score on a different scale (e.g. Elasticsearch, Atlas, Pinecone) document the deviation in their own docstring.- Return type:
- class vd.base.AsyncClient(*args, **kwargs)[source]#
Bases:
ProtocolThe async sibling of
Client.Same operations — collection create / fetch / drop / list — exposed as awaitables and async iterators. Construct via
vd.connect_async().
- class vd.base.AsyncCollection(*args, **kwargs)[source]#
Bases:
ProtocolThe async sibling of
Collection.Same conceptual surface — storage +
search— but every method is awaitable and iterators areAsyncIterator. The mapping interface is exposed as explicitget/set/delete/keys/countmethods (the stdlib’sMutableMappingABC has no async counterpart; explicit methods are the Motor / aiopg convention).Construct via
vd.connect_async(); the universalAsyncCollectionWrapperinvd.asynchronousadapts every backend to this protocol by dispatching to the sync API throughasyncio.to_thread(). Backends with native async SDKs override the wrapper and additionally satisfySupportsNativeAsync.
- exception vd.base.BackendNotInstalledError[source]#
Bases:
VdError,ImportErrorRaised when a known backend’s Python package is not installed.
Distinct from an unknown backend name (a plain
ValueError): the backend exists invd’s provider registry, but its client library is missing. The message carries thepip installcommand to fix it.
- class vd.base.Client(*args, **kwargs)[source]#
Bases:
ProtocolA live connection to one backend:
Mapping[str, Collection].Collections are created explicitly (so create-time parameters such as
dimensionandmetriccan be supplied) and fetched either byget_collection()or by mapping accessclient[name].- create_collection(name, *, dimension=None, metric='cosine', **index_config)[source]#
Create a new collection; raise
ValueErrorif it exists.- Return type:
- class vd.base.Collection(*args, **kwargs)[source]#
Bases:
ProtocolA collection of documents:
MutableMapping[str, Document]+search.The mapping half is storage;
search()is the single retrieval extension. This minimal surface is everythingvd’s tooling depends on. Batch insertion is an optional capability — seeSupportsBatch.
- class vd.base.Document(id, text='', vector=None, metadata=<factory>)[source]#
Bases:
objectThe unit stored in a
Collection.- Parameters:
id (
str) – Unique identifier; the key under which the document lives in a collection.text (
str) – The text content. May be empty for vector-first use cases where no text is associated with a vector.vector (
Optional[list[float]]) – The embedding. IfNonewhen written, the collection embedstextwith its client’sembedder— or raisesEmbeddingRequiredErrorif none is configured.metadata (
dict[str,Any]) – Arbitrary metadata, used for filtering and carried through search results.
Examples
>>> doc = Document(id="doc1", text="Hello world") >>> doc.id, doc.text, doc.metadata ('doc1', 'Hello world', {}) >>> Document(id="v1", vector=[0.1, 0.2]).text ''
- vd.base.DocumentInput#
A document may be supplied to batch operations in several flexible shapes.
- exception vd.base.EmbeddingRequiredError[source]#
Bases:
VdError,RuntimeErrorRaised when text is given but no embedder is configured.
vdoperates on vectors. Passing raw text tocollection[key] = textorcollection.search(text)only works when theClientwas created with anembedder. Otherwise, pass aDocumentwith avector(or a pre-computed query vector) directly.
- vd.base.METRICS = frozenset({'cosine', 'dot', 'l2'})#
Distance metrics the facade understands. Adapters map these to their own spellings (e.g.
"l2"-> QdrantDistance.EUCLID).
- exception vd.base.StaticIndexError[source]#
Bases:
VdErrorRaised on a write to a static (immutable) index.
Some backends — notably a plain FAISS flat index — build an index that cannot accept incremental
__setitem__/__delitem__after creation. Such collections setAbstractCollection.supports_incremental_writestoFalseand raise this on write. Callers branch on that flag before triggering the error, and use the adapter’s documentedrebuild()path.
- class vd.base.SupportsBatch(*args, **kwargs)[source]#
Bases:
ProtocolA collection that supports efficient batch insertion.
add_documentsandupsertare not part of the minimalCollectioncontract. Every adapter built onAbstractCollectionhappens to provide them, but generic code should still feature-discover:if isinstance(collection, SupportsBatch): collection.add_documents(many_docs, batch_size=256)
- class vd.base.SupportsHybrid(*args, **kwargs)[source]#
Bases:
ProtocolA collection that supports native hybrid (dense + lexical) search.
Hybrid search has no syntactic convergence across vector databases, so it is an opt-in capability, never baseline. Prefer the top-level
vd.hybrid_search()— it dispatches to this protocol when the collection implements it and falls back to a pure-Python BM25 + RRF fusion otherwise. Feature-discover directly only when you specifically need to refuse the fallback path:if isinstance(collection, SupportsHybrid): hits = collection.hybrid_search("query text", limit=20)
The portable contract is Reciprocal Rank Fusion (every native backend supports it). Weighted-blend (
alpha) and other backend-specific fusion variants are accepted via**kwargsand documented per adapter — they are not portable across backends.- Parameters:
query (str or list[float]) – Query text (embedded via the collection’s embedder if configured) or a pre-computed query vector for the dense side.
query_text (str, optional) – Explicit text for the lexical side. Defaults to
querywhenqueryis a string. Required whenqueryis a vector.limit (int) – Number of fused results to return.
filter (dict, optional) – Canonical
vdmetadata filter applied to both sub-searches.k_dense (int, optional) – How many results to fetch from each sub-search before fusion. Both default to
max(4 * limit, 50). Widen for higher recall.k_lexical (int, optional) – How many results to fetch from each sub-search before fusion. Both default to
max(4 * limit, 50). Widen for higher recall.rrf_k (int) – Reciprocal Rank Fusion constant (typically 60).
egress (callable, optional) – Transform applied to each fused result before it is yielded.
**kwargs – Backend-specific knobs (e.g.
alpha=0.7on weaviate,ranker="weighted"on milvus). Documented per adapter.
- class vd.base.SupportsNativeAsync(*args, **kwargs)[source]#
Bases:
ProtocolMarker protocol set on async clients/collections that use a backend’s native async SDK rather than the universal
asyncio.to_thread()wrapper.Why care: in high-concurrency event-loop apps (FastAPI, Starlette, etc.), a
to_thread-wrapped backend still blocks a worker thread per request. For real non-blocking I/O, prefer collections that satisfy this protocol. The wrapper sets this attribute toFalse; native adapters set it toTrue.isinstance(c, SupportsNativeAsync)matches both — checkc.native_asyncfor the boolean.
- exception vd.base.UnsupportedCapabilityError[source]#
Bases:
VdError,NotImplementedErrorRaised when an operation needs a capability the backend lacks.
Prefer feature-discovery —
isinstance(collection, SupportsHybrid)— over catching this, but it is the clear, typed fallback when an optional operation is called on a backend that does not implement it.
- exception vd.base.UnsupportedFilterError[source]#
Bases:
VdError,ValueErrorRaised when a metadata filter uses an operator a backend cannot honor.
The canonical, backend-agnostic filter language lives in
vd.filters(a MongoDB-style JSON dialect). When a filter uses an operator outside a backend’s documented subset — or one that does not exist at all — this is raised, so the caller can simplify the filter or drop to the backend’s native filter via the escape hatch (collection.native).