AsyncClient
Async sibling of Client — same surface,
await-driven. Use it for concurrent rollouts, agent loops, or any
context where a blocking sync call would tie up an event loop.
Construction
import dream async with dream.AsyncClient( api_key="dre_...", # optional; reads DREAM_API_KEY base_url="https://...", # optional; reads DREAM_BASE_URL timeout_s=300.0, retry_policy=dream.RetryPolicy(),) as client: ...The context manager closes the underlying httpx.AsyncClient on exit.
You can also use it without a context manager and call await client.close() yourself.
Methods
Mirror of Client — every method is a coroutine:
await client.healthz()await client.status() async for handle in await client.models.list(): # not actually `async for`; .list returns list ...model = await client.models.get("dreamdojo-2b-gr1")rollout = await model.predict(start_frame=img, actions=actions)batch = await model.predict_batch(start_frame=img, actions=k_seqs)AsyncModelHandle.predict / .predict_batch accept the same arguments
as the sync versions — full input coercion + validation runs before
the network call.
Concurrent rollouts
The headline use case. K independent rollouts (different inputs) in parallel:
import asyncio, dream async with dream.AsyncClient() as client: model = await client.models.get("dreamdojo-2b-gr1") rollouts = await asyncio.gather(*[ model.predict(start_frame=imgs[i], actions=acts[i]) for i in range(10) ]) for i, r in enumerate(rollouts): r.save(f"rollout_{i:02d}.mp4")Modal autoscales the engine container pool up to 8 concurrent rollouts; past 8, requests queue server-side. The SDK doesn't impose its own client-side concurrency cap — you can fire 100 in parallel and the server's queue + your retry policy will handle the rest.
For visual MPC where K candidates share one starting frame, prefer
predict_batch over gather — the
fused server-side forward is much cheaper than K independent rollouts.
Sync ↔ async
The two clients are independent — there's no "give me the async version
of this sync Client" helper. Pick one for your codebase. Same
api_key, same base_url, same engine.
If you need to mix (rare), construct both:
sync_client = dream.Client()async_client = dream.AsyncClient()They don't share a connection pool but they share the same retry policy semantics and error taxonomy.