MIT_Summer_2026 / app.py
lmbarak01
Use latest workflow version by default and switch UI text to English
badd65b
Raw
History Blame Contribute Delete
4.65 kB
"""Local FastAPI app for a pharmacovigilance demo with managed ChatKit sessions."""
from __future__ import annotations
import json
import os
import uuid
from pathlib import Path
from typing import Any, Mapping
import httpx
from dotenv import load_dotenv
from fastapi import FastAPI, Request
from fastapi.responses import FileResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
load_dotenv()
APP_ROOT = Path(__file__).parent
STATIC_DIR = APP_ROOT / "static"
DEFAULT_OPENAI_BASE = "https://api.openai.com"
SESSION_COOKIE_NAME = "chatkit_user_id"
SESSION_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 30 # 30 days
OPENAI_BETA_HEADER = "chatkit_beta=v1"
app = FastAPI(title="Pharmacovigilance MIT ChatKit Demo")
app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
@app.get("/health")
async def health() -> Mapping[str, str]:
return {"status": "ok"}
@app.get("/")
async def index() -> FileResponse:
return FileResponse(STATIC_DIR / "index.html")
@app.post("/api/chatkit/session")
async def create_chatkit_session(request: Request) -> JSONResponse:
api_key = os.getenv("OPENAI_API_KEY", "").strip()
workflow_id = os.getenv("WORKFLOW_ID", "").strip()
missing_env = [
name
for name, value in [
("OPENAI_API_KEY", api_key),
("WORKFLOW_ID", workflow_id),
]
if not value
]
if missing_env:
return response(
{"error": f"Missing required environment variable(s): {', '.join(missing_env)}"},
500,
)
user_id, cookie_value = resolve_user_id(request.cookies)
openai_base = os.getenv("OPENAI_BASE_URL", DEFAULT_OPENAI_BASE).strip()
payload = {
"workflow": {"id": workflow_id},
"user": user_id,
}
try:
upstream = await create_openai_session(api_key, openai_base, payload)
except httpx.RequestError as exc:
return response({"error": f"Failed to reach OpenAI API: {exc}"}, 502, cookie_value)
parsed = safe_json(upstream)
workflow_version_used: str | None = None
if not upstream.is_success:
message = "Failed to create ChatKit session"
if isinstance(parsed.get("error"), str):
message = parsed["error"]
elif isinstance(parsed.get("error"), Mapping):
message = str(parsed["error"].get("message") or message)
return response({"error": message}, upstream.status_code, cookie_value)
client_secret = parsed.get("client_secret")
expires_after = parsed.get("expires_after")
if not isinstance(client_secret, str) or not client_secret.strip():
return response(
{"error": "Missing client_secret in OpenAI response"},
502,
cookie_value,
)
return response(
{
"client_secret": client_secret,
"expires_after": expires_after,
"workflow_version_used": workflow_version_used,
},
200,
cookie_value,
)
def resolve_user_id(cookies: Mapping[str, str]) -> tuple[str, str | None]:
existing = cookies.get(SESSION_COOKIE_NAME)
if existing and existing.strip():
return existing.strip(), None
new_user_id = str(uuid.uuid4())
return new_user_id, new_user_id
def safe_json(http_response: httpx.Response) -> dict[str, Any]:
try:
parsed = http_response.json()
except (json.JSONDecodeError, httpx.DecodingError):
return {}
return parsed if isinstance(parsed, dict) else {}
async def create_openai_session(
api_key: str, openai_base: str, payload: Mapping[str, Any]
) -> httpx.Response:
async with httpx.AsyncClient(base_url=openai_base, timeout=20.0) as client:
return await client.post(
"/v1/chatkit/sessions",
headers={
"Authorization": f"Bearer {api_key}",
"OpenAI-Beta": OPENAI_BETA_HEADER,
"Content-Type": "application/json",
},
json=payload,
)
def is_production() -> bool:
env = (os.getenv("ENVIRONMENT") or os.getenv("NODE_ENV") or "").strip().lower()
return env == "production"
def response(
payload: Mapping[str, Any],
status_code: int,
cookie_value: str | None = None,
) -> JSONResponse:
res = JSONResponse(payload, status_code=status_code)
if cookie_value:
res.set_cookie(
key=SESSION_COOKIE_NAME,
value=cookie_value,
max_age=SESSION_COOKIE_MAX_AGE_SECONDS,
httponly=True,
samesite="lax",
secure=is_production(),
path="/",
)
return res