Spaces:
Sleeping
Sleeping
| """ | |
| Task configurations for the MedChain Env environment. | |
| Four tasks of increasing difficulty: | |
| 0. orientation_ward β 2 days, 1 ward, no events (intro) | |
| 1. single_ward_stable β 3 days, 1 ward, no events | |
| 2. multi_ward_seasonal β 6 days, 4 wards, flu surge + supplier delay | |
| 3. hospital_network_crisis β 12 days, 4 sites, 5 overlapping crises | |
| """ | |
| from dataclasses import dataclass, field | |
| from typing import Dict, List, Optional | |
| class Product: | |
| product_id: str | |
| name: str | |
| shelf_life_days: Optional[int] # None = non-perishable | |
| criticality: str # "CRITICAL", "HIGH", "NORMAL" | |
| unit_cost: float | |
| locations: List[str] | |
| base_demand: float | |
| demand_std: float | |
| seasonal_amplitude: float # 0.0 = no seasonality | |
| seasonal_period: int # 0 = no cycle | |
| seasonal_phase: float # phase offset in radians | |
| class Supplier: | |
| supplier_id: str | |
| name: str | |
| base_lead_time: int | |
| lead_time_std: float # 0.0 = deterministic | |
| cost_multiplier: float | |
| products: List[str] | |
| class Location: | |
| location_id: str | |
| name: str | |
| capacity: Optional[int] # None = unlimited | |
| class InboxMessageTemplate: | |
| priority: str | |
| sender: str | |
| subject: str | |
| body: str | |
| class SimEvent: | |
| event_id: str | |
| event_type: str # "mci", "supplier_disruption", "product_recall", | |
| # "demand_surge", "budget_tighten", "cold_chain_breach" | |
| trigger_day: int | |
| duration_days: int # 0 = instant/one-off | |
| params: Dict | |
| message: InboxMessageTemplate | |
| warning_message: Optional[InboxMessageTemplate] # fires on trigger_day-1 | |
| class TaskConfig: | |
| name: str | |
| max_days: int | |
| actions_per_shift: int | |
| budget_limit: float | |
| products: List[Product] | |
| suppliers: List[Supplier] | |
| locations: List[Location] | |
| events: List[SimEvent] | |
| initial_stock_days: int | |
| benchmark_score: float | |
| # βββ Task 0: Orientation Ward ββββββββββββββββββββββββββββββββββββββββββββββββ | |
| TASK0 = TaskConfig( | |
| name="orientation_ward", | |
| max_days=2, | |
| actions_per_shift=8, | |
| budget_limit=5_000.0, | |
| initial_stock_days=1, | |
| benchmark_score=0.88, | |
| locations=[ | |
| Location("ward_general", "General Ward", capacity=None), | |
| ], | |
| products=[ | |
| Product("GLOVE-001", "Surgical Gloves (box)", None, "NORMAL", 5.0, ["ward_general"], 20, 3, 0.0, 0, 0.0), | |
| Product("SYR-10", "Syringes 10ml", None, "NORMAL", 0.5, ["ward_general"], 30, 5, 0.0, 0, 0.0), | |
| Product("MASK-001", "Surgical Masks", None, "NORMAL", 1.0, ["ward_general"], 25, 4, 0.0, 0, 0.0), | |
| ], | |
| suppliers=[ | |
| Supplier("MEDLINE", "MedLine Medical", base_lead_time=1, lead_time_std=0.0, | |
| cost_multiplier=1.0, | |
| products=["GLOVE-001", "SYR-10", "MASK-001"]), | |
| ], | |
| events=[], | |
| ) | |
| # βββ Task 1: Single Ward Stable ββββββββββββββββββββββββββββββββββββββββββββββ | |
| TASK1 = TaskConfig( | |
| name="single_ward_stable", | |
| max_days=3, | |
| actions_per_shift=10, | |
| budget_limit=20_000.0, | |
| initial_stock_days=2, | |
| benchmark_score=0.68, | |
| locations=[ | |
| Location("ward_general", "General Ward", capacity=None), | |
| ], | |
| products=[ | |
| Product("GLOVE-001", "Surgical Gloves (box)", None, "NORMAL", 5.0, ["ward_general"], 50, 8, 0.0, 0, 0.0), | |
| Product("IV-500", "IV Bags 500ml", 540, "NORMAL", 3.5, ["ward_general"], 30, 5, 0.0, 0, 0.0), | |
| Product("SYR-10", "Syringes 10ml", None, "NORMAL", 0.5, ["ward_general"], 80, 12, 0.0, 0, 0.0), | |
| Product("PARA-500", "Paracetamol 500mg", 360, "NORMAL", 0.1, ["ward_general"], 40, 6, 0.0, 0, 0.0), | |
| Product("MASK-001", "Surgical Masks", None, "NORMAL", 1.0, ["ward_general"], 60, 10, 0.0, 0, 0.0), | |
| Product("SAL-001", "Saline Solution", 720, "NORMAL", 2.5, ["ward_general"], 25, 4, 0.0, 0, 0.0), | |
| ], | |
| suppliers=[ | |
| Supplier("MEDLINE", "MedLine Medical", base_lead_time=2, lead_time_std=0.0, | |
| cost_multiplier=1.0, | |
| products=["GLOVE-001", "IV-500", "SYR-10", "PARA-500", "MASK-001", "SAL-001"]), | |
| ], | |
| events=[], | |
| ) | |
| # βββ Task 2: Multi-Ward Seasonal βββββββββββββββββββββββββββββββββββββββββββββ | |
| TASK2 = TaskConfig( | |
| name="multi_ward_seasonal", | |
| max_days=6, | |
| actions_per_shift=14, | |
| budget_limit=50_000.0, | |
| initial_stock_days=3, | |
| benchmark_score=0.55, | |
| locations=[ | |
| Location("central_pharmacy", "Central Pharmacy", capacity=15_000), | |
| Location("ward_icu", "ICU", capacity=None), | |
| Location("ward_emergency", "Emergency", capacity=None), | |
| Location("ward_general", "General Medicine", capacity=None), | |
| ], | |
| products=[ | |
| Product("IV-500", "IV Bags 500ml", 540, "NORMAL", 3.5, ["central_pharmacy", "ward_icu"], 25, 4, 0.0, 0, 0.0), | |
| Product("SAL-001", "Saline Solution", 720, "NORMAL", 2.5, ["central_pharmacy", "ward_icu", "ward_emergency"], 20, 3, 0.0, 0, 0.0), | |
| Product("SYR-10", "Syringes 10ml", None, "NORMAL", 0.5, ["central_pharmacy", "ward_icu", "ward_emergency", "ward_general"], 70, 10, 0.0, 0, 0.0), | |
| Product("ANTIVIR-01","Antiviral Medication", 365, "HIGH", 15.0, ["central_pharmacy", "ward_emergency"], 10, 2, 0.55, 365, 0.0), | |
| Product("MASK-N95", "N95 Masks", None, "NORMAL", 2.5, ["central_pharmacy", "ward_emergency", "ward_icu"], 30, 5, 0.4, 365, 0.0), | |
| Product("PARA-500", "Paracetamol 500mg", 360, "NORMAL", 0.1, ["central_pharmacy", "ward_general"], 40, 6, 0.3, 180, 0.0), | |
| Product("GLOVE-001", "Surgical Gloves (box)", None, "NORMAL", 5.0, ["central_pharmacy", "ward_icu", "ward_emergency", "ward_general"], 50, 8, 0.0, 0, 0.0), | |
| Product("SUTURE-01", "Suture Kit", None, "NORMAL", 12.0, ["central_pharmacy", "ward_general"], 8, 2, 0.0, 0, 0.0), | |
| Product("DRAIN-01", "Surgical Drain", None, "NORMAL", 18.0, ["central_pharmacy", "ward_general"], 5, 1, 0.0, 0, 0.0), | |
| Product("DRESS-01", "Wound Dressing Kit", None, "NORMAL", 6.0, ["central_pharmacy", "ward_general", "ward_emergency"], 20, 4, 0.0, 0, 0.0), | |
| ], | |
| suppliers=[ | |
| Supplier("FASTMED", "FastMed Express", base_lead_time=1, lead_time_std=0.0, cost_multiplier=1.4, | |
| products=["IV-500", "SAL-001", "SYR-10", "ANTIVIR-01", "MASK-N95", "PARA-500", "GLOVE-001", "SUTURE-01", "DRAIN-01", "DRESS-01"]), | |
| Supplier("MEDLINE", "MedLine Medical", base_lead_time=4, lead_time_std=0.0, cost_multiplier=1.0, | |
| products=["IV-500", "SAL-001", "SYR-10", "ANTIVIR-01", "MASK-N95", "PARA-500", "GLOVE-001", "SUTURE-01", "DRAIN-01", "DRESS-01"]), | |
| ], | |
| events=[ | |
| SimEvent( | |
| event_id="flu_surge", | |
| event_type="demand_surge", | |
| trigger_day=3, | |
| duration_days=3, | |
| params={"products": ["ANTIVIR-01", "MASK-N95", "PARA-500"], "multiplier": 1.5}, | |
| message=InboxMessageTemplate( | |
| priority="HIGH", | |
| sender="Regional Health Authority", | |
| subject="Influenza Activity Alert β Demand Surge Active", | |
| body=( | |
| "Regional influenza activity has been elevated above seasonal baseline.\n" | |
| "Emergency department visits up 40-55%. Surge in effect now.\n" | |
| "Antiviral, N95, and analgesic stock levels require immediate review.\n" | |
| "Prepare for increased ICU admissions over the next 3 days." | |
| ), | |
| ), | |
| warning_message=InboxMessageTemplate( | |
| priority="LOW", | |
| sender="Regional Health Authority", | |
| subject="Early Warning: Influenza Activity Rising", | |
| body=( | |
| "Early indicators suggest influenza activity is rising above seasonal norms.\n" | |
| "Consider reviewing antiviral and PPE stock levels as a precaution." | |
| ), | |
| ), | |
| ), | |
| SimEvent( | |
| event_id="medline_delay", | |
| event_type="supplier_disruption", | |
| trigger_day=4, | |
| duration_days=3, | |
| params={"supplier_id": "MEDLINE", "new_lead_time": 7, "reason": "industrial action"}, | |
| message=InboxMessageTemplate( | |
| priority="HIGH", | |
| sender="MedLine Medical β Supply Chain", | |
| subject="Service Disruption Notice β Lead Time Extension", | |
| body=( | |
| "Due to ongoing industrial action at our primary warehouse facility,\n" | |
| "MedLine Medical lead times are currently extended from 4 to 7 days.\n" | |
| "This affects all standard orders. FastMed Express remains unaffected.\n" | |
| "We apologise for the inconvenience. Reference: SUPDIS-2026-0006." | |
| ), | |
| ), | |
| warning_message=None, | |
| ), | |
| ], | |
| ) | |
| # βββ Task 3: Hospital Network Crisis βββββββββββββββββββββββββββββββββββββββββ | |
| TASK3 = TaskConfig( | |
| name="hospital_network_crisis", | |
| max_days=12, | |
| actions_per_shift=18, | |
| budget_limit=150_000.0, | |
| initial_stock_days=4, | |
| benchmark_score=0.38, | |
| locations=[ | |
| Location("regional_dc", "Regional Distribution Centre", capacity=None), | |
| Location("hospital_a", "Hospital A", capacity=None), | |
| Location("hospital_b", "Hospital B", capacity=None), | |
| Location("hospital_c", "Hospital C", capacity=None), | |
| ], | |
| products=[ | |
| Product("B-001", "O-Negative Blood (RBC)", 42, "CRITICAL", 350.0, ["regional_dc", "hospital_a", "hospital_b", "hospital_c"], 3, 1, 0.0, 0, 0.0), | |
| Product("B-002", "Platelet Pack", 5, "CRITICAL", 250.0, ["regional_dc", "hospital_a", "hospital_b", "hospital_c"], 2, 1, 0.0, 0, 0.0), | |
| Product("FFP-001", "Fresh Frozen Plasma", 365, "HIGH", 120.0, ["regional_dc", "hospital_a", "hospital_b", "hospital_c"], 4, 1, 0.0, 0, 0.0), | |
| Product("INS-001", "Insulin (opened vial)", 28, "HIGH", 45.0, ["hospital_a", "hospital_b", "hospital_c"], 8, 2, 0.0, 0, 0.0), | |
| Product("CHEMO-01", "Chemotherapy Agent (recon.)", 7, "HIGH", 800.0, ["hospital_a", "hospital_b"], 1, 0, 0.0, 0, 0.0), | |
| Product("IV-SAL-500","IV Saline Solution 500ml", 720, "NORMAL", 3.5, ["regional_dc", "hospital_a", "hospital_b", "hospital_c"], 60, 10, 0.0, 0, 0.0), | |
| Product("IV-500", "IV Bags 500ml", 540, "NORMAL", 3.5, ["regional_dc", "hospital_a", "hospital_b", "hospital_c"], 40, 7, 0.0, 0, 0.0), | |
| Product("SYR-10", "Syringes 10ml", None, "NORMAL", 0.5, ["regional_dc", "hospital_a", "hospital_b", "hospital_c"], 100,15, 0.0, 0, 0.0), | |
| Product("GLOVE-001", "Surgical Gloves (box)", None, "NORMAL", 5.0, ["regional_dc", "hospital_a", "hospital_b", "hospital_c"], 40, 6, 0.0, 0, 0.0), | |
| Product("PARA-500", "Paracetamol 500mg", 360, "NORMAL", 0.1, ["hospital_a", "hospital_b", "hospital_c"], 60, 8, 0.0, 0, 0.0), | |
| Product("B-003", "AB-Positive Blood (RBC)", 42, "HIGH", 320.0, ["regional_dc", "hospital_a", "hospital_b", "hospital_c"], 2, 1, 0.0, 0, 0.0), | |
| Product("MASK-001", "Surgical Masks", None, "NORMAL", 1.0, ["regional_dc", "hospital_a", "hospital_b", "hospital_c"], 80, 12, 0.0, 0, 0.0), | |
| Product("DRAIN-01", "Surgical Drain", None, "NORMAL", 18.0, ["hospital_a", "hospital_b", "hospital_c"], 4, 1, 0.0, 0, 0.0), | |
| Product("SAL-001", "Saline Solution", 720, "NORMAL", 2.5, ["regional_dc", "hospital_a", "hospital_b", "hospital_c"], 30, 5, 0.0, 0, 0.0), | |
| Product("DRESS-01", "Wound Dressing Kit", None, "NORMAL", 6.0, ["hospital_a", "hospital_b", "hospital_c"], 25, 4, 0.0, 0, 0.0), | |
| ], | |
| suppliers=[ | |
| Supplier("BLOODBANK-A", "Regional Blood Bank", base_lead_time=1, lead_time_std=0.5, cost_multiplier=1.0, | |
| products=["B-001", "B-002", "FFP-001", "B-003"]), | |
| Supplier("SUPPLIER-A", "HealthCo Supplies", base_lead_time=3, lead_time_std=0.5, cost_multiplier=1.0, | |
| products=["IV-SAL-500", "IV-500", "SYR-10", "GLOVE-001", "PARA-500", "MASK-001", "DRAIN-01", "SAL-001", "DRESS-01"]), | |
| Supplier("SUPPLIER-B", "MedFast Express", base_lead_time=2, lead_time_std=0.0, cost_multiplier=1.4, | |
| products=["IV-SAL-500", "IV-500", "SYR-10", "GLOVE-001", "PARA-500", "MASK-001", "DRAIN-01", "SAL-001", "DRESS-01"]), | |
| Supplier("PHARMA-X", "PharmaCorp", base_lead_time=3, lead_time_std=0.5, cost_multiplier=1.0, | |
| products=["INS-001", "CHEMO-01", "PARA-500"]), | |
| ], | |
| events=[ | |
| SimEvent( | |
| event_id="cold_chain_breach", | |
| event_type="cold_chain_breach", | |
| trigger_day=3, | |
| duration_days=0, | |
| params={ | |
| "location_id": "regional_dc", | |
| "product_id": "B-002", | |
| "qty_affected": "all", | |
| }, | |
| message=InboxMessageTemplate( | |
| priority="CRITICAL", | |
| sender="Regional DC Facilities Management", | |
| subject="URGENT: Cold Chain Breach β Platelet Inventory Compromised", | |
| body=( | |
| "Cold chain monitoring detected temperature excursion in Fridge Unit 3.\n" | |
| "Recorded +8Β°C for 4 hours (safe range: +20 to +24Β°C).\n" | |
| "ALL platelet units at Regional DC are presumed compromised and auto-quarantined.\n" | |
| "Affected product: Platelet Pack (B-002) β ALL lots at regional_dc.\n" | |
| "Action required:\n" | |
| " 1. Arrange emergency replacement order from BLOODBANK-A\n" | |
| " 2. Notify clinical teams β platelet supply now limited to hospital stock only.\n" | |
| "Ref: CCBR-2026-0003" | |
| ), | |
| ), | |
| warning_message=None, | |
| ), | |
| SimEvent( | |
| event_id="supplier_a_disruption", | |
| event_type="supplier_disruption", | |
| trigger_day=6, | |
| duration_days=9, | |
| params={"supplier_id": "SUPPLIER-A", "new_lead_time": 7, "reason": "force majeure β flu absenteeism"}, | |
| message=InboxMessageTemplate( | |
| priority="HIGH", | |
| sender="HealthCo Supplies β Logistics", | |
| subject="Force Majeure Notice β Extended Lead Times", | |
| body=( | |
| "HealthCo Supplies hereby provides formal notice of force majeure event.\n" | |
| "Widespread workforce absenteeism due to influenza has impacted fulfilment operations.\n" | |
| "Effective immediately, standard lead times are extended from 3 to 7 days.\n" | |
| "All product lines affected. MedFast Express (SUPPLIER-B) is unaffected.\n" | |
| "Ref: FM-2026-0006" | |
| ), | |
| ), | |
| warning_message=None, | |
| ), | |
| SimEvent( | |
| event_id="mci_warning", | |
| event_type="demand_surge", | |
| trigger_day=8, | |
| duration_days=0, | |
| params={}, | |
| message=InboxMessageTemplate( | |
| priority="HIGH", | |
| sender="Emergency Management Coordination", | |
| subject="Mass Casualty Incident β STANDBY", | |
| body=( | |
| "Multi-vehicle collision reported on Interstate highway.\n" | |
| "Current status: STANDBY. Incident Command not yet activated.\n" | |
| "Preliminary estimate: 15-25 critically injured.\n" | |
| "All blood banks placed on AMBER alert.\n" | |
| "Recommend pre-emptive review of O-neg and platelet stock levels NOW.\n" | |
| "Further update to follow." | |
| ), | |
| ), | |
| warning_message=None, | |
| ), | |
| SimEvent( | |
| event_id="mci_activation", | |
| event_type="mci", | |
| trigger_day=9, | |
| duration_days=3, | |
| params={ | |
| "products": ["B-001", "B-002", "FFP-001"], | |
| "demand_multiplier": 3.0, | |
| "locations": ["hospital_a", "hospital_b", "hospital_c"], | |
| "mci_tracking": True, | |
| }, | |
| message=InboxMessageTemplate( | |
| priority="CRITICAL", | |
| sender="Incident Command System", | |
| subject="MCI ACTIVATION β Mass Casualty Event", | |
| body=( | |
| "INCIDENT COMMAND ACTIVATED.\n" | |
| "Multi-vehicle collision β confirmed 23 critically injured en route.\n" | |
| "Hospital A ETA: 18 min | Hospital B ETA: 29 min | Hospital C ETA: 41 min.\n" | |
| "Trauma surgery teams mobilised at all hospitals.\n" | |
| "All blood banks placed on RED alert.\n" | |
| "Est. blood product requirements: 60-90 units O-neg RBC, 30-40 platelet packs.\n" | |
| "IMMEDIATE ACTION: Verify blood product inventory and initiate emergency procurement." | |
| ), | |
| ), | |
| warning_message=None, | |
| ), | |
| SimEvent( | |
| event_id="iv_saline_recall", | |
| event_type="product_recall", | |
| trigger_day=11, | |
| duration_days=0, | |
| params={ | |
| "product_id": "IV-SAL-500", | |
| "lot_id": "RECALL-LOT-IV2026-9821", | |
| "locations_with_lot": ["hospital_a", "hospital_b", "hospital_c", "regional_dc"], | |
| "qty_per_location": 60, | |
| }, | |
| message=InboxMessageTemplate( | |
| priority="CRITICAL", | |
| sender="Pharmacy Automated System", | |
| subject="MANDATORY RECALL β IV Saline Solution 500ml", | |
| body=( | |
| "MANDATORY RECALL β Health Authority ref #HA-2026-0013\n" | |
| "Product: IV Saline Solution 500ml (IV-SAL-500)\n" | |
| "Affected lot: RECALL-LOT-IV2026-9821\n" | |
| "Reason: Potential endotoxin contamination detected in batch.\n" | |
| "ACTION REQUIRED:\n" | |
| " 1. Query inventory at ALL locations for lot RECALL-LOT-IV2026-9821\n" | |
| " 2. Quarantine ALL affected units immediately β do not use\n" | |
| " 3. Submit replacement order\n" | |
| "Supplier contact: SUPPLIER-A case #88291. Ref: RECALL-2026-0013." | |
| ), | |
| ), | |
| warning_message=None, | |
| ), | |
| ], | |
| ) | |
| def get_task_config(task_name: str) -> TaskConfig: | |
| if task_name == "orientation_ward": | |
| return TASK0 | |
| elif task_name == "single_ward_stable": | |
| return TASK1 | |
| elif task_name == "multi_ward_seasonal": | |
| return TASK2 | |
| elif task_name == "hospital_network_crisis": | |
| return TASK3 | |
| else: | |
| raise ValueError( | |
| f"Unknown task: {task_name!r}. " | |
| "Choose from: orientation_ward, single_ward_stable, multi_ward_seasonal, hospital_network_crisis" | |
| ) | |