Skip to content

Enable Async turbopuffer API #60

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,106 @@ for namespace in namespaces:
ns.delete([1, 2])
```

Async API
-------

The turbopuffer client also provides a fully-featured async API for use with asyncio-based applications. The async API follows the same patterns as the synchronous API but with async/await syntax.

```py
import asyncio
import turbopuffer as tpuf

async def main():
# Set API key and base URL
tpuf.api_key = 'your-token'
tpuf.api_base_url = "https://gcp-us-east4.turbopuffer.com"

# Create an AsyncNamespace instance
async_ns = tpuf.AsyncNamespace('hello_world')

# Check if namespace exists
if await async_ns.aexists():
print(f'Namespace {async_ns.name} exists with {await async_ns.adimensions()} dimensions')
print(f'and approximately {await async_ns.aapprox_count()} vectors.')

# Upsert data asynchronously
await async_ns.aupsert(
ids=[1, 2, 3],
vectors=[[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]],
attributes={'name': ['foo', 'bar', 'baz']},
distance_metric='cosine_distance',
)

# Upsert using row iterator or generator
await async_ns.aupsert(
{
'id': id,
'vector': [id/10, id/10],
'attributes': {'name': f'item_{id}', 'value': id*10}
} for id in range(4, 10)
)

# Query vectors asynchronously
result = await async_ns.aquery(
vector=[0.2, 0.3],
distance_metric='cosine_distance',
top_k=5,
include_vectors=True,
include_attributes=True,
)

# AsyncVectorResult can be used with async for
async for row in result:
print(f"ID: {row.id}, Distance: {row.dist}")

# Or loaded completely into memory
all_results = await result.load()
print(f"Found {len(all_results)} results")

# List all vectors in the namespace
all_vectors = await async_ns.avectors()
# Load all vectors into memory
vectors_list = await all_vectors.load()
print(f"Namespace contains {len(vectors_list)} vectors")

# Delete vectors asynchronously
await async_ns.adelete([1, 2])

# List all namespaces asynchronously
namespaces_iterator = await tpuf.anamespaces()
# Use async for to iterate through namespaces
async for namespace in namespaces_iterator:
print(f"Namespace: {namespace.name}")

# Or load all namespaces at once
all_namespaces = await namespaces_iterator.load()
print(f"Total namespaces: {len(all_namespaces)}")

# Run the async main function
asyncio.run(main())
```

### Context Manager Support

AsyncNamespace instances can be used as async context managers to ensure proper resource cleanup:

```py
async def process_data():
async with tpuf.AsyncNamespace('my_data') as ns:
# Perform operations
await ns.aupsert(ids=[1, 2], vectors=[[0.1, 0.2], [0.3, 0.4]])
results = await ns.aquery(vector=[0.15, 0.25], top_k=5)
# Resources will be cleaned up when exiting the context
```

### Converting Between Sync and Async APIs

The synchronous Namespace methods internally use the async methods by running them in an event loop. If you're mixing sync and async code, be aware of these considerations:

- Synchronous methods create an event loop if needed
- For best performance in async applications, use the async API directly
- In async contexts, avoid calling synchronous methods as they may cause event loop issues

Endpoint Documentation
----------------------

Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "turbopuffer"
version = "0.1.32"
version = "0.2.0"
description = "Python Client for accessing the turbopuffer API"
authors = ["turbopuffer Inc. <[email protected]>"]
homepage = "https://turbopuffer.com"
Expand Down Expand Up @@ -30,6 +30,7 @@ classifiers = [
[tool.poetry.dependencies]
python = "^3.9"
requests = "^2.31"
aiohttp = "^3.9.1"
iso8601 = "^2.1.0"
orjson = { version = ">=3.9, <=3.10.3", optional = true } # 3.10.4 errors on install
numpy = { version = ">=1.24.0", optional = true }
Expand Down
Loading