Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| """ | |
| GridMind-RL Full Unified Demo — All 4 Themes Running Together | |
| ------------------------------------------------------------- | |
| This single script demonstrates all hackathon themes in one flow. | |
| Each step proves a theme and prints WHY it matters for judges. | |
| Themes: | |
| Theme 1 — Multi-Agent: 3 buildings + coordinator + /feeder | |
| Theme 2 — Long-Horizon Planning: Task 4 instruction cards over 96 steps | |
| Theme 3 — World Modeling: /simulate before committing actions | |
| Theme 4 — Self-Improvement: Curriculum auto-advances task difficulty | |
| Run: python scripts/full_demo.py | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import sys | |
| import time | |
| import requests | |
| ENV_URL = "http://localhost:7860" | |
| def bold(text: str) -> str: | |
| return f"**{text}**" | |
| def section(n: int, title: str, theme: str) -> None: | |
| print(f"\n{'='*62}") | |
| print(f" STEP {n} — {bold(title)} [{theme}]") | |
| print(f"{'='*62}") | |
| def check_pass(text: str) -> None: | |
| print(f" [OK] {text}") | |
| def check_warn(text: str) -> None: | |
| print(f" [!] {text}") | |
| def show_info(label: str, value: str) -> None: | |
| print(f" {label}: {value}") | |
| def r(path: str, method="GET", json_data=None) -> dict: | |
| url = f"{ENV_URL}{path}" | |
| try: | |
| if method == "GET": | |
| resp = requests.get(url, timeout=10) | |
| else: | |
| resp = requests.post(url, json=json_data, timeout=10) | |
| resp.raise_for_status() | |
| return resp.json() | |
| except Exception as e: | |
| check_warn(f"Request failed: {e}") | |
| return {} | |
| def step(n: int, title: str, theme: str, fn): | |
| section(n, title, theme) | |
| result = fn() | |
| if result is not False: | |
| check_pass("Passed") | |
| return result | |
| # ── STEP 1 — Health Check ────────────────────────────────────────────── | |
| def do_step1(): | |
| data = r("/health") | |
| show_info("Status", data.get("status", "?") + " OK" if data.get("status") == "ok" else data.get("status", "?")) | |
| show_info("Version", data.get("version", "?")) | |
| return data.get("status") == "ok" | |
| # ── STEP 2 — OpenEnv /info Metadata ────────────────────────────────── | |
| def do_step2(): | |
| data = r("/info") | |
| show_info("Name", data.get("name", "?")) | |
| show_info("Version", data.get("version", "?")) | |
| themes = data.get("themes", []) | |
| show_info("Themes", ", ".join(themes)) | |
| endpoints = data.get("endpoints", []) | |
| show_info("Endpoints", f"{len(endpoints)} registered") | |
| print(f" Sample endpoints: {endpoints[:5]}") | |
| return len(themes) >= 4 and "multi-agent" in themes | |
| # ── STEP 3 — All 4 Tasks Available ──────────────────────────────────── | |
| def do_step3(): | |
| data = r("/tasks") | |
| tasks = data if isinstance(data, list) else data.get("tasks", []) | |
| print(f" {len(tasks)} tasks available:") | |
| for t in tasks: | |
| print(f" Task {t['id']}: {t['name']} ({t['difficulty']})") | |
| ids = [t["id"] for t in tasks] | |
| return 1 in ids and 2 in ids and 3 in ids and 4 in ids | |
| # ── STEP 4 — Theme 1: Multi-Agent Reset ─────────────────────────────── | |
| def do_step4(): | |
| data = r("/reset", "POST", {"task_id": 3, "num_buildings": 3, "seed": 42}) | |
| obs_list = data.get("observations", []) | |
| print(f" Buildings in federation: {len(obs_list)}") | |
| for b in obs_list: | |
| tid = b.get("building_id", 0) | |
| temp = round(b.get("indoor_temperature", 0), 1) | |
| storage = round(b.get("thermal_storage_level", 0) * 100) | |
| cost = round(b.get("cumulative_cost", 0), 2) | |
| print(f" Building {tid}: {temp}C | storage {storage}% | cost ${cost}") | |
| return len(obs_list) == 3 | |
| # ── STEP 5 — Theme 1: Fleet-wide Feeder View ───────────────────────── | |
| def do_step5(): | |
| data = r("/feeder") | |
| total = round(data.get("total_demand_kw", 0), 1) | |
| limit = data.get("feeder_limit_kw", 0) | |
| overload = data.get("feeder_overload", False) | |
| util = round(data.get("utilization_pct", 0), 1) | |
| buildings = data.get("buildings", []) | |
| feeder_status = "OK" if not overload else "OVERLOAD" | |
| print(f" Feeder: {total} / {limit} kW [{feeder_status}]") | |
| print(f" Utilization: {util}%") | |
| for b in buildings: | |
| bid = b.get("building_id", "?") | |
| dem = round(b.get("current_demand_kw", 0), 1) | |
| pm = b.get("price_multiplier", 1.0) | |
| print(f" Building {bid}: {dem} kW | price mult {pm}x") | |
| return total > 0 and len(buildings) == 3 | |
| # ── STEP 6 — Theme 1: Coordinator Price Signals ─────────────────────── | |
| def do_step6(): | |
| multipliers = [1.5, 0.8, 1.0] | |
| r("/coordinate", "POST", {"price_multipliers": multipliers}) | |
| print(f" Set price signals: {multipliers}") | |
| feeder = r("/feeder") | |
| blds = feeder.get("buildings", []) | |
| for i, b in enumerate(blds): | |
| pm = b.get("price_multiplier", "?") | |
| signal = ">> expensive (will conserve)" if pm > 1.2 else "<< cheap (can charge)" | |
| print(f" Building {i}: {pm}x | {signal}") | |
| return True | |
| # ── STEP 7 — Theme 3: World Modeling /simulate ─────────────────────── | |
| def do_step7(): | |
| action = { | |
| "hvac_power_level": 0.8, | |
| "thermal_charge_rate": 0.5, | |
| "batch_job_slot": 0, | |
| "load_shed_fraction": 0.0, | |
| "building_id": 0, | |
| } | |
| # Check step counter before simulate | |
| state_before = r("/state") | |
| step_before = state_before.get("step", "?") | |
| print(f" Step before simulate: {step_before}") | |
| sim_data = r("/simulate", "POST", [action]) | |
| results = sim_data.get("results", []) | |
| print(f" Simulated 1 action ahead") | |
| if results: | |
| r0 = results[0] | |
| obs = r0.get("observation", {}) | |
| reward = round(r0.get("reward", 0), 3) | |
| print(f" Predicted reward: {reward}") | |
| print(f" Predicted temp: {round(obs.get('indoor_temperature', 0), 1)}C") | |
| print(f" Predicted storage: {round(obs.get('thermal_storage_level', 0) * 100)}%") | |
| print(f" Would episode end? {sim_data.get('done', False)}") | |
| # Verify step did NOT advance | |
| state_after = r("/state") | |
| step_after = state_after.get("step", "?") | |
| if step_before == step_after: | |
| check_pass(f"World model confirmed: step unchanged ({step_before})") | |
| return True | |
| else: | |
| check_warn(f"Step changed from {step_before} to {step_after}") | |
| return False | |
| # ── STEP 8 — Take a Step + Check Fault System ───────────────────────── | |
| def do_step8(): | |
| action = { | |
| "hvac_power_level": 0.7, | |
| "thermal_charge_rate": 0.0, | |
| "batch_job_slot": 0, | |
| "load_shed_fraction": 0.0, | |
| "building_id": 0, | |
| } | |
| data = r("/step", "POST", [action]) | |
| if isinstance(data, list): | |
| results = data | |
| else: | |
| results = data.get("results", []) if isinstance(data, dict) else [] | |
| if not results: | |
| print(" Warning: /step returned unexpected format") | |
| return False | |
| obs = results[0].get("observation", {}) | |
| step_num = obs.get("step", "?") | |
| faults = obs.get("active_faults", []) | |
| reward = round(results[0].get("reward", 0), 3) | |
| show_info("Step", str(step_num)) | |
| show_info("Reward", str(reward)) | |
| show_info("Active faults", f"{len(faults)}") | |
| if faults: | |
| for f in faults: | |
| print(f" [!] FAULT: {f}") | |
| else: | |
| print(" No faults triggered yet (expected on hard difficulty)") | |
| return True | |
| # ── STEP 9 — Theme 2: Task 4 Instruction Card ─────────────────────────── | |
| def do_step9(): | |
| data = r("/reset", "POST", {"task_id": 4, "seed": 99}) | |
| obs_list = data.get("observations", []) | |
| if not obs_list: | |
| check_warn("No observations returned") | |
| return False | |
| obs = obs_list[0] | |
| card = obs.get("instruction_card") or {} | |
| card_text = card.get("text", "") | |
| targets = card.get("targets", {}) | |
| print(f" Task 4: Instruction Following") | |
| print(f" Objective: {card_text[:120]}...") | |
| if targets: | |
| print(f" Targets:") | |
| for k, v in targets.items(): | |
| print(f" {k}: {v}") | |
| return bool(card_text) | |
| # ── STEP 10 — Theme 4: /grade + Reward Decomposition ────────────────── | |
| def do_step10(): | |
| # First run a full episode (simplified: 10 steps) | |
| r("/reset", "POST", {"task_id": 3, "num_buildings": 3, "seed": 42}) | |
| for _ in range(10): | |
| action = { | |
| "hvac_power_level": 0.6, | |
| "thermal_charge_rate": 0.0, | |
| "batch_job_slot": 0, | |
| "load_shed_fraction": 0.0, | |
| "building_id": 0, | |
| } | |
| r("/step", "POST", [action]) | |
| grade_data = r("/grade") | |
| score = grade_data.get("score", "?") | |
| exploit = grade_data.get("exploit_detected", False) | |
| penalty = grade_data.get("penalty_applied", 0) | |
| sub = grade_data.get("sub_scores", {}) | |
| show_info("Episode score", f"{score} (0.0-1.0 clamped)" if score != "?" else "?") | |
| show_info("Exploit detected", str(exploit)) | |
| show_info("Penalty applied", str(penalty)) | |
| if sub: | |
| print(" Reward components:") | |
| for k, v in sub.items(): | |
| print(f" {k}: {round(v, 3)}") | |
| show_info("Task ID", str(grade_data.get("task_id", "?"))) | |
| return score not in ("?", None) and 0.0 < float(score) < 1.0 | |
| # ── Main ──────────────────────────────────────────────────────────────── | |
| def main(): | |
| print(bold("\nGridMind-RL — Full Unified Demo")) | |
| print(f" Environment: {ENV_URL}") | |
| print(f" Themes: Multi-Agent | Long-Horizon | World Modeling | Self-Improvement") | |
| results = [] | |
| results.append(step(1, "Health Check", "Foundation", do_step1)) | |
| results.append(step(2, "OpenEnv /info Metadata", "Foundation", do_step2)) | |
| results.append(step(3, "All 4 Tasks Available", "Foundation", do_step3)) | |
| results.append(step(4, "Multi-Agent Reset (3 Buildings)", "Theme 1: Multi-Agent", do_step4)) | |
| results.append(step(5, "Fleet-wide Feeder View", "Theme 1: Multi-Agent", do_step5)) | |
| results.append(step(6, "Coordinator Price Signals", "Theme 1: Multi-Agent", do_step6)) | |
| results.append(step(7, "World Modeling /simulate", "Theme 3: World Modeling", do_step7)) | |
| results.append(step(8, "Step + Fault Events", "Wild Card: Fault Resilience", do_step8)) | |
| results.append(step(9, "Task 4 Instruction Card", "Theme 2: Long-Horizon", do_step9)) | |
| results.append(step(10, "Episode /grade + Reward Decomposition", "Theme 4: Self-Improvement", do_step10)) | |
| sep = "=" * 62 | |
| print(f"\n{bold(sep)}") | |
| print(f" SUMMARY — All Themes Check") | |
| print(sep) | |
| passed = sum(1 for r in results if r) | |
| total = len(results) | |
| print(f" Passed: {passed}/{total}") | |
| if passed == total: | |
| print(f" [OK] ALL THEMES OPERATIONAL") | |
| else: | |
| failed = [i + 1 for i, r in enumerate(results) if not r] | |
| print(f" [FAIL] Failed steps: {failed}") | |
| print() | |
| if __name__ == "__main__": | |
| main() |