Python SDK
vadylInstall
pip install vadylpoetry add vadyluv add vadylpdm add vadylCreate the client
Configure the client once. Same object reaches every product surface — entities, workflows, agents, realtime.
from vadyl import Client import os vadyl = Client( api_url="https://api.vadyl.app/v1", token=os.environ["VADYL_TOKEN"], tenant="acme", project="billing", # optional — pin to a feature branch branch=os.environ.get("VADYL_BRANCH"), )
List, filter, paginate
Typed filter expressions, cursor pagination, eager relation loading.
# Typed filter, sort, pagination, and relation expansion recent = vadyl.orders.list( filter={ "status": {"in": ["paid", "fulfilled"]}, "total": {"gt": 100}, "createdAt": {"gte": "now-30d"}, }, sort=[{"field": "createdAt", "direction": "desc"}], page_size=50, include=["customer", "items.product"], ) # recent.data: list[Order] # recent.page.next: cursor for next page print(len(recent.data), recent.page.next)
Create, update, delete with idempotency
Every mutation supports idempotency keys. Retries are safe by default.
# Idempotent create — safe to retry customer = vadyl.customers.create( {"email": "ada@example.com", "name": "Ada Lovelace"}, idempotency_key=f"signup:{session.id}", ) # Update with optimistic concurrency updated = vadyl.customers.update( customer.id, {"name": "Ada L."}, if_match=customer.concurrency_token, ) # Delete (soft-delete on entities with that lifecycle) vadyl.customers.delete(customer.id)
Subscribe to entity changes
Field names only — never values. Re-read for values through CRUD with proper access enforcement.
# Sync streaming subscription with the same filter AST as queries with vadyl.orders.subscribe( filter={"status": {"eq": "paid"}}, kinds=["created", "updated"], ) as sub: for evt in sub.events(): # evt.kind, evt.entity_id, evt.changed_fields, evt.occurred_at # Field NAMES only — re-read for values order = vadyl.orders.read(evt.entity_id) print("paid:", order) # Or async async with vadyl.aio.orders.subscribe(...) as sub: async for evt in sub: ...
Start and signal durable workflows
Long-running product behavior that survives process restart. Same client interface; canonical durability underneath.
# Start a durable workflow run = vadyl.workflows.fulfill_order.start( order_id="ord_abc", ) # Send a signal (e.g. from a webhook) vadyl.workflows.fulfill_order.signal( run.id, "shipped", {"trackingNumber": "1Z..."}, ) # Query run state state = vadyl.workflows.fulfill_order.query(run.id)
Run agents against the product model
Typed plan IR, capability-aware model routing, token accounting — all behind one method.
# Run an agent — typed plan IR, capability-aware model routing run = vadyl.agents.SupportAgent.run( prompt="Refund order #12345 due to defect", user_id=session.user_id, budget={"max_tokens": 50_000, "max_tool_calls": 30}, ) # Stream the run for step in run.stream(): print(step.kind, step.summary) # Recall memory for an agent facts = vadyl.agents.SupportAgent.memory.recall( kind="preference", subject=session.user_id, )
Typed error contract
Stable error codes, stable reason codes, correlation IDs, and explainability links.
from vadyl import ( VadylError, AccessDeniedError, ValidationError, ConflictError, QuotaExceededError, ) try: vadyl.orders.update(id, {"status": "refunded"}) except AccessDeniedError as err: print(err.reason_code, err.correlation_id) except ConflictError: # optimistic-concurrency mismatch — re-read and retry ... except VadylError as err: print(err.code, str(err))
Define handlers, workflows, agents
The same package you use as a client also powers your authored runtime — defineCoreHandler, defineWorkflow, defineAgent.
from vadyl import handler @handler.core async def charge_order(ctx, order_id: str): order = await ctx.entities.Order.read(order_id) charge = await ctx.connections.stripe.create_charge( amount=order.total, currency=order.currency, idempotency_key=f"charge:{order.id}", ) await ctx.entities.Order.update(order.id, { "status": "paid", "chargeId": charge.id, }) await ctx.events.emit("order.paid", {"orderId": order.id}) return {"ok": True, "charge_id": charge.id}
What you get
Fully typed entities
Pydantic v2 models for every entity, field, relation, and enum. Static-type-checkable end-to-end.
Manifest verification
The SDK refuses to talk to a server whose generated-format version it does not support. Fail closed.
Realtime built-in
WebSocket and SSE subscriptions with the same predicate AST as queries. Reconnect-safe.
Sync & async
Both blocking (Client) and async (AsyncClient) APIs from the same package. Use what fits your stack.
Framework-friendly
Plays nicely with FastAPI, Django, Flask, Starlette, Jupyter notebooks, scripts. No magic globals.
Branch-aware
Pin to a feature branch via VADYL_BRANCH or the branch parameter. Sandbox testing without leaving prod.
Observability hooks
Hook into the canonical envelope. Emit operational trails from the client when needed.
Agent primitives
vadyl.agents exposes the Agent plane: definitions, runs, memory, MCP exposure.
Connections
Typed governed-connection clients per registered governed connection.
Deep-dive documentation
Build with the Python SDK in minutes.
Install the package and work with your product model through typed entities, workflows, agents, and events.