vd.asynchronous#
Async support for vd: universal wrapper + opt-in native implementations.
This module gives every vd backend an async/await surface day one,
without forking the adapter hierarchy. Three pieces:
AsyncCollectionWrapper/AsyncClientWrapper— thin adapters that take any syncvd.Collection/vd.Clientand dispatch every method toasyncio.to_thread(). This is the universal fallback: every backend works through it.AsyncAbstractCollection/AsyncAbstractClient— bases for native async adapters, which do real non-blocking I/O through a backend’s own async SDK. A backend implements a fewasyncraw primitives and registers its client withregister_async_backend(). Native today:qdrantagainst a server (url=), onqdrant_client.AsyncQdrantClient.connect_async()— the entry point. Mirrorsvd.connect(). It returns the backend’s registered native client when there is one, and the wrapper otherwise (or when called withnative=False).
The asyncio.to_thread wrapper does not make I/O non-blocking — it moves
blocking calls off the event loop onto a worker thread. For real
non-blocking I/O against a network backend, use a client whose
native_async attribute is True (see vd.SupportsNativeAsync).
The module name is vd.asynchronous (not vd.async) because async
is a Python keyword.
Functions
|
Register a native async client factory for backend |
Return the names of backends with a registered native async client. |
|
|
Async sibling of |
|
Async sibling of |
Classes
|
Adapt a sync |
|
Adapt a sync |
Base class for native async collections (the async sibling of |
|
|
Base class for native async clients (the async sibling of |
|
A collection that supports native hybrid (dense + lexical) search. |
- class vd.asynchronous.AsyncAbstractClient(*, embedder=None, **config)[source]#
Bases:
objectBase class for native async clients (the async sibling of
vd.AbstractClient).A backend implements
create_collection(),get_collection(),delete_collection()andlist_collections()as coroutines / async generators;get_or_create_collection(), theclientescape hatch,close()andasync withsupport come for free. Register the class withregister_async_backend()soconnect_async()returns it.- Parameters:
- backend_name: str = ''#
The registry name of this backend (set by
register_async_backend()).
- abstractmethod async create_collection(name, *, dimension=None, metric='cosine', **index_config)[source]#
Create a new collection; raise
ValueErrorif it exists.- Return type:
- abstractmethod async delete_collection(name)[source]#
Drop a collection; raise
KeyErrorif absent.- Return type:
- abstractmethod async get_collection(name)[source]#
Return an existing collection; raise
KeyErrorif absent.- Return type:
- async get_or_create_collection(name, *, dimension=None, metric='cosine', **index_config)[source]#
Return collection
name, creating it if missing.- Return type:
- class vd.asynchronous.AsyncAbstractCollection[source]#
Bases:
_CollectionPolicyBase class for native async collections (the async sibling of
vd.AbstractCollection).A backend subclasses this and implements
asyncraw primitives; the user-facingAsyncCollectionsurface is provided here, with the same input coercion, embedding, dimension checks, filter validation andegresshandling as the sync base (both share one policy mixin).Subclass responsibilities (async raw primitives)#
async _write_many(docs)Upsert documents; each
vectoris set and dimension-checked.async _read(key) -> DocumentFetch one document; raise
KeyErrorif absent.async _drop(key)Delete one document; raise
KeyErrorif absent._keys() -> AsyncIterator[str]An async generator of document ids.
async _count() -> intNumber of documents.
async _query(vector, *, limit, filter, **kwargs) -> list[SearchResult]Raw nearest-neighbor search;
filteris the canonical AST.
- async add_documents(documents, *, batch_size=100)[source]#
Batch upsert — mirrors
vd.AbstractCollection.add_documents().- Return type:
- async search(query, *, limit=10, filter=None, egress=None, **kwargs)[source]#
Yield the
limitdocuments most similar toquery.Same contract as
vd.AbstractCollection.search().- Return type:
AsyncIterator[dict[str,Any]]
- class vd.asynchronous.AsyncClientWrapper(sync_client)[source]#
Bases:
objectAdapt a sync
Clientto theAsyncClientcontract by dispatching every method toasyncio.to_thread().Use
connect_async()rather than instantiating this directly.- Parameters:
sync_client (
Any) – A liveClient(typically obtained fromvd.connect()).
- async close()[source]#
Release backend resources. Calls
close()on the sync client if present.- Return type:
- async create_collection(name, *, dimension=None, metric='cosine', **index_config)[source]#
Create a new collection; raise
ValueErrorif it exists.- Return type:
- async get_collection(name)[source]#
Return an existing collection; raise
KeyErrorif absent.- Return type:
- async get_or_create_collection(name, *, dimension=None, metric='cosine', **index_config)[source]#
Return collection
name, creating it if missing.- Return type:
- class vd.asynchronous.AsyncCollectionWrapper(sync_collection)[source]#
Bases:
objectAdapt a sync
Collectionto theAsyncCollectioncontract by dispatching every method toasyncio.to_thread().Use
connect_async()rather than instantiating this directly — it will pick this wrapper or a native async adapter as appropriate.- Parameters:
:: (sync_collection) – A live
Collection(typically obtained from aClient).
- native_async#
Always
Falsefor this wrapper. The wrapper still satisfiesSupportsNativeAsyncstructurally (the attribute is present), but the boolean tells callers that I/O is happening in a thread pool rather than on the event loop. Prefer a native implementation for high-concurrency workloads.- Type:
- async add_documents(documents, *, batch_size=100)[source]#
Batch upsert — mirrors
add_documents().- Return type:
- async search(query, *, limit=10, filter=None, egress=None, **kwargs)[source]#
Yield the
limitdocuments most similar toquery.The underlying search runs once on a worker thread; results stream from memory. (Most backends’ sync
searchalready returns a list or a fully-realized iterator under the hood.)- Return type:
AsyncIterator[dict[str,Any]]
- property sync: Any#
The underlying sync
Collection— a documented escape hatch.
- class vd.asynchronous.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.
- async vd.asynchronous.connect_async(backend, *, native=True, **kwargs)[source]#
Async sibling of
vd.connect().Returns an
AsyncClient. When the backend has a native async client (seelist_async_backends(); todayqdrant), its registered factory decides: it may return a native client doing real non-blocking I/O (qdrant withurl=) or the wrapper (embedded qdrant, whose async client would block the loop). Every other backend goes through the universalAsyncClientWrapper, built onasyncio.to_thread(). Checkclient.native_asyncto tell them apart.- Parameters:
backend (
str) – Backend name — same vocabulary asvd.connect().native (
bool) – Use the backend’s native async client when one is registered (default).Falseforces theto_threadwrapper around the sync adapter.**kwargs – Forwarded to the native client’s constructor, or to
vd.connect()for the wrapper. Both take the same arguments (embedder,url,path, …).
- Returns:
A live async client.
awaitonce at session start:client = await vd.connect_async("memory")- Return type:
Examples
>>> import asyncio, vd >>> async def go(): ... client = await vd.connect_async("memory") ... col = await client.create_collection("docs", dimension=2) ... await col.set("a", vd.Document(id="a", text="x", vector=[1.0, 0.0])) ... return await col.count() >>> asyncio.run(go()) 1
- async vd.asynchronous.hybrid_search_async(collection, query, *, query_text=None, limit=10, filter=None, k_dense=None, k_lexical=None, rrf_k=60, lexical_search=None, egress=None, **kwargs)[source]#
Async sibling of
vd.hybrid_search().For a wrapped sync collection, runs
vd.hybrid_search()(native hybrid if the backend has it, else the client-side BM25 + RRF fallback) on a worker thread. For a native async collection it awaits the collection’s ownhybrid_searchif it has one, and otherwise fuses the collection’s async dense search with a client-side BM25 scan (O(N): it reads every document) via RRF. On a native collection, anasync deflexical_searchreceives the async collection; a sync one receives a materialized{id: Document}dict (every document is read per call) and runs on a worker thread. Either way the awaitable + async iterator interface stays uniform.Parameters mirror
vd.hybrid_search()exactly; see that function for the full docs.- Yields:
dict – Fused result dicts.
- Return type:
AsyncIterator[dict[str, Any]]
Examples
>>> import asyncio, vd >>> async def go(): ... client = await vd.connect_async("memory") ... col = await client.create_collection("docs", dimension=2) ... await col.set("a", vd.Document(id="a", text="cats", ... vector=[1.0, 0.0])) ... await col.set("b", vd.Document(id="b", text="dogs", ... vector=[0.0, 1.0])) ... hits = [] ... async for h in vd.hybrid_search_async(col, [0.9, 0.1], ... query_text="cats", limit=1): ... hits.append(h["id"]) ... return hits >>> asyncio.run(go()) ['a']
- vd.asynchronous.list_async_backends()[source]#
Return the names of backends with a registered native async client.
Every other backend still works with
connect_async(), through the universalto_threadwrapper.
- vd.asynchronous.register_async_backend(name, factory=None)[source]#
Register a native async client factory for backend
name.Use as a class decorator on an
AsyncAbstractClientsubclass, or call it with afactory(a class or a function, sync orasync, taking theconnect_async()keyword arguments). Once registered,connect_async()returns the factory’s client instead of theto_threadwrapper.Examples
>>> @register_async_backend('example') ... class ExampleAsyncClient(AsyncAbstractClient): ... ...