File size: 6,603 Bytes
f27fd6a 1c03487 f27fd6a 1c03487 74f461a 1c03487 9b98195 1422c62 3e4a196 219555f 1422c62 1c03487 f27fd6a 9b98195 f27fd6a 1c03487 9b98195 f27fd6a 9b98195 f27fd6a 9b98195 1c03487 f27fd6a 1c03487 f27fd6a 1c03487 f27fd6a 1c03487 1422c62 1c03487 1422c62 1c03487 1422c62 1c03487 1422c62 1c03487 f27fd6a 1c03487 9b98195 f27fd6a 9b98195 1c03487 1f18d1f 1c03487 f27fd6a 0092607 1c03487 219555f 1c03487 f27fd6a 1c03487 f27fd6a 1c03487 9b98195 1c03487 f27fd6a 1c03487 0092607 | 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 | import os, sys, time
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from typing import List, Tuple, Any
from openai import OpenAI
from client import CascadeContainmentEnv
from models import ContainmentAction, CityObservation
from baseline.policy import get_client, build_prompt, call_llm, parse_action, build_prompt_with_memory
from core.trajectory import EpisodicMemory
from core.reward import normalise_score
from core.policy_update import compute_advantage, update_memory
import requests as http_requests
N_ROLLOUTS = {
"easy": 2,
"medium": 3,
"hard": 3,
}
EARLY_STOP_THRESHOLD = {
"easy": 0.85,
"medium": 0.72,
"hard": 0.65,
}
def run_rollout(
env: Any, task_name: str, client: OpenAI,
memory: EpisodicMemory, verbose: bool = True,
) -> Tuple[float, int, List[dict]]:
result = env.reset(task_name=task_name)
obs = result.observation
done = result.done
total_reward = 0.0
step = 0
trajectory = []
while not done:
prompt = build_prompt_with_memory(obs, memory)
response = call_llm(prompt, client)
action = parse_action(response, len(obs.districts))
try:
result = env.step(action)
except Exception as e:
if "close frame" in str(e).lower() or "websocket" in str(e).lower():
if verbose:
print(f" β WebSocket dropped at step {step+1}, ending early")
break
raise
next_obs = result.observation
reward = result.reward or 0.0
done = result.done
total_reward += reward
step += 1
trajectory.append({"obs": obs, "action": action, "reward": reward})
if verbose:
print(
f" step {step:2d}: {action.action_type:8} "
f"β district {action.district_id} | reward: {reward:+.4f}"
)
obs = next_obs
if done:
break
return total_reward, step, trajectory
def run_task_grpo(
env: Any, task_name: str, client: OpenAI,
base_url: str, verbose: bool = True,
) -> float:
n = N_ROLLOUTS[task_name]
if verbose:
print(f"\n Task: {task_name.upper()} | {n} rollouts")
print(f" {'β'*44}")
memory = EpisodicMemory(max_size=20)
rollouts = []
for i in range(n):
if verbose:
label = "base prompt" if len(memory) == 0 else f"memory: {len(memory)} entries"
print(f"\n Rollout {i+1}/{n} [{label}]")
total_reward, steps, trajectory = run_rollout(
env, task_name, client, memory, verbose
)
num_districts = {"easy": 2, "medium": 4, "hard": 6}.get(task_name, 2)
try:
grade_resp = http_requests.get(base_url.rstrip('/') + '/grade', timeout=10)
if grade_resp.status_code == 200:
data = grade_resp.json()
score = data["final_score"]
if verbose:
print(
f" β Grader: containment={data['containment_score']:.3f} "
f"hospital={data['hospital_score']:.3f} "
f"efficiency={data['efficiency_score']:.3f} "
f"speed={data['speed_score']:.3f}"
)
else:
score = normalise_score(total_reward, steps, num_districts)
except Exception:
score = normalise_score(total_reward, steps, num_districts)
rollouts.append((total_reward, steps, score))
if verbose:
print(f" β Reward: {total_reward:+.4f} | Score: {score:.4f}")
completed_rewards = [r[0] for r in rollouts]
advantage = compute_advantage(total_reward, completed_rewards[:-1])
stored = update_memory(memory, trajectory, advantage)
if verbose:
mean = sum(completed_rewards[:-1]) / max(len(completed_rewards) - 1, 1) \
if len(completed_rewards) > 1 else total_reward
print(
f" β Advantage: {advantage:+.4f} | "
+ (f"β Stored {stored} steps" if stored > 0 else "β Suppressed")
)
if score >= EARLY_STOP_THRESHOLD[task_name]:
if verbose:
print(f" β Early stop: score {score:.4f} β₯ threshold {EARLY_STOP_THRESHOLD[task_name]:.2f}")
break
all_rewards = [r[0] for r in rollouts]
mean_reward = sum(all_rewards) / len(all_rewards)
best_score = max(rollouts, key=lambda x: x[2])[2]
if verbose:
print(f"\n Rewards: {[round(r, 4) for r in all_rewards]}")
print(f" Mean: {mean_reward:+.4f}")
print(f" Advantages: {[round(r - mean_reward, 4) for r in all_rewards]}")
print(f" Best score: {best_score:.4f}")
return best_score
def run_evaluation(base_url: str = "http://localhost:7860", verbose: bool = True) -> dict:
if verbose:
print("\n" + "="*52)
print(" CASCADE CONTAINMENT β GRPO EVALUATION")
print("="*52)
print(f" Rollouts per task: {N_ROLLOUTS}")
print(f" Learning: Episodic memory + advantage gating")
client = get_client()
scores = {}
start = time.time()
with CascadeContainmentEnv(base_url=base_url).sync() as env:
for task_name in ["easy", "medium", "hard"]:
try:
score = run_task_grpo(env, task_name, client, base_url, verbose)
scores[task_name] = score
if verbose:
print(f"\n β {task_name.upper()} final score: {score:.4f}")
except Exception as e:
scores[task_name] = 0.0
if verbose:
print(f" β {task_name.upper()} failed: {e}")
import traceback
traceback.print_exc()
scores["average"] = round(
sum(v for k, v in scores.items() if k != "average") / 3, 4
)
elapsed = round(time.time() - start, 1)
if verbose:
print("\n" + "="*52)
print(" FINAL SCORES")
print("="*52)
print(f" Easy: {scores.get('easy', 0.0):.4f}")
print(f" Medium: {scores.get('medium', 0.0):.4f}")
print(f" Hard: {scores.get('hard', 0.0):.4f}")
print(f" {'β'*32}")
print(f" Average: {scores.get('average', 0.0):.4f}")
print(f" Time: {elapsed}s")
print("="*52 + "\n")
return scores
|