CogTraceEnv_ / app.py
Hashblue's picture
Upload folder using huggingface_hub
0095404 verified
Raw
History Blame Contribute Delete
5.76 kB
"""
app.py
──────
FastAPI application serving CogTraceEnv as an OpenEnv HTTP API.
Endpoints:
POST /reset β†’ Observation
POST /step β†’ {observation, reward, done, info}
GET /state β†’ EnvState
GET /tasks β†’ list of available tasks
GET /health β†’ {"status": "ok"}
GET /openenv.yaml β†’ serve the spec file
"""
from __future__ import annotations
import os
import random
from typing import Any, Dict, Optional
from fastapi import FastAPI, HTTPException
from fastapi.responses import PlainTextResponse
from pydantic import BaseModel
from cognitive_env import CogTraceEnv
from patient_simulator import PatientConfig
from models import Action
app = FastAPI(
title="CogTraceEnv",
description="OpenEnv environment for Alzheimer's cognitive monitoring",
version="1.0.0",
)
# Global env instance (single-session server)
_env: Optional[CogTraceEnv] = None
# ─── Request/Response models ──────────────────────────────────────────────────
class ResetRequest(BaseModel):
true_stage: Optional[int] = None # 0–4; None = random
episode_length: int = 30
decline_rate: float = 0.01
noise_level: float = 1.0
seed: Optional[int] = None
patient_id: str = "patient_001"
anomaly_day: Optional[int] = None
anomaly_duration: int = 5
class StepRequest(BaseModel):
action: int # 0–3
# ─── Routes ───────────────────────────────────────────────────────────────────
@app.get("/health")
def health():
return {"status": "ok", "env": "CogTraceEnv-v1"}
@app.get("/")
def root():
return {
"name": "CogTraceEnv",
"description": "OpenEnv RL environment for Alzheimer's cognitive monitoring",
"version": "1.0.0",
"endpoints": {
"health": "/health",
"reset": "/reset",
"step": "/step",
"state": "/state",
"tasks": "/tasks",
"spec": "/openenv.yaml"
}
}
@app.post("/reset")
def reset(req: ResetRequest = ResetRequest()):
global _env
stage = req.true_stage if req.true_stage is not None else random.randint(1, 3)
cfg = PatientConfig(
true_stage=stage,
episode_length=req.episode_length,
decline_rate=req.decline_rate,
noise_level=req.noise_level,
seed=req.seed,
patient_id=req.patient_id,
anomaly_day=req.anomaly_day,
anomaly_duration=req.anomaly_duration,
)
_env = CogTraceEnv(config=cfg)
obs = _env.reset()
return obs.model_dump()
@app.post("/step")
def step(req: StepRequest):
if _env is None:
raise HTTPException(status_code=400, detail="Call /reset first.")
try:
action = Action(action=req.action)
obs, reward, terminated, truncated, info = _env.step(action)
return {
"observation": obs.model_dump(),
"reward": float(reward) if isinstance(reward, (int, float)) else reward.model_dump(),
"done": terminated or truncated,
"terminated": terminated,
"truncated": truncated,
"info": info.model_dump(),
}
except RuntimeError as e:
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
raise HTTPException(status_code=500, detail=f"Internal Server Error: {str(e)}")
@app.get("/state")
def state():
if _env is None:
raise HTTPException(status_code=400, detail="Call /reset first.")
return _env.state().model_dump()
@app.get("/tasks")
def list_tasks():
return {
"tasks": [
{
"id": "task1_easy",
"name": "Cognitive Stage Classification",
"difficulty": "easy",
"description": (
"Given one snapshot of behavioral metrics, "
"predict the patient's Alzheimer's stage (0–4)."
),
},
{
"id": "task2_medium",
"name": "Anomaly Timing Detection",
"difficulty": "medium",
"description": (
"Observe 7 days of signals. Raise an alert on "
"the day you detect an anomaly."
),
},
{
"id": "task3_hard",
"name": "Full Triage Episode",
"difficulty": "hard",
"description": (
"Manage a 30-step episode, balancing sensitivity "
"and specificity across declining patient trajectories."
),
},
]
}
@app.get("/openenv.yaml", response_class=PlainTextResponse)
def serve_yaml():
yaml_path = os.path.join(os.path.dirname(__file__), "openenv.yaml")
if not os.path.exists(yaml_path):
raise HTTPException(status_code=404, detail="openenv.yaml not found")
with open(yaml_path) as f:
return f.read()
# ── Entry point ───────────────────────────────────────────────────────────────
def main():
import uvicorn
uvicorn.run("app:app", host="0.0.0.0", port=7860, reload=False)
if __name__ == "__main__":
main()