eptan/crisis-inbox
CrisisInbox
A reinforcement learning environment built on OpenEnv 0.2.1 for training language models to manage personal task overload during natural disasters.
Problem Statement 3.2 (Personalized Tasks) + Patronus AI Sub-Theme (Schema Drift)
HF Space: eptan-crisis-inbox.hf.space
The Problem
When disaster strikes, your phone explodes. Evacuation orders, panicked family texts, insurance deadlines, your boss demanding slides, your sister begging you to pick up her kids from school, your dad's heart medication left behind. Everything is urgent. Policies change mid-crisis. There are no clean answers — only tradeoffs.
CrisisInbox trains an agent to make those tradeoffs well.
How It Works
The agent manages a 48-hour post-disaster inbox as a working parent in Sacramento during a hurricane. 116 messages arrive in real time from 30+ senders across 6 channels. The agent must triage, respond, and adapt — while the rules keep changing underneath.
Three Layers of Difficulty
1. Cognitive Overload — 116 messages arrive over 48 hours. Reading costs time (6 min). Responding costs more (15 min). The agent can't handle everything — it must prioritize and let some things slide.
2. Conflicting Obligations — Your boss says come in. HR says take emergency leave. Your sister needs you to watch her kids. Mom wants you to drive to Tahoe. The evacuation shelter is full. A wheelchair-bound neighbor is trapped. A baby down the street needs your generator. There's no right answer, only better tradeoffs.
3. Scams & Misinformation — Fake FEMA texts ask for your SSN. A contractor demands cash upfront. Social media says the dam is failing. The agent must recognize and deprioritize traps while acting on real emergencies.
4. Schema Drift — Mid-episode, the rules change:
- Insurance deadline shortened from 72h to 48h
- Evacuation zone expands to include your workplace
- Employer switches from "use PTO" to "5 days paid emergency leave"
- Airline extends free rebooking from 48h to 7 days
- FEMA adds new documentation requirements
- Shelter closes due to structural damage — relocate immediately
- County implements fuel rationing (odd/even plates)
- Multiple pharmacies close — prescription transfers needed
- Curfew extended from 9PM-6AM to 6PM-8AM due to looting
Each episode randomly fires 3 of 9 drift events. The agent receives no explicit flag — it must detect changes from message content alone and reprioritize.
Sender Profiles
Tools (Agent Actions)
Reward Function
Episode Variation
Each episode has:
- 3 of 9 drift events randomly selected (seed-controlled)
- +/-15% jitter on message arrival times
- +/-10% jitter on deadlines
- Dependency chains that gate actions (e.g., must handle sister's request before school pickup confirmation)
Quick Start (Hosted)
from openenv import GenericEnvClient, GenericAction
with GenericEnvClient("https://eptan-crisis-inbox.hf.space", message_timeout_s=60.0).sync() as env:
env.reset(seed=42)
result = env.step(GenericAction(tool_name="get_inbox", arguments={}))
print(result.observation)Training
Training notebook connects to the live HF Space environment:
Trains Qwen2.5-0.5B-Instruct with GRPO using a multi-component reward function that bootstraps format compliance first, then optimizes triage quality.
Deployment
# 1. Create and activate a virtual environment
python3.12 -m venv .venv
source .venv/bin/activate
pip install -e .
# 2. Deploy to HF Spaces via OpenEnv CLI
openenv push -r eptan/crisis-inboxOr run locally:
source .venv/bin/activate
uvicorn server.app:app --host 0.0.0.0 --port 8000Local Development
git clone https://github.com/eptan/crisis-inbox.git
cd crisis-inbox
python3.12 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -e .
# Run server
uvicorn server.app:app --host 0.0.0.0 --port 8000Test with the client:
import json
from openenv import GenericEnvClient, GenericAction
with GenericEnvClient("http://localhost:8000", message_timeout_s=60.0).sync() as env:
env.reset(seed=42)
# View the inbox
result = env.step(GenericAction(tool_name="get_inbox", arguments={}))
inbox = result.observation.get("result", result.observation)
print(json.dumps(inbox, indent=2))
# Respond to a message
result = env.step(GenericAction(
tool_name="respond_to_message",
arguments={
"message_id": "msg_001",
"response": "Evacuating to Lincoln High School immediately with documents and medication."
}
))
print(f"Reward: {result.reward}, Done: {result.done}")
# Advance time and check status
env.step(GenericAction(tool_name="advance_time", arguments={"hours": 4.0}))
status = env.step(GenericAction(tool_name="get_status", arguments={}))
print(status.observation)Repository Structure
crisis-inbox/
├── models.py # Message data model (Channel, Urgency, Message)
├── messages.py # 116 pre-written messages across 48h timeline
├── drift_events.py # 9 schema drift events (3 fire per episode)
├── client.py # GenericEnvClient helper
├── __init__.py # Package exports
├── server/
│ ├── crisis_inbox_environment.py # OpenEnv Environment with timeline engine & rewards
│ ├── app.py # FastAPI app entry point
│ ├── demo_ui.py # HTML scenario overview at /demo
│ └── Dockerfile # HF Spaces deployment
├── notebooks/
│ └── crisisinbox_grpo_northflank.ipynb # GRPO training (Unsloth LoRA)
├── pyproject.toml # Package config
├── openenv.yaml # OpenEnv environment spec
├── requirements.txt # Docker build dependencies
└── demo.py # Interactive demo scriptStack
- Environment: OpenEnv 0.2.1 (Environment base class + GenericEnvClient)
- Deployment: HF Spaces (Docker)
- Training: Unsloth + HF TRL GRPO (LoRA, 2x speedup)
- Model: Qwen2.5-0.5B-Instruct
Team
Built at the OpenEnv Hackathon @ Shack15, SF — March 7-8, 2026
