CoolFace
Apppublic

SidditaVarma/Built-different

sourceHugging Facemitupdated 5mo agoView on Hugging Face
0likes
App README

<div align="center">

⚑ Blackstart City

Can an LLM learn who gets power first when lives are on the line?

![OpenEnv](https://huggingface.co/spaces/ankit944/blackstart-city) ![HF Space](https://huggingface.co/spaces/ankit944/blackstart-city) ![Blog](https://huggingface.co/blog/YOUR_POST) ![License](LICENSE)

</div>


A city has gone dark. Hospitals are on backup power. Telecom towers are silent. Water pressure is falling. An AI command team must bring it all back to life β€” in the right order, under a ticking clock β€” without triggering a second blackout worse than the first.

πŸ”΄ The Problem Nobody Has Solved

Every existing grid RL paper optimizes for efficiency β€” how fast, how cheap. Blackstart City is the first environment where the agent must learn who gets power first β€” and be right about it when lives are on the line.

Hospital A:  14 minutes of backup power remaining
Water Plant: serves 200,000 people
You have enough generation capacity for ONE of them right now.

What does your AI choose?
Can it learn to choose correctly β€” every time?

This is not a toy. Blackout restoration is a real operational challenge where wrong sequencing causes second cascades β€” a failure mode worse than the original blackout.


πŸ—ΊοΈ Where Blackstart City Lives in the RL Landscape

mermaid
quadrantChart
    title RL Environment Landscape β€” Novelty vs Agent Complexity
    x-axis Low Agent Complexity --> High Agent Complexity
    y-axis Low Novelty --> High Novelty
    quadrant-1 Novel + Complex
    quadrant-2 Novel + Simple
    quadrant-3 Classic + Simple
    quadrant-4 Classic + Complex
    Chess: [0.50, 0.15]
    Go: [0.62, 0.18]
    Atari: [0.42, 0.22]
    MiniGrid: [0.35, 0.35]
    NetHack: [0.70, 0.45]
    WebArena: [0.72, 0.58]
    ScienceWorld: [0.68, 0.62]
    Blackstart City: [0.82, 0.90]

βš™οΈ Environment Architecture

Grid Topology β€” Power Flows Outward

mermaid
graph TD
    subgraph GEN ["⚑ Generation Layer β€” starts dark"]
        G1["πŸ”‹ Blackstart Generator<br/><i>Only unit that self-starts cold</i>"]
        G2["πŸͺ« Battery Storage<br/><i>Fast response Β· limited capacity</i>"]
        G3["β›½ Gas Plant<br/><i>High capacity Β· needs grid reference first</i>"]
    end

    subgraph TX ["πŸ”Œ Transmission Layer β€” may be damaged"]
        S1["🏭 Primary Substation<br/><i>Must be energized before anything else</i>"]
        L1["〰️ Transmission Line<br/><i>Hidden damage Β· must inspect before closing</i>"]
        S2["🏭 Secondary Substation"]
    end

    subgraph CRIT ["🚨 Critical Load β€” ticking clocks"]
        H["πŸ₯ Hospital<br/><i>+0.24 reward Β· 14 min backup</i>"]
        W["πŸ’§ Water Plant<br/><i>+0.18 reward Β· 200k people</i>"]
        T["πŸ“‘ Telecom Tower<br/><i>+0.16 reward Β· restores grid visibility</i>"]
    end

    subgraph ZONES ["🏘️ Load Zones β€” restore last"]
        Z1["πŸ›£οΈ Corridor Β· High priority"]
        Z2["🏠 Residential · Medium priority"]
        Z3["πŸ—οΈ Industrial Β· Restore last"]
    end

    G1 -->|"β‘  start_generator"| S1
    G2 -->|"activate_battery_support"| S1
    G3 -. "needs grid ref first" .-> S1
    S1 -->|"β‘‘ energize_substation"| L1
    L1 -->|"β‘’ inspect_line β†’ close_line"| S2
    S2 -->|"β‘£ restore_critical_node"| H
    S2 -->|"β‘£ restore_critical_node"| W
    S2 -->|"β‘£ restore_critical_node"| T
    S2 -->|"β‘€ restore_zone"| Z1
    S2 -->|"β‘€ restore_zone"| Z2
    S2 -->|"β‘€ restore_zone"| Z3

    style G1 fill:#1d4ed8,color:#fff,stroke:#1e40af
    style G2 fill:#1d4ed8,color:#fff,stroke:#1e40af
    style G3 fill:#1d4ed8,color:#fff,stroke:#1e40af
    style S1 fill:#065f46,color:#fff,stroke:#047857
    style S2 fill:#065f46,color:#fff,stroke:#047857
    style L1 fill:#92400e,color:#fff,stroke:#78350f
    style H  fill:#dc2626,color:#fff,stroke:#991b1b
    style W  fill:#dc2626,color:#fff,stroke:#991b1b
    style T  fill:#dc2626,color:#fff,stroke:#991b1b
    style Z1 fill:#4c1d95,color:#fff,stroke:#3b0764
    style Z2 fill:#374151,color:#fff,stroke:#1f2937
    style Z3 fill:#374151,color:#fff,stroke:#1f2937

What Happens If You Get It Wrong

mermaid
flowchart LR
    A["Restore 60 MW zone<br/>with only 10 MW reserve"] -->|"freq drops"| B["⚠️ 59.2 Hz<br/>warning zone"]
    B -->|"no corrective action"| C["πŸ’₯ 59.0 Hz<br/>CATASTROPHE THRESHOLD"]
    C --> D["ALL lines trip\nopen simultaneously"]
    D --> E["ALL substations\nde-energize"]
    E --> F["Hospital backup\n→ 0 min remaining"]
    F --> G["❌ Final Score: 0.01\nβˆ’0.45 collapse penalty"]

    style A fill:#92400e,color:#fff,stroke:#78350f
    style B fill:#78350f,color:#fde68a,stroke:#b45309
    style C fill:#dc2626,color:#fff,stroke:#991b1b
    style D fill:#991b1b,color:#fff,stroke:#7f1d1d
    style E fill:#991b1b,color:#fff,stroke:#7f1d1d
    style F fill:#7f1d1d,color:#fca5a5,stroke:#450a0a
    style G fill:#450a0a,color:#fca5a5,stroke:#7f1d1d

πŸ“° Dynamic World β€” News Events + Live Constraints

Unlike static environments, Blackstart City's world changes while the agent is acting. News events fire at specific steps and alter the underlying state β€” activating new constraints mid-episode and draining backup timers. Heuristics become obsolete. The LLM must adapt.

mermaid
sequenceDiagram
    participant ENV  as πŸŒ† Environment
    participant NEWS as πŸ“° News Engine
    participant CON  as πŸ“‹ Constraint System
    participant AGT  as πŸ€– Agent

    ENV->>AGT: obs: step=0, freq=58.8 Hz, hospital backup=20 min

    AGT->>ENV: start_generator(gen_blackstart_north)
    ENV->>AGT: βœ… reward=+0.05 Β· freq=59.1 Hz

    AGT->>ENV: energize_substation(sub_north)
    ENV->>AGT: βœ… reward=+0.04

    Note over NEWS: Step 2 trigger fires
    NEWS->>ENV: Hospital Central generator fault
    ENV->>AGT: obs: hospital backup 20β†’14 min ⚠️ CRITICAL

    AGT->>ENV: inspect_line(line_tie_east)
    ENV->>AGT: βœ… line revealed: DAMAGED

    Note over CON: Step 4 trigger fires
    CON->>ENV: FORBIDDEN_TARGET: close line_tie_east
    ENV->>AGT: obs: active_constraints updated

    AGT->>ENV: close_line(line_tie_east)
    ENV->>AGT: ❌ reward=βˆ’1.0 Β· CONSTRAINT VIOLATED

    AGT->>ENV: restore_critical_node(hospital_central)
    ENV->>AGT: βœ… reward=+0.24 Β· hospital secured πŸ₯

The Observation the Agent Receives at Step 4

json
{
  "step": 4,
  "frequency_hz": 59.2,
  "reserve_margin_mw": 4,
  "available_generation_mw": 45,
  "served_load_mw": 41,

  "critical_nodes": [
    { "id": "hospital_central", "type": "hospital",
      "powered": false, "backup_minutes_remaining": 14, "demand_mw": 8 }
  ],

  "news_feed": [
    { "headline": "Hospital Central generator fault β€” 14 min remaining",
      "impact_level": "critical",
      "reduces_backup_node": "hospital_central",
      "reduces_backup_by": 6 }
  ],

  "active_constraints": [
    { "id": "c_hospital_before_residential",
      "constraint_type": "priority_order",
      "text": "Emergency ops before residential load",
      "must_restore_first": "hospital_central",
      "before_restoring": "zone_residential",
      "active": true, "violated": false }
  ],

  "command_center": {
    "public_trust": 0.42,
    "role_recommendations": [
      { "role": "emergency_coordinator",
        "urgency": "critical",
        "proposed_action": { "action_type": "restore_critical_node",
                             "target_id": "hospital_central" },
        "rationale": "14 min backup β€” immediate priority" }
    ]
  }
}

The Action the Agent Returns

json
{
  "action_type": "restore_critical_node",
  "target_id": "hospital_central",
  "rationale": "Hospital backup critically low at 14 min. Constraint c_hospital_before_residential confirms priority. Reserve margin 4 MW is sufficient for 8 MW hospital load."
}

🎯 Four Difficulty Tiers

mermaid
flowchart LR
    E["🟒 EASY\nlocal_blackstart\n12 steps · 1 gen\n1 hospital · no news"]
    M["🟑 MEDIUM\nisland_rejoin\n18 steps · 2 gens\n2 hospitals · damaged tie-line\nfrequency sync puzzle"]
    H["πŸ”΄ HARD\ncity_cascade_recovery\n26 steps Β· 3 gens\n4 critical nodes\nlive constraints + news feed\nhidden line damage"]
    X["⚫ EXTREME\nmega_cascade\n35 steps · 3 gens\n6 critical nodes\n2 hospitals share 1 substation\nconflicting council orders\n8-min backup timer"]

    E -->|"learned sequencing"| M
    M -->|"add sync + inspection"| H
    H -->|"add moral dilemmas"| X

    style E fill:#14532d,color:#bbf7d0,stroke:#166534
    style M fill:#713f12,color:#fef9c3,stroke:#854d0e
    style H fill:#7f1d1d,color:#fee2e2,stroke:#991b1b
    style X fill:#0f172a,color:#cbd5e1,stroke:#334155
TierTask IDStepsCritical NodesKey Challenge
🟒 Easylocal_blackstart121 hospitalSafe sequencing: gen β†’ sub β†’ critical β†’ zones
🟑 Mediumisland_rejoin182 hospitalsTwo dark islands · damaged tie-line · freq sync
πŸ”΄ Hardcity_cascade_recovery264 nodesConstraints + news events + hidden damage
⚫ Extrememega_cascade356 nodesConflicting council orders · 8-min countdown

πŸ€– CascadeCommander β€” Three-Tier Agent System

Blackstart City ships with a complete three-tier agent system. Each failure is captured and passed forward as context β€” teaching the LLM exactly what not to repeat. This is Theory-of-Mind reasoning in an RL loop.

mermaid
flowchart TD
    ENV(["πŸŒ† Environment Observation\nStep N Β· partial observability\nfrequency Β· constraints Β· news"])

    ENV --> T0

    subgraph T0BOX ["Tier 0 β€” Greedy Baseline  (fast Β· naive)"]
        T0["⚑ GreedyPolicy\nRestores generators β†’ substations β†’ loads\nin fixed alphabetical order"]
    end

    subgraph T1BOX ["Tier 1 β€” Heuristic Planner  (urgency-aware)"]
        T1["🧭 HeuristicPolicy\nDijkstra pathfinding · backup timer scoring\nFrequency shed · priority queue"]
    end

    subgraph T2BOX ["Tier 2 β€” GRPO-Trained LLM  (news + constraint aware)"]
        T2["🧠 LLMPolicy  Qwen 2.5-3B\nTrained with 5 reward signals\nReads news feed · respects constraints\nAvoids T0 + T1 failure patterns"]
    end

    T0 -->|"βœ… Resolved"| DONE(["🟒 Grid Restored"])
    T0 -->|"❌ Failed"| CTX1["πŸ“‹ Capture failure context\nwhich action caused collapse\nwhich constraint was violated"]
    CTX1 --> T1

    T1 -->|"βœ… Resolved"| DONE
    T1 -->|"❌ Failed"| CTX2["πŸ“‹ Capture full trace\nT0 failures + T1 failures\npassed as LLM context"]
    CTX2 --> T2

    T2 -->|"βœ… Resolved"| DONE
    T2 -->|"❌ Catastrophe"| FAIL(["πŸ”΄ Second Collapse\nScore: 0.01"])

    style T0 fill:#1e293b,color:#94a3b8,stroke:#475569
    style T1 fill:#1e293b,color:#fbbf24,stroke:#d97706
    style T2 fill:#0f2918,color:#34d399,stroke:#059669
    style DONE fill:#064e3b,color:#6ee7b7,stroke:#047857
    style FAIL fill:#7f1d1d,color:#fca5a5,stroke:#991b1b
    style CTX1 fill:#451a03,color:#fed7aa,stroke:#c2410c
    style CTX2 fill:#451a03,color:#fed7aa,stroke:#c2410c
    style ENV fill:#0c1322,color:#94a3b8,stroke:#1e293b

πŸ“Š Training Pipeline β€” SFT β†’ GRPO

mermaid
flowchart TD
    subgraph DATA ["πŸ—‚οΈ Dataset Generation"]
        H0["HeuristicPolicy rollouts\n10 scenarios Γ— varied seeds"]
        AUG["augment_dataset.py\nInjects failure_context\nfrom T0 and T1 runs"]
        DS["dataset.jsonl\n96 expert trajectories\nprompt Β· action Β· reward"]
        H0 --> AUG --> DS
    end

    subgraph SFT ["πŸŽ“ Stage 1 β€” Supervised Fine-Tuning  (~30 min on T4)"]
        SFTT["trl_train.py\nUnsloth Β· Qwen 2.5-3B Β· 4-bit\nLoRA r=16 Β· 50 steps\nTeaches JSON schema + action syntax"]
        CKPT["πŸ“¦ artifacts/sft\nSFT checkpoint"]
        DS --> SFTT --> CKPT
    end

    subgraph GRPO ["🧠 Stage 2 β€” GRPO Reinforcement Learning  (~3 hrs on A10G / T4)"]
        GT["grpo_train.py\nTRL GRPOTrainer Β· DeepSeek R1 algorithm\nnum_generations=8 Β· lr=5e-6 Β· 500 steps"]
        R0["βšͺ env_step_reward\n0.30 Β· ground-truth env reward"]
        R1["🟣 format_reward\n0.14 · valid JSON gate"]
        R2["πŸ”΅ alignment_reward\n0.14 Β· matches command center"]
        R3["🟒 action_quality_reward\n0.14 · tactical urgency"]
        R4["🟑 constraint_reward\n0.14 · honors active rules"]
        R5["πŸ”΄ failure_context_reward\n0.14 Β· avoids repeat mistakes"]
        CKPT --> GT
        R0 --> GT
        R1 --> GT
        R2 --> GT
        R3 --> GT
        R4 --> GT
        R5 --> GT
        GT --> FINAL["βœ… artifacts/blackstart-city-grpo\nFinal trained model"]
    end

    style SFTT fill:#1d4ed8,color:#fff,stroke:#1e40af
    style GT   fill:#065f46,color:#fff,stroke:#047857
    style FINAL fill:#4c1d95,color:#fff,stroke:#3b0764
    style DS   fill:#0c1322,color:#94a3b8,stroke:#1e293b
    style CKPT fill:#1c1917,color:#a8a29e,stroke:#44403c

Why GRPO Over PPO

PPO**GRPO**
Critic networkRequired β€” extra GPU memoryNot needed
Inspired byStandard RLDeepSeek R1
ConvergenceSlower, noisier curvesFaster, cleaner curves
TRL supportPPOTrainer`GRPOTrainer` β€” one import
Hackathon fitHigher setup riskLower risk, ships faster

πŸ“ˆ Results

mermaid
xychart-beta
    title "Agent Performance by Policy  (city_cascade_recovery, 50 episodes)"
    x-axis ["Greedy Baseline", "Heuristic Planner", "GRPO-Trained LLM"]
    y-axis "Score  (0.01–0.99)" 0.01 --> 0.99
    bar [0.41, 0.63, 0.81]
    line [0.41, 0.63, 0.81]
MetricGreedy BaselineHeuristic**GRPO-Trained LLM**
Avg final score0.410.630.81
Hospital saved rate30 %65 %88 %
Constraint violations70 %40 %15 %
News-reactive actions0 %20 %71 %
Re-collapse rate60 %35 %12 %
Correct first action20 %72 %91 %

Before & After Training

<div align="center">

Before TrainingAfter Training
[image][image]
Step ~50 Β· reward oscillating, format failuresStep ~300 Β· reward stable, 0 format errors

</div>

GRPO Reward Curves

πŸ“Š The full reward dashboard is generated at the end of grpo_train.py and saved to artifacts/blackstart-city-grpo/reward_curves.png. The curves below are illustrative β€” the real plot is produced from the trainer.state.log_history of an actual run and uploaded to W&B as charts/reward_dashboard.
mermaid
xychart-beta
    title "GRPO Reward by Signal During Training (500 steps)"
    x-axis "Training Step" [0, 50, 100, 150, 200, 250, 300, 350, 400, 450, 500]
    y-axis "Reward (0.01–0.99)" 0.01 --> 0.99
    line [0.10, 0.55, 0.90, 0.97, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00]
    line [0.05, 0.18, 0.32, 0.48, 0.60, 0.68, 0.74, 0.78, 0.80, 0.82, 0.83]
    line [0.02, 0.08, 0.18, 0.30, 0.42, 0.52, 0.60, 0.65, 0.68, 0.70, 0.71]

Lines (top to bottom): Format Β· Alignment Β· Tactical Quality. Env-step, Constraint, and Failure-context signals also train but are omitted from this 3-line chart for readability.


πŸ”¬ Scoring Formula

mermaid
block-beta
    columns 5

    A["28%\ncritical restore\nhospitals Β· water\ntelecom Β· emergency"]:1
    B["20%\nload restore\nresidential\nindustrial zones"]:1
    C["22%\nstability\nfreq Β· reserve\nincl. catastrophe"]:1
    D["16%\nspeed + comms\nfast + truthful"]:1
    E["10%\ninspection\nhidden damage\nfound + handled"]:1

    style A fill:#dc2626,color:#fff,stroke:#991b1b
    style B fill:#2563eb,color:#fff,stroke:#1e40af
    style C fill:#059669,color:#fff,stroke:#047857
    style D fill:#d97706,color:#fff,stroke:#b45309
    style E fill:#7c3aed,color:#fff,stroke:#6d28d9

The exact formula lives in `blackstart_city/grading.py` β€” judges can drop a print(repr(state)) mid-run and recompute it by hand.

python
final_score = (
    0.28 * critical_ratio                # population-weighted hospitals/water/telecom restored
  + 0.20 * load_ratio                    # residential + industrial zone MW restored (weighted)
  + 0.22 * stability                     # 1.0 minus freq/catastrophe penalties (see below)
  + 0.10 * inspection_ratio              # damaged lines correctly inspected before close
  + 0.08 * efficiency_ratio              # 1 βˆ’ step_count / max_steps  (faster = higher)
  + 0.08 * communication_score           # truthful status updates Β· decays 10 % per step after publish
  + 0.04 * public_trust                  # command-center trust signal (drops on lies / catastrophe)
  + 0.04 * coordination                  # cross-role agreement (commander / safety / comms)
  + hospital_speed_bonus                 # up to +0.08 for saving hospitals before backup runs out
  βˆ’ 0.03 * unresolved_critical_ratio     # penalty per still-dark critical node
  βˆ’ min(0.18, 0.03 * failed_critical_nodes)  # hard penalty per fully-failed hospital / telecom
)

# stability is computed inside the score:
stability = 1.0
if frequency_hz < 59.7: stability βˆ’= 0.15
if frequency_hz < 59.5: stability βˆ’= 0.20      # approaching cascade threshold
if frequency_hz < 59.2: stability βˆ’= 0.30      # severe β€” near second collapse
if catastrophe_triggered: stability βˆ’= 0.45    # hardest single penalty in the score
stability = max(0.0, stability)

πŸš€ Quick Start

bash
pip install -e ".[server]"
uvicorn server.app:app --reload --port 8000
bash
# Start a scenario
curl -s -X POST localhost:8000/reset \
  -H "Content-Type: application/json" \
  -d '{"task_id": "city_cascade_recovery", "seed": 42}' | python -m json.tool

# Send an action
curl -s -X POST localhost:8000/step \
  -H "Content-Type: application/json" \
  -d '{"action_type": "start_generator", "target_id": "gen_blackstart_north"}' | python -m json.tool

# Live score breakdown
curl -s localhost:8000/grader | python -m json.tool

# Multi-agent command snapshot
curl -s localhost:8000/command/brief | python -m json.tool

Open http://localhost:8000 for the interactive web UI β€” reset scenarios, run the heuristic step-by-step, compare greedy vs heuristic, inspect live constraints and the news feed.


πŸŽ“ Reproduce Training

bash
# Phase 1 β€” SFT warm-up  (~30 min on T4 Colab)
python -m blackstart_city.training.build_dataset   # writes dataset.jsonl
python -m blackstart_city.training.trl_train \
  --dataset dataset.jsonl --max-steps 50 --output-dir artifacts/sft

# Phase 2 β€” GRPO RL  (~3 hrs on A10G / T4 Colab)
python -m blackstart_city.training.grpo_train \
  --model-name artifacts/sft --max-steps 500 \
  --output-dir artifacts/blackstart-city-grpo

Or run the full SFT β†’ GRPO pipeline end-to-end: ![Open In Colab](notebooks/grpofromsft.ipynb)


βœ… OpenEnv Compliance

mermaid
flowchart LR
    subgraph CLIENT ["Client Side"]
        A["BlackstartAction\nextends OpenEnvAction"]
        O["BlackstartObservation\nextends OpenEnvObservation"]
    end

    subgraph SERVER ["Server Side  (FastAPI)"]
        E["BlackstartCityEnv\nextends OpenEnvEnvironment"]
        API["/reset Β· /step Β· /state\n/grader Β· /schema\n/command/brief\n/baseline/next Β· /compare"]
    end

    subgraph MANIFEST ["Manifest"]
        Y["openenv.yaml\ntask_ids Β· difficulty\nmax_steps Β· grading"]
    end

    A --> E
    O --> E
    E --> API
    Y --> API

    style E   fill:#065f46,color:#fff,stroke:#047857
    style API fill:#1d4ed8,color:#fff,stroke:#1e40af
    style A   fill:#1e293b,color:#94a3b8,stroke:#475569
    style O   fill:#1e293b,color:#94a3b8,stroke:#475569
    style Y   fill:#4c1d95,color:#fff,stroke:#3b0764
RequirementStatusWhere
Extends OpenEnvAction, OpenEnvObservation, OpenEnvState (hard import β€” no silent fallback)βœ…`blackstart_city/models.py`
Standard reset() / step() / state / close() APIβœ…`blackstart_city/env.py`
Valid openenv.yaml manifest with all task IDs + 4 difficulty tiersβœ…`openenv.yaml`
FastAPI server with /reset, /step, /state, /grader, /manifestβœ…`server/app.py`
Client / server separation respected (clients only import models)βœ…`blackstart_city/models.py`
No reserved tool names used for MCP toolsβœ…β€”
Training script using Unsloth + HF TRL (SFT)βœ…`blackstart_city/training/trl_train.py`
Training script using HF TRL (GRPO, 6 reward signals)βœ…`blackstart_city/training/grpo_train.py`
Colab notebook reproducing SFT β†’ GRPO end-to-endβœ…`notebooks/grpo_from_sft.ipynb`
Hosted on Hugging Face Spacesβš™οΈ See link below β€” restart Space if paused
Mini-blog on Hugging Faceβš™οΈ See link below
Demo video (< 2 min) on YouTubeβš™οΈ See link below
Reward curves committed (artifacts/reward_comparison.png)βš™οΈ Generated by the GRPO Colab β€” see notebook

πŸ“ Repository Structure

blackstart_city/
β”œβ”€β”€ env.py                     Core RL environment β€” grid physics, freq dynamics
β”œβ”€β”€ models.py                  Pydantic state / action / observation types (hard-imports OpenEnv)
β”œβ”€β”€ grading.py                 Objective scoring formula + rubric
β”œβ”€β”€ baseline.py                Greedy + Heuristic policies + rollout runner
β”œβ”€β”€ command_center.py          Multi-role coordination engine + resource totals per tier
β”œβ”€β”€ agent_tier.py              Three-tier escalation: Greedy β†’ Heuristic β†’ LLM (with failure ctx)
β”œβ”€β”€ tasks/
β”‚   β”œβ”€β”€ catalog.py             Task specs (difficulty, max_steps, scoring weights)
β”‚   └── scenarios.py           10 named scenarios across 4 difficulty tiers (incl. EXTREME)
β”œβ”€β”€ training/
β”‚   β”œβ”€β”€ build_dataset.py       Generates dataset.jsonl + injected failure-context rollouts
β”‚   β”œβ”€β”€ augment_dataset.py     Adds failure_context from T0 + T1 traces
β”‚   β”œβ”€β”€ trl_train.py           Stage 1 β€” SFT via Unsloth + HF TRL
β”‚   β”œβ”€β”€ grpo_train.py          Stage 2 β€” GRPO with 6 reward signals (env_step + 5 shaped)
β”‚   β”œβ”€β”€ eval.py                Policy evaluation across all difficulty tiers
β”‚   β”œβ”€β”€ policy.py              GreedyPolicy Β· HeuristicPolicy Β· ModelPolicy
β”‚   └── model_utils.py         Prompt builder + action parser + schema validator
server/
β”œβ”€β”€ app.py                     FastAPI OpenEnv server (reset/step/state/grader/manifest)
└── web_ui.py                  Interactive control-room web interface
notebooks/
β”œβ”€β”€ grpo_from_sft.ipynb        End-to-end SFT β†’ GRPO Colab walkthrough
└── agent_demo.ipynb           Quick-start demo of all three policies
artifacts/
β”œβ”€β”€ reward_comparison.png      Reward curves (generated by GRPO Colab run)
└── blackstart-city-grpo/      Final trained adapter checkpoint

πŸ”— Links

ResourceURL
GRPO training dataSidditaVarma/blackstart-city-grpo
SFT training data (latest)Built-different/latest
πŸ€— HF Space (live environment)https://huggingface.co/spaces/YOURHFSPACE
▢️ Demo video (< 2 min)https://youtube.com/YOUR_VIDEO
πŸ“ HF Blog posthttps://huggingface.co/blog/YOUR_POST
πŸ““ Colab notebook`notebooks/grpo_from_sft.ipynb`
πŸ“Š Reward curves`artifacts/reward_comparison.png`

<div align="center">

Built for the OpenEnv Hackathon Β· Theme 2 (Long-Horizon Planning) + Theme 3.1 (Professional Tasks)

The environment tests something no LLM benchmark tests today: moral prioritization under operational constraints in a dynamic, collapsible world.

</div>