A Python/FastAPI sample demonstrating all three Embr platform primitives wired together with a Foundry-backed agent:
| Primitive | Used for | embr.yaml field | Env var injected |
|---|---|---|---|
| Compute | The FastAPI app itself | platform: python |
— |
| Postgres | Per-user → conversation_id mapping (durable threads per user across reloads/restarts) | database.enabled: true |
DATABASE_URL |
| Cache (Valkey/Redis) | Per-user rate limit (10 req/60s default) | cache.enabled: true |
CACHE_URL, REDIS_URL |
| Foundry | The model — Responses + Conversations API via OpenAI SDK | (env vars only) | FOUNDRY_BASE_URL, FOUNDRY_API_KEY, FOUNDRY_MODEL_DEPLOYMENT |
Each user gets a durable Foundry conversation thread keyed off X-User-Id (or ?user= query, or a user_id cookie). Refresh the page, restart the deployment, switch devices — same thread.
The earlier embr-foundry-agent-sample showed Foundry-side state (server-side conversations) but kept the user-to-thread mapping in browser memory. Real apps need to persist that mapping somewhere reliable. This sample shows how to do that on Embr without having to bring your own database or Redis.
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
export DATABASE_URL=postgres://localhost/postgres
export REDIS_URL=redis://localhost:6379/0
export FOUNDRY_BASE_URL=https://<your-foundry>.services.ai.azure.com/api/projects/<project>/openai/v1
export FOUNDRY_API_KEY=<your-key>
export FOUNDRY_MODEL_DEPLOYMENT=gpt-4.1-mini
# load schema
psql "$DATABASE_URL" < db/schema.sql
uvicorn app.main:app --reload --port 8000Then visit http://localhost:8000. Note that the local-DB setup is optional for kicking the tires — on Embr, both DATABASE_URL and REDIS_URL are auto-injected.
embr quickstart deploy embr-devs/embr-foundry-stateful-agent-sample
# capture project + env IDs, then:
embr variables set FOUNDRY_BASE_URL "<...>" -p $PROJ -e $ENV
embr variables set FOUNDRY_API_KEY "<...>" -p $PROJ -e $ENV --secret
embr variables set FOUNDRY_MODEL_DEPLOYMENT "<deployment>" -p $PROJ -e $ENV
embr deployments trigger -c HEAD -p $PROJ -e $ENVEmbr runs the SQL in db/schema.sql against the managed Postgres before the app starts (per the database.framework: raw + schema: field). The cache primitive starts an embedded Valkey alongside the app.
| Method | Path | Description |
|---|---|---|
GET |
/health |
Liveness + reports db and cache reachability |
GET |
/api/config |
Reports which envs are present and the rate-limit config |
POST |
/api/chat |
{ message } body. Reads user from X-User-Id / ?user=. Creates the user's thread on first hit. |
GET |
/api/me/conversation |
Returns the user's thread + Foundry server-side items |
DELETE |
/api/me/conversation |
Deletes the Foundry thread + clears the row |
GET |
/api/admin/users |
Lists all (user_id, conversation_id, updated_at) rows |
Rate-limited responses come back as HTTP 429 with { error: "rate_limited", reset_at }.
- Cross-device thread continuity. Open the page in two browsers, set the same user, talk to the agent in one, then
Show server-side threadin the other. - Restart resilience.
embr deployments trigger -c HEAD ...and confirm the agent still remembers facts you told it. - Rate limiting. Hit
/api/chat11 times in a minute and you'll get a 429 from Valkey. - Primitive composition.
GET /healthwill showdbandcachehealth independently — useful when investigating which primitive is slowing things down.