Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Embr × Foundry — Stateful Agent Sample

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.

Why this sample exists

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.

Quickstart (local)

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 8000

Then 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.

Deploy to Embr

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 $ENV

Embr 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.

API surface

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 }.

What's worth poking 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 thread in the other.
  • Restart resilience. embr deployments trigger -c HEAD ... and confirm the agent still remembers facts you told it.
  • Rate limiting. Hit /api/chat 11 times in a minute and you'll get a 429 from Valkey.
  • Primitive composition. GET /health will show db and cache health independently — useful when investigating which primitive is slowing things down.

About

Stateful Foundry agent on Embr — Postgres for user→conversation mapping + Valkey for per-user rate limiting.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages