Spaces:
Sleeping
Sleeping
Upload folder using huggingface_hub
Browse files- README.md +133 -228
- app/environment/core.py +4 -4
- app/environment/graders.py +4 -3
- app/environment/validation.py +7 -6
- app/models/observation.py +1 -1
- app/models/state.py +3 -3
- data/learning_archive.json +773 -0
- data/learning_memory.json +26 -26
- data/trajectory_history.jsonl +28 -0
- inference.py +18 -14
README.md
CHANGED
|
@@ -1,300 +1,205 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: green
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
|
|
|
| 8 |
base_path: /web
|
|
|
|
|
|
|
| 9 |
---
|
| 10 |
|
| 11 |
-
#
|
| 12 |
|
| 13 |
-
|
| 14 |
|
| 15 |
-
|
| 16 |
-
- A patient needs urgent care.
|
| 17 |
-
- Several hospitals are available.
|
| 18 |
-
- Each hospital has trade-offs (distance, traffic, ICU certainty, specialization).
|
| 19 |
-
- Conditions can change while the ambulance is moving.
|
| 20 |
-
- The agent must decide where to go, step by step.
|
| 21 |
|
| 22 |
-
|
| 23 |
|
| 24 |
-
|
|
|
|
|
|
|
| 25 |
|
| 26 |
-
|
| 27 |
|
| 28 |
-
|
| 29 |
-
- `acde_easy`
|
| 30 |
-
- `acde_medium`
|
| 31 |
-
- `acde_hard`
|
| 32 |
|
| 33 |
-
|
|
|
|
|
|
|
| 34 |
|
| 35 |
-
|
| 36 |
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
- Patient condition
|
| 42 |
-
- Required specialization
|
| 43 |
-
- Hospital list with visible signals
|
| 44 |
-
3. The policy scores hospitals.
|
| 45 |
-
4. One hospital is selected.
|
| 46 |
-
5. The environment validates arrival using hidden checks.
|
| 47 |
-
6. You receive outcome + reward.
|
| 48 |
-
7. If not done, repeat until success or failure.
|
| 49 |
-
|
| 50 |
-
## What Makes It Realistic
|
| 51 |
-
|
| 52 |
-
This is not a static lookup problem. It includes realistic uncertainty:
|
| 53 |
-
|
| 54 |
-
- Displayed ICU status can differ from actual ICU status.
|
| 55 |
-
- Traffic can change between steps.
|
| 56 |
-
- Hospital overload can change outcomes.
|
| 57 |
-
- Specialist availability can fail at arrival.
|
| 58 |
-
- A hospital that failed once may become usable later.
|
| 59 |
-
|
| 60 |
-
The policy includes safety rules such as:
|
| 61 |
-
- Immediate retry protection after rejection.
|
| 62 |
-
- Cooldown handling for recently failed hospitals.
|
| 63 |
-
- Exploration among top options (not blind random picks).
|
| 64 |
-
|
| 65 |
-
## Project Layout
|
| 66 |
-
|
| 67 |
-
Key files:
|
| 68 |
-
|
| 69 |
-
- `app/environment/core.py`
|
| 70 |
-
- Main environment loop (`reset`, `step`, transition logic)
|
| 71 |
-
- `app/environment/validation.py`
|
| 72 |
-
- Hidden validation checks (ICU, specialist, overload, outcome)
|
| 73 |
-
- `app/environment/graders.py`
|
| 74 |
-
- Final scoring and pass/fail grading
|
| 75 |
-
- `app/models/`
|
| 76 |
-
- Pydantic models for state, observation, reward, action
|
| 77 |
-
- `app/server/app.py`
|
| 78 |
-
- FastAPI server endpoints
|
| 79 |
-
- `inference.py`
|
| 80 |
-
- Local policy runner (CLI episodes)
|
| 81 |
-
- `data/learning_memory.json`
|
| 82 |
-
- Rolling policy memory
|
| 83 |
-
- `data/trajectory_history.jsonl`
|
| 84 |
-
- Per-step trajectory logs
|
| 85 |
-
|
| 86 |
-
## API Endpoints
|
| 87 |
-
|
| 88 |
-
When server mode is running:
|
| 89 |
-
|
| 90 |
-
- `GET /health`
|
| 91 |
-
- `POST /reset`
|
| 92 |
-
- `POST /step`
|
| 93 |
-
- `GET /state`
|
| 94 |
|
| 95 |
## Action Space
|
| 96 |
|
| 97 |
-
|
| 98 |
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
"hospital_id": "H3",
|
| 103 |
-
"rationale": "short decision reason"
|
| 104 |
-
}
|
| 105 |
-
```
|
| 106 |
-
|
| 107 |
-
Action fields:
|
| 108 |
-
- `step` (int, >=1): must match current environment step
|
| 109 |
-
- `hospital_id` (str): target hospital identifier
|
| 110 |
-
- `rationale` (str, optional): policy explanation
|
| 111 |
|
| 112 |
## Observation Space
|
| 113 |
|
| 114 |
-
|
| 115 |
-
- episode metadata: `episode_id`, `seed`, `task_id`, `scenario_name`, `scenario_difficulty`
|
| 116 |
-
- patient state: `patient_condition`, `required_specialization`, remaining time fields
|
| 117 |
-
- hospital list: `hospital_id`, `distance_km`, `icu`, `specialization`, `traffic`
|
| 118 |
-
- routing history: visited/failed hospitals and failure reasons
|
| 119 |
-
- hidden-state feedback: `last_arrival_outcome` summary (status/reason/suitability)
|
| 120 |
-
- memory snapshot used by the baseline policy
|
| 121 |
-
|
| 122 |
-
Core schema is defined by Pydantic models in:
|
| 123 |
-
- `app/models/action.py`
|
| 124 |
-
- `app/models/observation.py`
|
| 125 |
-
- `app/models/state.py`
|
| 126 |
-
- `app/models/reward.py`
|
| 127 |
-
|
| 128 |
-
## Required Environment Variables
|
| 129 |
-
|
| 130 |
-
Before running `inference.py`, define:
|
| 131 |
-
- `API_BASE_URL`: API base URL for the OpenAI-compatible endpoint
|
| 132 |
-
- `MODEL_NAME`: model name used for rationale generation
|
| 133 |
-
- `HF_TOKEN`: API key/token
|
| 134 |
-
|
| 135 |
-
Windows PowerShell example:
|
| 136 |
-
|
| 137 |
-
```powershell
|
| 138 |
-
$env:API_BASE_URL = "https://api-inference.huggingface.co/v1"
|
| 139 |
-
$env:MODEL_NAME = "your-model-id"
|
| 140 |
-
$env:HF_TOKEN = "your-token"
|
| 141 |
-
```
|
| 142 |
|
| 143 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
|
| 145 |
-
##
|
| 146 |
|
| 147 |
-
|
| 148 |
-
- `pip`
|
| 149 |
|
| 150 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
|
| 152 |
-
|
| 153 |
-
- `my_env`
|
| 154 |
|
| 155 |
-
|
| 156 |
|
| 157 |
-
|
|
|
|
|
|
|
| 158 |
|
| 159 |
-
|
| 160 |
-
python -m venv .venv
|
| 161 |
-
.\.venv\Scripts\Activate.ps1
|
| 162 |
-
```
|
| 163 |
|
| 164 |
-
|
| 165 |
|
| 166 |
-
|
| 167 |
-
python -m venv .venv
|
| 168 |
-
source .venv/bin/activate
|
| 169 |
-
```
|
| 170 |
|
| 171 |
-
|
|
|
|
|
|
|
|
|
|
| 172 |
|
| 173 |
-
|
| 174 |
-
pip install -e .
|
| 175 |
-
```
|
| 176 |
|
| 177 |
-
|
|
|
|
|
|
|
| 178 |
|
| 179 |
-
|
| 180 |
-
pip install .
|
| 181 |
-
```
|
| 182 |
|
| 183 |
-
|
| 184 |
|
| 185 |
-
|
|
|
|
|
|
|
| 186 |
|
| 187 |
-
|
| 188 |
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
```
|
| 192 |
|
| 193 |
-
|
| 194 |
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
|
|
|
| 198 |
|
| 199 |
-
|
| 200 |
|
| 201 |
-
|
| 202 |
-
python inference.py --mode full --episodes 3 --seed 555
|
| 203 |
-
```
|
| 204 |
|
| 205 |
-
|
|
|
|
|
|
|
|
|
|
| 206 |
|
| 207 |
-
|
| 208 |
|
| 209 |
-
|
| 210 |
|
| 211 |
-
|
| 212 |
-
uvicorn app.server.app:app --host 0.0.0.0 --port 7860
|
| 213 |
-
```
|
| 214 |
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
```bash
|
| 218 |
-
curl http://127.0.0.1:7860/health
|
| 219 |
-
```
|
| 220 |
|
| 221 |
-
|
| 222 |
|
| 223 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 224 |
|
| 225 |
-
|
| 226 |
-
- Hospital options and scores
|
| 227 |
-
- Decision strategy text
|
| 228 |
-
- Outcome per step (`ACCEPTED`, `PARTIAL`, `REJECTED`)
|
| 229 |
-
- Final episode summary
|
| 230 |
-
- Batch summary (success rate, average score, average steps)
|
| 231 |
-
|
| 232 |
-
Example summary:
|
| 233 |
|
| 234 |
```text
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
|
|
|
| 239 |
```
|
| 240 |
|
| 241 |
-
##
|
| 242 |
-
|
| 243 |
-
The simulation writes data to `data/`:
|
| 244 |
-
|
| 245 |
-
- `learning_memory.json`
|
| 246 |
-
- Long-term policy memory
|
| 247 |
-
- `trajectory_history.jsonl`
|
| 248 |
-
- One JSON object per step
|
| 249 |
-
- `learning_archive.json`
|
| 250 |
-
- Aggregate run history and profiles
|
| 251 |
-
|
| 252 |
-
If you want a clean run baseline, back up and clear these files.
|
| 253 |
-
|
| 254 |
-
## Typical Targets (Guideline)
|
| 255 |
-
|
| 256 |
-
These are practical targets, not strict rules:
|
| 257 |
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
If hard success is too high, increase uncertainty or rejection pressure.
|
| 263 |
-
If hard success is too low, ease one or two hard-only probabilities.
|
| 264 |
-
|
| 265 |
-
## Troubleshooting
|
| 266 |
-
|
| 267 |
-
## "NameError" or model field errors
|
| 268 |
-
|
| 269 |
-
Make sure model fields and observation fields match after logic changes.
|
| 270 |
-
If you added new state keys, also add them in observation models.
|
| 271 |
-
|
| 272 |
-
## Script asks for seed/level unexpectedly
|
| 273 |
|
| 274 |
-
|
| 275 |
|
| 276 |
```bash
|
| 277 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 278 |
```
|
| 279 |
|
| 280 |
-
##
|
| 281 |
-
|
| 282 |
-
Run commands from inside `my_env` folder, and ensure install succeeded:
|
| 283 |
|
| 284 |
```bash
|
| 285 |
-
|
|
|
|
| 286 |
```
|
| 287 |
|
| 288 |
-
|
| 289 |
|
| 290 |
-
|
| 291 |
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 295 |
|
| 296 |
-
##
|
| 297 |
|
| 298 |
-
-
|
| 299 |
-
-
|
| 300 |
-
-
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Acde-Openenv Environment Server
|
| 3 |
+
emoji: 🚑
|
| 4 |
colorFrom: blue
|
| 5 |
colorTo: green
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
| 8 |
+
app_port: 8000
|
| 9 |
base_path: /web
|
| 10 |
+
tags:
|
| 11 |
+
- openenv
|
| 12 |
---
|
| 13 |
|
| 14 |
+
# Adaptive Crisis Decision Environment (ACDE)
|
| 15 |
|
| 16 |
+
ACDE is an OpenEnv-compliant, real-world emergency dispatch environment for evaluating decision-making under uncertainty. The task models ambulance routing to hospitals under changing traffic, hidden ICU truth, and patient deterioration.
|
| 17 |
|
| 18 |
+
## Why This Is Real-World
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
+
This environment simulates a practical operation used by emergency coordinators:
|
| 21 |
|
| 22 |
+
- choosing destination hospitals under incomplete information
|
| 23 |
+
- trading off travel time vs ICU availability vs specialization
|
| 24 |
+
- handling a non-stationary world where traffic and capacity shift
|
| 25 |
|
| 26 |
+
## OpenEnv API
|
| 27 |
|
| 28 |
+
Implemented in [app/environment/core.py](app/environment/core.py) and exposed via [app/server/app.py](app/server/app.py).
|
|
|
|
|
|
|
|
|
|
| 29 |
|
| 30 |
+
- reset(seed, task_id) -> observation
|
| 31 |
+
- step(action) -> observation, reward, done, info
|
| 32 |
+
- state() -> state
|
| 33 |
|
| 34 |
+
HTTP endpoints:
|
| 35 |
|
| 36 |
+
- GET /health
|
| 37 |
+
- POST /reset
|
| 38 |
+
- POST /step
|
| 39 |
+
- GET /state
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
|
| 41 |
## Action Space
|
| 42 |
|
| 43 |
+
Action model: [app/models/action.py](app/models/action.py)
|
| 44 |
|
| 45 |
+
- step: int (1..3)
|
| 46 |
+
- hospital_id: string (H1/H2/H3)
|
| 47 |
+
- rationale: optional string
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
## Observation Space
|
| 50 |
|
| 51 |
+
Observation model: [app/models/observation.py](app/models/observation.py)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
+
- task_id, task_objective
|
| 54 |
+
- scenario metadata (name, difficulty, condition)
|
| 55 |
+
- required_specialization
|
| 56 |
+
- critical_time_limit_minutes
|
| 57 |
+
- current step and max_steps
|
| 58 |
+
- hospital list:
|
| 59 |
+
- hospital_id
|
| 60 |
+
- distance_km
|
| 61 |
+
- icu (available/unknown)
|
| 62 |
+
- specialization
|
| 63 |
+
- traffic (low/medium/high)
|
| 64 |
|
| 65 |
+
## Reward and Info Models
|
| 66 |
|
| 67 |
+
Reward and grader metadata: [app/models/reward.py](app/models/reward.py)
|
|
|
|
| 68 |
|
| 69 |
+
- RewardModel.value in [0, 1]
|
| 70 |
+
- RewardBreakdown:
|
| 71 |
+
- survival_component
|
| 72 |
+
- time_efficiency_component
|
| 73 |
+
- specialization_component
|
| 74 |
+
- delay_penalty
|
| 75 |
+
- StepInfo includes:
|
| 76 |
+
- last_action_error
|
| 77 |
+
- task_id, difficulty, objective
|
| 78 |
+
- progress_score
|
| 79 |
+
- reward_model
|
| 80 |
+
- grader (final step)
|
| 81 |
|
| 82 |
+
## Tasks and Agent Graders
|
|
|
|
| 83 |
|
| 84 |
+
Three deterministic tasks are supported:
|
| 85 |
|
| 86 |
+
1. acde_easy
|
| 87 |
+
2. acde_medium
|
| 88 |
+
3. acde_hard
|
| 89 |
|
| 90 |
+
Task objective and difficulty are fixed by task_id and drive scenario selection.
|
|
|
|
|
|
|
|
|
|
| 91 |
|
| 92 |
+
Deterministic grader implementation: [app/environment/graders.py](app/environment/graders.py)
|
| 93 |
|
| 94 |
+
Final grader score in [0, 1] is based on:
|
|
|
|
|
|
|
|
|
|
| 95 |
|
| 96 |
+
- survival_rate
|
| 97 |
+
- margin_rate (time margin vs critical limit)
|
| 98 |
+
- specialization_rate
|
| 99 |
+
- repeat_failure_penalty
|
| 100 |
|
| 101 |
+
Difficulty-specific pass thresholds:
|
|
|
|
|
|
|
| 102 |
|
| 103 |
+
- easy: 0.75
|
| 104 |
+
- medium: 0.65
|
| 105 |
+
- hard: 0.50
|
| 106 |
|
| 107 |
+
## Physics and Dynamics
|
|
|
|
|
|
|
| 108 |
|
| 109 |
+
Travel model:
|
| 110 |
|
| 111 |
+
- speed = base_speed * traffic_factor
|
| 112 |
+
- traffic_factor: low=1.0, medium=0.6, high=0.3
|
| 113 |
+
- travel_time_minutes = (distance_km / speed_kmh) * 60
|
| 114 |
|
| 115 |
+
Hidden truth:
|
| 116 |
|
| 117 |
+
- displayed ICU can be available/unknown
|
| 118 |
+
- actual ICU true/false is hidden and used for survival logic
|
|
|
|
| 119 |
|
| 120 |
+
Environment changes between steps:
|
| 121 |
|
| 122 |
+
- traffic can change
|
| 123 |
+
- ICU truth can flip
|
| 124 |
+
- displayed ICU can drift from truth
|
| 125 |
+
- patient critical time window shrinks
|
| 126 |
|
| 127 |
+
## Reward Function (Partial Signals)
|
| 128 |
|
| 129 |
+
Per-step reward in [0, 1] combines:
|
|
|
|
|
|
|
| 130 |
|
| 131 |
+
- survival (primary)
|
| 132 |
+
- time efficiency
|
| 133 |
+
- specialization match
|
| 134 |
+
- delay penalty
|
| 135 |
|
| 136 |
+
This gives dense trajectory feedback and penalizes poor choices.
|
| 137 |
|
| 138 |
+
## Reproducibility
|
| 139 |
|
| 140 |
+
Seed controls scenario generation, hospital parameters, traffic, and hidden ICU state. Same seed + task_id yields deterministic episode construction.
|
|
|
|
|
|
|
| 141 |
|
| 142 |
+
## Inference Baseline (Required Format)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
|
| 144 |
+
Script: [inference.py](inference.py)
|
| 145 |
|
| 146 |
+
- uses OpenAI client for model calls
|
| 147 |
+
- reads API_BASE_URL, MODEL_NAME, HF_TOKEN (or OPENAI_API_KEY)
|
| 148 |
+
- emits strict structured logs:
|
| 149 |
+
- [START]
|
| 150 |
+
- [STEP]
|
| 151 |
+
- [END]
|
| 152 |
|
| 153 |
+
Log format example:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
|
| 155 |
```text
|
| 156 |
+
[START] task=acde_easy env=acde-openenv model=Qwen/Qwen2.5-72B-Instruct
|
| 157 |
+
[STEP] step=1 action=route('H1') reward=0.83 done=false error=null
|
| 158 |
+
[STEP] step=2 action=route('H2') reward=0.54 done=false error=null
|
| 159 |
+
[STEP] step=3 action=route('H1') reward=0.92 done=true error=null
|
| 160 |
+
[END] success=true steps=3 score=0.84 rewards=0.83,0.54,0.92
|
| 161 |
```
|
| 162 |
|
| 163 |
+
## Local Setup
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
|
| 165 |
+
```bash
|
| 166 |
+
pip install -e .
|
| 167 |
+
uvicorn app.server.app:app --host 0.0.0.0 --port 7860
|
| 168 |
+
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 169 |
|
| 170 |
+
Run inference:
|
| 171 |
|
| 172 |
```bash
|
| 173 |
+
set API_BASE_URL=https://router.huggingface.co/v1
|
| 174 |
+
set MODEL_NAME=Qwen/Qwen2.5-72B-Instruct
|
| 175 |
+
set HF_TOKEN=***
|
| 176 |
+
set ENV_BASE_URL=http://127.0.0.1:7860
|
| 177 |
+
python inference.py
|
| 178 |
```
|
| 179 |
|
| 180 |
+
## Docker
|
|
|
|
|
|
|
| 181 |
|
| 182 |
```bash
|
| 183 |
+
docker build -t acde-openenv .
|
| 184 |
+
docker run --rm -p 7860:7860 acde-openenv
|
| 185 |
```
|
| 186 |
|
| 187 |
+
Target runtime envelope: 2 vCPU, 8 GB RAM.
|
| 188 |
|
| 189 |
+
## Hugging Face Spaces Deployment
|
| 190 |
|
| 191 |
+
1. Create a new Docker Space.
|
| 192 |
+
2. Push this repository as the Space source.
|
| 193 |
+
3. Set Space metadata tag to openenv.
|
| 194 |
+
4. Add Secrets/Variables:
|
| 195 |
+
- API_BASE_URL
|
| 196 |
+
- MODEL_NAME
|
| 197 |
+
- HF_TOKEN
|
| 198 |
+
5. Space should expose port 7860 and respond to /health and /reset.
|
| 199 |
|
| 200 |
+
## Validation Checklist
|
| 201 |
|
| 202 |
+
- openenv validate passes
|
| 203 |
+
- docker build succeeds
|
| 204 |
+
- /health, /reset, /step, /state respond
|
| 205 |
+
- inference.py runs end-to-end and prints required structured logs
|
app/environment/core.py
CHANGED
|
@@ -676,7 +676,7 @@ class EmergencyEnv:
|
|
| 676 |
status="rejected",
|
| 677 |
reason="Hidden mismatch at arrival (wrong risky guess). Rerouting required.",
|
| 678 |
validation_details=arrival_outcome.validation_details,
|
| 679 |
-
reward_modifier=0.
|
| 680 |
)
|
| 681 |
return (
|
| 682 |
forced_reject,
|
|
@@ -754,7 +754,7 @@ class EmergencyEnv:
|
|
| 754 |
status="rejected",
|
| 755 |
reason=reason,
|
| 756 |
validation_details=new_validation,
|
| 757 |
-
reward_modifier=0.
|
| 758 |
),
|
| 759 |
0.12,
|
| 760 |
f"Hidden case: {reason}. Rerouting required.",
|
|
@@ -805,7 +805,7 @@ class EmergencyEnv:
|
|
| 805 |
status="rejected",
|
| 806 |
reason="Condition became irreversible after delays",
|
| 807 |
validation_details=arrival_outcome.validation_details,
|
| 808 |
-
reward_modifier=0.
|
| 809 |
),
|
| 810 |
"Partial chain cap: condition became irreversible.",
|
| 811 |
)
|
|
@@ -883,7 +883,7 @@ class EmergencyEnv:
|
|
| 883 |
status="rejected",
|
| 884 |
reason="Condition relapsed after temporary stabilization",
|
| 885 |
validation_details=arrival_outcome.validation_details,
|
| 886 |
-
reward_modifier=0.
|
| 887 |
terminal=False,
|
| 888 |
),
|
| 889 |
"Recovery guard: partial relapsed to rejected.",
|
|
|
|
| 676 |
status="rejected",
|
| 677 |
reason="Hidden mismatch at arrival (wrong risky guess). Rerouting required.",
|
| 678 |
validation_details=arrival_outcome.validation_details,
|
| 679 |
+
reward_modifier=0.001,
|
| 680 |
)
|
| 681 |
return (
|
| 682 |
forced_reject,
|
|
|
|
| 754 |
status="rejected",
|
| 755 |
reason=reason,
|
| 756 |
validation_details=new_validation,
|
| 757 |
+
reward_modifier=0.001,
|
| 758 |
),
|
| 759 |
0.12,
|
| 760 |
f"Hidden case: {reason}. Rerouting required.",
|
|
|
|
| 805 |
status="rejected",
|
| 806 |
reason="Condition became irreversible after delays",
|
| 807 |
validation_details=arrival_outcome.validation_details,
|
| 808 |
+
reward_modifier=0.001,
|
| 809 |
),
|
| 810 |
"Partial chain cap: condition became irreversible.",
|
| 811 |
)
|
|
|
|
| 883 |
status="rejected",
|
| 884 |
reason="Condition relapsed after temporary stabilization",
|
| 885 |
validation_details=arrival_outcome.validation_details,
|
| 886 |
+
reward_modifier=0.001,
|
| 887 |
terminal=False,
|
| 888 |
),
|
| 889 |
"Recovery guard: partial relapsed to rejected.",
|
app/environment/graders.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
from app.models.reward import GraderResult
|
| 2 |
|
| 3 |
|
|
@@ -39,7 +40,7 @@ def grade_task(
|
|
| 39 |
margin_rate = sum(
|
| 40 |
_norm_margin(t.get("travel_time", 0.0), t.get("critical_limit", 1.0))
|
| 41 |
for t in trajectory
|
| 42 |
-
) / steps if trajectory else
|
| 43 |
|
| 44 |
# Penalty for repeated failures at same hospital
|
| 45 |
repeat_failures = 0
|
|
@@ -90,8 +91,8 @@ def grade_task(
|
|
| 90 |
score = max(MIN_SCORE, min(MAX_SCORE, score))
|
| 91 |
|
| 92 |
return GraderResult(
|
| 93 |
-
task_id=task_id,
|
| 94 |
-
difficulty=difficulty,
|
| 95 |
objective=objective,
|
| 96 |
score=score,
|
| 97 |
passed=score >= threshold,
|
|
|
|
| 1 |
+
from typing import Literal, cast
|
| 2 |
from app.models.reward import GraderResult
|
| 3 |
|
| 4 |
|
|
|
|
| 40 |
margin_rate = sum(
|
| 41 |
_norm_margin(t.get("travel_time", 0.0), t.get("critical_limit", 1.0))
|
| 42 |
for t in trajectory
|
| 43 |
+
) / steps if trajectory else MIN_SCORE
|
| 44 |
|
| 45 |
# Penalty for repeated failures at same hospital
|
| 46 |
repeat_failures = 0
|
|
|
|
| 91 |
score = max(MIN_SCORE, min(MAX_SCORE, score))
|
| 92 |
|
| 93 |
return GraderResult(
|
| 94 |
+
task_id=cast(Literal["acde_easy", "acde_hard", "acde_medium"], task_id),
|
| 95 |
+
difficulty=cast(Literal["easy", "hard", "medium"], difficulty),
|
| 96 |
objective=objective,
|
| 97 |
score=score,
|
| 98 |
passed=score >= threshold,
|
app/environment/validation.py
CHANGED
|
@@ -4,6 +4,7 @@ Simulates hidden validation checks performed when an ambulance arrives at a hosp
|
|
| 4 |
Outcomes are based on difficulty level, hospital capacity, patient suitability, and randomness.
|
| 5 |
"""
|
| 6 |
|
|
|
|
| 7 |
from app.models.state import ArrivalOutcome, HospitalValidationDetails, HospitalState
|
| 8 |
from app.utils.randomizer import SeededRandomizer
|
| 9 |
|
|
@@ -64,7 +65,7 @@ class HospitalValidator:
|
|
| 64 |
icu_available=icu_available,
|
| 65 |
doctor_available=doctor_available,
|
| 66 |
equipment_functional=equipment_functional,
|
| 67 |
-
overload_status=overload_status,
|
| 68 |
patient_suitability=patient_suitability,
|
| 69 |
)
|
| 70 |
|
|
@@ -79,7 +80,7 @@ class HospitalValidator:
|
|
| 79 |
)
|
| 80 |
|
| 81 |
return ArrivalOutcome(
|
| 82 |
-
status=status,
|
| 83 |
reason=reason,
|
| 84 |
validation_details=validation_details,
|
| 85 |
reward_modifier=reward_modifier,
|
|
@@ -190,7 +191,7 @@ class HospitalValidator:
|
|
| 190 |
# Add difficulty-based noise
|
| 191 |
if difficulty == "hard":
|
| 192 |
noise = self.rng.uniform(-0.15, 0.15)
|
| 193 |
-
suitability = max(0.
|
| 194 |
|
| 195 |
return suitability
|
| 196 |
|
|
@@ -269,7 +270,7 @@ class HospitalValidator:
|
|
| 269 |
return (
|
| 270 |
"rejected",
|
| 271 |
f"Hospital cannot admit: {', '.join(rejection_reasons[:2])}",
|
| 272 |
-
0.
|
| 273 |
False,
|
| 274 |
)
|
| 275 |
|
|
@@ -345,7 +346,7 @@ class HospitalValidator:
|
|
| 345 |
return (
|
| 346 |
"rejected",
|
| 347 |
"Condition became non-transferable during delay; immediate critical care failed",
|
| 348 |
-
0.
|
| 349 |
True,
|
| 350 |
)
|
| 351 |
|
|
@@ -376,7 +377,7 @@ class HospitalValidator:
|
|
| 376 |
return (
|
| 377 |
"rejected",
|
| 378 |
"Unexpected complication at arrival",
|
| 379 |
-
0.
|
| 380 |
False,
|
| 381 |
)
|
| 382 |
|
|
|
|
| 4 |
Outcomes are based on difficulty level, hospital capacity, patient suitability, and randomness.
|
| 5 |
"""
|
| 6 |
|
| 7 |
+
from typing import Literal, cast
|
| 8 |
from app.models.state import ArrivalOutcome, HospitalValidationDetails, HospitalState
|
| 9 |
from app.utils.randomizer import SeededRandomizer
|
| 10 |
|
|
|
|
| 65 |
icu_available=icu_available,
|
| 66 |
doctor_available=doctor_available,
|
| 67 |
equipment_functional=equipment_functional,
|
| 68 |
+
overload_status=cast(Literal["clear", "moderate", "severe"], overload_status),
|
| 69 |
patient_suitability=patient_suitability,
|
| 70 |
)
|
| 71 |
|
|
|
|
| 80 |
)
|
| 81 |
|
| 82 |
return ArrivalOutcome(
|
| 83 |
+
status=cast(Literal["accepted", "partial", "rejected"], status),
|
| 84 |
reason=reason,
|
| 85 |
validation_details=validation_details,
|
| 86 |
reward_modifier=reward_modifier,
|
|
|
|
| 191 |
# Add difficulty-based noise
|
| 192 |
if difficulty == "hard":
|
| 193 |
noise = self.rng.uniform(-0.15, 0.15)
|
| 194 |
+
suitability = max(0.001, min(0.999, suitability + noise))
|
| 195 |
|
| 196 |
return suitability
|
| 197 |
|
|
|
|
| 270 |
return (
|
| 271 |
"rejected",
|
| 272 |
f"Hospital cannot admit: {', '.join(rejection_reasons[:2])}",
|
| 273 |
+
0.001,
|
| 274 |
False,
|
| 275 |
)
|
| 276 |
|
|
|
|
| 346 |
return (
|
| 347 |
"rejected",
|
| 348 |
"Condition became non-transferable during delay; immediate critical care failed",
|
| 349 |
+
0.001,
|
| 350 |
True,
|
| 351 |
)
|
| 352 |
|
|
|
|
| 377 |
return (
|
| 378 |
"rejected",
|
| 379 |
"Unexpected complication at arrival",
|
| 380 |
+
0.001,
|
| 381 |
False,
|
| 382 |
)
|
| 383 |
|
app/models/observation.py
CHANGED
|
@@ -15,7 +15,7 @@ class ArrivalOutcomeObservation(BaseModel):
|
|
| 15 |
"""What happened when ambulance arrived at hospital"""
|
| 16 |
status: Literal["accepted", "partial", "rejected"]
|
| 17 |
reason: str
|
| 18 |
-
suitability_score: float = Field(ge=0.
|
| 19 |
|
| 20 |
|
| 21 |
class Observation(BaseModel):
|
|
|
|
| 15 |
"""What happened when ambulance arrived at hospital"""
|
| 16 |
status: Literal["accepted", "partial", "rejected"]
|
| 17 |
reason: str
|
| 18 |
+
suitability_score: float = Field(ge=0.001, le=0.999)
|
| 19 |
|
| 20 |
|
| 21 |
class Observation(BaseModel):
|
app/models/state.py
CHANGED
|
@@ -6,7 +6,7 @@ from pydantic import BaseModel, Field
|
|
| 6 |
class LearningEntry(BaseModel):
|
| 7 |
success: int = Field(default=0, ge=0)
|
| 8 |
fail: int = Field(default=0, ge=0)
|
| 9 |
-
avg: float = Field(default=0.
|
| 10 |
accepted: int = Field(default=0, ge=0)
|
| 11 |
rejected: int = Field(default=0, ge=0)
|
| 12 |
|
|
@@ -17,7 +17,7 @@ class HospitalValidationDetails(BaseModel):
|
|
| 17 |
doctor_available: bool
|
| 18 |
equipment_functional: bool
|
| 19 |
overload_status: Literal["clear", "moderate", "severe"]
|
| 20 |
-
patient_suitability: float = Field(ge=0.
|
| 21 |
|
| 22 |
|
| 23 |
class ArrivalOutcome(BaseModel):
|
|
@@ -25,7 +25,7 @@ class ArrivalOutcome(BaseModel):
|
|
| 25 |
status: Literal["accepted", "partial", "rejected"]
|
| 26 |
reason: str
|
| 27 |
validation_details: HospitalValidationDetails | None = None
|
| 28 |
-
reward_modifier: float = Field(default=1.0, ge=0.
|
| 29 |
terminal: bool = False
|
| 30 |
|
| 31 |
|
|
|
|
| 6 |
class LearningEntry(BaseModel):
|
| 7 |
success: int = Field(default=0, ge=0)
|
| 8 |
fail: int = Field(default=0, ge=0)
|
| 9 |
+
avg: float = Field(default=0.001, ge=0.001, le=0.999)
|
| 10 |
accepted: int = Field(default=0, ge=0)
|
| 11 |
rejected: int = Field(default=0, ge=0)
|
| 12 |
|
|
|
|
| 17 |
doctor_available: bool
|
| 18 |
equipment_functional: bool
|
| 19 |
overload_status: Literal["clear", "moderate", "severe"]
|
| 20 |
+
patient_suitability: float = Field(ge=0.001, le=0.999) # near-0=unsuitable, near-1=ideal
|
| 21 |
|
| 22 |
|
| 23 |
class ArrivalOutcome(BaseModel):
|
|
|
|
| 25 |
status: Literal["accepted", "partial", "rejected"]
|
| 26 |
reason: str
|
| 27 |
validation_details: HospitalValidationDetails | None = None
|
| 28 |
+
reward_modifier: float = Field(default=1.0, ge=0.001, le=1.5)
|
| 29 |
terminal: bool = False
|
| 30 |
|
| 31 |
|
data/learning_archive.json
CHANGED
|
@@ -8086,6 +8086,643 @@
|
|
| 8086 |
"best_scenario_name": "Wildfire Front (Evacuation Gridlock)",
|
| 8087 |
"best_difficulty": "hard",
|
| 8088 |
"best_required_specialization": "general"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8089 |
}
|
| 8090 |
},
|
| 8091 |
"episodes": [
|
|
@@ -10137,6 +10774,142 @@
|
|
| 10137 |
"H5"
|
| 10138 |
],
|
| 10139 |
"timestamp": "2026-04-09T05:19:13.261267+00:00"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10140 |
}
|
| 10141 |
]
|
| 10142 |
}
|
|
|
|
| 8086 |
"best_scenario_name": "Wildfire Front (Evacuation Gridlock)",
|
| 8087 |
"best_difficulty": "hard",
|
| 8088 |
"best_required_specialization": "general"
|
| 8089 |
+
},
|
| 8090 |
+
"509813872|acde_easy": {
|
| 8091 |
+
"attempts": 1,
|
| 8092 |
+
"best_score": 0.5568666666666667,
|
| 8093 |
+
"best_actions": [
|
| 8094 |
+
"H3",
|
| 8095 |
+
"H1",
|
| 8096 |
+
"H3"
|
| 8097 |
+
],
|
| 8098 |
+
"best_steps": 3,
|
| 8099 |
+
"step_stats": {
|
| 8100 |
+
"1": {
|
| 8101 |
+
"H3": {
|
| 8102 |
+
"count": 1,
|
| 8103 |
+
"success": 0,
|
| 8104 |
+
"accepted": 0,
|
| 8105 |
+
"partial": 1,
|
| 8106 |
+
"rejected": 0,
|
| 8107 |
+
"total_reward": 0.15500000000000003,
|
| 8108 |
+
"avg_reward": 0.15500000000000003,
|
| 8109 |
+
"last_status": "PARTIAL",
|
| 8110 |
+
"last_reason": "Early rejection mitigated by emergency field stabilization",
|
| 8111 |
+
"success_rate": 0.0
|
| 8112 |
+
}
|
| 8113 |
+
},
|
| 8114 |
+
"2": {
|
| 8115 |
+
"H1": {
|
| 8116 |
+
"count": 1,
|
| 8117 |
+
"success": 0,
|
| 8118 |
+
"accepted": 0,
|
| 8119 |
+
"partial": 0,
|
| 8120 |
+
"rejected": 1,
|
| 8121 |
+
"total_reward": 0.139,
|
| 8122 |
+
"avg_reward": 0.139,
|
| 8123 |
+
"last_status": "REJECTED",
|
| 8124 |
+
"last_reason": "Hospital cannot admit: ICU unavailable, Hospital overloaded",
|
| 8125 |
+
"success_rate": 0.0
|
| 8126 |
+
}
|
| 8127 |
+
},
|
| 8128 |
+
"3": {
|
| 8129 |
+
"H3": {
|
| 8130 |
+
"count": 1,
|
| 8131 |
+
"success": 1,
|
| 8132 |
+
"accepted": 1,
|
| 8133 |
+
"partial": 0,
|
| 8134 |
+
"rejected": 0,
|
| 8135 |
+
"total_reward": 0.7160000000000001,
|
| 8136 |
+
"avg_reward": 0.7160000000000001,
|
| 8137 |
+
"last_status": "ACCEPTED",
|
| 8138 |
+
"last_reason": "Patient stabilized after delayed admission",
|
| 8139 |
+
"success_rate": 1.0
|
| 8140 |
+
}
|
| 8141 |
+
}
|
| 8142 |
+
},
|
| 8143 |
+
"last_score": 0.5568666666666667,
|
| 8144 |
+
"last_success": true,
|
| 8145 |
+
"last_run_at": "2026-04-09T08:39:45.824383+00:00",
|
| 8146 |
+
"last_actions": [
|
| 8147 |
+
"H3",
|
| 8148 |
+
"H1",
|
| 8149 |
+
"H3"
|
| 8150 |
+
],
|
| 8151 |
+
"last_required_specialization": "trauma",
|
| 8152 |
+
"last_scenario_type": "accident",
|
| 8153 |
+
"last_scenario_name": "Highway Collision (Severe)",
|
| 8154 |
+
"best_success": true,
|
| 8155 |
+
"best_scenario_name": "Highway Collision (Severe)",
|
| 8156 |
+
"best_difficulty": "easy",
|
| 8157 |
+
"best_required_specialization": "trauma"
|
| 8158 |
+
},
|
| 8159 |
+
"509813873|acde_medium": {
|
| 8160 |
+
"attempts": 1,
|
| 8161 |
+
"best_score": 0.15059999999999998,
|
| 8162 |
+
"best_actions": [
|
| 8163 |
+
"H3"
|
| 8164 |
+
],
|
| 8165 |
+
"best_steps": 1,
|
| 8166 |
+
"step_stats": {
|
| 8167 |
+
"1": {
|
| 8168 |
+
"H3": {
|
| 8169 |
+
"count": 1,
|
| 8170 |
+
"success": 0,
|
| 8171 |
+
"accepted": 0,
|
| 8172 |
+
"partial": 0,
|
| 8173 |
+
"rejected": 1,
|
| 8174 |
+
"total_reward": 0.001,
|
| 8175 |
+
"avg_reward": 0.001,
|
| 8176 |
+
"last_status": "REJECTED",
|
| 8177 |
+
"last_reason": "Condition became non-transferable during delay; immediate critical care failed",
|
| 8178 |
+
"success_rate": 0.0
|
| 8179 |
+
}
|
| 8180 |
+
}
|
| 8181 |
+
},
|
| 8182 |
+
"last_score": 0.15059999999999998,
|
| 8183 |
+
"last_success": false,
|
| 8184 |
+
"last_run_at": "2026-04-09T08:39:46.224541+00:00",
|
| 8185 |
+
"last_actions": [
|
| 8186 |
+
"H3"
|
| 8187 |
+
],
|
| 8188 |
+
"last_required_specialization": "general",
|
| 8189 |
+
"last_scenario_type": "fire",
|
| 8190 |
+
"last_scenario_name": "Factory Fire (Chemical Exposure)",
|
| 8191 |
+
"best_success": false,
|
| 8192 |
+
"best_scenario_name": "Factory Fire (Chemical Exposure)",
|
| 8193 |
+
"best_difficulty": "medium",
|
| 8194 |
+
"best_required_specialization": "general"
|
| 8195 |
+
},
|
| 8196 |
+
"509813874|acde_hard": {
|
| 8197 |
+
"attempts": 1,
|
| 8198 |
+
"best_score": 0.15297499999999997,
|
| 8199 |
+
"best_actions": [
|
| 8200 |
+
"H1",
|
| 8201 |
+
"H6",
|
| 8202 |
+
"H4",
|
| 8203 |
+
"H6"
|
| 8204 |
+
],
|
| 8205 |
+
"best_steps": 4,
|
| 8206 |
+
"step_stats": {
|
| 8207 |
+
"1": {
|
| 8208 |
+
"H1": {
|
| 8209 |
+
"count": 1,
|
| 8210 |
+
"success": 0,
|
| 8211 |
+
"accepted": 0,
|
| 8212 |
+
"partial": 0,
|
| 8213 |
+
"rejected": 1,
|
| 8214 |
+
"total_reward": 0.001,
|
| 8215 |
+
"avg_reward": 0.001,
|
| 8216 |
+
"last_status": "REJECTED",
|
| 8217 |
+
"last_reason": "Hospital cannot admit: ICU unavailable, Hospital overloaded",
|
| 8218 |
+
"success_rate": 0.0
|
| 8219 |
+
}
|
| 8220 |
+
},
|
| 8221 |
+
"2": {
|
| 8222 |
+
"H6": {
|
| 8223 |
+
"count": 1,
|
| 8224 |
+
"success": 0,
|
| 8225 |
+
"accepted": 0,
|
| 8226 |
+
"partial": 0,
|
| 8227 |
+
"rejected": 1,
|
| 8228 |
+
"total_reward": 0.039,
|
| 8229 |
+
"avg_reward": 0.039,
|
| 8230 |
+
"last_status": "REJECTED",
|
| 8231 |
+
"last_reason": "Hospital cannot admit: ICU unavailable",
|
| 8232 |
+
"success_rate": 0.0
|
| 8233 |
+
}
|
| 8234 |
+
},
|
| 8235 |
+
"3": {
|
| 8236 |
+
"H4": {
|
| 8237 |
+
"count": 1,
|
| 8238 |
+
"success": 0,
|
| 8239 |
+
"accepted": 0,
|
| 8240 |
+
"partial": 0,
|
| 8241 |
+
"rejected": 1,
|
| 8242 |
+
"total_reward": 0.001,
|
| 8243 |
+
"avg_reward": 0.001,
|
| 8244 |
+
"last_status": "REJECTED",
|
| 8245 |
+
"last_reason": "Hospital cannot admit: ICU unavailable",
|
| 8246 |
+
"success_rate": 0.0
|
| 8247 |
+
}
|
| 8248 |
+
},
|
| 8249 |
+
"4": {
|
| 8250 |
+
"H6": {
|
| 8251 |
+
"count": 1,
|
| 8252 |
+
"success": 0,
|
| 8253 |
+
"accepted": 0,
|
| 8254 |
+
"partial": 0,
|
| 8255 |
+
"rejected": 1,
|
| 8256 |
+
"total_reward": 0.001,
|
| 8257 |
+
"avg_reward": 0.001,
|
| 8258 |
+
"last_status": "REJECTED",
|
| 8259 |
+
"last_reason": "Hidden mismatch at arrival (wrong risky guess). Rerouting required.",
|
| 8260 |
+
"success_rate": 0.0
|
| 8261 |
+
}
|
| 8262 |
+
}
|
| 8263 |
+
},
|
| 8264 |
+
"last_score": 0.15297499999999997,
|
| 8265 |
+
"last_success": false,
|
| 8266 |
+
"last_run_at": "2026-04-09T08:39:47.915419+00:00",
|
| 8267 |
+
"last_actions": [
|
| 8268 |
+
"H1",
|
| 8269 |
+
"H6",
|
| 8270 |
+
"H4",
|
| 8271 |
+
"H6"
|
| 8272 |
+
],
|
| 8273 |
+
"last_required_specialization": "general",
|
| 8274 |
+
"last_scenario_type": "fire",
|
| 8275 |
+
"last_scenario_name": "Wildfire Front (Evacuation Gridlock)",
|
| 8276 |
+
"best_success": false,
|
| 8277 |
+
"best_scenario_name": "Wildfire Front (Evacuation Gridlock)",
|
| 8278 |
+
"best_difficulty": "hard",
|
| 8279 |
+
"best_required_specialization": "general"
|
| 8280 |
+
},
|
| 8281 |
+
"579018919|acde_easy": {
|
| 8282 |
+
"attempts": 1,
|
| 8283 |
+
"best_score": 0.4829,
|
| 8284 |
+
"best_actions": [
|
| 8285 |
+
"H4",
|
| 8286 |
+
"H1",
|
| 8287 |
+
"H4"
|
| 8288 |
+
],
|
| 8289 |
+
"best_steps": 3,
|
| 8290 |
+
"step_stats": {
|
| 8291 |
+
"1": {
|
| 8292 |
+
"H4": {
|
| 8293 |
+
"count": 1,
|
| 8294 |
+
"success": 0,
|
| 8295 |
+
"accepted": 0,
|
| 8296 |
+
"partial": 1,
|
| 8297 |
+
"rejected": 0,
|
| 8298 |
+
"total_reward": 0.30400000000000005,
|
| 8299 |
+
"avg_reward": 0.30400000000000005,
|
| 8300 |
+
"last_status": "PARTIAL",
|
| 8301 |
+
"last_reason": "Early rejection mitigated by emergency field stabilization",
|
| 8302 |
+
"success_rate": 0.0
|
| 8303 |
+
}
|
| 8304 |
+
},
|
| 8305 |
+
"2": {
|
| 8306 |
+
"H1": {
|
| 8307 |
+
"count": 1,
|
| 8308 |
+
"success": 0,
|
| 8309 |
+
"accepted": 0,
|
| 8310 |
+
"partial": 0,
|
| 8311 |
+
"rejected": 1,
|
| 8312 |
+
"total_reward": 0.109,
|
| 8313 |
+
"avg_reward": 0.109,
|
| 8314 |
+
"last_status": "REJECTED",
|
| 8315 |
+
"last_reason": "Condition became irreversible after delays",
|
| 8316 |
+
"success_rate": 0.0
|
| 8317 |
+
}
|
| 8318 |
+
},
|
| 8319 |
+
"3": {
|
| 8320 |
+
"H4": {
|
| 8321 |
+
"count": 1,
|
| 8322 |
+
"success": 1,
|
| 8323 |
+
"accepted": 1,
|
| 8324 |
+
"partial": 0,
|
| 8325 |
+
"rejected": 0,
|
| 8326 |
+
"total_reward": 0.562,
|
| 8327 |
+
"avg_reward": 0.562,
|
| 8328 |
+
"last_status": "ACCEPTED",
|
| 8329 |
+
"last_reason": "Condition stabilized after progressive treatment",
|
| 8330 |
+
"success_rate": 1.0
|
| 8331 |
+
}
|
| 8332 |
+
}
|
| 8333 |
+
},
|
| 8334 |
+
"last_score": 0.4829,
|
| 8335 |
+
"last_success": true,
|
| 8336 |
+
"last_run_at": "2026-04-09T08:42:06.312270+00:00",
|
| 8337 |
+
"last_actions": [
|
| 8338 |
+
"H4",
|
| 8339 |
+
"H1",
|
| 8340 |
+
"H4"
|
| 8341 |
+
],
|
| 8342 |
+
"last_required_specialization": "general",
|
| 8343 |
+
"last_scenario_type": "fire",
|
| 8344 |
+
"last_scenario_name": "Apartment Fire (Smoke Inhalation)",
|
| 8345 |
+
"best_success": true,
|
| 8346 |
+
"best_scenario_name": "Apartment Fire (Smoke Inhalation)",
|
| 8347 |
+
"best_difficulty": "easy",
|
| 8348 |
+
"best_required_specialization": "general"
|
| 8349 |
+
},
|
| 8350 |
+
"579018920|acde_medium": {
|
| 8351 |
+
"attempts": 1,
|
| 8352 |
+
"best_score": 0.17291249999999997,
|
| 8353 |
+
"best_actions": [
|
| 8354 |
+
"H3",
|
| 8355 |
+
"H5",
|
| 8356 |
+
"H1",
|
| 8357 |
+
"H4"
|
| 8358 |
+
],
|
| 8359 |
+
"best_steps": 4,
|
| 8360 |
+
"step_stats": {
|
| 8361 |
+
"1": {
|
| 8362 |
+
"H3": {
|
| 8363 |
+
"count": 1,
|
| 8364 |
+
"success": 0,
|
| 8365 |
+
"accepted": 0,
|
| 8366 |
+
"partial": 0,
|
| 8367 |
+
"rejected": 1,
|
| 8368 |
+
"total_reward": 0.001,
|
| 8369 |
+
"avg_reward": 0.001,
|
| 8370 |
+
"last_status": "REJECTED",
|
| 8371 |
+
"last_reason": "Hospital cannot admit: ICU unavailable",
|
| 8372 |
+
"success_rate": 0.0
|
| 8373 |
+
}
|
| 8374 |
+
},
|
| 8375 |
+
"2": {
|
| 8376 |
+
"H5": {
|
| 8377 |
+
"count": 1,
|
| 8378 |
+
"success": 0,
|
| 8379 |
+
"accepted": 0,
|
| 8380 |
+
"partial": 1,
|
| 8381 |
+
"rejected": 0,
|
| 8382 |
+
"total_reward": 0.358,
|
| 8383 |
+
"avg_reward": 0.358,
|
| 8384 |
+
"last_status": "PARTIAL",
|
| 8385 |
+
"last_reason": "Admitted with significant risk: No specialist available",
|
| 8386 |
+
"success_rate": 0.0
|
| 8387 |
+
}
|
| 8388 |
+
},
|
| 8389 |
+
"3": {
|
| 8390 |
+
"H1": {
|
| 8391 |
+
"count": 1,
|
| 8392 |
+
"success": 0,
|
| 8393 |
+
"accepted": 0,
|
| 8394 |
+
"partial": 0,
|
| 8395 |
+
"rejected": 1,
|
| 8396 |
+
"total_reward": 0.001,
|
| 8397 |
+
"avg_reward": 0.001,
|
| 8398 |
+
"last_status": "REJECTED",
|
| 8399 |
+
"last_reason": "Hospital cannot admit: ICU unavailable",
|
| 8400 |
+
"success_rate": 0.0
|
| 8401 |
+
}
|
| 8402 |
+
},
|
| 8403 |
+
"4": {
|
| 8404 |
+
"H4": {
|
| 8405 |
+
"count": 1,
|
| 8406 |
+
"success": 0,
|
| 8407 |
+
"accepted": 0,
|
| 8408 |
+
"partial": 0,
|
| 8409 |
+
"rejected": 1,
|
| 8410 |
+
"total_reward": 0.001,
|
| 8411 |
+
"avg_reward": 0.001,
|
| 8412 |
+
"last_status": "REJECTED",
|
| 8413 |
+
"last_reason": "Hidden mismatch at arrival (wrong risky guess). Rerouting required.",
|
| 8414 |
+
"success_rate": 0.0
|
| 8415 |
+
}
|
| 8416 |
+
}
|
| 8417 |
+
},
|
| 8418 |
+
"last_score": 0.17291249999999997,
|
| 8419 |
+
"last_success": false,
|
| 8420 |
+
"last_run_at": "2026-04-09T08:42:08.231621+00:00",
|
| 8421 |
+
"last_actions": [
|
| 8422 |
+
"H3",
|
| 8423 |
+
"H5",
|
| 8424 |
+
"H1",
|
| 8425 |
+
"H4"
|
| 8426 |
+
],
|
| 8427 |
+
"last_required_specialization": "general",
|
| 8428 |
+
"last_scenario_type": "fire",
|
| 8429 |
+
"last_scenario_name": "Factory Fire (Chemical Exposure)",
|
| 8430 |
+
"best_success": false,
|
| 8431 |
+
"best_scenario_name": "Factory Fire (Chemical Exposure)",
|
| 8432 |
+
"best_difficulty": "medium",
|
| 8433 |
+
"best_required_specialization": "general"
|
| 8434 |
+
},
|
| 8435 |
+
"579018921|acde_hard": {
|
| 8436 |
+
"attempts": 1,
|
| 8437 |
+
"best_score": 0.3184875000000001,
|
| 8438 |
+
"best_actions": [
|
| 8439 |
+
"H1",
|
| 8440 |
+
"H6",
|
| 8441 |
+
"H3",
|
| 8442 |
+
"H5"
|
| 8443 |
+
],
|
| 8444 |
+
"best_steps": 4,
|
| 8445 |
+
"step_stats": {
|
| 8446 |
+
"1": {
|
| 8447 |
+
"H1": {
|
| 8448 |
+
"count": 1,
|
| 8449 |
+
"success": 0,
|
| 8450 |
+
"accepted": 0,
|
| 8451 |
+
"partial": 0,
|
| 8452 |
+
"rejected": 1,
|
| 8453 |
+
"total_reward": 0.001,
|
| 8454 |
+
"avg_reward": 0.001,
|
| 8455 |
+
"last_status": "REJECTED",
|
| 8456 |
+
"last_reason": "Hospital cannot admit: ICU unavailable",
|
| 8457 |
+
"success_rate": 0.0
|
| 8458 |
+
}
|
| 8459 |
+
},
|
| 8460 |
+
"2": {
|
| 8461 |
+
"H6": {
|
| 8462 |
+
"count": 1,
|
| 8463 |
+
"success": 0,
|
| 8464 |
+
"accepted": 0,
|
| 8465 |
+
"partial": 0,
|
| 8466 |
+
"rejected": 1,
|
| 8467 |
+
"total_reward": 0.001,
|
| 8468 |
+
"avg_reward": 0.001,
|
| 8469 |
+
"last_status": "REJECTED",
|
| 8470 |
+
"last_reason": "Hospital cannot admit: Hospital overloaded",
|
| 8471 |
+
"success_rate": 0.0
|
| 8472 |
+
}
|
| 8473 |
+
},
|
| 8474 |
+
"3": {
|
| 8475 |
+
"H3": {
|
| 8476 |
+
"count": 1,
|
| 8477 |
+
"success": 0,
|
| 8478 |
+
"accepted": 0,
|
| 8479 |
+
"partial": 0,
|
| 8480 |
+
"rejected": 1,
|
| 8481 |
+
"total_reward": 0.001,
|
| 8482 |
+
"avg_reward": 0.001,
|
| 8483 |
+
"last_status": "REJECTED",
|
| 8484 |
+
"last_reason": "Hospital cannot admit: Hospital overloaded",
|
| 8485 |
+
"success_rate": 0.0
|
| 8486 |
+
}
|
| 8487 |
+
},
|
| 8488 |
+
"4": {
|
| 8489 |
+
"H5": {
|
| 8490 |
+
"count": 1,
|
| 8491 |
+
"success": 0,
|
| 8492 |
+
"accepted": 0,
|
| 8493 |
+
"partial": 1,
|
| 8494 |
+
"rejected": 0,
|
| 8495 |
+
"total_reward": 0.40800000000000014,
|
| 8496 |
+
"avg_reward": 0.40800000000000014,
|
| 8497 |
+
"last_status": "PARTIAL",
|
| 8498 |
+
"last_reason": "Admitted with delays: prolonged transfer strain",
|
| 8499 |
+
"success_rate": 0.0
|
| 8500 |
+
}
|
| 8501 |
+
}
|
| 8502 |
+
},
|
| 8503 |
+
"last_score": 0.3184875000000001,
|
| 8504 |
+
"last_success": false,
|
| 8505 |
+
"last_run_at": "2026-04-09T08:42:10.003857+00:00",
|
| 8506 |
+
"last_actions": [
|
| 8507 |
+
"H1",
|
| 8508 |
+
"H6",
|
| 8509 |
+
"H3",
|
| 8510 |
+
"H5"
|
| 8511 |
+
],
|
| 8512 |
+
"last_required_specialization": "general",
|
| 8513 |
+
"last_scenario_type": "fire",
|
| 8514 |
+
"last_scenario_name": "Wildfire Front (Evacuation Gridlock)",
|
| 8515 |
+
"best_success": false,
|
| 8516 |
+
"best_scenario_name": "Wildfire Front (Evacuation Gridlock)",
|
| 8517 |
+
"best_difficulty": "hard",
|
| 8518 |
+
"best_required_specialization": "general"
|
| 8519 |
+
},
|
| 8520 |
+
"468098215|acde_easy": {
|
| 8521 |
+
"attempts": 1,
|
| 8522 |
+
"best_score": 0.23815,
|
| 8523 |
+
"best_actions": [
|
| 8524 |
+
"H3",
|
| 8525 |
+
"H4",
|
| 8526 |
+
"H1"
|
| 8527 |
+
],
|
| 8528 |
+
"best_steps": 3,
|
| 8529 |
+
"step_stats": {
|
| 8530 |
+
"1": {
|
| 8531 |
+
"H3": {
|
| 8532 |
+
"count": 1,
|
| 8533 |
+
"success": 0,
|
| 8534 |
+
"accepted": 0,
|
| 8535 |
+
"partial": 1,
|
| 8536 |
+
"rejected": 0,
|
| 8537 |
+
"total_reward": 0.334,
|
| 8538 |
+
"avg_reward": 0.334,
|
| 8539 |
+
"last_status": "PARTIAL",
|
| 8540 |
+
"last_reason": "Critical deterioration managed temporarily; reroute still needed",
|
| 8541 |
+
"success_rate": 0.0
|
| 8542 |
+
}
|
| 8543 |
+
},
|
| 8544 |
+
"2": {
|
| 8545 |
+
"H4": {
|
| 8546 |
+
"count": 1,
|
| 8547 |
+
"success": 0,
|
| 8548 |
+
"accepted": 0,
|
| 8549 |
+
"partial": 0,
|
| 8550 |
+
"rejected": 1,
|
| 8551 |
+
"total_reward": 0.001,
|
| 8552 |
+
"avg_reward": 0.001,
|
| 8553 |
+
"last_status": "REJECTED",
|
| 8554 |
+
"last_reason": "Emergency beds became unavailable during arrival",
|
| 8555 |
+
"success_rate": 0.0
|
| 8556 |
+
}
|
| 8557 |
+
},
|
| 8558 |
+
"3": {
|
| 8559 |
+
"H1": {
|
| 8560 |
+
"count": 1,
|
| 8561 |
+
"success": 0,
|
| 8562 |
+
"accepted": 0,
|
| 8563 |
+
"partial": 0,
|
| 8564 |
+
"rejected": 1,
|
| 8565 |
+
"total_reward": 0.139,
|
| 8566 |
+
"avg_reward": 0.139,
|
| 8567 |
+
"last_status": "REJECTED",
|
| 8568 |
+
"last_reason": "Condition became non-transferable during delay; immediate critical care failed",
|
| 8569 |
+
"success_rate": 0.0
|
| 8570 |
+
}
|
| 8571 |
+
}
|
| 8572 |
+
},
|
| 8573 |
+
"last_score": 0.23815,
|
| 8574 |
+
"last_success": false,
|
| 8575 |
+
"last_run_at": "2026-04-09T08:48:34.391142+00:00",
|
| 8576 |
+
"last_actions": [
|
| 8577 |
+
"H3",
|
| 8578 |
+
"H4",
|
| 8579 |
+
"H1"
|
| 8580 |
+
],
|
| 8581 |
+
"last_required_specialization": "general",
|
| 8582 |
+
"last_scenario_type": "fire",
|
| 8583 |
+
"last_scenario_name": "Apartment Fire (Smoke Inhalation)",
|
| 8584 |
+
"best_success": false,
|
| 8585 |
+
"best_scenario_name": "Apartment Fire (Smoke Inhalation)",
|
| 8586 |
+
"best_difficulty": "easy",
|
| 8587 |
+
"best_required_specialization": "general"
|
| 8588 |
+
},
|
| 8589 |
+
"468098216|acde_medium": {
|
| 8590 |
+
"attempts": 1,
|
| 8591 |
+
"best_score": 0.7869000000000002,
|
| 8592 |
+
"best_actions": [
|
| 8593 |
+
"H2",
|
| 8594 |
+
"H3"
|
| 8595 |
+
],
|
| 8596 |
+
"best_steps": 2,
|
| 8597 |
+
"step_stats": {
|
| 8598 |
+
"1": {
|
| 8599 |
+
"H2": {
|
| 8600 |
+
"count": 1,
|
| 8601 |
+
"success": 0,
|
| 8602 |
+
"accepted": 0,
|
| 8603 |
+
"partial": 1,
|
| 8604 |
+
"rejected": 0,
|
| 8605 |
+
"total_reward": 0.37,
|
| 8606 |
+
"avg_reward": 0.37,
|
| 8607 |
+
"last_status": "PARTIAL",
|
| 8608 |
+
"last_reason": "Initial triage completed; transfer monitoring still required",
|
| 8609 |
+
"success_rate": 0.0
|
| 8610 |
+
}
|
| 8611 |
+
},
|
| 8612 |
+
"2": {
|
| 8613 |
+
"H3": {
|
| 8614 |
+
"count": 1,
|
| 8615 |
+
"success": 1,
|
| 8616 |
+
"accepted": 1,
|
| 8617 |
+
"partial": 0,
|
| 8618 |
+
"rejected": 0,
|
| 8619 |
+
"total_reward": 0.8660000000000001,
|
| 8620 |
+
"avg_reward": 0.8660000000000001,
|
| 8621 |
+
"last_status": "ACCEPTED",
|
| 8622 |
+
"last_reason": "Patient stabilized after delayed admission",
|
| 8623 |
+
"success_rate": 1.0
|
| 8624 |
+
}
|
| 8625 |
+
}
|
| 8626 |
+
},
|
| 8627 |
+
"last_score": 0.7869000000000002,
|
| 8628 |
+
"last_success": true,
|
| 8629 |
+
"last_run_at": "2026-04-09T08:48:35.228474+00:00",
|
| 8630 |
+
"last_actions": [
|
| 8631 |
+
"H2",
|
| 8632 |
+
"H3"
|
| 8633 |
+
],
|
| 8634 |
+
"last_required_specialization": "trauma",
|
| 8635 |
+
"last_scenario_type": "accident",
|
| 8636 |
+
"last_scenario_name": "Urban Pile-up (Rush Hour)",
|
| 8637 |
+
"best_success": true,
|
| 8638 |
+
"best_scenario_name": "Urban Pile-up (Rush Hour)",
|
| 8639 |
+
"best_difficulty": "medium",
|
| 8640 |
+
"best_required_specialization": "trauma"
|
| 8641 |
+
},
|
| 8642 |
+
"468098217|acde_hard": {
|
| 8643 |
+
"attempts": 1,
|
| 8644 |
+
"best_score": 0.15059999999999998,
|
| 8645 |
+
"best_actions": [
|
| 8646 |
+
"H2",
|
| 8647 |
+
"H3",
|
| 8648 |
+
"H5",
|
| 8649 |
+
"H2"
|
| 8650 |
+
],
|
| 8651 |
+
"best_steps": 4,
|
| 8652 |
+
"step_stats": {
|
| 8653 |
+
"1": {
|
| 8654 |
+
"H2": {
|
| 8655 |
+
"count": 1,
|
| 8656 |
+
"success": 0,
|
| 8657 |
+
"accepted": 0,
|
| 8658 |
+
"partial": 0,
|
| 8659 |
+
"rejected": 1,
|
| 8660 |
+
"total_reward": 0.001,
|
| 8661 |
+
"avg_reward": 0.001,
|
| 8662 |
+
"last_status": "REJECTED",
|
| 8663 |
+
"last_reason": "Hospital cannot admit: Hospital overloaded",
|
| 8664 |
+
"success_rate": 0.0
|
| 8665 |
+
}
|
| 8666 |
+
},
|
| 8667 |
+
"2": {
|
| 8668 |
+
"H3": {
|
| 8669 |
+
"count": 1,
|
| 8670 |
+
"success": 0,
|
| 8671 |
+
"accepted": 0,
|
| 8672 |
+
"partial": 0,
|
| 8673 |
+
"rejected": 1,
|
| 8674 |
+
"total_reward": 0.001,
|
| 8675 |
+
"avg_reward": 0.001,
|
| 8676 |
+
"last_status": "REJECTED",
|
| 8677 |
+
"last_reason": "Hidden mismatch at arrival (wrong risky guess). Rerouting required.",
|
| 8678 |
+
"success_rate": 0.0
|
| 8679 |
+
}
|
| 8680 |
+
},
|
| 8681 |
+
"3": {
|
| 8682 |
+
"H5": {
|
| 8683 |
+
"count": 1,
|
| 8684 |
+
"success": 0,
|
| 8685 |
+
"accepted": 0,
|
| 8686 |
+
"partial": 0,
|
| 8687 |
+
"rejected": 1,
|
| 8688 |
+
"total_reward": 0.001,
|
| 8689 |
+
"avg_reward": 0.001,
|
| 8690 |
+
"last_status": "REJECTED",
|
| 8691 |
+
"last_reason": "Hidden mismatch at arrival (wrong risky guess). Rerouting required.",
|
| 8692 |
+
"success_rate": 0.0
|
| 8693 |
+
}
|
| 8694 |
+
},
|
| 8695 |
+
"4": {
|
| 8696 |
+
"H2": {
|
| 8697 |
+
"count": 1,
|
| 8698 |
+
"success": 0,
|
| 8699 |
+
"accepted": 0,
|
| 8700 |
+
"partial": 0,
|
| 8701 |
+
"rejected": 1,
|
| 8702 |
+
"total_reward": 0.001,
|
| 8703 |
+
"avg_reward": 0.001,
|
| 8704 |
+
"last_status": "REJECTED",
|
| 8705 |
+
"last_reason": "Hospital cannot admit: ICU unavailable, No specialist available",
|
| 8706 |
+
"success_rate": 0.0
|
| 8707 |
+
}
|
| 8708 |
+
}
|
| 8709 |
+
},
|
| 8710 |
+
"last_score": 0.15059999999999998,
|
| 8711 |
+
"last_success": false,
|
| 8712 |
+
"last_run_at": "2026-04-09T08:48:36.666940+00:00",
|
| 8713 |
+
"last_actions": [
|
| 8714 |
+
"H2",
|
| 8715 |
+
"H3",
|
| 8716 |
+
"H5",
|
| 8717 |
+
"H2"
|
| 8718 |
+
],
|
| 8719 |
+
"last_required_specialization": "cardiac",
|
| 8720 |
+
"last_scenario_type": "medical",
|
| 8721 |
+
"last_scenario_name": "Mass Cardiac Event (Overload)",
|
| 8722 |
+
"best_success": false,
|
| 8723 |
+
"best_scenario_name": "Mass Cardiac Event (Overload)",
|
| 8724 |
+
"best_difficulty": "hard",
|
| 8725 |
+
"best_required_specialization": "cardiac"
|
| 8726 |
}
|
| 8727 |
},
|
| 8728 |
"episodes": [
|
|
|
|
| 10774 |
"H5"
|
| 10775 |
],
|
| 10776 |
"timestamp": "2026-04-09T05:19:13.261267+00:00"
|
| 10777 |
+
},
|
| 10778 |
+
{
|
| 10779 |
+
"seed": 509813872,
|
| 10780 |
+
"task_id": "acde_easy",
|
| 10781 |
+
"difficulty": "easy",
|
| 10782 |
+
"required_specialization": "trauma",
|
| 10783 |
+
"scenario_name": "Highway Collision (Severe)",
|
| 10784 |
+
"score": 0.5568666666666667,
|
| 10785 |
+
"success": true,
|
| 10786 |
+
"actions": [
|
| 10787 |
+
"H3",
|
| 10788 |
+
"H1",
|
| 10789 |
+
"H3"
|
| 10790 |
+
],
|
| 10791 |
+
"timestamp": "2026-04-09T08:39:45.824383+00:00"
|
| 10792 |
+
},
|
| 10793 |
+
{
|
| 10794 |
+
"seed": 509813873,
|
| 10795 |
+
"task_id": "acde_medium",
|
| 10796 |
+
"difficulty": "medium",
|
| 10797 |
+
"required_specialization": "general",
|
| 10798 |
+
"scenario_name": "Factory Fire (Chemical Exposure)",
|
| 10799 |
+
"score": 0.15059999999999998,
|
| 10800 |
+
"success": false,
|
| 10801 |
+
"actions": [
|
| 10802 |
+
"H3"
|
| 10803 |
+
],
|
| 10804 |
+
"timestamp": "2026-04-09T08:39:46.224541+00:00"
|
| 10805 |
+
},
|
| 10806 |
+
{
|
| 10807 |
+
"seed": 509813874,
|
| 10808 |
+
"task_id": "acde_hard",
|
| 10809 |
+
"difficulty": "hard",
|
| 10810 |
+
"required_specialization": "general",
|
| 10811 |
+
"scenario_name": "Wildfire Front (Evacuation Gridlock)",
|
| 10812 |
+
"score": 0.15297499999999997,
|
| 10813 |
+
"success": false,
|
| 10814 |
+
"actions": [
|
| 10815 |
+
"H1",
|
| 10816 |
+
"H6",
|
| 10817 |
+
"H4",
|
| 10818 |
+
"H6"
|
| 10819 |
+
],
|
| 10820 |
+
"timestamp": "2026-04-09T08:39:47.915419+00:00"
|
| 10821 |
+
},
|
| 10822 |
+
{
|
| 10823 |
+
"seed": 579018919,
|
| 10824 |
+
"task_id": "acde_easy",
|
| 10825 |
+
"difficulty": "easy",
|
| 10826 |
+
"required_specialization": "general",
|
| 10827 |
+
"scenario_name": "Apartment Fire (Smoke Inhalation)",
|
| 10828 |
+
"score": 0.4829,
|
| 10829 |
+
"success": true,
|
| 10830 |
+
"actions": [
|
| 10831 |
+
"H4",
|
| 10832 |
+
"H1",
|
| 10833 |
+
"H4"
|
| 10834 |
+
],
|
| 10835 |
+
"timestamp": "2026-04-09T08:42:06.312270+00:00"
|
| 10836 |
+
},
|
| 10837 |
+
{
|
| 10838 |
+
"seed": 579018920,
|
| 10839 |
+
"task_id": "acde_medium",
|
| 10840 |
+
"difficulty": "medium",
|
| 10841 |
+
"required_specialization": "general",
|
| 10842 |
+
"scenario_name": "Factory Fire (Chemical Exposure)",
|
| 10843 |
+
"score": 0.17291249999999997,
|
| 10844 |
+
"success": false,
|
| 10845 |
+
"actions": [
|
| 10846 |
+
"H3",
|
| 10847 |
+
"H5",
|
| 10848 |
+
"H1",
|
| 10849 |
+
"H4"
|
| 10850 |
+
],
|
| 10851 |
+
"timestamp": "2026-04-09T08:42:08.231621+00:00"
|
| 10852 |
+
},
|
| 10853 |
+
{
|
| 10854 |
+
"seed": 579018921,
|
| 10855 |
+
"task_id": "acde_hard",
|
| 10856 |
+
"difficulty": "hard",
|
| 10857 |
+
"required_specialization": "general",
|
| 10858 |
+
"scenario_name": "Wildfire Front (Evacuation Gridlock)",
|
| 10859 |
+
"score": 0.3184875000000001,
|
| 10860 |
+
"success": false,
|
| 10861 |
+
"actions": [
|
| 10862 |
+
"H1",
|
| 10863 |
+
"H6",
|
| 10864 |
+
"H3",
|
| 10865 |
+
"H5"
|
| 10866 |
+
],
|
| 10867 |
+
"timestamp": "2026-04-09T08:42:10.003857+00:00"
|
| 10868 |
+
},
|
| 10869 |
+
{
|
| 10870 |
+
"seed": 468098215,
|
| 10871 |
+
"task_id": "acde_easy",
|
| 10872 |
+
"difficulty": "easy",
|
| 10873 |
+
"required_specialization": "general",
|
| 10874 |
+
"scenario_name": "Apartment Fire (Smoke Inhalation)",
|
| 10875 |
+
"score": 0.23815,
|
| 10876 |
+
"success": false,
|
| 10877 |
+
"actions": [
|
| 10878 |
+
"H3",
|
| 10879 |
+
"H4",
|
| 10880 |
+
"H1"
|
| 10881 |
+
],
|
| 10882 |
+
"timestamp": "2026-04-09T08:48:34.391142+00:00"
|
| 10883 |
+
},
|
| 10884 |
+
{
|
| 10885 |
+
"seed": 468098216,
|
| 10886 |
+
"task_id": "acde_medium",
|
| 10887 |
+
"difficulty": "medium",
|
| 10888 |
+
"required_specialization": "trauma",
|
| 10889 |
+
"scenario_name": "Urban Pile-up (Rush Hour)",
|
| 10890 |
+
"score": 0.7869000000000002,
|
| 10891 |
+
"success": true,
|
| 10892 |
+
"actions": [
|
| 10893 |
+
"H2",
|
| 10894 |
+
"H3"
|
| 10895 |
+
],
|
| 10896 |
+
"timestamp": "2026-04-09T08:48:35.228474+00:00"
|
| 10897 |
+
},
|
| 10898 |
+
{
|
| 10899 |
+
"seed": 468098217,
|
| 10900 |
+
"task_id": "acde_hard",
|
| 10901 |
+
"difficulty": "hard",
|
| 10902 |
+
"required_specialization": "cardiac",
|
| 10903 |
+
"scenario_name": "Mass Cardiac Event (Overload)",
|
| 10904 |
+
"score": 0.15059999999999998,
|
| 10905 |
+
"success": false,
|
| 10906 |
+
"actions": [
|
| 10907 |
+
"H2",
|
| 10908 |
+
"H3",
|
| 10909 |
+
"H5",
|
| 10910 |
+
"H2"
|
| 10911 |
+
],
|
| 10912 |
+
"timestamp": "2026-04-09T08:48:36.666940+00:00"
|
| 10913 |
}
|
| 10914 |
]
|
| 10915 |
}
|
data/learning_memory.json
CHANGED
|
@@ -1,44 +1,44 @@
|
|
| 1 |
{
|
| 2 |
"H2": {
|
| 3 |
-
"success":
|
| 4 |
-
"fail":
|
| 5 |
-
"avg": 0.
|
| 6 |
-
"accepted":
|
| 7 |
-
"rejected":
|
| 8 |
},
|
| 9 |
"H6": {
|
| 10 |
"success": 50,
|
| 11 |
-
"fail":
|
| 12 |
-
"avg": 0.
|
| 13 |
"accepted": 50,
|
| 14 |
-
"rejected":
|
| 15 |
},
|
| 16 |
"H5": {
|
| 17 |
-
"success":
|
| 18 |
-
"fail":
|
| 19 |
-
"avg": 0.
|
| 20 |
-
"accepted":
|
| 21 |
-
"rejected":
|
| 22 |
},
|
| 23 |
"H1": {
|
| 24 |
"success": 111,
|
| 25 |
-
"fail":
|
| 26 |
-
"avg": 0.
|
| 27 |
"accepted": 111,
|
| 28 |
-
"rejected":
|
| 29 |
},
|
| 30 |
"H3": {
|
| 31 |
-
"success":
|
| 32 |
-
"fail":
|
| 33 |
-
"avg": 0.
|
| 34 |
-
"accepted":
|
| 35 |
-
"rejected":
|
| 36 |
},
|
| 37 |
"H4": {
|
| 38 |
-
"success":
|
| 39 |
-
"fail":
|
| 40 |
-
"avg": 0.
|
| 41 |
-
"accepted":
|
| 42 |
-
"rejected":
|
| 43 |
}
|
| 44 |
}
|
|
|
|
| 1 |
{
|
| 2 |
"H2": {
|
| 3 |
+
"success": 111,
|
| 4 |
+
"fail": 214,
|
| 5 |
+
"avg": 0.3359261538461545,
|
| 6 |
+
"accepted": 111,
|
| 7 |
+
"rejected": 214
|
| 8 |
},
|
| 9 |
"H6": {
|
| 10 |
"success": 50,
|
| 11 |
+
"fail": 145,
|
| 12 |
+
"avg": 0.291038461538462,
|
| 13 |
"accepted": 50,
|
| 14 |
+
"rejected": 145
|
| 15 |
},
|
| 16 |
"H5": {
|
| 17 |
+
"success": 116,
|
| 18 |
+
"fail": 179,
|
| 19 |
+
"avg": 0.38248847457627094,
|
| 20 |
+
"accepted": 116,
|
| 21 |
+
"rejected": 179
|
| 22 |
},
|
| 23 |
"H1": {
|
| 24 |
"success": 111,
|
| 25 |
+
"fail": 117,
|
| 26 |
+
"avg": 0.401454385964912,
|
| 27 |
"accepted": 111,
|
| 28 |
+
"rejected": 117
|
| 29 |
},
|
| 30 |
"H3": {
|
| 31 |
+
"success": 106,
|
| 32 |
+
"fail": 160,
|
| 33 |
+
"avg": 0.35322067669172913,
|
| 34 |
+
"accepted": 106,
|
| 35 |
+
"rejected": 160
|
| 36 |
},
|
| 37 |
"H4": {
|
| 38 |
+
"success": 43,
|
| 39 |
+
"fail": 108,
|
| 40 |
+
"avg": 0.34369470198675456,
|
| 41 |
+
"accepted": 43,
|
| 42 |
+
"rejected": 108
|
| 43 |
}
|
| 44 |
}
|
data/trajectory_history.jsonl
CHANGED
|
@@ -355,3 +355,31 @@
|
|
| 355 |
{"seed": 600335718, "task": "acde_medium", "difficulty": "medium", "step": 2, "state": {"patient_condition": "serious", "remaining_time_minutes": 17.0, "failed_hospitals": ["H4"], "visited_hospitals": ["H4"], "ambulance_status": "rerouting"}, "action": {"hospital_id": "H4", "policy_score": 0.07313118251968914, "strategy": "risk-aware policy + anti-stupidity guard + immediate-retry override"}, "outcome": {"status": "ACCEPTED", "reason": "Patient stabilized after delayed admission"}, "reward": 0.516}
|
| 356 |
{"seed": 600335719, "task": "acde_hard", "difficulty": "hard", "step": 1, "state": {"patient_condition": "critical", "remaining_time_minutes": 11.0, "failed_hospitals": [], "visited_hospitals": [], "ambulance_status": "en_route"}, "action": {"hospital_id": "H6", "policy_score": 0.3244976864117649, "strategy": "safe policy + critical triage + anti-stupidity guard"}, "outcome": {"status": "REJECTED", "reason": "Hidden mismatch at arrival (wrong risky guess). Rerouting required."}, "reward": 0.001}
|
| 357 |
{"seed": 600335719, "task": "acde_hard", "difficulty": "hard", "step": 2, "state": {"patient_condition": "critical", "remaining_time_minutes": 11.0, "failed_hospitals": ["H6"], "visited_hospitals": ["H6"], "ambulance_status": "rerouting"}, "action": {"hospital_id": "H5", "policy_score": 0.2976383394515107, "strategy": "risk-aware policy"}, "outcome": {"status": "REJECTED", "reason": "Condition became non-transferable during delay; immediate critical care failed"}, "reward": 0.001}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 355 |
{"seed": 600335718, "task": "acde_medium", "difficulty": "medium", "step": 2, "state": {"patient_condition": "serious", "remaining_time_minutes": 17.0, "failed_hospitals": ["H4"], "visited_hospitals": ["H4"], "ambulance_status": "rerouting"}, "action": {"hospital_id": "H4", "policy_score": 0.07313118251968914, "strategy": "risk-aware policy + anti-stupidity guard + immediate-retry override"}, "outcome": {"status": "ACCEPTED", "reason": "Patient stabilized after delayed admission"}, "reward": 0.516}
|
| 356 |
{"seed": 600335719, "task": "acde_hard", "difficulty": "hard", "step": 1, "state": {"patient_condition": "critical", "remaining_time_minutes": 11.0, "failed_hospitals": [], "visited_hospitals": [], "ambulance_status": "en_route"}, "action": {"hospital_id": "H6", "policy_score": 0.3244976864117649, "strategy": "safe policy + critical triage + anti-stupidity guard"}, "outcome": {"status": "REJECTED", "reason": "Hidden mismatch at arrival (wrong risky guess). Rerouting required."}, "reward": 0.001}
|
| 357 |
{"seed": 600335719, "task": "acde_hard", "difficulty": "hard", "step": 2, "state": {"patient_condition": "critical", "remaining_time_minutes": 11.0, "failed_hospitals": ["H6"], "visited_hospitals": ["H6"], "ambulance_status": "rerouting"}, "action": {"hospital_id": "H5", "policy_score": 0.2976383394515107, "strategy": "risk-aware policy"}, "outcome": {"status": "REJECTED", "reason": "Condition became non-transferable during delay; immediate critical care failed"}, "reward": 0.001}
|
| 358 |
+
{"seed": 509813872, "task": "acde_easy", "difficulty": "easy", "step": 1, "state": {"patient_condition": "serious", "remaining_time_minutes": 20.0, "failed_hospitals": [], "visited_hospitals": [], "ambulance_status": "en_route"}, "action": {"hospital_id": "H3", "policy_score": 0.3729382136813107, "strategy": "safe policy"}, "outcome": {"status": "PARTIAL", "reason": "Early rejection mitigated by emergency field stabilization"}, "reward": 0.15500000000000003}
|
| 359 |
+
{"seed": 509813872, "task": "acde_easy", "difficulty": "easy", "step": 2, "state": {"patient_condition": "serious", "remaining_time_minutes": 20.0, "failed_hospitals": [], "visited_hospitals": ["H3"], "ambulance_status": "in_transit"}, "action": {"hospital_id": "H1", "policy_score": 0.4212703202476848, "strategy": "balanced policy"}, "outcome": {"status": "REJECTED", "reason": "Hospital cannot admit: ICU unavailable, Hospital overloaded"}, "reward": 0.139}
|
| 360 |
+
{"seed": 509813872, "task": "acde_easy", "difficulty": "easy", "step": 3, "state": {"patient_condition": "serious", "remaining_time_minutes": 20.0, "failed_hospitals": ["H1"], "visited_hospitals": ["H3", "H1"], "ambulance_status": "rerouting"}, "action": {"hospital_id": "H3", "policy_score": 0.4992501253122812, "strategy": "risk-aware policy + anti-stupidity guard"}, "outcome": {"status": "ACCEPTED", "reason": "Patient stabilized after delayed admission"}, "reward": 0.7160000000000001}
|
| 361 |
+
{"seed": 509813873, "task": "acde_medium", "difficulty": "medium", "step": 1, "state": {"patient_condition": "critical", "remaining_time_minutes": 15.0, "failed_hospitals": [], "visited_hospitals": [], "ambulance_status": "en_route"}, "action": {"hospital_id": "H3", "policy_score": 0.4050067561693999, "strategy": "safe policy + critical triage"}, "outcome": {"status": "REJECTED", "reason": "Condition became non-transferable during delay; immediate critical care failed"}, "reward": 0.001}
|
| 362 |
+
{"seed": 509813874, "task": "acde_hard", "difficulty": "hard", "step": 1, "state": {"patient_condition": "critical", "remaining_time_minutes": 11.0, "failed_hospitals": [], "visited_hospitals": [], "ambulance_status": "en_route"}, "action": {"hospital_id": "H1", "policy_score": 0.49875162318940436, "strategy": "safe policy + critical triage"}, "outcome": {"status": "REJECTED", "reason": "Hospital cannot admit: ICU unavailable, Hospital overloaded"}, "reward": 0.001}
|
| 363 |
+
{"seed": 509813874, "task": "acde_hard", "difficulty": "hard", "step": 2, "state": {"patient_condition": "critical", "remaining_time_minutes": 11.0, "failed_hospitals": ["H1"], "visited_hospitals": ["H1"], "ambulance_status": "rerouting"}, "action": {"hospital_id": "H6", "policy_score": 0.49875162318940436, "strategy": "risk-aware policy + guided-exploration + anti-stupidity guard"}, "outcome": {"status": "REJECTED", "reason": "Hospital cannot admit: ICU unavailable"}, "reward": 0.039}
|
| 364 |
+
{"seed": 509813874, "task": "acde_hard", "difficulty": "hard", "step": 3, "state": {"patient_condition": "critical", "remaining_time_minutes": 11.0, "failed_hospitals": ["H1", "H3"], "visited_hospitals": ["H1", "H3"], "ambulance_status": "rerouting"}, "action": {"hospital_id": "H4", "policy_score": 0.14285712242855392, "strategy": "risk-aware policy"}, "outcome": {"status": "REJECTED", "reason": "Hospital cannot admit: ICU unavailable"}, "reward": 0.001}
|
| 365 |
+
{"seed": 509813874, "task": "acde_hard", "difficulty": "hard", "step": 4, "state": {"patient_condition": "critical", "remaining_time_minutes": 11.0, "failed_hospitals": ["H1", "H3", "H4"], "visited_hospitals": ["H1", "H3", "H4"], "ambulance_status": "rerouting"}, "action": {"hospital_id": "H6", "policy_score": 0.49875162318940436, "strategy": "risk-aware policy + anti-stupidity guard"}, "outcome": {"status": "REJECTED", "reason": "Hidden mismatch at arrival (wrong risky guess). Rerouting required."}, "reward": 0.001}
|
| 366 |
+
{"seed": 579018919, "task": "acde_easy", "difficulty": "easy", "step": 1, "state": {"patient_condition": "serious", "remaining_time_minutes": 18.0, "failed_hospitals": [], "visited_hospitals": [], "ambulance_status": "en_route"}, "action": {"hospital_id": "H4", "policy_score": 0.32949151496142953, "strategy": "safe policy"}, "outcome": {"status": "PARTIAL", "reason": "Early rejection mitigated by emergency field stabilization"}, "reward": 0.30400000000000005}
|
| 367 |
+
{"seed": 579018919, "task": "acde_easy", "difficulty": "easy", "step": 2, "state": {"patient_condition": "serious", "remaining_time_minutes": 18.0, "failed_hospitals": [], "visited_hospitals": ["H4"], "ambulance_status": "in_transit"}, "action": {"hospital_id": "H1", "policy_score": 0.34824268005046066, "strategy": "balanced policy + anti-stupidity guard"}, "outcome": {"status": "REJECTED", "reason": "Condition became irreversible after delays"}, "reward": 0.109}
|
| 368 |
+
{"seed": 579018919, "task": "acde_easy", "difficulty": "easy", "step": 3, "state": {"patient_condition": "unstable", "remaining_time_minutes": 18.0, "failed_hospitals": ["H1"], "visited_hospitals": ["H4", "H1"], "ambulance_status": "rerouting"}, "action": {"hospital_id": "H4", "policy_score": 0.4992501253122812, "strategy": "risk-aware policy + anti-stupidity guard"}, "outcome": {"status": "ACCEPTED", "reason": "Condition stabilized after progressive treatment"}, "reward": 0.562}
|
| 369 |
+
{"seed": 579018920, "task": "acde_medium", "difficulty": "medium", "step": 1, "state": {"patient_condition": "critical", "remaining_time_minutes": 15.0, "failed_hospitals": [], "visited_hospitals": [], "ambulance_status": "en_route"}, "action": {"hospital_id": "H3", "policy_score": 0.4843607591902248, "strategy": "safe policy + critical triage + anti-stupidity guard"}, "outcome": {"status": "REJECTED", "reason": "Hospital cannot admit: ICU unavailable"}, "reward": 0.001}
|
| 370 |
+
{"seed": 579018920, "task": "acde_medium", "difficulty": "medium", "step": 2, "state": {"patient_condition": "critical", "remaining_time_minutes": 15.0, "failed_hospitals": ["H3"], "visited_hospitals": ["H3"], "ambulance_status": "rerouting"}, "action": {"hospital_id": "H5", "policy_score": 0.48373386963831955, "strategy": "risk-aware policy + anti-stupidity guard"}, "outcome": {"status": "PARTIAL", "reason": "Admitted with significant risk: No specialist available"}, "reward": 0.358}
|
| 371 |
+
{"seed": 579018920, "task": "acde_medium", "difficulty": "medium", "step": 3, "state": {"patient_condition": "critical", "remaining_time_minutes": 15.0, "failed_hospitals": ["H3"], "visited_hospitals": ["H3", "H5"], "ambulance_status": "in_transit"}, "action": {"hospital_id": "H1", "policy_score": 0.16666663886108793, "strategy": "balanced policy"}, "outcome": {"status": "REJECTED", "reason": "Hospital cannot admit: ICU unavailable"}, "reward": 0.001}
|
| 372 |
+
{"seed": 579018920, "task": "acde_medium", "difficulty": "medium", "step": 4, "state": {"patient_condition": "critical", "remaining_time_minutes": 15.0, "failed_hospitals": ["H3", "H1"], "visited_hospitals": ["H3", "H5", "H1"], "ambulance_status": "rerouting"}, "action": {"hospital_id": "H4", "policy_score": 0.16666663886108793, "strategy": "risk-aware policy"}, "outcome": {"status": "REJECTED", "reason": "Hidden mismatch at arrival (wrong risky guess). Rerouting required."}, "reward": 0.001}
|
| 373 |
+
{"seed": 579018921, "task": "acde_hard", "difficulty": "hard", "step": 1, "state": {"patient_condition": "critical", "remaining_time_minutes": 11.0, "failed_hospitals": [], "visited_hospitals": [], "ambulance_status": "en_route"}, "action": {"hospital_id": "H1", "policy_score": 0.35191055672786264, "strategy": "safe policy + critical triage"}, "outcome": {"status": "REJECTED", "reason": "Hospital cannot admit: ICU unavailable"}, "reward": 0.001}
|
| 374 |
+
{"seed": 579018921, "task": "acde_hard", "difficulty": "hard", "step": 2, "state": {"patient_condition": "critical", "remaining_time_minutes": 11.0, "failed_hospitals": ["H1"], "visited_hospitals": ["H1"], "ambulance_status": "rerouting"}, "action": {"hospital_id": "H6", "policy_score": 0.49875162318940436, "strategy": "risk-aware policy"}, "outcome": {"status": "REJECTED", "reason": "Hospital cannot admit: Hospital overloaded"}, "reward": 0.001}
|
| 375 |
+
{"seed": 579018921, "task": "acde_hard", "difficulty": "hard", "step": 3, "state": {"patient_condition": "critical", "remaining_time_minutes": 11.0, "failed_hospitals": ["H1", "H6"], "visited_hospitals": ["H1", "H6"], "ambulance_status": "rerouting"}, "action": {"hospital_id": "H3", "policy_score": 0.14285712242855392, "strategy": "risk-aware policy"}, "outcome": {"status": "REJECTED", "reason": "Hospital cannot admit: Hospital overloaded"}, "reward": 0.001}
|
| 376 |
+
{"seed": 579018921, "task": "acde_hard", "difficulty": "hard", "step": 4, "state": {"patient_condition": "critical", "remaining_time_minutes": 11.0, "failed_hospitals": ["H1", "H6", "H4"], "visited_hospitals": ["H1", "H6", "H4"], "ambulance_status": "rerouting"}, "action": {"hospital_id": "H5", "policy_score": 0.49875162318940436, "strategy": "risk-aware policy + anti-stupidity guard"}, "outcome": {"status": "PARTIAL", "reason": "Admitted with delays: prolonged transfer strain"}, "reward": 0.40800000000000014}
|
| 377 |
+
{"seed": 468098215, "task": "acde_easy", "difficulty": "easy", "step": 1, "state": {"patient_condition": "serious", "remaining_time_minutes": 18.0, "failed_hospitals": [], "visited_hospitals": [], "ambulance_status": "en_route"}, "action": {"hospital_id": "H3", "policy_score": 0.3443315337257757, "strategy": "safe policy + anti-stupidity guard"}, "outcome": {"status": "PARTIAL", "reason": "Critical deterioration managed temporarily; reroute still needed"}, "reward": 0.334}
|
| 378 |
+
{"seed": 468098215, "task": "acde_easy", "difficulty": "easy", "step": 2, "state": {"patient_condition": "stable", "remaining_time_minutes": 18.0, "failed_hospitals": [], "visited_hospitals": ["H3"], "ambulance_status": "in_transit"}, "action": {"hospital_id": "H4", "policy_score": 0.24369633883371342, "strategy": "balanced policy"}, "outcome": {"status": "REJECTED", "reason": "Emergency beds became unavailable during arrival"}, "reward": 0.001}
|
| 379 |
+
{"seed": 468098215, "task": "acde_easy", "difficulty": "easy", "step": 3, "state": {"patient_condition": "stable", "remaining_time_minutes": 18.0, "failed_hospitals": ["H4"], "visited_hospitals": ["H3", "H4"], "ambulance_status": "rerouting"}, "action": {"hospital_id": "H1", "policy_score": 0.27278944968751606, "strategy": "risk-aware policy"}, "outcome": {"status": "REJECTED", "reason": "Condition became non-transferable during delay; immediate critical care failed"}, "reward": 0.139}
|
| 380 |
+
{"seed": 468098216, "task": "acde_medium", "difficulty": "medium", "step": 1, "state": {"patient_condition": "serious", "remaining_time_minutes": 17.0, "failed_hospitals": [], "visited_hospitals": [], "ambulance_status": "en_route"}, "action": {"hospital_id": "H2", "policy_score": 0.4171322328020302, "strategy": "safe policy + guided-exploration"}, "outcome": {"status": "PARTIAL", "reason": "Initial triage completed; transfer monitoring still required"}, "reward": 0.37}
|
| 381 |
+
{"seed": 468098216, "task": "acde_medium", "difficulty": "medium", "step": 2, "state": {"patient_condition": "serious", "remaining_time_minutes": 17.0, "failed_hospitals": [], "visited_hospitals": ["H2"], "ambulance_status": "in_transit"}, "action": {"hospital_id": "H3", "policy_score": 0.43825439745695755, "strategy": "balanced policy + anti-stupidity guard"}, "outcome": {"status": "ACCEPTED", "reason": "Patient stabilized after delayed admission"}, "reward": 0.8660000000000001}
|
| 382 |
+
{"seed": 468098217, "task": "acde_hard", "difficulty": "hard", "step": 1, "state": {"patient_condition": "critical", "remaining_time_minutes": 12.0, "failed_hospitals": [], "visited_hospitals": [], "ambulance_status": "en_route"}, "action": {"hospital_id": "H2", "policy_score": 0.49964989496849055, "strategy": "safe policy + critical triage"}, "outcome": {"status": "REJECTED", "reason": "Hospital cannot admit: Hospital overloaded"}, "reward": 0.001}
|
| 383 |
+
{"seed": 468098217, "task": "acde_hard", "difficulty": "hard", "step": 2, "state": {"patient_condition": "critical", "remaining_time_minutes": 12.0, "failed_hospitals": ["H2"], "visited_hospitals": ["H2"], "ambulance_status": "rerouting"}, "action": {"hospital_id": "H3", "policy_score": 0.29411764705882354, "strategy": "risk-aware policy"}, "outcome": {"status": "REJECTED", "reason": "Hidden mismatch at arrival (wrong risky guess). Rerouting required."}, "reward": 0.001}
|
| 384 |
+
{"seed": 468098217, "task": "acde_hard", "difficulty": "hard", "step": 3, "state": {"patient_condition": "critical", "remaining_time_minutes": 12.0, "failed_hospitals": ["H2", "H3"], "visited_hospitals": ["H2", "H3"], "ambulance_status": "rerouting"}, "action": {"hospital_id": "H5", "policy_score": 0.039999999999999994, "strategy": "risk-aware policy"}, "outcome": {"status": "REJECTED", "reason": "Hidden mismatch at arrival (wrong risky guess). Rerouting required."}, "reward": 0.001}
|
| 385 |
+
{"seed": 468098217, "task": "acde_hard", "difficulty": "hard", "step": 4, "state": {"patient_condition": "critical", "remaining_time_minutes": 12.0, "failed_hospitals": ["H2", "H3", "H1"], "visited_hospitals": ["H2", "H3", "H1"], "ambulance_status": "rerouting"}, "action": {"hospital_id": "H2", "policy_score": 0.29411764705882354, "strategy": "risk-aware policy + immediate-retry override"}, "outcome": {"status": "REJECTED", "reason": "Hospital cannot admit: ICU unavailable, No specialist available"}, "reward": 0.001}
|
inference.py
CHANGED
|
@@ -18,13 +18,17 @@ import random
|
|
| 18 |
from pathlib import Path
|
| 19 |
from datetime import datetime, timezone
|
| 20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
from app.environment.core import EmergencyEnv
|
| 22 |
from app.models.action import Action
|
| 23 |
|
| 24 |
try:
|
| 25 |
from openai import OpenAI
|
| 26 |
except Exception: # pragma: no cover - fallback for missing optional dependency
|
| 27 |
-
OpenAI = None
|
| 28 |
|
| 29 |
TASK_ORDER = ["acde_easy", "acde_medium", "acde_hard"]
|
| 30 |
LEVEL_TO_TASK = {
|
|
@@ -71,7 +75,7 @@ def runtime_llm_config() -> dict[str, str]:
|
|
| 71 |
}
|
| 72 |
|
| 73 |
|
| 74 |
-
def require_llm_config() -> tuple[
|
| 75 |
config = runtime_llm_config()
|
| 76 |
missing = [name for name, value in config.items() if not value]
|
| 77 |
if missing:
|
|
@@ -88,7 +92,7 @@ def require_llm_config() -> tuple[object, str]:
|
|
| 88 |
|
| 89 |
|
| 90 |
def llm_rationale(
|
| 91 |
-
client:
|
| 92 |
model_name: str,
|
| 93 |
observation: dict,
|
| 94 |
chosen: dict,
|
|
@@ -315,7 +319,7 @@ def memory_score_for_hospital(
|
|
| 315 |
if recent_failed:
|
| 316 |
value -= 0.3
|
| 317 |
|
| 318 |
-
return max(0.
|
| 319 |
|
| 320 |
|
| 321 |
def score_hospitals(observation: dict, learning_profile: dict | None = None) -> list[dict]:
|
|
@@ -328,8 +332,8 @@ def score_hospitals(observation: dict, learning_profile: dict | None = None) ->
|
|
| 328 |
last_status = str(last_arrival.get("status", "")).lower()
|
| 329 |
|
| 330 |
scored: list[dict] = []
|
| 331 |
-
initial_limit = float(observation.get("initial_critical_time_limit_minutes"
|
| 332 |
-
remaining_time = float(observation.get("remaining_time_minutes"
|
| 333 |
urgency = 1.0 - min(1.0, max(0.0, remaining_time / max(initial_limit, 1e-6)))
|
| 334 |
|
| 335 |
patient_condition = observation.get("patient_condition", "").lower()
|
|
@@ -473,7 +477,7 @@ def score_hospitals(observation: dict, learning_profile: dict | None = None) ->
|
|
| 473 |
"specialization": hospital["specialization"],
|
| 474 |
"travel_time": travel_time,
|
| 475 |
"memory_score": mem_score,
|
| 476 |
-
"policy_score": max(0.
|
| 477 |
"specialization_match": spec_match,
|
| 478 |
"tie_break_score": (
|
| 479 |
(distance_score * 0.35)
|
|
@@ -500,7 +504,7 @@ def score_hospitals(observation: dict, learning_profile: dict | None = None) ->
|
|
| 500 |
)
|
| 501 |
jitter_rng = random.Random(jitter_seed)
|
| 502 |
normalized *= jitter_rng.uniform(0.3, 0.7)
|
| 503 |
-
item["policy_score"] = max(0.
|
| 504 |
elif max_score > 0:
|
| 505 |
for item in scored:
|
| 506 |
normalized = item["policy_score"] / max_score
|
|
@@ -512,7 +516,7 @@ def score_hospitals(observation: dict, learning_profile: dict | None = None) ->
|
|
| 512 |
)
|
| 513 |
jitter_rng = random.Random(jitter_seed)
|
| 514 |
normalized *= jitter_rng.uniform(0.3, 0.7)
|
| 515 |
-
item["policy_score"] = max(0.
|
| 516 |
else:
|
| 517 |
tie_min = min(item.get("tie_break_score", 0.0) for item in scored)
|
| 518 |
tie_max = max(item.get("tie_break_score", 0.0) for item in scored)
|
|
@@ -528,10 +532,10 @@ def score_hospitals(observation: dict, learning_profile: dict | None = None) ->
|
|
| 528 |
)
|
| 529 |
jitter_rng = random.Random(jitter_seed)
|
| 530 |
normalized *= jitter_rng.uniform(0.3, 0.7)
|
| 531 |
-
item["policy_score"] = max(0.
|
| 532 |
else:
|
| 533 |
for item in scored:
|
| 534 |
-
item["policy_score"] = 0.
|
| 535 |
|
| 536 |
# Remove hard-zero scores and normalize to probability-like values.
|
| 537 |
for item in scored:
|
|
@@ -585,7 +589,7 @@ def score_hospitals(observation: dict, learning_profile: dict | None = None) ->
|
|
| 585 |
)
|
| 586 |
jitter_rng = random.Random(jitter_seed)
|
| 587 |
normalized_score = jitter_rng.uniform(0.01, 0.03)
|
| 588 |
-
item["policy_score"] = normalized_score
|
| 589 |
|
| 590 |
scored.sort(key=lambda item: item["policy_score"], reverse=True)
|
| 591 |
|
|
@@ -1060,9 +1064,9 @@ def run_episode(
|
|
| 1060 |
"seed": seed,
|
| 1061 |
"result": final_result,
|
| 1062 |
"success": final_result == "SUCCESS",
|
| 1063 |
-
"score": round(final_score, 4),
|
| 1064 |
"steps": steps,
|
| 1065 |
-
"average_reward": round(total_reward / max(1, steps), 4),
|
| 1066 |
},
|
| 1067 |
)
|
| 1068 |
|
|
|
|
| 18 |
from pathlib import Path
|
| 19 |
from datetime import datetime, timezone
|
| 20 |
|
| 21 |
+
from typing import Any, Literal, cast, TYPE_CHECKING
|
| 22 |
+
if TYPE_CHECKING:
|
| 23 |
+
from openai import OpenAI
|
| 24 |
+
|
| 25 |
from app.environment.core import EmergencyEnv
|
| 26 |
from app.models.action import Action
|
| 27 |
|
| 28 |
try:
|
| 29 |
from openai import OpenAI
|
| 30 |
except Exception: # pragma: no cover - fallback for missing optional dependency
|
| 31 |
+
OpenAI = None # type: ignore
|
| 32 |
|
| 33 |
TASK_ORDER = ["acde_easy", "acde_medium", "acde_hard"]
|
| 34 |
LEVEL_TO_TASK = {
|
|
|
|
| 75 |
}
|
| 76 |
|
| 77 |
|
| 78 |
+
def require_llm_config() -> tuple[Any, str]:
|
| 79 |
config = runtime_llm_config()
|
| 80 |
missing = [name for name, value in config.items() if not value]
|
| 81 |
if missing:
|
|
|
|
| 92 |
|
| 93 |
|
| 94 |
def llm_rationale(
|
| 95 |
+
client: Any,
|
| 96 |
model_name: str,
|
| 97 |
observation: dict,
|
| 98 |
chosen: dict,
|
|
|
|
| 319 |
if recent_failed:
|
| 320 |
value -= 0.3
|
| 321 |
|
| 322 |
+
return max(0.001, min(0.999, value))
|
| 323 |
|
| 324 |
|
| 325 |
def score_hospitals(observation: dict, learning_profile: dict | None = None) -> list[dict]:
|
|
|
|
| 332 |
last_status = str(last_arrival.get("status", "")).lower()
|
| 333 |
|
| 334 |
scored: list[dict] = []
|
| 335 |
+
initial_limit = float(observation.get("initial_critical_time_limit_minutes") or observation.get("critical_time_limit_minutes") or 15.0)
|
| 336 |
+
remaining_time = float(observation.get("remaining_time_minutes") or observation.get("critical_time_limit_minutes") or 15.0)
|
| 337 |
urgency = 1.0 - min(1.0, max(0.0, remaining_time / max(initial_limit, 1e-6)))
|
| 338 |
|
| 339 |
patient_condition = observation.get("patient_condition", "").lower()
|
|
|
|
| 477 |
"specialization": hospital["specialization"],
|
| 478 |
"travel_time": travel_time,
|
| 479 |
"memory_score": mem_score,
|
| 480 |
+
"policy_score": max(0.001, min(0.999, score)),
|
| 481 |
"specialization_match": spec_match,
|
| 482 |
"tie_break_score": (
|
| 483 |
(distance_score * 0.35)
|
|
|
|
| 504 |
)
|
| 505 |
jitter_rng = random.Random(jitter_seed)
|
| 506 |
normalized *= jitter_rng.uniform(0.3, 0.7)
|
| 507 |
+
item["policy_score"] = max(0.001, min(0.999, normalized))
|
| 508 |
elif max_score > 0:
|
| 509 |
for item in scored:
|
| 510 |
normalized = item["policy_score"] / max_score
|
|
|
|
| 516 |
)
|
| 517 |
jitter_rng = random.Random(jitter_seed)
|
| 518 |
normalized *= jitter_rng.uniform(0.3, 0.7)
|
| 519 |
+
item["policy_score"] = max(0.001, min(0.999, normalized))
|
| 520 |
else:
|
| 521 |
tie_min = min(item.get("tie_break_score", 0.0) for item in scored)
|
| 522 |
tie_max = max(item.get("tie_break_score", 0.0) for item in scored)
|
|
|
|
| 532 |
)
|
| 533 |
jitter_rng = random.Random(jitter_seed)
|
| 534 |
normalized *= jitter_rng.uniform(0.3, 0.7)
|
| 535 |
+
item["policy_score"] = max(0.001, min(0.999, normalized))
|
| 536 |
else:
|
| 537 |
for item in scored:
|
| 538 |
+
item["policy_score"] = 0.001
|
| 539 |
|
| 540 |
# Remove hard-zero scores and normalize to probability-like values.
|
| 541 |
for item in scored:
|
|
|
|
| 589 |
)
|
| 590 |
jitter_rng = random.Random(jitter_seed)
|
| 591 |
normalized_score = jitter_rng.uniform(0.01, 0.03)
|
| 592 |
+
item["policy_score"] = max(0.001, min(0.999, normalized_score))
|
| 593 |
|
| 594 |
scored.sort(key=lambda item: item["policy_score"], reverse=True)
|
| 595 |
|
|
|
|
| 1064 |
"seed": seed,
|
| 1065 |
"result": final_result,
|
| 1066 |
"success": final_result == "SUCCESS",
|
| 1067 |
+
"score": max(0.001, min(0.999, round(final_score, 4))),
|
| 1068 |
"steps": steps,
|
| 1069 |
+
"average_reward": max(0.001, min(0.999, round(total_reward / max(1, steps), 4))),
|
| 1070 |
},
|
| 1071 |
)
|
| 1072 |
|