heet6017/police-control-room-env
Police Control Room AI — OpenEnv Environment
A real-world reinforcement learning environment simulating an emergency police dispatch control room. An AI agent receives live incident reports and must decide — without any hardcoded rules — how many police units, ambulances, and fire trucks to deploy, which unit types to send, and how to triage when resources are scarce.
Built for the Meta PyTorch × Hugging Face OpenEnv Hackathon.
Why this environment?
Real police dispatchers make high-stakes allocation decisions every day under time pressure and resource constraints. This environment models those exact decisions:
- An armed robbery at night with 2 suspects — how many units, which type, ambulance standby?
- A vehicle collision with spreading fire and trapped civilians — police + fire + ambulance coordination?
- 5 simultaneous incidents across the city with only 8 police units available — triage under pressure?
These are not toy problems. They are simplified but faithful models of real dispatch decisions.
Environment overview
Tasks
Task 1 — Single dispatch (easy)
Scenario: Armed robbery in progress. Night. 2 armed suspects. 3 civilians at risk. 2.4km away.
The agent must send the correct number and type of police units, decide on ambulance standby, and set the right priority level.
Grader: Unit count (±1 of optimal), unit type correctness, priority level, ambulance decision, officers per unit.
Expected score: 0.75 – 1.0
Task 2 — Multi-agency incident (medium)
Scenario: Vehicle collision with spreading fire. 2 injured, 1 trapped. Rain. 1.1km away.
The agent must coordinate three services simultaneously — police, fire, and ambulance — with the right counts and types.
Grader: Fire truck dispatch (critical — missing = −0.40), ambulance dispatch (critical — missing = −0.30), police count and type, priority level.
Expected score: 0.40 – 0.80
Task 3 — Resource-constrained surge (hard)
Scenario: 5 simultaneous incidents across the city. Only 8 police units, 2 ambulances, 1 fire truck available. INC-A: hostage severity=5 distance=3.2km INC-B: vehicle fire severity=5 distance=0.8km fire=true medical=true INC-C: riot severity=3 distance=1.5km INC-D: break-in severity=2 distance=4.1km INC-E: medical severity=4 distance=2.0km medical=true
The agent must triage all 5 incidents and allocate all available resources optimally — no idle units.
Grader: Severity-based priority (sev-5 incidents get resources first), correct service assignment per incident, no over-staffing of low-severity calls, full resource utilisation.
Expected score: 0.20 – 0.60
Observation space
IncidentObservation (Pydantic model):
Action space
Tasks 1 & 2 — `PoliceDispatchAction`:
Task 3 — `SurgeAllocationAction`: Dict of 5 SingleIncidentAllocation objects keyed by INC-A through INC-E.
Reward function
All rewards strictly in (0.0, 1.0) with partial credit at every component:
Task 1 components:
- Unit count in optimal range [2–4]: +0.30
- Correct unit type (patrol/swat): +0.25
- Priority level = 1: +0.20
- Ambulance sent: +0.15
- Officers per unit in range [3–5]: +0.10
- Over-dispatch (>6 units): −0.15
- Zero units sent: −1.00 (critical fail)
Task 2 components:
- Fire truck sent: +0.30 | Missing fire truck: −0.40
- Ambulance sent: +0.25 | Missing ambulance: −0.30
- Police count correct: +0.20
- Correct unit type: +0.10
- Priority = 1: +0.15
Task 3 components:
- Sev-5 incidents get majority resources: +0.35
- INC-B gets fire truck: +0.20
- Medical incidents get ambulance: +0.15
- Sev-2 not over-staffed: +0.10
- All 8 units allocated: +0.10
- SWAT for hostage: +0.10
- Inverted priority (sev-2 > sev-5): −0.30
Baseline scores (Qwen/Qwen2.5-72B-Instruct)
Live demo & links
Setup & running
Local setup
git clone https://huggingface.co/spaces/heet6017/police-control-room-env
cd police-control-room-env
python -m venv venv
venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env
# Add your HF_TOKEN in .envRun tests
pytest tests/ -v
# Expected: 16 passed in 0.23sRun baseline inference
python inference.pyExpected output: [START] task=singledispatch env=policecontrolroom model=Qwen/Qwen2.5-72B-Instruct [STEP] step=1 action=4xswatpri1 reward=0.85 done=true error=null [END] success=true steps=1 score=0.850 rewards=0.85 [START] task=multiagency env=policecontrolroom model=Qwen/Qwen2.5-72B-Instruct [STEP] step=1 action=2xpatrolpri1 reward=0.97 done=true error=null [END] success=true steps=1 score=0.970 rewards=0.97 [START] task=resourcesurge env=policecontrolroom model=Qwen/Qwen2.5-72B-Instruct [STEP] step=1 action=Prioritizedby_severity reward=1.00 done=true error=null [END] success=true steps=1 score=0.999 rewards=1.00
Run UI locally
python app.py
# Open http://localhost:7860Docker
docker build -t police-control-room-env .
docker run -p 7860:7860 -e HF_TOKEN=your_token police-control-room-envAPI endpoints
Example API call
import httpx
BASE = "https://heet6017-police-control-room-env.hf.space"
obs = httpx.post(f"{BASE}/reset?task_id=single_dispatch").json()
action = {
"police_units": 3, "officers_per_unit": 4, "unit_type": "swat",
"send_ambulance": True, "ambulance_count": 1,
"send_fire_truck": False, "fire_truck_count": 0,
"priority_level": 1, "cordon_area": False,
"notify_supervisor": False, "reasoning": "armed robbery at night"
}
result = httpx.post(f"{BASE}/step?task_id=single_dispatch", json=action).json()
print(result["reward"], result["feedback"])Project structure
police-control-room-env/ ├── models.py — Pydantic models (Observation, Action, Reward) ├── scenarios.py — Deterministic incident generators ├── reward.py — Quantified grader functions ├── graders.py — Task router ├── env.py — OpenEnv interface (reset/step/state) ├── inference.py — Baseline inference script ├── app.py — Gradio UI + FastAPI server ├── openenv.yaml — OpenEnv metadata ├── Dockerfile — Container definition ├── pyproject.toml — Package config ├── uv.lock — Dependency lock file ├── requirements.txt — Dependencies ├── README.md — This file └── tests/ ├── testmodels.py ├── testgraders.py └── test_env.py
License
MIT
