Spaces:
Sleeping
Sleeping
| """DispatchPulse data models. | |
| Two layers: | |
| 1. **OpenEnv interface models** — ``DispatchPulseAction``, ``DispatchPulseObservation``, | |
| ``DispatchPulseState``. These inherit directly from openenv-core base classes | |
| and form the wire format the server/client/grader exchange. | |
| 2. **Internal simulation models** — ``Position``, ``EmergencyType``, ``Severity``, | |
| ``UnitType``, ``UnitStatus``, ``EmergencyCall``, ``EmergencyUnit``, ``Hospital``, | |
| ``WorldConfig``, ``Reward``. These are plain Pydantic models the simulation | |
| engine uses internally; they never cross the OpenEnv boundary directly. | |
| """ | |
| from __future__ import annotations | |
| from enum import Enum | |
| from typing import List, Optional | |
| from pydantic import BaseModel, Field | |
| # --------------------------------------------------------------------------- | |
| # OpenEnv base classes | |
| # --------------------------------------------------------------------------- | |
| from openenv.core.env_server.types import Action, Observation, State | |
| # =========================================================================== | |
| # OpenEnv-facing wire types | |
| # =========================================================================== | |
| class DispatchPulseAction(Action): | |
| """A single dispatcher action. | |
| The agent supplies ``action_type`` plus optional fields. The simplest | |
| possible interface for an LLM is the ``text`` field — the server will | |
| parse it as a command line like ``"dispatch CALL-001 ALS-1 H1"``. | |
| Supported action types: | |
| - ``dispatch`` : send a unit to a call (call_id, unit_id, hospital_id?) | |
| - ``classify`` : reclassify a call's severity (call_id, severity) | |
| - ``callback`` : phone the caller back (call_id, message) | |
| - ``wait`` : skip ahead in simulation time (minutes) | |
| - ``view`` : free inspection (no time cost) | |
| """ | |
| action_type: str = Field( | |
| ..., description="One of: dispatch, classify, callback, wait, view" | |
| ) | |
| text: str = Field( | |
| default="", | |
| description="Free-text representation of the action (e.g. 'dispatch CALL-001 ALS-1 H1')", | |
| ) | |
| call_id: Optional[str] = Field(default=None) | |
| unit_id: Optional[str] = Field(default=None) | |
| hospital_id: Optional[str] = Field(default=None) | |
| severity: Optional[int] = Field(default=None, ge=1, le=5) | |
| message: Optional[str] = Field(default=None) | |
| minutes: Optional[int] = Field(default=None, ge=1, le=5) | |
| class DispatchPulseObservation(Observation): | |
| """What the dispatcher sees each turn. | |
| The ``text`` field is the human-readable dispatch center view that the | |
| LLM agent reads. The structured fields underneath are useful for | |
| programmatic agents and grading. | |
| """ | |
| text: str = Field(default="", description="Formatted dispatch center view for the agent") | |
| current_time: int = Field(default=0, description="Simulation minute") | |
| time_limit: int = Field(default=30, description="Episode time limit (minutes)") | |
| calls_pending: int = Field(default=0, description="Number of calls waiting for dispatch") | |
| units_available: int = Field(default=0, description="Number of free units") | |
| calls_completed: int = Field(default=0) | |
| calls_timed_out: int = Field(default=0) | |
| total_calls: int = Field(default=0) | |
| last_action_error: Optional[str] = Field( | |
| default=None, description="Error message from the last action, or None" | |
| ) | |
| info_message: Optional[str] = Field( | |
| default=None, description="Free-text message describing what just happened" | |
| ) | |
| class DispatchPulseState(State): | |
| """Internal state snapshot exposed via ``GET /state``.""" | |
| current_time: int = Field(default=0) | |
| episode_done: bool = Field(default=False) | |
| total_calls: int = Field(default=0) | |
| calls_dispatched: int = Field(default=0) | |
| calls_completed: int = Field(default=0) | |
| calls_timed_out: int = Field(default=0) | |
| calls_pending: int = Field(default=0) | |
| units_available: int = Field(default=0) | |
| running_reward: float = Field(default=0.0) | |
| task_name: str = Field(default="easy") | |
| # =========================================================================== | |
| # Internal simulation models (plain Pydantic, never cross OpenEnv boundary) | |
| # =========================================================================== | |
| class Position(BaseModel): | |
| """A 2D coordinate on the city grid (km).""" | |
| x: float = Field(..., ge=0.0) | |
| y: float = Field(..., ge=0.0) | |
| class EmergencyType(str, Enum): | |
| CARDIAC_ARREST = "cardiac_arrest" | |
| TRAUMA = "trauma" | |
| STROKE = "stroke" | |
| FIRE = "fire" | |
| MINOR_INJURY = "minor_injury" | |
| BREATHING = "breathing_difficulty" | |
| MENTAL_HEALTH = "mental_health_crisis" | |
| class Severity(int, Enum): | |
| CRITICAL = 1 | |
| URGENT = 2 | |
| MODERATE = 3 | |
| LOW = 4 | |
| FALSE_ALARM = 5 | |
| class UnitType(str, Enum): | |
| ALS_AMBULANCE = "als_ambulance" | |
| BLS_AMBULANCE = "bls_ambulance" | |
| FIRE_ENGINE = "fire_engine" | |
| POLICE = "police" | |
| class UnitStatus(str, Enum): | |
| AVAILABLE = "available" | |
| EN_ROUTE = "en_route" | |
| ON_SCENE = "on_scene" | |
| RETURNING = "returning" | |
| class EmergencyCall(BaseModel): | |
| call_id: str | |
| timestamp: int | |
| caller_description: str | |
| location: Position | |
| true_type: EmergencyType | |
| true_severity: Severity | |
| reported_type: Optional[EmergencyType] = None | |
| reported_severity: Optional[Severity] = None | |
| requires_unit_types: List[UnitType] = Field(default_factory=list) | |
| optimal_unit_type: UnitType | |
| active: bool = True | |
| dispatched_unit_id: Optional[str] = None | |
| response_time: Optional[float] = None | |
| outcome_score: Optional[float] = None | |
| delivered_hospital_id: Optional[str] = None | |
| class EmergencyUnit(BaseModel): | |
| unit_id: str | |
| unit_type: UnitType | |
| position: Position | |
| base_position: Position | |
| status: UnitStatus = UnitStatus.AVAILABLE | |
| speed_kmh: float = Field(..., gt=0) | |
| assigned_call_id: Optional[str] = None | |
| assigned_hospital_id: Optional[str] = None | |
| busy_until: Optional[int] = None | |
| capabilities: List[EmergencyType] = Field(default_factory=list) | |
| class Hospital(BaseModel): | |
| hospital_id: str | |
| name: str | |
| position: Position | |
| capacity: int = Field(..., ge=0) | |
| available_beds: int = Field(..., ge=0) | |
| has_trauma_center: bool = False | |
| has_cardiac_unit: bool = False | |
| has_stroke_unit: bool = False | |
| on_diversion: bool = False | |
| class WorldConfig(BaseModel): | |
| grid_size_km: float = 10.0 | |
| time_limit_minutes: int = 30 | |
| step_duration_minutes: int = 1 | |
| call_timeout_minutes: int = 20 | |
| max_wait_step_minutes: int = 5 | |
| class Reward(BaseModel): | |
| """Final episode reward, all components in [0.0, 1.0].""" | |
| total: float = Field(..., ge=0.0, le=1.0) | |
| survival_score: float | |
| efficiency_score: float | |
| triage_accuracy: float | |
| penalty: float | |
| details: str = "" | |