Download server/environment.py from TheRubberDuckDebuggers/cascade-containment: direct link, hf CLI and curl.
- Browser
- Download file 10.5 kB
-
https://huggingface.co/spaces/TheRubberDuckDebuggers/cascade-containment/resolve/main/server/environment.py
- Command line
-
hf download hf://spaces/TheRubberDuckDebuggers/cascade-containment/server/environment.py
-
curl -L -o environment.py https://huggingface.co/spaces/TheRubberDuckDebuggers/cascade-containment/resolve/main/server/environment.py
10.5 kB
| import sys | |
| import os | |
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) | |
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../..')) | |
| import copy | |
| from uuid import uuid4 | |
| from typing import Optional, Tuple | |
| from server.grader import TrajectoryStep | |
| from openenv.core.env_server.types import State | |
| from openenv.core.env_server.interfaces import Environment | |
| from models import ( | |
| CityState, | |
| CityObservation, | |
| ContainmentAction, | |
| ) | |
| from server.constants import ( | |
| HOSPITAL_BREACH_POINT, | |
| TASK_CONFIG, | |
| INFECTION_THRESHOLD, | |
| SAFE_THRESHOLD, | |
| LOW_THRESHOLD, | |
| ALLOCATE_REDUCTION, | |
| RESTRICT_REDUCTION, | |
| TREATMENT_REDUCTION, | |
| RESOURCE_REPLENISH, | |
| REWARD_INFECTION_PENALTY, | |
| REWARD_HOSPITAL_BREACH, | |
| REWARD_EARLY_CONTAINMENT, | |
| REWARD_UNNECESSARY_RESTRICTION, | |
| REWARD_CORRECT_PRIORITISATION, | |
| ) | |
| from server.utils import ( | |
| build_observation, | |
| compute_spread, | |
| get_highest_infected_district, | |
| all_districts_contained, | |
| any_hospital_breached, | |
| districts_above_threshold, | |
| snapshot_infection_rates, | |
| generate_episode_id, | |
| ) | |
| from server.tasks.registry import get_task | |
| from server.grader import grade_trajectory | |
| _last_grade: dict = {} | |
| class EpidemicContainmentEnv(Environment): | |
| """ | |
| Cascade Containment β RL environment for epidemic response. | |
| The agent acts as a city health authority making sequential resource | |
| allocation decisions under uncertainty and potentially delayed data. | |
| Interface: | |
| reset(task_name) β CityObservation | |
| step(action) β CityObservation | |
| state β State (property) | |
| """ | |
| def __init__(self): | |
| self._city: CityState = CityState() | |
| self._state: State = State(episode_id=str(uuid4()), step_count=0) | |
| self._task_name: str = "easy" | |
| self._trajectory: list = [] | |
| # ββ Public interface ββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def reset(self, task_name: str = "easy") -> CityObservation: | |
| self._task_name = task_name | |
| task = get_task(task_name) | |
| self._city = task.build_initial_state() | |
| self._state = State(episode_id=generate_episode_id(), step_count=0) | |
| self._trajectory = [] | |
| return build_observation( | |
| state = self._city, | |
| step_count = self._state.step_count, | |
| reward = None, | |
| message = ( | |
| f"Episode started. Task: {task_name}. " | |
| f"Districts: {len(self._city.districts)}, " | |
| f"Steps: {self._city.max_steps} available." | |
| ), | |
| done = False, | |
| ) | |
| def step(self, action: ContainmentAction) -> CityObservation: | |
| assert self._city is not None, "Call reset() before step()." | |
| assert self._state is not None, "Call reset() before step()." | |
| action, message = self._validate_action(action) | |
| self._city.infection_history.append( | |
| snapshot_infection_rates(self._city.districts) | |
| ) | |
| self._apply_action(action) | |
| new_rates = compute_spread(self._city.districts) | |
| for i, district in enumerate(self._city.districts): | |
| district.true_infection_rate = new_rates[i] | |
| self._update_hospital_capacity() | |
| self._city.available_resources = min( | |
| self._city.available_resources + RESOURCE_REPLENISH, | |
| TASK_CONFIG[self._task_name]["resource_pool"], | |
| ) | |
| for district in self._city.districts: | |
| district.deployed_resources = 0 | |
| self._city.day += 1 | |
| self._state.step_count += 1 | |
| reward = self._compute_reward(action) | |
| self._trajectory.append(TrajectoryStep( | |
| step = self._state.step_count, | |
| city_state = copy.deepcopy(self._city), | |
| action = action, | |
| reward = reward, | |
| done = False, | |
| )) | |
| done, terminal_message = self._check_terminal() | |
| if done and self._trajectory: | |
| self._trajectory[-1].done = True | |
| import server.environment as _self_module | |
| result = grade_trajectory(self._trajectory, self._task_name) | |
| _self_module._last_grade = { | |
| "final_score": result.final_score, | |
| "containment_score": result.containment_score, | |
| "hospital_score": result.hospital_score, | |
| "efficiency_score": result.efficiency_score, | |
| "speed_score": result.speed_score, | |
| "hospital_breached": result.hospital_breached, | |
| "districts_contained": result.districts_contained, | |
| "total_steps": result.total_steps, | |
| "task_name": self._task_name, | |
| } | |
| return build_observation( | |
| state = self._city, | |
| step_count = self._state.step_count, | |
| reward = reward, | |
| message = terminal_message if terminal_message else message, | |
| done = done, | |
| ) | |
| def state(self) -> State: | |
| return self._state | |
| def get_trajectory(self) -> list: | |
| return self._trajectory | |
| # ββ Action handling βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def _validate_action( | |
| self, action: ContainmentAction | |
| ) -> Tuple[ContainmentAction, str]: | |
| """ | |
| Invalid actions are replaced with a safe default rather than raising β | |
| the episode must continue even when the LLM returns malformed output. | |
| """ | |
| valid_types = {"test", "restrict", "allocate"} | |
| num_districts = len(self._city.districts) | |
| if action.action_type not in valid_types: | |
| return ( | |
| ContainmentAction(action_type="allocate", district_id=0), | |
| f"Invalid action_type '{action.action_type}'. Defaulted to allocate on district 0.", | |
| ) | |
| if not (0 <= action.district_id < num_districts): | |
| safe_id = max(0, min(action.district_id, num_districts - 1)) | |
| return ( | |
| ContainmentAction(action_type=action.action_type, district_id=safe_id), | |
| f"district_id {action.district_id} out of range. Clamped to {safe_id}.", | |
| ) | |
| if action.action_type in {"test", "allocate"} and self._city.available_resources <= 0: | |
| return ( | |
| ContainmentAction(action_type="restrict", district_id=action.district_id), | |
| f"No resources left. Switched to restrict on district {action.district_id}.", | |
| ) | |
| return action, f"{action.action_type.capitalize()} on district {action.district_id}." | |
| def _apply_action(self, action: ContainmentAction) -> None: | |
| district = self._city.districts[action.district_id] | |
| if action.action_type == "test": | |
| district.days_since_tested = 0 | |
| self._city.available_resources -= 1 | |
| elif action.action_type == "restrict": | |
| district.restriction_active = True | |
| district.days_since_tested += 1 | |
| elif action.action_type == "allocate": | |
| district.deployed_resources += 1 | |
| district.true_infection_rate = max(0.0, district.true_infection_rate - TREATMENT_REDUCTION) | |
| self._city.available_resources -= 1 | |
| district.days_since_tested += 1 | |
| for d in self._city.districts: | |
| if d.district_id != action.district_id: | |
| d.days_since_tested += 1 | |
| # ββ Simulation mechanics ββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def _update_hospital_capacity(self) -> None: | |
| for district in self._city.districts: | |
| if district.true_infection_rate > INFECTION_THRESHOLD: | |
| excess = district.true_infection_rate - INFECTION_THRESHOLD | |
| drain = round(excess * 0.25, 4) | |
| district.hospital_capacity_remaining = max( | |
| 0.0, | |
| district.hospital_capacity_remaining - drain | |
| ) | |
| else: | |
| district.hospital_capacity_remaining = min( | |
| 1.0, | |
| district.hospital_capacity_remaining + 0.02 | |
| ) | |
| if district.true_infection_rate < SAFE_THRESHOLD: | |
| district.restriction_active = False | |
| def _compute_reward(self, action: ContainmentAction) -> float: | |
| reward = 0.0 | |
| for district in districts_above_threshold(self._city.districts): | |
| density_weight = max(0.5, district.population_density * len(self._city.districts)) | |
| reward += REWARD_INFECTION_PENALTY * min(2.0, density_weight) | |
| for district in self._city.districts: | |
| if district.hospital_capacity_remaining <= HOSPITAL_BREACH_POINT: | |
| reward += REWARD_HOSPITAL_BREACH | |
| for district in self._city.districts: | |
| if district.true_infection_rate < SAFE_THRESHOLD: | |
| time_factor = 1 - (self._state.step_count / self._city.max_steps) | |
| reward += REWARD_EARLY_CONTAINMENT * time_factor | |
| if action.action_type == "restrict": | |
| target = self._city.districts[action.district_id] | |
| if target.true_infection_rate < LOW_THRESHOLD: | |
| reward += REWARD_UNNECESSARY_RESTRICTION | |
| if action.action_type == "allocate": | |
| if action.district_id == get_highest_infected_district(self._city.districts): | |
| reward += REWARD_CORRECT_PRIORITISATION | |
| return round(reward, 4) | |
| def _check_terminal(self) -> Tuple[bool, Optional[str]]: | |
| if all_districts_contained(self._city.districts): | |
| return True, "β Outbreak contained. All districts below safe threshold." | |
| if any_hospital_breached(self._city.districts): | |
| return True, "β Hospital capacity breached. Episode failed." | |
| if self._state.step_count >= self._city.max_steps: | |
| return True, f"Episode complete. {self._city.max_steps} steps reached." | |
| return False, None | |