Cookbook
FastAPI
Wire a scan into a FastAPI endpoint with the async client.
A minimal pattern using AsyncTawaGuard inside a FastAPI app.
from fastapi import FastAPI, HTTPException
from tawaguard import AsyncTawaGuard
app = FastAPI()
# Constructed once at import time, shared across requests. Lazy by default
# (no verification round trip on import) — see Configuration if you want to
# verify eagerly during startup instead.
tawa = AsyncTawaGuard("YOUR_API_KEY")
_BLOCKED_ACTIONS = {"block", "hard_block", "soft_block"}
@app.post("/chat")
async def chat(message: str):
scan = await tawa.analyze(message)
if scan.action in _BLOCKED_ACTIONS:
raise HTTPException(
status_code=403,
detail={"error": "Message blocked by content policy.", "rationale": scan.rationale},
)
# ... forward `message` to your model of choice ...
return {"ok": True}
@app.on_event("shutdown")
async def shutdown():
await tawa.aclose()Verifying eagerly on startup
Constructing AsyncTawaGuard directly defers key verification to the first
request. To catch a bad key at startup instead, verify explicitly in a
lifespan handler:
from contextlib import asynccontextmanager
from fastapi import FastAPI
from tawaguard import AsyncTawaGuard
tawa: AsyncTawaGuard
@asynccontextmanager
async def lifespan(app: FastAPI):
global tawa
tawa = await AsyncTawaGuard.create("YOUR_API_KEY")
yield
await tawa.aclose()
app = FastAPI(lifespan=lifespan)See Configuration for the full breakdown of eager vs. lazy verification.