| --- |
| title: MedTriage OpenEnv |
| emoji: π₯ |
| colorFrom: blue |
| colorTo: green |
| sdk: docker |
| app_port: 7860 |
| tags: |
| - openenv |
| - medical |
| - emergency-medicine |
| - reinforcement-learning |
| - clinical-decision-support |
| pinned: false |
| license: mit |
| --- |
| |
| # π₯ MedTriage OpenEnv |
|
|
| > **Emergency Department Triage & Clinical Decision Support** |
| > An [OpenEnv](https://github.com/meta-pytorch/OpenEnv)-compliant reinforcement learning environment where AI agents act as emergency physicians. |
|
|
| [](https://github.com/meta-pytorch/OpenEnv) |
| [](https://python.org) |
| [](LICENSE) |
|
|
| --- |
|
|
| ## π Environment Description & Motivation |
|
|
| Emergency departments worldwide face critical challenges: physician shortages, overcrowding, and delayed triage lead to preventable deaths. AI agents capable of clinical reasoning could dramatically improve throughput and safety. |
|
|
| **MedTriage-Env** trains agents to master three core emergency medicine skills, each modelled on real clinical workflows used by certified emergency physicians: |
|
|
| 1. **Vital Sign Triage** β classify patient urgency using the internationally-standardized Emergency Severity Index (ESI v4) |
| 2. **Differential Diagnosis** β generate ranked differential diagnoses and identify life-threatening red flags from clinical presentations and lab data |
| 3. **Treatment Safety** β propose evidence-based treatment plans while avoiding drug allergies, contraindications, and nephrotoxic agents |
|
|
| All patient cases are synthetic but clinically realistic, designed by emergency medicine scenarios. All graders are **fully deterministic** β using rule-based algorithms, clinical reference ranges, and drug safety lookup tables. |
|
|
| --- |
|
|
| ## π― Action Space |
|
|
| ```python |
| class MedTriageAction(BaseModel): |
| # Task 1 β vital-triage |
| esi_level: Optional[int] # 1 (immediate) β 5 (non-urgent) |
| triage_reason: Optional[str] # Brief clinical justification |
| |
| # Task 2 β differential-diagnosis |
| diagnoses: Optional[List[str]] # Ranked list (most likely first) |
| red_flags: Optional[List[str]] # Critical findings identified |
| recommended_tests: Optional[List[str]] # Additional tests to order |
| |
| # Task 3 β treatment-safety |
| diagnosis: Optional[str] # Working diagnosis |
| drug_name: Optional[str] # Chosen drug or intervention |
| dose_mg: Optional[float] # Dose in mg (None for non-drug interventions) |
| route: Optional[str] # "IV" | "PO" | "IM" | "Inhaled" | "Other" |
| rationale: Optional[str] # Clinical reasoning including safety checks |
| ``` |
|
|
| --- |
|
|
| ## ποΈ Observation Space |
|
|
| ```python |
| class MedTriageObservation(BaseModel): |
| patient: PatientInfo # Full patient data (age, sex, CC, history, vitals, labs) |
| current_task: str # "vital-triage" | "differential-diagnosis" | "treatment-safety" |
| task_instruction: str # Natural-language instruction |
| step_number: int |
| max_steps: int # 30 |
| last_action_result: str # Human-readable feedback |
| last_action_error: Optional[str] |
| cumulative_reward: float # Running total |
| progress: float # 0.0β1.0 |
| done: bool |
| context: Dict[str, Any] # Task-specific context (e.g., confirmed diagnosis for Task 3) |
| ``` |
|
|
| **PatientInfo contains:** |
| - Demographics (age, sex) |
| - Chief complaint & medical history |
| - Vital signs (HR, BP, RR, Temp, SpO2, GCS, pain score) |
| - Lab results with reference ranges and critical flags |
| - Allergies & current medications |
| - Known conditions |
|
|
| --- |
|
|
| ## π Tasks |
|
|
| ### Task 1: `vital-triage` β Easy |
|
|
| | Property | Value | |
| |----------|-------| |
| | Difficulty | Easy | |
| | Max Steps | 30 | |
| | Score Range | 0.0 β 1.0 | |
|
|
| **Objective:** Classify patient urgency using Emergency Severity Index (ESI v4): |
| - **ESI 1** (Immediate): Life-threatening β airway compromise, pulselessness, shock |
| - **ESI 2** (Emergent): High-risk, confused/lethargic, or severe pain |
| - **ESI 3** (Urgent): Stable but requires multiple diagnostic resources |
| - **ESI 4** (Less Urgent): Stable, requires one resource |
| - **ESI 5** (Non-Urgent): No resources needed |
|
|
| **Grading:** |
| - Exact ESI match β 1.0 |
| - Off by 1 β 0.5 |
| - Off by 2 β 0.2 |
| - Off by 3+ β 0.0 |
| - Bonus +0.1 for mentioning key clinical features in rationale (capped at 1.0) |
|
|
| --- |
|
|
| ### Task 2: `differential-diagnosis` β Medium |
|
|
| | Property | Value | |
| |----------|-------| |
| | Difficulty | Medium | |
| | Max Steps | 30 | |
| | Score Range | 0.0 β 1.0 | |
|
|
| **Objective:** Given symptoms, vitals, and lab results, generate: |
| - Ranked differential diagnoses (most likely first, up to 3) |
| - Critical red flags in the presentation |
| - Recommended additional diagnostic tests |
|
|
| **Grading (weighted score):** |
| - Primary diagnosis correct (top of list matches expected): **50%** |
| - Differential overlap (recall over top-3 correct diagnoses): **25%** |
| - Red flags identified (recall over ground-truth flags): **25%** |
|
|
| --- |
|
|
| ### Task 3: `treatment-safety` β Hard |
|
|
| | Property | Value | |
| |----------|-------| |
| | Difficulty | Hard | |
| | Max Steps | 30 | |
| | Score Range | 0.0 β 1.0 | |
|
|
| **Objective:** Given a confirmed working diagnosis, propose a safe, evidence-based treatment plan. Must avoid: |
| - Drug allergies documented in patient record |
| - Condition-specific contraindications (e.g., beta-blockers in asthma) |
| - Nephrotoxic agents in CKD patients |
| - Drugs known to cause harm in specific diagnoses |
|
|
| **Grading:** |
| - Contraindicated drug selected β **0.0 immediately** |
| - Correct drug: base **0.6** |
| - Dose within therapeutic range: +**0.2** |
| - Correct route: +**0.1** |
| - Rationale contains relevant clinical keywords: +**0.1** |
| - Unknown drug (not contraindicated, not in list): **0.15** (partial) |
|
|
| --- |
|
|
| ## π§ Setup & Usage |
|
|
| ### Prerequisites |
|
|
| - Python 3.10+ |
| - Docker (for containerized deployment) |
| - OpenAI-compatible API key (for inference) |
|
|
| ### Local Installation |
|
|
| ```bash |
| git clone <your-repo-url> |
| cd medtriage-env |
| |
| pip install -e . |
| # or |
| pip install -e ".[dev]" |
| ``` |
|
|
| ### Run Server Locally |
|
|
| ```bash |
| uvicorn medtriage_env.server.app:app --host 0.0.0.0 --port 7860 |
| ``` |
|
|
| Then open `http://localhost:7860/docs` for the interactive API docs. |
|
|
| ### Docker Build & Run |
|
|
| ```bash |
| docker build -t medtriage-env . |
| docker run -p 7860:7860 medtriage-env |
| ``` |
|
|
| ### Python Client Usage |
|
|
| ```python |
| import asyncio |
| from medtriage_env import MedTriageEnv, MedTriageAction |
| |
| async def main(): |
| async with MedTriageEnv(base_url="http://localhost:7860", task="vital-triage") as env: |
| result = await env.reset() |
| print(result.observation.patient.chief_complaint) |
| |
| action = MedTriageAction(esi_level=2, triage_reason="Hypotension and hypoxia") |
| result = await env.step(action) |
| print(f"Reward: {result.reward}, Done: {result.done}") |
| |
| asyncio.run(main()) |
| ``` |
|
|
| ### Sync Usage |
|
|
| ```python |
| from medtriage_env import MedTriageEnv, MedTriageAction |
| |
| with MedTriageEnv(base_url="http://localhost:7860").sync() as env: |
| result = env.reset(task="differential-diagnosis") |
| action = MedTriageAction( |
| diagnoses=["STEMI", "Aortic dissection", "NSTEMI"], |
| red_flags=["ST elevation", "hypotension", "diaphoresis"], |
| recommended_tests=["12-lead ECG", "Troponin", "CXR"] |
| ) |
| result = env.step(action) |
| print(f"Score: {result.reward:.2f}") |
| ``` |
|
|
| --- |
|
|
| ## π Running Inference |
|
|
| ```bash |
| export API_BASE_URL="https://router.huggingface.co/v1" |
| export MODEL_NAME="Qwen/Qwen2.5-72B-Instruct" |
| export HF_TOKEN="hf_your_token_here" |
| export MEDTRIAGE_URL="https://your-space.hf.space" |
| |
| python inference.py |
| ``` |
|
|
| --- |
|
|
| ## π Baseline Scores |
|
|
| Scores obtained with `Qwen/Qwen2.5-72B-Instruct` via HuggingFace Inference Router: |
|
|
| | Task | Score | Notes | |
| |------|-------|-------| |
| | `vital-triage` | ~0.75 | ESI classification is straightforward with good clinical context | |
| | `differential-diagnosis` | ~0.60 | Primary diagnosis often correct; red flag recall varies | |
| | `treatment-safety` | ~0.55 | Drug safety requires careful allergy/contraindication checking | |
| | **Average** | **~0.63** | | |
|
|
| > Scores are reproducible across runs given the same model and fixed temperature=0.2. |
|
|
| --- |
|
|
| ## ποΈ API Endpoints |
|
|
| | Method | Endpoint | Description | |
| |--------|----------|-------------| |
| | `GET` | `/health` | Health check | |
| | `GET` | `/tasks` | List available tasks with metadata | |
| | `POST` | `/reset` | Start new episode (`{"task": "vital-triage"}`) | |
| | `POST` | `/step` | Execute an action | |
| | `GET` | `/state` | Get current episode state | |
| | `GET` | `/docs` | Interactive Swagger UI | |
|
|
| --- |
|
|
| ## π Project Structure |
|
|
| ``` |
| medtriage-env/ |
| βββ inference.py # Baseline inference script (root) |
| βββ Dockerfile # Production container |
| βββ openenv.yaml # OpenEnv manifest |
| βββ pyproject.toml # Package config |
| βββ README.md # This file |
| βββ .dockerignore |
| βββ medtriage_env/ |
| βββ __init__.py # Public API exports |
| βββ models.py # Pydantic models (Action, Obs, State) |
| βββ client.py # HTTP client (async + sync) |
| βββ server/ |
| βββ app.py # FastAPI application |
| βββ medical_environment.py # Core environment logic |
| βββ graders.py # Deterministic graders |
| βββ patient_data.py # Synthetic patient dataset |
| βββ requirements.txt |
| ``` |
|
|
| --- |
|
|
| ## β οΈ Disclaimer |
|
|
| All patient cases are **entirely fictional** and intended solely for AI training and evaluation purposes. This environment does **not** constitute medical advice and should not be used for actual clinical decision-making. |
|
|
|
|