PlatformsPython
Python + FastAPI
Complete aelio-sdk integration with FastAPI and uvicorn.
Based on the official examples/python-fastapi/ example in the Aelio monorepo.
1. Install
pip install aelio-sdk fastapi uvicorn websockets2. Create main.py
import os
import threading
import uvicorn
from fastapi import FastAPI
from aelio_sdk import Aelio
app = FastAPI()
aelio = Aelio(
secret=os.getenv("AELIO_SDK_SECRET", "change-me-in-production"),
url=os.getenv("AELIO_SERVER_URL", "ws://127.0.0.1:3010"),
)
@aelio.expose(
"getOrderStatus",
description="Get the status of a customer order",
params={"orderId": "string"},
safety="read",
)
async def get_order_status(args, ctx):
order_id = args.get("orderId")
customer_id = ctx.get("customerId")
# Query your database here
return {
"orderId": order_id,
"customerId": customer_id,
"status": "shipped",
"tracking": "1Z999AA10123456784",
}
@aelio.expose(
"cancelOrder",
description="Cancel a pending customer order",
params={"orderId": "string"},
safety="write",
)
async def cancel_order(args, ctx):
order_id = args.get("orderId")
# Cancel in your database
return {
"orderId": order_id,
"customerId": ctx.get("customerId"),
"status": "cancelled",
}
@aelio.expose(
"listOrders",
description="List customer orders, optionally filtered by status",
params={"status": "string?", "limit": "integer?"},
safety="read",
)
async def list_orders(args, ctx):
orders = [
{"orderId": "A123", "status": "shipped"},
{"orderId": "B456", "status": "pending"},
]
status_filter = args.get("status")
if status_filter:
orders = [o for o in orders if o["status"] == status_filter]
limit = args.get("limit", len(orders))
return {"customerId": ctx.get("customerId"), "orders": orders[:limit]}
@app.get("/health")
async def health():
return {"ok": True}3. Run SDK in background thread
The SDK WebSocket connection runs in a separate thread so it doesn't block FastAPI:
def _run_sdk():
aelio.run()
if __name__ == "__main__":
sdk_thread = threading.Thread(target=_run_sdk, daemon=True)
sdk_thread.start()
uvicorn.run(app, host="0.0.0.0", port=int(os.getenv("PORT", "8080")))4. Run
AELIO_SDK_SECRET=change-me-in-production \
AELIO_SERVER_URL=ws://127.0.0.1:3010 \
python main.py5. Lifecycle states
aelio.state("active", description="Fully onboarded customer with full tool access.")
aelio.state("onboarding", description="New user. Setup only.", blocked_tools=["cancelOrder"])
# Push state from API route
@app.post("/api/onboarding/complete")
async def complete_onboarding(user_id: str):
await aelio.set_customer_state(user_id, "active", "Onboarding completed")
return {"status": "ok"}6. BYO channel
@aelio.on_send
async def deliver_message(msg):
channel = msg.get("channel")
to = msg.get("to")
content = msg.get("content")
await my_provider.send(to=to, body=content)
@app.post("/whatsapp-webhook")
async def whatsapp_webhook(request: Request):
body = await request.json()
from_number = body.get("from")
text = body.get("text")
aelio.ingest(channel="whatsapp", from_=from_number, text=text)
return {"status": "ok"}7. Async alternative
Instead of a background thread, use asyncio:
import asyncio
@app.on_event("startup")
async def startup():
asyncio.create_task(aelio.listen())8. Embed widget
The widget is frontend-only. Add to your HTML or React frontend:
<script
src="http://localhost:3010/widget.js"
data-server-url="http://localhost:3010"
data-customer-id="user_abc123"
data-email="user@example.com"
></script>Parameter types
| Form | Example | Meaning |
|---|---|---|
| Required | "orderId": "string" | Required string |
| Optional | "status": "string?" | Optional (trailing ?) |
| Typed | "limit": "integer?" | Optional integer |
Supported types: string, number, integer, boolean, array, object.