Skip to content

AsyncClient + AsyncCollection — Phase 1 (universal wrapper) #18

Description

@thorwhalen

AsyncClient + AsyncCollection — Phase 1 (universal wrapper)

The fourth deferred item from #11. Adds async/await support to vd so it composes with FastAPI, Starlette, modern LLM SDKs, and other event-loop apps — without breaking the synchronous API.

Design (confirmed with maintainer)

Wrapper-default + native opt-in, mirroring the SupportsHybrid + RRF-fallback pattern from PR #16:

  1. A universal AsyncClient/AsyncCollection wrapper exposes every sync vd Collection/Client as async/await methods via asyncio.to_thread. Every backend gets async support day one through this wrapper.
  2. Adapters with native async SDKs (chroma, qdrant, weaviate, elasticsearch, redis, mongodb, lancedb, milvus, pinecone, turbopuffer — 10 of 15) override the wrapper with adapter-specific implementations in follow-up PRs.
  3. A SupportsNativeAsync runtime-checkable marker lets consumers distinguish to_thread-wrapped from truly non-blocking adapters when it matters (high-concurrency HTTP servers).

API

# Entry point — async sibling of vd.connect()
client = await vd.connect_async("chroma", embedder=my_embedder)

# AsyncClient surface
async for name in client.list_collections():
    ...
col = await client.create_collection("docs", dimension=384)

# AsyncCollection — explicit get/set/delete/keys/count (MutableMapping
# doesn't have an async counterpart in the stdlib; explicit methods
# are the Motor / aiopg pattern)
await col.set("doc1", vd.Document(id="doc1", text="..."))
doc = await col.get("doc1")
async for doc_id in col.keys():
    ...
n = await col.count()
async for hit in col.search(query_vec, limit=10):
    ...

# Async hybrid mirrors the sync entry
async for hit in vd.hybrid_search_async(col, query_vec, query_text="..."):
    ...

# Context manager
async with await vd.connect_async("redis") as client:
    ...

Phase 1 scope (this issue)

File Change
vd/base.py New AsyncClient/AsyncCollection/SupportsNativeAsync runtime-checkable protocols.
vd/asynchronous.py (new) AsyncClientWrapper/AsyncCollectionWrapper (asyncio.to_thread-based), connect_async, hybrid_search_async. (Module named asynchronous to avoid the async keyword.)
vd/__init__.py Export new names.
tests/test_async.py Contract tests on the memory backend (sufficient to exercise the universal wrapper; native paths will get their own test parametrization in follow-ups).

Phase 2 (follow-ups, one per backend)

Native async implementations for the 10 backends with async SDKs. Same shape as the SupportsHybrid follow-up (#17) — each backend is its own clean piece of work and its own PR.

Refs

Refs #11. Follows #16.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions