New_gpu_space / training /evaluate.py
hiitsesh's picture
Deploy ReleaseOps Arena GPU Space
9aa9d87
Raw
History Blame Contribute Delete
5.49 kB
import json
from pathlib import Path
import sys
REPO_ROOT = Path(__file__).resolve().parents[1]
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
from releaseops_arena.baselines import naive_baseline, rule_baseline
from releaseops_arena.tool_env import ReleaseOpsToolEnv
def load_jsonl(path: Path) -> list[dict]:
if not path.exists():
return []
with open(path, "r", encoding="utf-8") as handle:
return [json.loads(line) for line in handle if line.strip()]
def summarize_rollout(env, reward, initial_budget):
reason = env.state.get("terminal_reason")
return {
"reward": reward,
"safe_ship": 1 if reason == "safe_ship" else 0,
"unsafe_ship": 1 if reason == "unsafe_ship" else 0,
"missed_deadline": 1 if reason == "missed_deadline" else 0,
"false_blocks": env.metrics.get("false_blocks", 0),
"true_blocks": env.metrics.get("true_blocks", 0),
"invalid_actions": env.metrics.get("invalid_actions", 0),
"budget_spent": initial_budget - env.state["review_budget_remaining"],
}
def aggregate(rows):
if not rows:
return {
"avg_reward": 0.0,
"safe_ship_rate": 0.0,
"unsafe_ship_rate": 0.0,
"missed_deadline_rate": 0.0,
"avg_false_blocks": 0.0,
"avg_true_blocks": 0.0,
"avg_invalid_actions": 0.0,
"avg_budget_spent": 0.0,
}
return {
"avg_reward": round(sum(row["reward"] for row in rows) / len(rows), 3),
"safe_ship_rate": round(sum(row["safe_ship"] for row in rows) / len(rows), 3),
"unsafe_ship_rate": round(sum(row["unsafe_ship"] for row in rows) / len(rows), 3),
"missed_deadline_rate": round(sum(row["missed_deadline"] for row in rows) / len(rows), 3),
"avg_false_blocks": round(sum(row["false_blocks"] for row in rows) / len(rows), 3),
"avg_true_blocks": round(sum(row["true_blocks"] for row in rows) / len(rows), 3),
"avg_invalid_actions": round(sum(row["invalid_actions"] for row in rows) / len(rows), 3),
"avg_budget_spent": round(sum(row["budget_spent"] for row in rows) / len(rows), 3),
}
def run_slice(rows: list[dict]):
naive_rows = []
rule_rows = []
phase_aware_rule_rows = []
for kwargs in rows:
env_naive = ReleaseOpsToolEnv()
env_naive.reset(**kwargs)
naive_initial_budget = env_naive.state["review_budget_remaining"]
naive_reward = naive_baseline(env_naive)
naive_rows.append(summarize_rollout(env_naive, naive_reward, naive_initial_budget))
env_rule = ReleaseOpsToolEnv()
env_rule.reset(**kwargs)
rule_initial_budget = env_rule.state["review_budget_remaining"]
rule_reward = rule_baseline(env_rule)
rule_rows.append(summarize_rollout(env_rule, rule_reward, rule_initial_budget))
# Imported here to keep baseline selection explicit in evaluation output.
from releaseops_arena.baselines import phase_aware_rule_baseline
env_phase = ReleaseOpsToolEnv()
env_phase.reset(**kwargs)
phase_initial_budget = env_phase.state["review_budget_remaining"]
phase_reward = phase_aware_rule_baseline(env_phase)
phase_aware_rule_rows.append(
summarize_rollout(env_phase, phase_reward, phase_initial_budget)
)
return {
"count": len(rows),
"naive": aggregate(naive_rows),
"rule": aggregate(rule_rows),
"phase_aware_rule": aggregate(phase_aware_rule_rows),
}
def load_eval_slices():
seen_path = Path("training/data/eval_seen.jsonl")
unseen_path = Path("training/data/eval_unseen.jsonl")
seen_rows = load_jsonl(seen_path)
unseen_rows = load_jsonl(unseen_path)
if seen_rows or unseen_rows:
return seen_rows, unseen_rows
# Backward-compatible fallback: split eval.jsonl by family.
all_rows = load_jsonl(Path("training/data/eval.jsonl"))
unseen_family = "release_manager_ship_before_evidence"
seen_rows = [row for row in all_rows if row.get("family") != unseen_family]
unseen_rows = [row for row in all_rows if row.get("family") == unseen_family]
return seen_rows, unseen_rows
def run_eval():
print("Evaluating baselines on seen and unseen slices...")
seen_rows, unseen_rows = load_eval_slices()
all_rows = seen_rows + unseen_rows
seen_results = run_slice(seen_rows)
unseen_results = run_slice(unseen_rows)
overall_results = run_slice(all_rows)
results = {
"seen": seen_results,
"unseen": unseen_results,
"overall": overall_results,
# Legacy keys retained for scripts that expect top-level aggregates.
"naive": overall_results["naive"],
"rule": overall_results["rule"],
"naive_avg": overall_results["naive"]["avg_reward"],
"rule_avg": overall_results["rule"]["avg_reward"],
}
print("Seen slice:")
print(json.dumps(seen_results, indent=2))
print("Unseen slice:")
print(json.dumps(unseen_results, indent=2))
print("Overall:")
print(json.dumps(overall_results, indent=2))
output_path = Path("outputs/eval_results.json")
output_path.parent.mkdir(parents=True, exist_ok=True)
with open(output_path, "w", encoding="utf-8") as handle:
json.dump(results, handle, indent=2)
print(f"Wrote evaluation results to {output_path}")
if __name__ == "__main__":
run_eval()