Spaces:
Sleeping
Sleeping
File size: 10,137 Bytes
c276e2c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | """
benchmark.py β Multi-Model Comparative Benchmark
==================================================
Runs multiple LLM models against all 3 task difficulties and produces
a formatted comparison table + JSON leaderboard.
Usage:
python examples/benchmark.py
python examples/benchmark.py --models gpt-4o-mini,llama-3.1-8b-instant --url http://localhost:8000
Requires: pip install requests tabulate
"""
import argparse
import json
import os
import sys
import time
from datetime import datetime
try:
import requests
except ImportError:
print("pip install requests"); sys.exit(1)
try:
from tabulate import tabulate
HAS_TABULATE = True
except ImportError:
HAS_TABULATE = False
# ββ Colors βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
G = "\033[92m"; Y = "\033[93m"; R = "\033[91m"; C = "\033[96m"
B = "\033[1m"; D = "\033[2m"; X = "\033[0m"
DEFAULT_URL = "https://leavin1611-logistics-hackathon-env.hf.space"
TASKS = ["TASK-EASY", "TASK-MEDIUM", "TASK-HARD"]
# Pre-defined action sequences for deterministic benchmarking
# (No LLM needed β pure environment stress test)
ACTION_SCRIPTS = {
"TASK-EASY": [
{"action_type": "get_network_status"},
{"action_type": "reroute_shipment", "shipment_id": "SHIP-001", "new_route": "R2", "new_carrier": "SpeedLane", "reason": "R1 heavy congestion"},
{"action_type": "communicate_eta", "shipment_id": "SHIP-001", "message": "We sincerely apologise for the delay to your vegetable shipment. Due to port congestion at JNPT, we have rerouted your cargo via Western Highway. Expected arrival by 4:00 PM."},
{"action_type": "end_turn"},
{"action_type": "reroute_shipment", "shipment_id": "SHIP-002", "new_route": "R2", "reason": "Clear backlog"},
{"action_type": "communicate_eta", "shipment_id": "SHIP-002", "message": "We apologise for the inconvenience. Your auto parts shipment has been rerouted to avoid JNPT congestion. New ETA: 5:30 PM today."},
{"action_type": "end_turn"},
{"action_type": "end_turn"},
],
"TASK-MEDIUM": [
{"action_type": "get_network_status"},
{"action_type": "set_priority", "priority_ids": ["SHIP-001", "SHIP-003"]},
{"action_type": "reroute_shipment", "shipment_id": "SHIP-001", "new_route": "R2", "new_carrier": "SpeedLane", "reason": "Reefer stuck on R1 due to carrier strike"},
{"action_type": "communicate_eta", "shipment_id": "SHIP-001", "message": "We sincerely apologise for the delay to your pharmaceutical shipment. Due to the carrier strike at JNPT, we have rerouted via Western Highway with SpeedLane. Expected arrival by 6:00 PM."},
{"action_type": "end_turn"},
{"action_type": "get_network_status"},
{"action_type": "reroute_shipment", "shipment_id": "SHIP-003", "new_route": "R2", "new_carrier": "IndiaFreight", "reason": "High-value server hardware needs fastest lane"},
{"action_type": "communicate_eta", "shipment_id": "SHIP-003", "message": "We regret the delay to your server hardware shipment. Due to customs blockage, we have arranged alternative carrier IndiaFreight. ETA: 8:00 PM today."},
{"action_type": "end_turn"},
{"action_type": "reroute_shipment", "shipment_id": "SHIP-004", "new_route": "R2", "reason": "Clear hazmat queue"},
{"action_type": "end_turn"},
{"action_type": "end_turn"},
{"action_type": "end_turn"},
],
"TASK-HARD": [
{"action_type": "get_network_status"},
{"action_type": "set_priority", "priority_ids": ["SHIP-001", "SHIP-002", "SHIP-006"]},
{"action_type": "reroute_shipment", "shipment_id": "SHIP-001", "new_route": "R2", "new_carrier": "SpeedLane", "reason": "COVID vaccines critical β BlueLine bankrupt"},
{"action_type": "communicate_eta", "shipment_id": "SHIP-001", "message": "URGENT: We deeply apologise for the severe delay to your COVID vaccine shipment. Due to JNPT closure and BlueLine bankruptcy, we have arranged emergency rerouting via Western Highway with SpeedLane. Expected delivery by 4:00 PM. Cold chain integrity is being actively monitored."},
{"action_type": "end_turn"},
{"action_type": "reroute_shipment", "shipment_id": "SHIP-002", "new_route": "R4", "new_carrier": "NorthStar", "reason": "Election ballots critical priority"},
{"action_type": "reroute_shipment", "shipment_id": "SHIP-003", "new_route": "R6", "new_carrier": "IndiaFreight", "reason": "Surgical equipment β bypass Chennai congestion"},
{"action_type": "communicate_eta", "shipment_id": "SHIP-002", "message": "We apologise for the delay to your election ballot shipment. Due to carrier suspension, we have arranged NorthStar via Yamuna Expressway. ETA: 3:00 PM."},
{"action_type": "end_turn"},
{"action_type": "reroute_shipment", "shipment_id": "SHIP-004", "new_route": "R2", "reason": "Hazmat clear from blocked R1"},
{"action_type": "reroute_shipment", "shipment_id": "SHIP-006", "new_route": "R6", "new_carrier": "CoastCargo", "reason": "Blood bank reefer failure bypass"},
{"action_type": "communicate_eta", "shipment_id": "SHIP-006", "message": "We sincerely regret the delay to your blood bank supplies. Due to reefer failure on the primary route, we have rerouted via Bangalore Alt Bypass. Expected arrival by 7:00 PM."},
{"action_type": "end_turn"},
{"action_type": "communicate_eta", "shipment_id": "SHIP-003", "message": "We apologise for the delay to your surgical equipment. Due to Chennai port capacity restrictions, we have arranged alternative routing. ETA: 9:00 PM today."},
{"action_type": "end_turn"},
{"action_type": "end_turn"},
{"action_type": "end_turn"},
{"action_type": "end_turn"},
],
}
def run_scripted_episode(base_url: str, task_id: str) -> dict:
"""Run a deterministic scripted episode and return metrics."""
sess = requests.Session()
sess.headers["Content-Type"] = "application/json"
t0 = time.time()
# Reset
r = sess.post(f"{base_url}/reset", json={"task_id": task_id}, timeout=15)
if not r.ok:
return {"task": task_id, "error": f"Reset failed: {r.status_code}"}
steps = 0
total_reward = 0.0
actions_taken = []
for action in ACTION_SCRIPTS.get(task_id, []):
r = sess.post(f"{base_url}/step", json={"action": action}, timeout=15)
steps += 1
if not r.ok:
continue
data = r.json()
reward = data.get("reward", 0) or 0
total_reward += reward
actions_taken.append({
"step": steps,
"action": action["action_type"],
"reward": round(reward, 4),
})
if data.get("done"):
break
elapsed = time.time() - t0
return {
"task": task_id,
"steps": steps,
"total_reward": round(total_reward, 4),
"normalized_score": round(min(max(total_reward / 5.0, 0.001), 0.999), 4),
"elapsed_s": round(elapsed, 2),
"actions": actions_taken,
"timestamp": datetime.utcnow().isoformat() + "Z",
}
def main():
parser = argparse.ArgumentParser(description="Logistics RL Benchmark")
parser.add_argument("--url", default=DEFAULT_URL, help="API base URL")
parser.add_argument("--output", default="outputs/benchmark_results.json", help="Output file")
args = parser.parse_args()
base = args.url.rstrip("/")
print(f"\n{B}{'='*65}{X}")
print(f"{B}π Logistics Shipment RL β Benchmark Suite{X}")
print(f"{D} API: {base}{X}")
print(f"{B}{'='*65}{X}\n")
# Ping
try:
r = requests.get(f"{base}/schema", timeout=8)
assert r.ok
print(f" {G}β API Online{X}\n")
except Exception:
print(f" {R}β API Offline β is the space sleeping?{X}\n")
return
results = []
for task in TASKS:
print(f" {C}βΆ Running {task}β¦{X}", end="", flush=True)
result = run_scripted_episode(base, task)
results.append(result)
if "error" in result:
print(f" {R}β {result['error']}{X}")
else:
color = G if result["total_reward"] > 1.0 else Y if result["total_reward"] > 0.3 else R
print(f"\r {G}β{X} {task:<14} "
f"reward={color}{result['total_reward']:+.4f}{X} "
f"score={result['normalized_score']:.4f} "
f"steps={result['steps']} "
f"time={result['elapsed_s']}s")
# Summary table
print(f"\n{B}{'β'*65}{X}")
if HAS_TABULATE:
table = [[r["task"], f"{r.get('total_reward',0):+.4f}",
f"{r.get('normalized_score',0):.4f}",
r.get("steps", "?"), f"{r.get('elapsed_s', 0):.1f}s"]
for r in results]
print(tabulate(table, headers=["Task", "Reward", "Score", "Steps", "Time"],
tablefmt="rounded_outline"))
avg_score = sum(r.get("normalized_score", 0) for r in results) / len(results) if results else 0
avg_reward = sum(r.get("total_reward", 0) for r in results) / len(results) if results else 0
print(f"\n {B}Average Score: {avg_score:.4f}{X}")
print(f" {B}Average Reward: {avg_reward:+.4f}{X}")
print(f"{B}{'β'*65}{X}\n")
# Save
os.makedirs(os.path.dirname(args.output) if os.path.dirname(args.output) else "outputs", exist_ok=True)
output = {
"benchmark": "logistics_shipment_rl",
"api_url": base,
"timestamp": datetime.utcnow().isoformat() + "Z",
"results": results,
"summary": {
"avg_score": avg_score,
"avg_reward": avg_reward,
"tasks_run": len(results),
}
}
with open(args.output, "w") as f:
json.dump(output, f, indent=2)
print(f" {D}Results saved to {args.output}{X}\n")
if __name__ == "__main__":
main()
|