Delete eval/benchmark_v9_results.json, eval/bert_vs_xgboost_results.json/*, eval/e2e_standalone_results.json, training/e2e_*.py
Browse files- training/e2e_benchmark.py +0 -429
- training/e2e_benchmark_v2.py +0 -355
- training/e2e_standalone.py +0 -397
- training/e2e_v2_fixed.py +0 -291
training/e2e_benchmark.py
DELETED
|
@@ -1,429 +0,0 @@
|
|
| 1 |
-
"""End-to-End ACO Evaluation on SWE-bench Agent Traces.
|
| 2 |
-
|
| 3 |
-
Simulates a coding agent running on SWE-bench tasks, with the ACO optimizer
|
| 4 |
-
intercepting every decision point. Uses SWE-Router execution data for
|
| 5 |
-
ground-truth model costs and outcomes.
|
| 6 |
-
|
| 7 |
-
Measures:
|
| 8 |
-
- Total cost with optimizer vs without
|
| 9 |
-
- Per-module cost impact (ablation study)
|
| 10 |
-
- Which optimizer decisions actually saved money
|
| 11 |
-
- Which optimizer decisions wasted money
|
| 12 |
-
|
| 13 |
-
Architecture:
|
| 14 |
-
For each SWE-bench task:
|
| 15 |
-
1. ACO predicts task type, difficulty, optimal tier
|
| 16 |
-
2. ACO routes to a model (cascade)
|
| 17 |
-
3. Agent "runs" using SWE-Router ground truth (did it resolve? how much did it cost?)
|
| 18 |
-
4. ACO decides: verify? retry? escalate? stop?
|
| 19 |
-
5. Measure: actual cost path vs baseline
|
| 20 |
-
|
| 21 |
-
The "agent" is simulated: we use SWE-Router traces to know what each model
|
| 22 |
-
actually did (cost, api_calls, success). ACO makes the decisions, we look up
|
| 23 |
-
the real outcomes, ACO reacts, etc.
|
| 24 |
-
"""
|
| 25 |
-
import json, sys, os, copy, random, hashlib
|
| 26 |
-
from collections import defaultdict
|
| 27 |
-
from typing import Dict, List, Optional, Tuple
|
| 28 |
-
import numpy as np
|
| 29 |
-
|
| 30 |
-
# ── Load ACO package ────────────────────────────────────────────
|
| 31 |
-
sys.path.insert(0, "/app")
|
| 32 |
-
from aco.optimizer import ACOOptimizer
|
| 33 |
-
from aco.config import ACOConfig, RoutingPolicy
|
| 34 |
-
from aco.router import RoutingDecision
|
| 35 |
-
from aco.router_v10 import V10Router
|
| 36 |
-
from datasets import load_dataset
|
| 37 |
-
|
| 38 |
-
REPO = "narcolepticchicken/agent-cost-optimizer"
|
| 39 |
-
|
| 40 |
-
print("=" * 70)
|
| 41 |
-
print("ACO END-TO-END BENCHMARK ON SWE-BENCH AGENT TRACES")
|
| 42 |
-
print("=" * 70)
|
| 43 |
-
|
| 44 |
-
# ── 1. Load SWE-Router ground truth ─────────────────────────────
|
| 45 |
-
print("\n[1] Loading SWE-Router execution data (8 models × 500 tasks)...")
|
| 46 |
-
|
| 47 |
-
MODELS = [
|
| 48 |
-
"deepseek-v4-flash", "gpt-5-nano", "gpt-5-mini", "deepseek-v3.2",
|
| 49 |
-
"gemini-2.5-pro", "claude-opus-4.7", "gpt-5.2", "gemini-3-pro",
|
| 50 |
-
]
|
| 51 |
-
|
| 52 |
-
TIER = {
|
| 53 |
-
"deepseek-v4-flash": 1, "gpt-5-nano": 1,
|
| 54 |
-
"gpt-5-mini": 2, "deepseek-v3.2": 2,
|
| 55 |
-
"gemini-2.5-pro": 3,
|
| 56 |
-
"claude-opus-4.7": 4, "gpt-5.2": 4,
|
| 57 |
-
"gemini-3-pro": 5,
|
| 58 |
-
}
|
| 59 |
-
|
| 60 |
-
TIER_MODEL_MAP = {
|
| 61 |
-
1: "deepseek-v4-flash", 2: "gpt-5-mini",
|
| 62 |
-
3: "gemini-2.5-pro", 4: "claude-opus-4.7", 5: "gemini-3-pro",
|
| 63 |
-
}
|
| 64 |
-
|
| 65 |
-
# Load all SWE-Router traces
|
| 66 |
-
traces = defaultdict(dict)
|
| 67 |
-
for model_name in MODELS:
|
| 68 |
-
ds = load_dataset(f"SWE-Router/swebench-verified-{model_name}", split="test")
|
| 69 |
-
for row in ds:
|
| 70 |
-
iid = row["instance_id"]
|
| 71 |
-
traces[iid][model_name] = {
|
| 72 |
-
"resolved": row["resolved"],
|
| 73 |
-
"cost": float(row["instance_cost"]),
|
| 74 |
-
"api_calls": int(row["api_calls"]),
|
| 75 |
-
"problem": row.get("problem_statement", row.get("problem", "")),
|
| 76 |
-
}
|
| 77 |
-
print(f" Loaded {len(traces)} tasks × {len(MODELS)} models = {len(traces) * len(MODELS)} outcomes")
|
| 78 |
-
|
| 79 |
-
# ── 2. Set up ACO optimizer ─────────────────────────────────────
|
| 80 |
-
print("\n[2] Setting up ACO optimizer...")
|
| 81 |
-
|
| 82 |
-
config = ACOConfig()
|
| 83 |
-
config.routing_policy = RoutingPolicy(
|
| 84 |
-
routing_mode="cascade",
|
| 85 |
-
feedback_escalation=True,
|
| 86 |
-
max_retries=2,
|
| 87 |
-
max_cost_per_task=1.0,
|
| 88 |
-
safety_threshold=0.5,
|
| 89 |
-
)
|
| 90 |
-
config.enable_meta_tools = False # no trace data to mine yet
|
| 91 |
-
config.enable_doom_detector = True
|
| 92 |
-
config.router_model_path = "/tmp/router_models/router_bundle_v10_fixed.pkl"
|
| 93 |
-
|
| 94 |
-
# Download the router bundle
|
| 95 |
-
from huggingface_hub import hf_hub_download
|
| 96 |
-
try:
|
| 97 |
-
bundle_path = hf_hub_download(
|
| 98 |
-
repo_id=REPO,
|
| 99 |
-
filename="router_models/router_bundle_v10_fixed.pkl",
|
| 100 |
-
cache_dir="/tmp/router_models"
|
| 101 |
-
)
|
| 102 |
-
config.router_model_path = bundle_path
|
| 103 |
-
print(f" Router model: {bundle_path}")
|
| 104 |
-
except Exception as e:
|
| 105 |
-
print(f" Router bundle download failed: {e}")
|
| 106 |
-
config.router_model_path = None
|
| 107 |
-
|
| 108 |
-
optimizer = ACOOptimizer(config)
|
| 109 |
-
|
| 110 |
-
# ── 3. Simulate agent runs ──────────────────────────────────────
|
| 111 |
-
print("\n[3] Running end-to-end simulation...")
|
| 112 |
-
|
| 113 |
-
def simulate_agent_run(task_id: str, task_problem: str, model_name: str,
|
| 114 |
-
optimizer: ACOOptimizer) -> Dict:
|
| 115 |
-
"""Simulate one agent run with ACO intercepting decisions."""
|
| 116 |
-
result = {
|
| 117 |
-
"task_id": task_id,
|
| 118 |
-
"problem": task_problem[:200],
|
| 119 |
-
"steps": [],
|
| 120 |
-
"total_cost": 0.0,
|
| 121 |
-
"total_api_calls": 0,
|
| 122 |
-
"success": False,
|
| 123 |
-
"routed_model": model_name,
|
| 124 |
-
"routed_tier": TIER.get(model_name, 4),
|
| 125 |
-
"escalated": False,
|
| 126 |
-
"terminated_early": False,
|
| 127 |
-
"verifier_calls": 0,
|
| 128 |
-
"tool_gate_skips": 0,
|
| 129 |
-
"doom_stops": 0,
|
| 130 |
-
}
|
| 131 |
-
|
| 132 |
-
# Step 1: ACO routes to a model
|
| 133 |
-
start = optimizer.start_run(task_problem)
|
| 134 |
-
routed_model = start["routing"]["model_id"]
|
| 135 |
-
routed_tier = start["routing"]["tier"]
|
| 136 |
-
|
| 137 |
-
result["routed_model"] = routed_model
|
| 138 |
-
result["routed_tier"] = routed_tier
|
| 139 |
-
result["aco_start"] = start
|
| 140 |
-
|
| 141 |
-
# Step 2: Simulate agent execution with this model
|
| 142 |
-
model_trace = traces[task_id].get(routed_model, {})
|
| 143 |
-
resolved = model_trace.get("resolved", False)
|
| 144 |
-
cost = model_trace.get("cost", 0.30)
|
| 145 |
-
api_calls = model_trace.get("api_calls", 10)
|
| 146 |
-
|
| 147 |
-
# Simulate 3 agent steps (model call + tool calls per step)
|
| 148 |
-
step_names = ["analyze_problem", "generate_patch", "verify_patch"]
|
| 149 |
-
for i, step_name in enumerate(step_names):
|
| 150 |
-
step_cost = cost / len(step_names)
|
| 151 |
-
step_api = max(1, api_calls // len(step_names))
|
| 152 |
-
|
| 153 |
-
# ACO: gate tools (simulate 2-3 tool calls per step)
|
| 154 |
-
tool_decisions = []
|
| 155 |
-
for tool_idx in range(random.randint(2, 4)):
|
| 156 |
-
td = optimizer.gate_tool(f"tool_{tool_idx}", {"step": step_name})
|
| 157 |
-
tool_decisions.append(td)
|
| 158 |
-
if td.action == "skip":
|
| 159 |
-
result["tool_gate_skips"] += 1
|
| 160 |
-
|
| 161 |
-
# ACO: should we verify?
|
| 162 |
-
is_final = (i == len(step_names) - 1)
|
| 163 |
-
vd = optimizer.should_verify(
|
| 164 |
-
is_irreversible=is_final,
|
| 165 |
-
has_prior_failures=(not resolved and i > 0),
|
| 166 |
-
)
|
| 167 |
-
if vd.should_verify:
|
| 168 |
-
result["verifier_calls"] += 1
|
| 169 |
-
# Verifier adds ~20% to cost
|
| 170 |
-
step_cost *= 1.2
|
| 171 |
-
|
| 172 |
-
# ACO: check for doom
|
| 173 |
-
total_so_far = result["total_cost"] + step_cost
|
| 174 |
-
doom = optimizer.check_doom(current_cost=total_so_far)
|
| 175 |
-
|
| 176 |
-
if doom.doomed:
|
| 177 |
-
result["terminated_early"] = True
|
| 178 |
-
result["doom_stops"] += 1
|
| 179 |
-
result["doom_reason"] = doom.reasoning
|
| 180 |
-
break
|
| 181 |
-
|
| 182 |
-
# Record step
|
| 183 |
-
optimizer.record_step(
|
| 184 |
-
model_call={
|
| 185 |
-
"model_id": routed_model,
|
| 186 |
-
"input_tokens": 5000 // len(step_names),
|
| 187 |
-
"output_tokens": 500,
|
| 188 |
-
"cost": step_cost,
|
| 189 |
-
"latency_ms": 2000,
|
| 190 |
-
},
|
| 191 |
-
tool_calls=[{
|
| 192 |
-
"tool_name": td.tool_name,
|
| 193 |
-
"args": td.args,
|
| 194 |
-
"success": td.action != "skip",
|
| 195 |
-
"cost": td.estimated_cost,
|
| 196 |
-
"latency_ms": td.estimated_latency,
|
| 197 |
-
} for td in tool_decisions],
|
| 198 |
-
context_size=5000 + (i * 1000),
|
| 199 |
-
retry_num=0,
|
| 200 |
-
)
|
| 201 |
-
|
| 202 |
-
result["total_cost"] += step_cost
|
| 203 |
-
result["total_api_calls"] += step_api
|
| 204 |
-
result["steps"].append(step_name)
|
| 205 |
-
|
| 206 |
-
# If escalated, try next tier
|
| 207 |
-
if not resolved and not result["terminated_early"]:
|
| 208 |
-
for escalate_tier in range(routed_tier + 1, 6):
|
| 209 |
-
escalate_model = TIER_MODEL_MAP[escalate_tier]
|
| 210 |
-
esc_trace = traces[task_id].get(escalate_model, {})
|
| 211 |
-
if esc_trace.get("resolved", False):
|
| 212 |
-
result["total_cost"] += esc_trace.get("cost", 0.30)
|
| 213 |
-
result["escalated"] = True
|
| 214 |
-
result["final_model_used"] = escalate_model
|
| 215 |
-
resolved = True
|
| 216 |
-
break
|
| 217 |
-
else:
|
| 218 |
-
# Escalation failed, add cost and continue
|
| 219 |
-
result["total_cost"] += esc_trace.get("cost", 0.30)
|
| 220 |
-
|
| 221 |
-
result["success"] = resolved
|
| 222 |
-
|
| 223 |
-
# End run
|
| 224 |
-
outcome = "completed" if resolved else ("terminated" if result["terminated_early"] else "abandoned")
|
| 225 |
-
trace = optimizer.end_run(
|
| 226 |
-
success=resolved,
|
| 227 |
-
outcome=outcome,
|
| 228 |
-
artifacts=["patch.diff"] if resolved else [],
|
| 229 |
-
failure_tags=[] if resolved else ["not_resolved"],
|
| 230 |
-
)
|
| 231 |
-
|
| 232 |
-
return result
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
def compute_baseline_cost(task_id: str) -> Dict:
|
| 236 |
-
"""Get baseline metrics for a task."""
|
| 237 |
-
best = None
|
| 238 |
-
for model_name, mt in traces[task_id].items():
|
| 239 |
-
# Frontier: claude-opus-4.7
|
| 240 |
-
if model_name == "claude-opus-4.7":
|
| 241 |
-
frontier = mt
|
| 242 |
-
# Cheapest that solves it
|
| 243 |
-
if mt["resolved"]:
|
| 244 |
-
if best is None or mt["cost"] < best["cost"]:
|
| 245 |
-
best = mt
|
| 246 |
-
|
| 247 |
-
return {
|
| 248 |
-
"frontier_cost": traces[task_id].get("claude-opus-4.7", {}).get("cost", 0.317),
|
| 249 |
-
"frontier_resolved": traces[task_id].get("claude-opus-4.7", {}).get("resolved", False),
|
| 250 |
-
"cheapest_success_cost": best["cost"] if best else None,
|
| 251 |
-
"cheapest_resolved": best is not None,
|
| 252 |
-
"always_cheap_cost": traces[task_id].get("deepseek-v4-flash", {}).get("cost", 0.014),
|
| 253 |
-
"always_cheap_resolved": traces[task_id].get("deepseek-v4-flash", {}).get("resolved", False),
|
| 254 |
-
}
|
| 255 |
-
|
| 256 |
-
# Run on all 500 tasks
|
| 257 |
-
all_results = []
|
| 258 |
-
for idx, (task_id, task_traces) in enumerate(traces.items()):
|
| 259 |
-
problem = next(iter(task_traces.values()))["problem"]
|
| 260 |
-
result = simulate_agent_run(task_id, problem, "deepseek-v4-flash", optimizer)
|
| 261 |
-
baseline = compute_baseline_cost(task_id)
|
| 262 |
-
result["baseline"] = baseline
|
| 263 |
-
all_results.append(result)
|
| 264 |
-
|
| 265 |
-
if (idx + 1) % 100 == 0:
|
| 266 |
-
n = idx + 1
|
| 267 |
-
resolved = sum(1 for r in all_results if r["success"])
|
| 268 |
-
total_cost = sum(r["total_cost"] for r in all_results)
|
| 269 |
-
print(f" [{n}/500] resolved={resolved} ({resolved/n*100:.1f}%), "
|
| 270 |
-
f"avg_cost=${total_cost/n:.4f}")
|
| 271 |
-
|
| 272 |
-
# ── 4. Compute metrics ──────────────────────────────────────────
|
| 273 |
-
print(f"\n[4] Computing metrics...")
|
| 274 |
-
|
| 275 |
-
n = len(all_results)
|
| 276 |
-
resolved = sum(1 for r in all_results if r["success"])
|
| 277 |
-
total_cost = sum(r["total_cost"] for r in all_results)
|
| 278 |
-
baseline_frontier_cost = sum(r["baseline"]["frontier_cost"] for r in all_results)
|
| 279 |
-
baseline_cheap_cost = sum(r["baseline"]["always_cheap_cost"] for r in all_results)
|
| 280 |
-
escalated = sum(1 for r in all_results if r["escalated"])
|
| 281 |
-
terminated = sum(1 for r in all_results if r["terminated_early"])
|
| 282 |
-
verifier_calls = sum(r["verifier_calls"] for r in all_results)
|
| 283 |
-
tool_skips = sum(r["tool_gate_skips"] for r in all_results)
|
| 284 |
-
|
| 285 |
-
# Baseline metrics
|
| 286 |
-
frontier_resolved = sum(1 for r in all_results if r["baseline"]["frontier_resolved"])
|
| 287 |
-
cheap_resolved = sum(1 for r in all_results if r["baseline"]["always_cheap_resolved"])
|
| 288 |
-
|
| 289 |
-
print(f"""
|
| 290 |
-
{'='*70}
|
| 291 |
-
ACO END-TO-END RESULTS (500 SWE-bench tasks)
|
| 292 |
-
{'='*70}
|
| 293 |
-
|
| 294 |
-
TASKS: {n}
|
| 295 |
-
|
| 296 |
-
{'Policy':<25} {'Resolved':>10} {'Rate':>10} {'TotalCost':>12} {'AvgCost':>10} {'CostRed':>10}
|
| 297 |
-
{'-'*77}
|
| 298 |
-
{'ACO Optimized':<25} {resolved:>10} {resolved/n*100:>9.1f}% ${total_cost:>10.2f} ${total_cost/n:>9.4f} {(1-total_cost/baseline_frontier_cost)*100:>9.1f}%
|
| 299 |
-
{'Frontier (always)':<25} {frontier_resolved:>10} {frontier_resolved/n*100:>9.1f}% ${baseline_frontier_cost:>10.2f} ${baseline_frontier_cost/n:>9.4f} {'--':>10}
|
| 300 |
-
{'Always Cheap':<25} {cheap_resolved:>10} {cheap_resolved/n*100:>9.1f}% ${baseline_cheap_cost:>10.2f} ${baseline_cheap_cost/n:>9.4f} {(1-baseline_cheap_cost/baseline_frontier_cost)*100:>9.1f}%
|
| 301 |
-
|
| 302 |
-
PER-MODULE IMPACT:
|
| 303 |
-
Escalations triggered: {escalated} ({escalated/n*100:.1f}%)
|
| 304 |
-
Early terminations: {terminated} ({terminated/n*100:.1f}%)
|
| 305 |
-
Verifier calls: {verifier_calls} ({verifier_calls/n*100:.1f} avg/task)
|
| 306 |
-
Tool gate skips: {tool_skips} ({tool_skips/n*100:.1f} avg/task)
|
| 307 |
-
|
| 308 |
-
COST BREAKDOWN:
|
| 309 |
-
Avg cost per run: ${total_cost/n:.4f}
|
| 310 |
-
Avg cost per resolved: ${total_cost/max(resolved,1):.4f}
|
| 311 |
-
Waste on failed runs: ${sum(r['total_cost'] for r in all_results if not r['success']):.2f}
|
| 312 |
-
""")
|
| 313 |
-
|
| 314 |
-
# ── 5. Ablation study ───────────────────────────────────────────
|
| 315 |
-
print(f"[5] Running ablation study...")
|
| 316 |
-
|
| 317 |
-
def run_ablation(disable_modules: List[str]) -> Dict:
|
| 318 |
-
"""Run with specific modules disabled."""
|
| 319 |
-
ab_config = ACOConfig()
|
| 320 |
-
ab_config.routing_policy = RoutingPolicy(
|
| 321 |
-
routing_mode="cascade" if "router" not in disable_modules else "frontier",
|
| 322 |
-
feedback_escalation="feedback_escalation" not in disable_modules,
|
| 323 |
-
max_retries=2,
|
| 324 |
-
max_cost_per_task=1.0,
|
| 325 |
-
safety_threshold=0.5,
|
| 326 |
-
)
|
| 327 |
-
ab_config.enable_meta_tools = "meta_tools" not in disable_modules
|
| 328 |
-
ab_config.enable_doom_detector = "doom_detector" not in disable_modules
|
| 329 |
-
ab_config.router_model_path = config.router_model_path
|
| 330 |
-
|
| 331 |
-
ab_opt = ACOOptimizer(ab_config)
|
| 332 |
-
|
| 333 |
-
results = []
|
| 334 |
-
for task_id, task_traces in list(traces.items())[:100]: # Use 100 for ablation
|
| 335 |
-
problem = next(iter(task_traces.values()))["problem"]
|
| 336 |
-
result = simulate_agent_run(task_id, problem, "deepseek-v4-flash", ab_opt)
|
| 337 |
-
# Override with ablation behavior
|
| 338 |
-
if "verifier" in disable_modules:
|
| 339 |
-
result["verifier_calls"] = 0
|
| 340 |
-
if "tool_gate" in disable_modules:
|
| 341 |
-
result["tool_gate_skips"] = 0
|
| 342 |
-
if "context" in disable_modules:
|
| 343 |
-
pass # mock agent doesn't use context
|
| 344 |
-
results.append(result)
|
| 345 |
-
|
| 346 |
-
return {
|
| 347 |
-
"modules_disabled": disable_modules,
|
| 348 |
-
"n": len(results),
|
| 349 |
-
"resolved": sum(1 for r in results if r["success"]),
|
| 350 |
-
"total_cost": sum(r["total_cost"] for r in results),
|
| 351 |
-
"avg_cost": sum(r["total_cost"] for r in results) / max(len(results), 1),
|
| 352 |
-
"verifier_calls": sum(r["verifier_calls"] for r in results),
|
| 353 |
-
"escalated": sum(1 for r in results if r["escalated"]),
|
| 354 |
-
"terminated": sum(1 for r in results if r["terminated_early"]),
|
| 355 |
-
}
|
| 356 |
-
|
| 357 |
-
ablations = {
|
| 358 |
-
"full_aco": run_ablation([]),
|
| 359 |
-
"no_router": run_ablation(["router", "feedback_escalation"]),
|
| 360 |
-
"no_feedback": run_ablation(["feedback_escalation"]),
|
| 361 |
-
"no_verifier": run_ablation(["verifier"]),
|
| 362 |
-
"no_doom_detector": run_ablation(["doom_detector"]),
|
| 363 |
-
"no_tool_gate": run_ablation(["tool_gate"]),
|
| 364 |
-
"frontier_only": run_ablation(["router", "feedback_escalation", "verifier", "doom_detector", "tool_gate"]),
|
| 365 |
-
}
|
| 366 |
-
|
| 367 |
-
print(f"\n{'='*70}")
|
| 368 |
-
print("ABLATION STUDY (100 tasks)")
|
| 369 |
-
print(f"{'='*70}")
|
| 370 |
-
print(f"\n{'Configuration':<25} {'Resolved':>10} {'Rate':>8} {'AvgCost':>10} {'vsFull':>10}")
|
| 371 |
-
print("-" * 63)
|
| 372 |
-
|
| 373 |
-
full_cost = ablations["full_aco"]["avg_cost"]
|
| 374 |
-
full_resolved = ablations["full_aco"]["resolved"]
|
| 375 |
-
|
| 376 |
-
for name, ab in sorted(ablations.items(), key=lambda x: x[1]["avg_cost"]):
|
| 377 |
-
delta_cost = (ab["avg_cost"] - full_cost) / max(full_cost, 0.0001) * 100
|
| 378 |
-
delta_resolved = ab["resolved"] - full_resolved
|
| 379 |
-
flag = ""
|
| 380 |
-
if delta_cost > 0 and ab["resolved"] < full_resolved:
|
| 381 |
-
flag = " ← REGRESSION"
|
| 382 |
-
elif delta_cost < 0 and ab["resolved"] >= full_resolved:
|
| 383 |
-
flag = " ← IMPROVEMENT"
|
| 384 |
-
print(f" {name:<23} {ab['resolved']:>10} {ab['resolved']/10:>7.1f}% ${ab['avg_cost']:>9.4f} {delta_cost:>+9.1f}%{flag}")
|
| 385 |
-
|
| 386 |
-
# ── 6. Save results ─────────────────────────────────────────────
|
| 387 |
-
output = {
|
| 388 |
-
"end_to_end": {
|
| 389 |
-
"n": n,
|
| 390 |
-
"resolved": resolved,
|
| 391 |
-
"success_rate": round(resolved / n, 4),
|
| 392 |
-
"total_cost": round(total_cost, 2),
|
| 393 |
-
"avg_cost": round(total_cost / n, 4),
|
| 394 |
-
"avg_cost_per_resolved": round(total_cost / max(resolved, 1), 4),
|
| 395 |
-
"cost_reduction_vs_frontier": round((1 - total_cost / baseline_frontier_cost) * 100, 1),
|
| 396 |
-
"frontier_cost": round(baseline_frontier_cost, 2),
|
| 397 |
-
"frontier_resolved": frontier_resolved,
|
| 398 |
-
"escalations": escalated,
|
| 399 |
-
"early_terminations": terminated,
|
| 400 |
-
"verifier_calls": verifier_calls,
|
| 401 |
-
"tool_gate_skips": tool_skips,
|
| 402 |
-
},
|
| 403 |
-
"ablations": {
|
| 404 |
-
name: {
|
| 405 |
-
"resolved": a["resolved"],
|
| 406 |
-
"avg_cost": round(a["avg_cost"], 4),
|
| 407 |
-
"cost_delta_vs_full": round((a["avg_cost"] - full_cost) / max(full_cost, 0.0001) * 100, 1),
|
| 408 |
-
}
|
| 409 |
-
for name, a in ablations.items()
|
| 410 |
-
},
|
| 411 |
-
}
|
| 412 |
-
|
| 413 |
-
with open("/tmp/aco_e2e_results.json", "w") as f:
|
| 414 |
-
json.dump(output, f, indent=2)
|
| 415 |
-
|
| 416 |
-
# Upload
|
| 417 |
-
from huggingface_hub import HfApi
|
| 418 |
-
api = HfApi()
|
| 419 |
-
api.upload_file(
|
| 420 |
-
path_or_fileobj="/tmp/aco_e2e_results.json",
|
| 421 |
-
path_in_repo="eval/e2e_benchmark_results.json",
|
| 422 |
-
repo_id=REPO,
|
| 423 |
-
repo_type="model",
|
| 424 |
-
)
|
| 425 |
-
|
| 426 |
-
print(f"\n ✓ Results uploaded to eval/e2e_benchmark_results.json")
|
| 427 |
-
print(f"\n{'='*70}")
|
| 428 |
-
print("E2E BENCHMARK COMPLETE")
|
| 429 |
-
print("=" * 70)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
training/e2e_benchmark_v2.py
DELETED
|
@@ -1,355 +0,0 @@
|
|
| 1 |
-
"""End-to-End ACO Evaluation on SWE-bench Agent Traces.
|
| 2 |
-
|
| 3 |
-
FIXED: Clones the ACO repo first for imports.
|
| 4 |
-
"""
|
| 5 |
-
import json, sys, os, copy, random, subprocess
|
| 6 |
-
from collections import defaultdict
|
| 7 |
-
from typing import Dict, List, Optional, Tuple
|
| 8 |
-
import numpy as np
|
| 9 |
-
|
| 10 |
-
# Clone repo for ACO imports
|
| 11 |
-
repo_url = "https://huggingface.co/narcolepticchicken/agent-cost-optimizer"
|
| 12 |
-
if not os.path.exists("/tmp/aco_repo"):
|
| 13 |
-
print(f"Cloning {repo_url}...")
|
| 14 |
-
subprocess.run(["git", "clone", repo_url, "/tmp/aco_repo"], check=True)
|
| 15 |
-
subprocess.run(["pip", "install", "pyyaml"], check=True, capture_output=True)
|
| 16 |
-
|
| 17 |
-
sys.path.insert(0, "/tmp/aco_repo")
|
| 18 |
-
from aco.optimizer import ACOOptimizer
|
| 19 |
-
from aco.config import ACOConfig, RoutingPolicy
|
| 20 |
-
from datasets import load_dataset
|
| 21 |
-
|
| 22 |
-
REPO = "narcolepticchicken/agent-cost-optimizer"
|
| 23 |
-
|
| 24 |
-
print("=" * 70)
|
| 25 |
-
print("ACO END-TO-END BENCHMARK ON SWE-BENCH AGENT TRACES (V2)")
|
| 26 |
-
print("=" * 70)
|
| 27 |
-
|
| 28 |
-
# ── 1. Load SWE-Router ground truth ─────────────────────────────
|
| 29 |
-
print("\n[1] Loading SWE-Router execution data...")
|
| 30 |
-
|
| 31 |
-
MODELS = [
|
| 32 |
-
"deepseek-v4-flash", "gpt-5-nano", "gpt-5-mini", "deepseek-v3.2",
|
| 33 |
-
"gemini-2.5-pro", "claude-opus-4.7", "gpt-5.2", "gemini-3-pro",
|
| 34 |
-
]
|
| 35 |
-
|
| 36 |
-
TIER = {
|
| 37 |
-
"deepseek-v4-flash": 1, "gpt-5-nano": 1,
|
| 38 |
-
"gpt-5-mini": 2, "deepseek-v3.2": 2,
|
| 39 |
-
"gemini-2.5-pro": 3,
|
| 40 |
-
"claude-opus-4.7": 4, "gpt-5.2": 4,
|
| 41 |
-
"gemini-3-pro": 5,
|
| 42 |
-
}
|
| 43 |
-
|
| 44 |
-
TIER_MODEL_MAP = {
|
| 45 |
-
1: "deepseek-v4-flash", 2: "gpt-5-mini",
|
| 46 |
-
3: "gemini-2.5-pro", 4: "claude-opus-4.7", 5: "gemini-3-pro",
|
| 47 |
-
}
|
| 48 |
-
|
| 49 |
-
# Load all SWE-Router traces
|
| 50 |
-
traces = defaultdict(dict)
|
| 51 |
-
for model_name in MODELS:
|
| 52 |
-
ds = load_dataset(f"SWE-Router/swebench-verified-{model_name}", split="test")
|
| 53 |
-
for row in ds:
|
| 54 |
-
iid = row["instance_id"]
|
| 55 |
-
traces[iid][model_name] = {
|
| 56 |
-
"resolved": row["resolved"],
|
| 57 |
-
"cost": float(row["instance_cost"]),
|
| 58 |
-
"api_calls": int(row["api_calls"]),
|
| 59 |
-
"problem": row.get("problem_statement", row.get("problem", "")),
|
| 60 |
-
}
|
| 61 |
-
print(f" Loaded {len(traces)} tasks × {len(MODELS)} models")
|
| 62 |
-
|
| 63 |
-
# ── 2. Set up ACO optimizer ─────────────────────────────────────
|
| 64 |
-
print("\n[2] Setting up ACO optimizer...")
|
| 65 |
-
|
| 66 |
-
config = ACOConfig()
|
| 67 |
-
config.routing_policy = RoutingPolicy(
|
| 68 |
-
routing_mode="cascade",
|
| 69 |
-
feedback_escalation=True,
|
| 70 |
-
max_retries=2,
|
| 71 |
-
max_cost_per_task=1.0,
|
| 72 |
-
safety_threshold=0.5,
|
| 73 |
-
)
|
| 74 |
-
config.enable_meta_tools = False
|
| 75 |
-
config.enable_doom_detector = True
|
| 76 |
-
|
| 77 |
-
# Download router bundle
|
| 78 |
-
from huggingface_hub import hf_hub_download
|
| 79 |
-
try:
|
| 80 |
-
bundle_path = hf_hub_download(
|
| 81 |
-
repo_id=REPO,
|
| 82 |
-
filename="router_models/router_bundle_v10_fixed.pkl",
|
| 83 |
-
)
|
| 84 |
-
config.router_model_path = bundle_path
|
| 85 |
-
print(f" Router model loaded")
|
| 86 |
-
except Exception as e:
|
| 87 |
-
print(f" Router not available: {e}")
|
| 88 |
-
config.router_model_path = None
|
| 89 |
-
|
| 90 |
-
optimizer = ACOOptimizer(config)
|
| 91 |
-
|
| 92 |
-
# ── 3. Simulate agent runs ──────────────────────────────────────
|
| 93 |
-
print("\n[3] Running end-to-end simulation...")
|
| 94 |
-
|
| 95 |
-
def simulate_agent_run(task_id: str, task_problem: str,
|
| 96 |
-
optimizer: ACOOptimizer) -> Dict:
|
| 97 |
-
"""Simulate one agent run with ACO intercepting decisions."""
|
| 98 |
-
result = {
|
| 99 |
-
"task_id": task_id,
|
| 100 |
-
"total_cost": 0.0,
|
| 101 |
-
"success": False,
|
| 102 |
-
"escalated": False,
|
| 103 |
-
"terminated_early": False,
|
| 104 |
-
"verifier_calls": 0,
|
| 105 |
-
"tool_gate_skips": 0,
|
| 106 |
-
"doom_stops": 0,
|
| 107 |
-
}
|
| 108 |
-
|
| 109 |
-
# Step 1: ACO routes to a model
|
| 110 |
-
start = optimizer.start_run(task_problem)
|
| 111 |
-
routed_model = start["routing"]["model_id"]
|
| 112 |
-
routed_tier = start["routing"]["tier"]
|
| 113 |
-
|
| 114 |
-
result["routed_model"] = routed_model
|
| 115 |
-
result["routed_tier"] = routed_tier
|
| 116 |
-
|
| 117 |
-
# Step 2: Simulate agent execution
|
| 118 |
-
model_trace = traces[task_id].get(routed_model, {})
|
| 119 |
-
resolved = model_trace.get("resolved", False)
|
| 120 |
-
cost = model_trace.get("cost", 0.30)
|
| 121 |
-
api_calls = model_trace.get("api_calls", 10)
|
| 122 |
-
|
| 123 |
-
step_names = ["analyze", "generate", "verify"]
|
| 124 |
-
n_steps = min(len(step_names), api_calls)
|
| 125 |
-
|
| 126 |
-
for i, step_name in enumerate(step_names[:n_steps]):
|
| 127 |
-
step_cost = cost / max(n_steps, 1)
|
| 128 |
-
|
| 129 |
-
# Gate tools
|
| 130 |
-
for tool_idx in range(random.randint(1, 3)):
|
| 131 |
-
td = optimizer.gate_tool(f"tool_{tool_idx}", {"step": step_name})
|
| 132 |
-
if td.action == "skip":
|
| 133 |
-
result["tool_gate_skips"] += 1
|
| 134 |
-
|
| 135 |
-
# Should we verify?
|
| 136 |
-
is_final = (i == n_steps - 1)
|
| 137 |
-
vd = optimizer.should_verify(
|
| 138 |
-
is_irreversible=is_final,
|
| 139 |
-
has_prior_failures=(not resolved and i > 0),
|
| 140 |
-
)
|
| 141 |
-
if vd.should_verify:
|
| 142 |
-
result["verifier_calls"] += 1
|
| 143 |
-
step_cost *= 1.2
|
| 144 |
-
|
| 145 |
-
# Doom check
|
| 146 |
-
total_so_far = result["total_cost"] + step_cost
|
| 147 |
-
doom = optimizer.check_doom(current_cost=total_so_far)
|
| 148 |
-
if doom.doomed:
|
| 149 |
-
result["terminated_early"] = True
|
| 150 |
-
result["doom_stops"] += 1
|
| 151 |
-
break
|
| 152 |
-
|
| 153 |
-
# Record step
|
| 154 |
-
optimizer.record_step(
|
| 155 |
-
model_call={
|
| 156 |
-
"model_id": routed_model,
|
| 157 |
-
"input_tokens": 5000 // max(n_steps, 1),
|
| 158 |
-
"output_tokens": 500,
|
| 159 |
-
"cost": step_cost,
|
| 160 |
-
"latency_ms": 2000,
|
| 161 |
-
},
|
| 162 |
-
context_size=5000 + (i * 1000),
|
| 163 |
-
retry_num=0,
|
| 164 |
-
)
|
| 165 |
-
|
| 166 |
-
result["total_cost"] += step_cost
|
| 167 |
-
|
| 168 |
-
# Escalate if needed
|
| 169 |
-
if not resolved and not result["terminated_early"]:
|
| 170 |
-
for escalate_tier in range(routed_tier + 1, 6):
|
| 171 |
-
escalate_model = TIER_MODEL_MAP[escalate_tier]
|
| 172 |
-
esc_trace = traces[task_id].get(escalate_model, {})
|
| 173 |
-
if esc_trace.get("resolved", False):
|
| 174 |
-
result["total_cost"] += esc_trace.get("cost", 0.30)
|
| 175 |
-
result["escalated"] = True
|
| 176 |
-
resolved = True
|
| 177 |
-
break
|
| 178 |
-
else:
|
| 179 |
-
result["total_cost"] += esc_trace.get("cost", 0.30)
|
| 180 |
-
|
| 181 |
-
result["success"] = resolved
|
| 182 |
-
|
| 183 |
-
optimizer.end_run(
|
| 184 |
-
success=resolved,
|
| 185 |
-
outcome="completed" if resolved else "abandoned",
|
| 186 |
-
artifacts=["fix"] if resolved else [],
|
| 187 |
-
failure_tags=[] if resolved else ["not_resolved"],
|
| 188 |
-
)
|
| 189 |
-
|
| 190 |
-
return result
|
| 191 |
-
|
| 192 |
-
def compute_baseline(task_id: str) -> Dict:
|
| 193 |
-
ft = traces[task_id].get("claude-opus-4.7", {})
|
| 194 |
-
ch = traces[task_id].get("deepseek-v4-flash", {})
|
| 195 |
-
best = None
|
| 196 |
-
for m, mt in traces[task_id].items():
|
| 197 |
-
if mt["resolved"] and (best is None or mt["cost"] < best["cost"]):
|
| 198 |
-
best = mt
|
| 199 |
-
return {
|
| 200 |
-
"frontier_cost": ft.get("cost", 0.317),
|
| 201 |
-
"frontier_resolved": ft.get("resolved", False),
|
| 202 |
-
"always_cheap_cost": ch.get("cost", 0.014),
|
| 203 |
-
"always_cheap_resolved": ch.get("resolved", False),
|
| 204 |
-
"oracle_cost": best["cost"] if best else ft.get("cost", 0.317),
|
| 205 |
-
"oracle_resolved": best is not None,
|
| 206 |
-
}
|
| 207 |
-
|
| 208 |
-
# Run on all 500 tasks
|
| 209 |
-
all_results = []
|
| 210 |
-
for idx, (task_id, task_traces) in enumerate(traces.items()):
|
| 211 |
-
problem = next(iter(task_traces.values()))["problem"]
|
| 212 |
-
result = simulate_agent_run(task_id, problem, optimizer)
|
| 213 |
-
baseline = compute_baseline(task_id)
|
| 214 |
-
result["baseline"] = baseline
|
| 215 |
-
all_results.append(result)
|
| 216 |
-
|
| 217 |
-
if (idx + 1) % 100 == 0:
|
| 218 |
-
n = idx + 1
|
| 219 |
-
resolved = sum(1 for r in all_results if r["success"])
|
| 220 |
-
total_cost = sum(r["total_cost"] for r in all_results)
|
| 221 |
-
print(f" [{n}/500] resolved={resolved} ({resolved/n*100:.1f}%), "
|
| 222 |
-
f"avg_cost=${total_cost/n:.4f}")
|
| 223 |
-
|
| 224 |
-
# ── 4. Compute metrics ──────────────────────────────────────────
|
| 225 |
-
print(f"\n[4] Computing metrics...")
|
| 226 |
-
|
| 227 |
-
n = len(all_results)
|
| 228 |
-
resolved = sum(1 for r in all_results if r["success"])
|
| 229 |
-
total_cost = sum(r["total_cost"] for r in all_results)
|
| 230 |
-
baseline_frontier_cost = sum(r["baseline"]["frontier_cost"] for r in all_results)
|
| 231 |
-
baseline_cheap_cost = sum(r["baseline"]["always_cheap_cost"] for r in all_results)
|
| 232 |
-
baseline_oracle_cost = sum(r["baseline"]["oracle_cost"] for r in all_results)
|
| 233 |
-
frontier_resolved = sum(1 for r in all_results if r["baseline"]["frontier_resolved"])
|
| 234 |
-
cheap_resolved = sum(1 for r in all_results if r["baseline"]["always_cheap_resolved"])
|
| 235 |
-
oracle_resolved = sum(1 for r in all_results if r["baseline"]["oracle_resolved"])
|
| 236 |
-
escalated = sum(1 for r in all_results if r["escalated"])
|
| 237 |
-
terminated = sum(1 for r in all_results if r["terminated_early"])
|
| 238 |
-
verifier_calls = sum(r["verifier_calls"] for r in all_results)
|
| 239 |
-
|
| 240 |
-
print(f"""
|
| 241 |
-
{'='*70}
|
| 242 |
-
ACO END-TO-END RESULTS ({n} SWE-bench tasks)
|
| 243 |
-
{'='*70}
|
| 244 |
-
|
| 245 |
-
{'Policy':<25} {'Resolved':>10} {'Rate':>10} {'TotalCost':>12} {'AvgCost':>10} {'vsFront':>10}
|
| 246 |
-
{'-'*77}
|
| 247 |
-
{'ACO Optimized':<25} {resolved:>10} {resolved/n*100:>9.1f}% ${total_cost:>10.2f} ${total_cost/n:>9.4f} {(1-total_cost/baseline_frontier_cost)*100:>9.1f}%
|
| 248 |
-
{'Frontier (always)':<25} {frontier_resolved:>10} {frontier_resolved/n*100:>9.1f}% ${baseline_frontier_cost:>10.2f} ${baseline_frontier_cost/n:>9.4f} {'--':>10}
|
| 249 |
-
{'Always Cheap':<25} {cheap_resolved:>10} {cheap_resolved/n*100:>9.1f}% ${baseline_cheap_cost:>10.2f} ${baseline_cheap_cost/n:>9.4f} {(1-baseline_cheap_cost/baseline_frontier_cost)*100:>9.1f}%
|
| 250 |
-
{'Oracle':<25} {oracle_resolved:>10} {oracle_resolved/n*100:>9.1f}% ${baseline_oracle_cost:>10.2f} ${baseline_oracle_cost/n:>9.4f} {(1-baseline_oracle_cost/baseline_frontier_cost)*100:>9.1f}%
|
| 251 |
-
|
| 252 |
-
MODULE IMPACT:
|
| 253 |
-
Escalations: {escalated} ({escalated/n*100:.1f}%)
|
| 254 |
-
Early stops: {terminated} ({terminated/n*100:.1f}%)
|
| 255 |
-
Verifier calls: {verifier_calls} ({verifier_calls/max(n,1):.2f}/task)
|
| 256 |
-
Avg cost/resolved: ${total_cost/max(resolved,1):.4f}
|
| 257 |
-
Waste on failures: ${sum(r['total_cost'] for r in all_results if not r['success']):.2f}
|
| 258 |
-
""")
|
| 259 |
-
|
| 260 |
-
# ── 5. Ablation study (100 tasks) ───────────────────────────────
|
| 261 |
-
print(f"\n[5] Ablation study (100 tasks)...")
|
| 262 |
-
|
| 263 |
-
def run_ablation(disable_modules: List[str], n_tasks: int = 100) -> Dict:
|
| 264 |
-
ab_config = ACOConfig()
|
| 265 |
-
ab_config.routing_policy = RoutingPolicy(
|
| 266 |
-
routing_mode="cascade" if "router" not in disable_modules else "frontier",
|
| 267 |
-
feedback_escalation="feedback" not in disable_modules,
|
| 268 |
-
max_retries=2, max_cost_per_task=1.0, safety_threshold=0.5,
|
| 269 |
-
)
|
| 270 |
-
ab_config.enable_doom_detector = "doom" not in disable_modules
|
| 271 |
-
ab_config.router_model_path = config.router_model_path
|
| 272 |
-
|
| 273 |
-
ab_opt = ACOOptimizer(ab_config)
|
| 274 |
-
results = []
|
| 275 |
-
task_items = list(traces.items())[:n_tasks]
|
| 276 |
-
|
| 277 |
-
for task_id, task_traces in task_items:
|
| 278 |
-
problem = next(iter(task_traces.values()))["problem"]
|
| 279 |
-
result = simulate_agent_run(task_id, problem, ab_opt)
|
| 280 |
-
if "verifier" in disable_modules:
|
| 281 |
-
result["verifier_calls"] = 0
|
| 282 |
-
if "tool_gate" in disable_modules:
|
| 283 |
-
result["tool_gate_skips"] = 0
|
| 284 |
-
results.append(result)
|
| 285 |
-
|
| 286 |
-
return {
|
| 287 |
-
"n": len(results),
|
| 288 |
-
"resolved": sum(1 for r in results if r["success"]),
|
| 289 |
-
"total_cost": sum(r["total_cost"] for r in results),
|
| 290 |
-
"avg_cost": sum(r["total_cost"] for r in results) / max(len(results), 1),
|
| 291 |
-
}
|
| 292 |
-
|
| 293 |
-
ablations = {
|
| 294 |
-
"full_aco": run_ablation([]),
|
| 295 |
-
"no_router": run_ablation(["router", "feedback"]),
|
| 296 |
-
"no_feedback": run_ablation(["feedback"]),
|
| 297 |
-
"no_verifier": run_ablation(["verifier"]),
|
| 298 |
-
"no_doom": run_ablation(["doom"]),
|
| 299 |
-
"no_tool_gate": run_ablation(["tool_gate"]),
|
| 300 |
-
"frontier_only": run_ablation(["router", "feedback", "verifier", "doom", "tool_gate"]),
|
| 301 |
-
}
|
| 302 |
-
|
| 303 |
-
full_cost = ablations["full_aco"]["avg_cost"]
|
| 304 |
-
full_resolved = ablations["full_aco"]["resolved"]
|
| 305 |
-
|
| 306 |
-
print(f"\n{'Configuration':<25} {'Resolved':>10} {'Rate':>8} {'AvgCost':>10} {'vsFull':>10}")
|
| 307 |
-
print("-" * 63)
|
| 308 |
-
|
| 309 |
-
for name in ["full_aco", "no_feedback", "no_doom", "no_tool_gate", "no_verifier", "no_router", "frontier_only"]:
|
| 310 |
-
ab = ablations[name]
|
| 311 |
-
dc = (ab["avg_cost"] - full_cost) / max(full_cost, 0.0001) * 100
|
| 312 |
-
dr = ab["resolved"] - full_resolved
|
| 313 |
-
flag = ""
|
| 314 |
-
if dc > 0 and dr < 0: flag = " ← REGRESSION"
|
| 315 |
-
elif dc < 0 and dr >= 0: flag = " ← BETTER"
|
| 316 |
-
print(f" {name:<23} {ab['resolved']:>10} {ab['resolved']/ab['n']*100:>7.1f}% ${ab['avg_cost']:>9.4f} {dc:>+9.1f}%{flag}")
|
| 317 |
-
|
| 318 |
-
# ── 6. Save ─────────────────────────────────────────────────────
|
| 319 |
-
output = {
|
| 320 |
-
"end_to_end": {
|
| 321 |
-
"n": n, "resolved": resolved,
|
| 322 |
-
"success_rate": round(resolved/n, 4),
|
| 323 |
-
"total_cost": round(total_cost, 2),
|
| 324 |
-
"avg_cost": round(total_cost/n, 4),
|
| 325 |
-
"cost_reduction_vs_frontier": round((1 - total_cost/baseline_frontier_cost) * 100, 1),
|
| 326 |
-
"frontier_cost": round(baseline_frontier_cost, 2),
|
| 327 |
-
"frontier_resolved": frontier_resolved,
|
| 328 |
-
"oracle_cost": round(baseline_oracle_cost, 2),
|
| 329 |
-
"oracle_resolved": oracle_resolved,
|
| 330 |
-
"escalations": escalated,
|
| 331 |
-
"early_terminations": terminated,
|
| 332 |
-
"verifier_calls": verifier_calls,
|
| 333 |
-
},
|
| 334 |
-
"ablations": {
|
| 335 |
-
name: {"resolved": a["resolved"], "avg_cost": round(a["avg_cost"], 4),
|
| 336 |
-
"cost_delta_vs_full": round((a["avg_cost"] - full_cost) / max(full_cost, 0.0001) * 100, 1)}
|
| 337 |
-
for name, a in ablations.items()
|
| 338 |
-
},
|
| 339 |
-
}
|
| 340 |
-
|
| 341 |
-
with open("/tmp/aco_e2e_v2.json", "w") as f:
|
| 342 |
-
json.dump(output, f, indent=2)
|
| 343 |
-
|
| 344 |
-
from huggingface_hub import HfApi
|
| 345 |
-
api = HfApi()
|
| 346 |
-
api.upload_file(
|
| 347 |
-
path_or_fileobj="/tmp/aco_e2e_v2.json",
|
| 348 |
-
path_in_repo="eval/e2e_benchmark_v2.json",
|
| 349 |
-
repo_id=REPO, repo_type="model",
|
| 350 |
-
)
|
| 351 |
-
|
| 352 |
-
print(f"\n✓ Results uploaded: eval/e2e_benchmark_v2.json")
|
| 353 |
-
print(f"\n{'='*70}")
|
| 354 |
-
print("E2E BENCHMARK V2 COMPLETE")
|
| 355 |
-
print("=" * 70)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
training/e2e_standalone.py
DELETED
|
@@ -1,397 +0,0 @@
|
|
| 1 |
-
"""Standalone ACO E2E Benchmark — no external aco/ imports.
|
| 2 |
-
|
| 3 |
-
Evaluates the Agent Cost Optimizer's model cascade routing against simulated
|
| 4 |
-
SWE-bench agent runs using SWE-Router execution traces as ground truth.
|
| 5 |
-
|
| 6 |
-
Measures per-module impact through ablation: disable each module one at a time.
|
| 7 |
-
"""
|
| 8 |
-
import json, sys, os, random, pickle
|
| 9 |
-
from collections import defaultdict
|
| 10 |
-
from typing import Dict, List, Optional
|
| 11 |
-
import numpy as np
|
| 12 |
-
|
| 13 |
-
# ── Core Router (inlined from aco/router_v10.py) ─────────────────
|
| 14 |
-
CODE_KW = ["python","javascript","code","function","bug","debug","refactor",
|
| 15 |
-
"implement","test","compile","runtime","segfault","thread","async","class",
|
| 16 |
-
"module","import","error","traceback"]
|
| 17 |
-
CRITICAL_KW = ["critical","production","urgent","emergency","live","deployed","safety","security"]
|
| 18 |
-
SIMPLE_KW = ["typo","simple","quick","brief","minor","small","easy","trivial","just"]
|
| 19 |
-
RESEARCH_KW = ["research","investigate","compare","analyze","survey","paper"]
|
| 20 |
-
TOOL_KW = ["search","fetch","retrieve","query","api","database","scrape","aggregate"]
|
| 21 |
-
LONG_KW = ["plan","project","roadmap","orchestrate","migrate","pipeline","deploy","architecture"]
|
| 22 |
-
|
| 23 |
-
FEAT_KEYS = sorted([
|
| 24 |
-
'req_len','num_words','has_code','n_code','has_legal','has_research',
|
| 25 |
-
'has_tool','has_critical','has_simple','has_long','has_math',
|
| 26 |
-
'has_error_msg','has_file_path','n_lines','has_version','has_add',
|
| 27 |
-
'has_fix','has_change','has_remove','has_test','has_doc',
|
| 28 |
-
'has_see_also','has_steps_to_reproduce',
|
| 29 |
-
])
|
| 30 |
-
|
| 31 |
-
TIER_TO_MODEL = {
|
| 32 |
-
1: 'deepseek-v4-flash', 2: 'gpt-5-mini',
|
| 33 |
-
3: 'gemini-2.5-pro', 4: 'claude-opus-4.7', 5: 'gemini-3-pro',
|
| 34 |
-
}
|
| 35 |
-
|
| 36 |
-
TIER_COST = {1:0.01, 2:0.05, 3:0.15, 4:0.30, 5:0.50}
|
| 37 |
-
|
| 38 |
-
MODELS = ["deepseek-v4-flash","gpt-5-nano","gpt-5-mini","deepseek-v3.2",
|
| 39 |
-
"gemini-2.5-pro","claude-opus-4.7","gpt-5.2","gemini-3-pro"]
|
| 40 |
-
MODEL_TIER = {"deepseek-v4-flash":1,"gpt-5-nano":1,"gpt-5-mini":2,"deepseek-v3.2":2,
|
| 41 |
-
"gemini-2.5-pro":3,"claude-opus-4.7":4,"gpt-5.2":4,"gemini-3-pro":5}
|
| 42 |
-
|
| 43 |
-
def extract_features(text: str) -> np.ndarray:
|
| 44 |
-
r = text.lower()
|
| 45 |
-
feats = {
|
| 46 |
-
'req_len': len(text), 'num_words': len(text.split()),
|
| 47 |
-
'has_code': int(any(k in r for k in CODE_KW)),
|
| 48 |
-
'n_code': sum(1 for k in CODE_KW if k in r),
|
| 49 |
-
'has_legal': int(any(k in r for k in ["contract","legal","compliance"])),
|
| 50 |
-
'has_research': int(any(k in r for k in RESEARCH_KW)),
|
| 51 |
-
'has_tool': int(any(k in r for k in TOOL_KW)),
|
| 52 |
-
'has_critical': int(any(k in r for k in CRITICAL_KW)),
|
| 53 |
-
'has_simple': int(any(k in r for k in SIMPLE_KW)),
|
| 54 |
-
'has_long': int(any(k in r for k in LONG_KW)),
|
| 55 |
-
'has_math': int(any(k in r for k in ["calculate","compute","solve","equation"])),
|
| 56 |
-
'has_error_msg': int('error' in r or 'traceback' in r or 'exception' in r),
|
| 57 |
-
'has_file_path': int('/' in r),
|
| 58 |
-
'n_lines': text.count('\n') + 1,
|
| 59 |
-
'has_version': int('version' in r or 'update' in r),
|
| 60 |
-
'has_add': int('add' in r or 'new' in r or 'create' in r),
|
| 61 |
-
'has_fix': int('fix' in r or 'bug' in r or 'issue' in r),
|
| 62 |
-
'has_change': int('change' in r or 'modify' in r),
|
| 63 |
-
'has_remove': int('remove' in r or 'delete' in r),
|
| 64 |
-
'has_test': int('test' in r or 'spec' in r),
|
| 65 |
-
'has_doc': int('doc' in r or 'readme' in r),
|
| 66 |
-
'has_see_also': int('see also' in r or 'related' in r),
|
| 67 |
-
'has_steps_to_reproduce': int('reproduce' in r or 'steps' in r),
|
| 68 |
-
}
|
| 69 |
-
return np.array([float(feats.get(k,0.0)) for k in FEAT_KEYS], dtype=np.float32)
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
# ── Load Router Bundle ──────────────────────────────────────────
|
| 73 |
-
from huggingface_hub import hf_hub_download
|
| 74 |
-
from sklearn.isotonic import IsotonicRegression
|
| 75 |
-
|
| 76 |
-
print("Loading router model...")
|
| 77 |
-
try:
|
| 78 |
-
bundle_path = hf_hub_download(
|
| 79 |
-
repo_id="narcolepticchicken/agent-cost-optimizer",
|
| 80 |
-
filename="router_models/router_bundle_v10_fixed.pkl",
|
| 81 |
-
)
|
| 82 |
-
bundle = pickle.load(open(bundle_path, 'rb'))
|
| 83 |
-
tier_clfs = {int(k):v for k,v in bundle.get('tier_clfs',{}).items()}
|
| 84 |
-
tier_calibs = {int(k):v for k,v in bundle.get('tier_calibrators',{}).items()}
|
| 85 |
-
print(f" Loaded v10 router: {len(tier_clfs)} tier classifiers")
|
| 86 |
-
except Exception as e:
|
| 87 |
-
print(f" WARNING: v10 bundle failed ({e}), using fallback routing")
|
| 88 |
-
tier_clfs = {}
|
| 89 |
-
tier_calibs = {}
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
def route_cascade(text: str, threshold: float = 0.5) -> int:
|
| 93 |
-
"""Route to cheapest tier with P(success) >= threshold."""
|
| 94 |
-
if not tier_clfs:
|
| 95 |
-
# Fallback: use hardcoded tier success rates
|
| 96 |
-
return 1 # always try cheapest
|
| 97 |
-
|
| 98 |
-
x = extract_features(text).reshape(1, -1)
|
| 99 |
-
|
| 100 |
-
for t in range(1, 6):
|
| 101 |
-
if t in tier_clfs:
|
| 102 |
-
try:
|
| 103 |
-
p_raw = tier_clfs[t].predict_proba(x)[0, 1]
|
| 104 |
-
p_cal = float(tier_calibs[t].transform([p_raw])[0])
|
| 105 |
-
if p_cal >= threshold:
|
| 106 |
-
return t
|
| 107 |
-
except:
|
| 108 |
-
pass
|
| 109 |
-
|
| 110 |
-
return 5 # escalate to strongest
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
# ── 1. Load SWE-Router Data ─────────────────────────────────────
|
| 114 |
-
from datasets import load_dataset
|
| 115 |
-
|
| 116 |
-
print("\n[1] Loading SWE-Router execution data...")
|
| 117 |
-
traces = defaultdict(dict)
|
| 118 |
-
for model_name in MODELS:
|
| 119 |
-
ds = load_dataset(f"SWE-Router/swebench-verified-{model_name}", split="test")
|
| 120 |
-
for row in ds:
|
| 121 |
-
iid = row["instance_id"]
|
| 122 |
-
traces[iid][model_name] = {
|
| 123 |
-
"resolved": row["resolved"],
|
| 124 |
-
"cost": float(row["instance_cost"]),
|
| 125 |
-
"api_calls": int(row["api_calls"]),
|
| 126 |
-
"problem": row.get("problem_statement", row.get("problem", "")),
|
| 127 |
-
}
|
| 128 |
-
print(f" Loaded {len(traces)} tasks × {len(MODELS)} models = {sum(len(v) for v in traces.values())} outcomes")
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
# ── 2. Simulate ACO Agent Run ────────────────────────────────────
|
| 132 |
-
print("\n[2] Simulating ACO-optimized agent runs...")
|
| 133 |
-
|
| 134 |
-
def simulate_acorun(task_id: str, problem: str,
|
| 135 |
-
use_router: bool = True,
|
| 136 |
-
use_feedback: bool = True,
|
| 137 |
-
use_verifier: bool = True,
|
| 138 |
-
use_doom: bool = True,
|
| 139 |
-
use_tool_gate: bool = True,
|
| 140 |
-
max_doom_cost: float = 1.0,
|
| 141 |
-
verifier_max_per_run: int = 3) -> Dict:
|
| 142 |
-
"""Simulate one ACO-optimized agent run."""
|
| 143 |
-
result = {
|
| 144 |
-
"task_id": task_id,
|
| 145 |
-
"total_cost": 0.0,
|
| 146 |
-
"success": False, "escalated": False,
|
| 147 |
-
"terminated_early": False,
|
| 148 |
-
"verifier_calls": 0, "tool_skips": 0,
|
| 149 |
-
"routed_tier": 1,
|
| 150 |
-
}
|
| 151 |
-
|
| 152 |
-
# Step 1: Route
|
| 153 |
-
routed_tier = route_cascade(problem) if use_router else 4
|
| 154 |
-
routed_model = TIER_TO_MODEL.get(routed_tier, "claude-opus-4.7")
|
| 155 |
-
result["routed_tier"] = routed_tier
|
| 156 |
-
|
| 157 |
-
# Step 2: Execute tier 1
|
| 158 |
-
model_trace = traces[task_id].get(routed_model, {})
|
| 159 |
-
resolved = model_trace.get("resolved", False)
|
| 160 |
-
cost = model_trace.get("cost", 0.30)
|
| 161 |
-
api_calls = model_trace.get("api_calls", 10)
|
| 162 |
-
|
| 163 |
-
# Simulate 3 agent steps
|
| 164 |
-
n_steps = min(3, api_calls)
|
| 165 |
-
verifier_count = 0
|
| 166 |
-
|
| 167 |
-
for i in range(n_steps):
|
| 168 |
-
step_cost = cost / max(n_steps, 1)
|
| 169 |
-
|
| 170 |
-
# Tool gate
|
| 171 |
-
if use_tool_gate and random.random() < 0.3:
|
| 172 |
-
result["tool_skips"] += 1
|
| 173 |
-
step_cost *= 0.9 # save 10% on skipped tools
|
| 174 |
-
|
| 175 |
-
# Verifier
|
| 176 |
-
should_verify = False
|
| 177 |
-
if use_verifier:
|
| 178 |
-
is_final = (i == n_steps - 1)
|
| 179 |
-
is_high_risk = routed_tier <= 2
|
| 180 |
-
has_failures = not resolved and i > 0
|
| 181 |
-
|
| 182 |
-
if verifier_count < verifier_max_per_run and (is_final or is_high_risk or has_failures):
|
| 183 |
-
should_verify = True
|
| 184 |
-
verifier_count += 1
|
| 185 |
-
result["verifier_calls"] += 1
|
| 186 |
-
step_cost *= 1.15 # verifier adds 15%
|
| 187 |
-
|
| 188 |
-
# Doom check (only after step 1)
|
| 189 |
-
if use_doom and i > 0:
|
| 190 |
-
total_so_far = result["total_cost"] + step_cost
|
| 191 |
-
failed = not resolved
|
| 192 |
-
if total_so_far > max_doom_cost * 0.7 and failed:
|
| 193 |
-
result["terminated_early"] = True
|
| 194 |
-
result["total_cost"] += step_cost * 0.5
|
| 195 |
-
return result
|
| 196 |
-
|
| 197 |
-
result["total_cost"] += step_cost
|
| 198 |
-
|
| 199 |
-
# If not resolved and feedback is on, escalate
|
| 200 |
-
if not resolved and use_feedback:
|
| 201 |
-
result["escalated"] = True
|
| 202 |
-
for escalate_tier in range(routed_tier + 1, 6):
|
| 203 |
-
esc_model = TIER_TO_MODEL[escalate_tier]
|
| 204 |
-
esc_trace = traces[task_id].get(esc_model, {})
|
| 205 |
-
esc_cost = esc_trace.get("cost", 0.30)
|
| 206 |
-
result["total_cost"] += esc_cost
|
| 207 |
-
|
| 208 |
-
if esc_trace.get("resolved", False):
|
| 209 |
-
resolved = True
|
| 210 |
-
break
|
| 211 |
-
|
| 212 |
-
# Doom check during escalation
|
| 213 |
-
if use_doom and result["total_cost"] > max_doom_cost * 0.8:
|
| 214 |
-
result["terminated_early"] = True
|
| 215 |
-
break
|
| 216 |
-
|
| 217 |
-
result["success"] = resolved
|
| 218 |
-
return result
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
# ── 3. Run Full Evaluation ──────────────────────────────────────
|
| 222 |
-
N_TASKS = len(traces)
|
| 223 |
-
eval_results = []
|
| 224 |
-
configs = {
|
| 225 |
-
"full_aco": (True, True, True, True, True),
|
| 226 |
-
"no_router": (False, False, True, True, True),
|
| 227 |
-
"no_feedback": (True, False, True, True, True),
|
| 228 |
-
"no_verifier": (True, True, False, True, True),
|
| 229 |
-
"no_doom": (True, True, True, False, True),
|
| 230 |
-
"no_tool_gate": (True, True, True, True, False),
|
| 231 |
-
"frontier_only": (False, False, False, False, False),
|
| 232 |
-
}
|
| 233 |
-
|
| 234 |
-
for config_name, (router, feedback, verifier, doom, tool_gate) in configs.items():
|
| 235 |
-
results = []
|
| 236 |
-
for task_id, task_traces in traces.items():
|
| 237 |
-
problem = next(iter(task_traces.values()))["problem"]
|
| 238 |
-
result = simulate_acorun(
|
| 239 |
-
task_id, problem,
|
| 240 |
-
use_router=router, use_feedback=feedback,
|
| 241 |
-
use_verifier=verifier, use_doom=doom,
|
| 242 |
-
use_tool_gate=tool_gate,
|
| 243 |
-
)
|
| 244 |
-
results.append(result)
|
| 245 |
-
|
| 246 |
-
n = len(results)
|
| 247 |
-
resolved = sum(1 for r in results if r["success"])
|
| 248 |
-
total_cost = sum(r["total_cost"] for r in results)
|
| 249 |
-
escalated = sum(1 for r in results if r["escalated"])
|
| 250 |
-
terminated = sum(1 for r in results if r["terminated_early"])
|
| 251 |
-
|
| 252 |
-
eval_results.append({
|
| 253 |
-
"config": config_name,
|
| 254 |
-
"n": n, "resolved": resolved,
|
| 255 |
-
"success_rate": resolved / n,
|
| 256 |
-
"total_cost": total_cost,
|
| 257 |
-
"avg_cost": total_cost / n,
|
| 258 |
-
"escalated": escalated,
|
| 259 |
-
"terminated_early": terminated,
|
| 260 |
-
"verifier_calls": sum(r["verifier_calls"] for r in results),
|
| 261 |
-
"tool_skips": sum(r["tool_skips"] for r in results),
|
| 262 |
-
})
|
| 263 |
-
|
| 264 |
-
# ── 4. Compute Baselines ────────────────────────────────────────
|
| 265 |
-
frontier_cost = sum(
|
| 266 |
-
traces[tid].get("claude-opus-4.7", {}).get("cost", 0.317)
|
| 267 |
-
for tid in traces
|
| 268 |
-
)
|
| 269 |
-
frontier_resolved = sum(
|
| 270 |
-
1 for tid in traces
|
| 271 |
-
if traces[tid].get("claude-opus-4.7", {}).get("resolved", False)
|
| 272 |
-
)
|
| 273 |
-
cheap_cost = sum(
|
| 274 |
-
traces[tid].get("deepseek-v4-flash", {}).get("cost", 0.014)
|
| 275 |
-
for tid in traces
|
| 276 |
-
)
|
| 277 |
-
cheap_resolved = sum(
|
| 278 |
-
1 for tid in traces
|
| 279 |
-
if traces[tid].get("deepseek-v4-flash", {}).get("resolved", False)
|
| 280 |
-
)
|
| 281 |
-
|
| 282 |
-
# Oracle: cheapest model that succeeded per task
|
| 283 |
-
oracle_cost = 0
|
| 284 |
-
oracle_resolved = 0
|
| 285 |
-
for tid, ttraces in traces.items():
|
| 286 |
-
best = None
|
| 287 |
-
for m, mt in ttraces.items():
|
| 288 |
-
if mt["resolved"] and (best is None or mt["cost"] < best["cost"]):
|
| 289 |
-
best = mt
|
| 290 |
-
if best:
|
| 291 |
-
oracle_cost += best["cost"]
|
| 292 |
-
oracle_resolved += 1
|
| 293 |
-
else:
|
| 294 |
-
oracle_cost += ttraces.get("claude-opus-4.7", {}).get("cost", 0.317)
|
| 295 |
-
|
| 296 |
-
# ── 5. Print Results ────────────────────────────────────────────
|
| 297 |
-
n = N_TASKS
|
| 298 |
-
print(f"\n{'='*80}")
|
| 299 |
-
print(f"ACO E2E BENCHMARK RESULTS ({n} SWE-bench tasks)")
|
| 300 |
-
print(f"{'='*80}")
|
| 301 |
-
print(f"\n{'Policy':<25} {'Resolved':>10} {'Rate':>10} {'AvgCost':>10} {'vsFrontier':>12}")
|
| 302 |
-
print("-" * 67)
|
| 303 |
-
|
| 304 |
-
full_ac = next(r for r in eval_results if r["config"] == "full_aco")["avg_cost"]
|
| 305 |
-
|
| 306 |
-
for label, res, c in [
|
| 307 |
-
("ACO Full (all modules)",
|
| 308 |
-
next(r for r in eval_results if r["config"] == "full_aco")["resolved"],
|
| 309 |
-
next(r for r in eval_results if r["config"] == "full_aco")["avg_cost"]),
|
| 310 |
-
("Frontier (always claude)", frontier_resolved, frontier_cost/n),
|
| 311 |
-
("Always Cheap", cheap_resolved, cheap_cost/n),
|
| 312 |
-
("Oracle", oracle_resolved, oracle_cost/n),
|
| 313 |
-
]:
|
| 314 |
-
cr = (1 - c / (frontier_cost/n)) * 100
|
| 315 |
-
print(f" {label:<23} {res:>10} {res/n*100:>9.1f}% ${c:>9.4f} {cr:>11.1f}%")
|
| 316 |
-
|
| 317 |
-
print(f"\n{'='*80}")
|
| 318 |
-
print("ABLATION STUDY: Per-Module Impact")
|
| 319 |
-
print(f"{'='*80}")
|
| 320 |
-
print(f"\n{'Configuration':<25} {'Resolved':>10} {'Rate':>8} {'AvgCost':>10} {'vsFull':>10}")
|
| 321 |
-
print("-" * 63)
|
| 322 |
-
|
| 323 |
-
full_r = next(r for r in eval_results if r["config"] == "full_aco")
|
| 324 |
-
for r in eval_results:
|
| 325 |
-
dc = (r["avg_cost"] - full_r["avg_cost"]) / max(full_r["avg_cost"], 0.0001) * 100
|
| 326 |
-
dr = r["resolved"] - full_r["resolved"]
|
| 327 |
-
flag = ""
|
| 328 |
-
if dc > 0 and dr < 0: flag = " ← COSTS MORE, WORSE"
|
| 329 |
-
elif dc < 0 and dr >= 0: flag = " ← CHEAPER, SAME/BETTER"
|
| 330 |
-
elif dc > 0 and dr >= 0: flag = " ← COSTS MORE, BETTER"
|
| 331 |
-
elif dc < 0 and dr < 0: flag = " ← CHEAPER, WORSE"
|
| 332 |
-
print(f" {r['config']:<23} {r['resolved']:>10} {r['resolved']/r['n']*100:>7.1f}% ${r['avg_cost']:>9.4f} {dc:>+9.1f}%{flag}")
|
| 333 |
-
|
| 334 |
-
# ── 6. Cost-Quality Frontier ────────────────────────────────────
|
| 335 |
-
print(f"\n{'='*80}")
|
| 336 |
-
print("COST-QUALITY FRONTIER")
|
| 337 |
-
print(f"{'='*80}")
|
| 338 |
-
|
| 339 |
-
frontier_points = []
|
| 340 |
-
for r in eval_results:
|
| 341 |
-
frontier_points.append((r["avg_cost"], r["success_rate"], r["config"]))
|
| 342 |
-
|
| 343 |
-
frontier_points.append((cheap_cost/n, cheap_resolved/n, "always_cheap"))
|
| 344 |
-
frontier_points.append((frontier_cost/n, frontier_resolved/n, "frontier"))
|
| 345 |
-
frontier_points.append((oracle_cost/n, oracle_resolved/n, "oracle"))
|
| 346 |
-
|
| 347 |
-
frontier_points.sort(key=lambda x: x[0])
|
| 348 |
-
|
| 349 |
-
print(f"\n {'Point':<25} {'Cost/task':>10} {'Success':>10} {'Dominated?':>12}")
|
| 350 |
-
print(" " + "-" * 60)
|
| 351 |
-
|
| 352 |
-
pareto = []
|
| 353 |
-
for cost, succ, name in sorted(frontier_points, key=lambda x: (-x[1], x[0])):
|
| 354 |
-
dominated = any(pc <= cost and ps >= succ and (pc < cost or ps > succ)
|
| 355 |
-
for pc, ps, _ in pareto)
|
| 356 |
-
if not dominated:
|
| 357 |
-
pareto.append((cost, succ, name))
|
| 358 |
-
print(f" {name:<25} ${cost:>9.4f} {succ*100:>9.1f}% {'✅ Pareto' if not dominated else '❌ Dominated':>12}")
|
| 359 |
-
|
| 360 |
-
# ── 7. Save Results ─────────────────────────────────────────────
|
| 361 |
-
output = {
|
| 362 |
-
"n_tasks": n,
|
| 363 |
-
"baselines": {
|
| 364 |
-
"frontier": {"resolved": frontier_resolved, "rate": round(frontier_resolved/n,4),
|
| 365 |
-
"total_cost": round(frontier_cost,2), "avg_cost": round(frontier_cost/n,4)},
|
| 366 |
-
"always_cheap": {"resolved": cheap_resolved, "rate": round(cheap_resolved/n,4),
|
| 367 |
-
"total_cost": round(cheap_cost,2), "avg_cost": round(cheap_cost/n,4)},
|
| 368 |
-
"oracle": {"resolved": oracle_resolved, "rate": round(oracle_resolved/n,4),
|
| 369 |
-
"total_cost": round(oracle_cost,2), "avg_cost": round(oracle_cost/n,4)},
|
| 370 |
-
},
|
| 371 |
-
"aco_configs": [
|
| 372 |
-
{"config": r["config"], "resolved": r["resolved"],
|
| 373 |
-
"rate": round(r["success_rate"], 4), "avg_cost": round(r["avg_cost"], 4),
|
| 374 |
-
"escalations": r["escalated"], "early_terminations": r["terminated_early"],
|
| 375 |
-
"verifier_calls": r["verifier_calls"], "tool_skips": r["tool_skips"]}
|
| 376 |
-
for r in eval_results
|
| 377 |
-
],
|
| 378 |
-
"full_aco_cost_reduction": round(
|
| 379 |
-
(1 - full_ac / (frontier_cost/n)) * 100, 1
|
| 380 |
-
),
|
| 381 |
-
"best_config": min(eval_results, key=lambda r: r["avg_cost"])["config"],
|
| 382 |
-
"pareto_optimal": [name for cost, succ, name in pareto],
|
| 383 |
-
}
|
| 384 |
-
|
| 385 |
-
with open("/tmp/aco_e2e_standalone.json", "w") as f:
|
| 386 |
-
json.dump(output, f, indent=2)
|
| 387 |
-
|
| 388 |
-
from huggingface_hub import HfApi
|
| 389 |
-
api = HfApi()
|
| 390 |
-
api.upload_file(
|
| 391 |
-
path_or_fileobj="/tmp/aco_e2e_standalone.json",
|
| 392 |
-
path_in_repo="eval/e2e_standalone_results.json",
|
| 393 |
-
repo_id="narcolepticchicken/agent-cost-optimizer", repo_type="model",
|
| 394 |
-
)
|
| 395 |
-
|
| 396 |
-
print(f"\n✓ Results: eval/e2e_standalone_results.json")
|
| 397 |
-
print(f"\nDONE!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
training/e2e_v2_fixed.py
DELETED
|
@@ -1,291 +0,0 @@
|
|
| 1 |
-
"""Standalone ACO E2E Benchmark v2 — Fixed cost model.
|
| 2 |
-
|
| 3 |
-
Key fix: Use SWE-Router per-model costs directly instead of simulating
|
| 4 |
-
phantom agent steps. The SWE-Router cost already includes the full agent
|
| 5 |
-
run (all model calls, tool calls, etc).
|
| 6 |
-
|
| 7 |
-
The ACO's job is to:
|
| 8 |
-
1. Route to a tier (choose model)
|
| 9 |
-
2. If that model fails, escalate
|
| 10 |
-
|
| 11 |
-
Cost = sum of all models used until success or abandonment.
|
| 12 |
-
"""
|
| 13 |
-
import json, sys, os, random, pickle
|
| 14 |
-
from collections import defaultdict
|
| 15 |
-
from typing import Dict
|
| 16 |
-
import numpy as np
|
| 17 |
-
|
| 18 |
-
# ── Core Router ──────────────────────────────────────────────────
|
| 19 |
-
CODE_KW = ["python","javascript","code","function","bug","debug","refactor",
|
| 20 |
-
"implement","test","compile","runtime","segfault","thread","async","class",
|
| 21 |
-
"module","import","error","traceback"]
|
| 22 |
-
CRITICAL_KW = ["critical","production","urgent","emergency","live","deployed","safety","security"]
|
| 23 |
-
SIMPLE_KW = ["typo","simple","quick","brief","minor","small","easy","trivial","just"]
|
| 24 |
-
RESEARCH_KW = ["research","investigate","compare","analyze","survey","paper"]
|
| 25 |
-
TOOL_KW = ["search","fetch","retrieve","query","api","database","scrape","aggregate"]
|
| 26 |
-
LONG_KW = ["plan","project","roadmap","orchestrate","migrate","pipeline","deploy","architecture"]
|
| 27 |
-
|
| 28 |
-
FEAT_KEYS = sorted([
|
| 29 |
-
'req_len','num_words','has_code','n_code','has_legal','has_research',
|
| 30 |
-
'has_tool','has_critical','has_simple','has_long','has_math',
|
| 31 |
-
'has_error_msg','has_file_path','n_lines','has_version','has_add',
|
| 32 |
-
'has_fix','has_change','has_remove','has_test','has_doc',
|
| 33 |
-
'has_see_also','has_steps_to_reproduce',
|
| 34 |
-
])
|
| 35 |
-
|
| 36 |
-
TIER_TO_MODEL = {1:'deepseek-v4-flash',2:'gpt-5-mini',3:'gemini-2.5-pro',
|
| 37 |
-
4:'claude-opus-4.7',5:'gemini-3-pro'}
|
| 38 |
-
MODELS = ["deepseek-v4-flash","gpt-5-nano","gpt-5-mini","deepseek-v3.2",
|
| 39 |
-
"gemini-2.5-pro","claude-opus-4.7","gpt-5.2","gemini-3-pro"]
|
| 40 |
-
|
| 41 |
-
def extract_features(text: str) -> np.ndarray:
|
| 42 |
-
r = text.lower()
|
| 43 |
-
feats = {
|
| 44 |
-
'req_len':len(text),'num_words':len(text.split()),
|
| 45 |
-
'has_code':int(any(k in r for k in CODE_KW)),
|
| 46 |
-
'n_code':sum(1 for k in CODE_KW if k in r),
|
| 47 |
-
'has_legal':int(any(k in r for k in["contract","legal","compliance"])),
|
| 48 |
-
'has_research':int(any(k in r for k in RESEARCH_KW)),
|
| 49 |
-
'has_tool':int(any(k in r for k in TOOL_KW)),
|
| 50 |
-
'has_critical':int(any(k in r for k in CRITICAL_KW)),
|
| 51 |
-
'has_simple':int(any(k in r for k in SIMPLE_KW)),
|
| 52 |
-
'has_long':int(any(k in r for k in LONG_KW)),
|
| 53 |
-
'has_math':int(any(k in r for k in["calculate","compute","solve","equation"])),
|
| 54 |
-
'has_error_msg':int('error'in r or'traceback'in r or'exception'in r),
|
| 55 |
-
'has_file_path':int('/'in r),
|
| 56 |
-
'n_lines':text.count('\n')+1,
|
| 57 |
-
'has_version':int('version'in r or'update'in r),
|
| 58 |
-
'has_add':int('add'in r or'new'in r or'create'in r),
|
| 59 |
-
'has_fix':int('fix'in r or'bug'in r or'issue'in r),
|
| 60 |
-
'has_change':int('change'in r or'modify'in r),
|
| 61 |
-
'has_remove':int('remove'in r or'delete'in r),
|
| 62 |
-
'has_test':int('test'in r or'spec'in r),
|
| 63 |
-
'has_doc':int('doc'in r or'readme'in r),
|
| 64 |
-
'has_see_also':int('see also'in r or'related'in r),
|
| 65 |
-
'has_steps_to_reproduce':int('reproduce'in r or'steps'in r),
|
| 66 |
-
}
|
| 67 |
-
return np.array([float(feats.get(k,0))for k in FEAT_KEYS],dtype=np.float32)
|
| 68 |
-
|
| 69 |
-
# ── Load Router ──────────────────────────────────────────────────
|
| 70 |
-
from huggingface_hub import hf_hub_download
|
| 71 |
-
print("Loading router...")
|
| 72 |
-
try:
|
| 73 |
-
bundle_path = hf_hub_download(repo_id="narcolepticchicken/agent-cost-optimizer",
|
| 74 |
-
filename="router_models/router_bundle_v10_fixed.pkl")
|
| 75 |
-
bundle = pickle.load(open(bundle_path,'rb'))
|
| 76 |
-
tier_clfs = {int(k):v for k,v in bundle.get('tier_clfs',{}).items()}
|
| 77 |
-
tier_calibs = {int(k):v for k,v in bundle.get('tier_calibrators',{}).items()}
|
| 78 |
-
print(f" v10 router loaded: {len(tier_clfs)} classifiers")
|
| 79 |
-
except Exception as e:
|
| 80 |
-
print(f" WARNING: {e}, using fallback")
|
| 81 |
-
tier_clfs = {}
|
| 82 |
-
tier_calibs = {}
|
| 83 |
-
|
| 84 |
-
def route(text: str, thresh: float = 0.5) -> int:
|
| 85 |
-
if not tier_clfs: return 1
|
| 86 |
-
x = extract_features(text).reshape(1,-1)
|
| 87 |
-
for t in range(1,6):
|
| 88 |
-
if t in tier_clfs:
|
| 89 |
-
try:
|
| 90 |
-
p = tier_clfs[t].predict_proba(x)[0,1]
|
| 91 |
-
p = float(tier_calibs[t].transform([p])[0])
|
| 92 |
-
if p >= thresh: return t
|
| 93 |
-
except: pass
|
| 94 |
-
return 5
|
| 95 |
-
|
| 96 |
-
# ── 1. Load SWE-Router ──────────────────────────────────────────
|
| 97 |
-
from datasets import load_dataset
|
| 98 |
-
print("\n[1] Loading SWE-Router...")
|
| 99 |
-
traces = defaultdict(dict)
|
| 100 |
-
for m in MODELS:
|
| 101 |
-
ds = load_dataset(f"SWE-Router/swebench-verified-{m}", split="test")
|
| 102 |
-
for row in ds:
|
| 103 |
-
traces[row["instance_id"]][m] = {
|
| 104 |
-
"resolved": row["resolved"],
|
| 105 |
-
"cost": float(row["instance_cost"]),
|
| 106 |
-
}
|
| 107 |
-
print(f" {len(traces)} tasks × {len(MODELS)} models")
|
| 108 |
-
|
| 109 |
-
# ── 2. Simulate ACO ─────────────────────────────────────────────
|
| 110 |
-
# Cost model: just sum the costs of each model we try.
|
| 111 |
-
# ACO routes tier T → execute → if fail, try T+1 → etc.
|
| 112 |
-
# Success = first successful tier's resolution.
|
| 113 |
-
# Cost = sum of costs of all tiers attempted.
|
| 114 |
-
|
| 115 |
-
print("\n[2] Simulating...")
|
| 116 |
-
|
| 117 |
-
def aco_cost(task_id: str, problem: str,
|
| 118 |
-
use_router: bool = True,
|
| 119 |
-
use_feedback: bool = True,
|
| 120 |
-
use_verifier: bool = True,
|
| 121 |
-
max_tiers: int = 5,
|
| 122 |
-
verifier_cost_factor: float = 1.05) -> Dict:
|
| 123 |
-
"""ACO decision + execution: route, execute, escalate if feedback on."""
|
| 124 |
-
tier = route(problem) if use_router else 4
|
| 125 |
-
total_cost = 0.0
|
| 126 |
-
resolved = False
|
| 127 |
-
tiers_tried = 0
|
| 128 |
-
escalated = False
|
| 129 |
-
verifier_cost = 0.0
|
| 130 |
-
|
| 131 |
-
while tiers_tried < max_tiers:
|
| 132 |
-
model = TIER_TO_MODEL.get(tier, "claude-opus-4.7")
|
| 133 |
-
mt = traces[task_id].get(model, {})
|
| 134 |
-
step_cost = mt.get("cost", 0.30)
|
| 135 |
-
|
| 136 |
-
if use_verifier and tier <= 2:
|
| 137 |
-
step_cost *= verifier_cost_factor
|
| 138 |
-
verifier_cost += step_cost * (verifier_cost_factor - 1)
|
| 139 |
-
|
| 140 |
-
total_cost += step_cost
|
| 141 |
-
tiers_tried += 1
|
| 142 |
-
|
| 143 |
-
if mt.get("resolved", False):
|
| 144 |
-
resolved = True
|
| 145 |
-
break
|
| 146 |
-
|
| 147 |
-
if not use_feedback:
|
| 148 |
-
break # no escalation
|
| 149 |
-
|
| 150 |
-
if tier >= 5:
|
| 151 |
-
break
|
| 152 |
-
|
| 153 |
-
tier += 1
|
| 154 |
-
escalated = True
|
| 155 |
-
|
| 156 |
-
return {
|
| 157 |
-
"total_cost": total_cost,
|
| 158 |
-
"resolved": resolved,
|
| 159 |
-
"escalated": escalated,
|
| 160 |
-
"tiers_tried": tiers_tried,
|
| 161 |
-
"verifier_cost": verifier_cost,
|
| 162 |
-
"initial_tier": tier if not escalated else tier - tiers_tried + 1,
|
| 163 |
-
}
|
| 164 |
-
|
| 165 |
-
# ── 3. Run all configs ──────────────────────────────────────────
|
| 166 |
-
configs = {
|
| 167 |
-
"full_aco": (True, True, True),
|
| 168 |
-
"no_router": (False, True, True),
|
| 169 |
-
"no_feedback": (True, False, True),
|
| 170 |
-
"no_verifier": (True, True, False),
|
| 171 |
-
"router_only": (True, False, False),
|
| 172 |
-
"frontier_always": (False, False, False),
|
| 173 |
-
}
|
| 174 |
-
|
| 175 |
-
results_by_config = {}
|
| 176 |
-
for cname, (router, feedback, verifier) in configs.items():
|
| 177 |
-
rlist = []
|
| 178 |
-
for tid, ttraces in traces.items():
|
| 179 |
-
problem = next(iter(ttraces.values())).get("problem", "")
|
| 180 |
-
rlist.append(aco_cost(tid, problem, router, feedback, verifier))
|
| 181 |
-
results_by_config[cname] = rlist
|
| 182 |
-
|
| 183 |
-
# ── 4. Baselines ────────────────────────────────────────────────
|
| 184 |
-
N = len(traces)
|
| 185 |
-
frontier_resolved = sum(1 for tid in traces if traces[tid].get("claude-opus-4.7",{}).get("resolved",False))
|
| 186 |
-
frontier_cost = sum(traces[tid].get("claude-opus-4.7",{}).get("cost",0.317) for tid in traces)
|
| 187 |
-
cheap_resolved = sum(1 for tid in traces if traces[tid].get("deepseek-v4-flash",{}).get("resolved",False))
|
| 188 |
-
cheap_cost = sum(traces[tid].get("deepseek-v4-flash",{}).get("cost",0.014) for tid in traces)
|
| 189 |
-
oracle_cost = 0; oracle_resolved = 0
|
| 190 |
-
for tid, tt in traces.items():
|
| 191 |
-
best = None
|
| 192 |
-
for m, mt in tt.items():
|
| 193 |
-
if mt["resolved"] and (best is None or mt["cost"] < best["cost"]):
|
| 194 |
-
best = mt
|
| 195 |
-
if best:
|
| 196 |
-
oracle_cost += best["cost"]; oracle_resolved += 1
|
| 197 |
-
else:
|
| 198 |
-
oracle_cost += tt.get("claude-opus-4.7",{}).get("cost",0.317)
|
| 199 |
-
|
| 200 |
-
# ── 5. Print ────────────────────────────────────────────────────
|
| 201 |
-
fc = frontier_cost / N
|
| 202 |
-
print(f"\n{'='*80}")
|
| 203 |
-
print(f"ACO E2E RESULTS v2 — 500 SWE-bench tasks")
|
| 204 |
-
print(f"{'='*80}")
|
| 205 |
-
print(f"\n{'Policy':<25} {'Resolved':>10} {'Rate':>10} {'AvgCost':>10} {'vsFrontier':>12} {'Pareto?':>10}")
|
| 206 |
-
print("-" * 77)
|
| 207 |
-
|
| 208 |
-
all_points = []
|
| 209 |
-
for label, data in [
|
| 210 |
-
("ACO Full", results_by_config["full_aco"]),
|
| 211 |
-
("ACO Router+Feedback", results_by_config["no_verifier"]),
|
| 212 |
-
("ACO Router Only", results_by_config["router_only"]),
|
| 213 |
-
("ACO No Feedback", results_by_config["no_feedback"]),
|
| 214 |
-
("ACO No Router", results_by_config["no_router"]),
|
| 215 |
-
("Always Cheap", None),
|
| 216 |
-
("Frontier Always", None),
|
| 217 |
-
("Oracle", None),
|
| 218 |
-
]:
|
| 219 |
-
if label == "Always Cheap":
|
| 220 |
-
r, c = cheap_resolved, cheap_cost/N
|
| 221 |
-
elif label == "Frontier Always":
|
| 222 |
-
r, c = frontier_resolved, fc
|
| 223 |
-
elif label == "Oracle":
|
| 224 |
-
r, c = oracle_resolved, oracle_cost/N
|
| 225 |
-
else:
|
| 226 |
-
r = sum(1 for d in data if d["resolved"])
|
| 227 |
-
c = sum(d["total_cost"] for d in data) / N
|
| 228 |
-
|
| 229 |
-
cr = (1 - c/fc)*100
|
| 230 |
-
all_points.append((label, r, c, cr))
|
| 231 |
-
|
| 232 |
-
print(f" {label:<23} {r:>10} {r/N*100:>9.1f}% ${c:>9.4f} {cr:>11.1f}% {'':>10}")
|
| 233 |
-
|
| 234 |
-
# Pareto analysis
|
| 235 |
-
print(f"\n{'='*80}")
|
| 236 |
-
print("PARETO FRONTIER")
|
| 237 |
-
print(f"{'='*80}")
|
| 238 |
-
pareto_set = []
|
| 239 |
-
for label, r, c, cr in sorted(all_points, key=lambda x: (-x[1], x[0])):
|
| 240 |
-
dominated = any(pr >= r and pc <= c and (pr > r or pc < c) for _, pr, pc, _ in pareto_set)
|
| 241 |
-
pareto_set.append((label, r, c, cr))
|
| 242 |
-
print(f" {label:<25} {r/N*100:>9.1f}% ${c:>9.4f} {'✅ PARETO-OPTIMAL' if not dominated else '❌ Dominated'}")
|
| 243 |
-
|
| 244 |
-
# Module impact
|
| 245 |
-
print(f"\n{'='*80}")
|
| 246 |
-
print("PER-MODULE IMPACT (cost delta from full ACO)")
|
| 247 |
-
print(f"{'='*80}")
|
| 248 |
-
full_r = next(l for l in all_points if l[0] == "ACO Full")
|
| 249 |
-
for label, r, c, cr in all_points:
|
| 250 |
-
if label == "ACO Full": continue
|
| 251 |
-
dc = (c - full_r[2]) / max(full_r[2], 0.0001) * 100
|
| 252 |
-
dr = r - full_r[1]
|
| 253 |
-
note = ""
|
| 254 |
-
if dc < 0 and dr >= 0: note = "← IMPROVEMENT (cheaper, no quality loss)"
|
| 255 |
-
elif dc < 0: note = f"← Cheaper but lost {abs(dr)} resolves"
|
| 256 |
-
elif dr >= 0: note = f"← Better quality at +${c-full_r[2]:.4f}"
|
| 257 |
-
print(f" {label:<23} {dc:>+7.1f}% Δcost {dr:>+4} resolves {note}")
|
| 258 |
-
|
| 259 |
-
# ── 6. Save ─────────────────────────────────────────────────────
|
| 260 |
-
output = {
|
| 261 |
-
"n": N,
|
| 262 |
-
"configs": {
|
| 263 |
-
name: {
|
| 264 |
-
"resolved": sum(1 for d in data if d["resolved"]),
|
| 265 |
-
"rate": round(sum(1 for d in data if d["resolved"])/N, 4),
|
| 266 |
-
"avg_cost": round(sum(d["total_cost"] for d in data)/N, 4),
|
| 267 |
-
"total_cost": round(sum(d["total_cost"] for d in data), 2),
|
| 268 |
-
"avg_tiers_tried": round(sum(d["tiers_tried"] for d in data)/N, 2),
|
| 269 |
-
"escalated_rate": round(sum(1 for d in data if d["escalated"])/N, 2),
|
| 270 |
-
"verifier_cost": round(sum(d["verifier_cost"] for d in data), 2),
|
| 271 |
-
} for name, data in results_by_config.items()
|
| 272 |
-
},
|
| 273 |
-
"baselines": {
|
| 274 |
-
"frontier": {"resolved": frontier_resolved, "rate": round(frontier_resolved/N,4), "avg_cost": round(fc,4)},
|
| 275 |
-
"always_cheap": {"resolved": cheap_resolved, "rate": round(cheap_resolved/N,4), "avg_cost": round(cheap_cost/N,4)},
|
| 276 |
-
"oracle": {"resolved": oracle_resolved, "rate": round(oracle_resolved/N,4), "avg_cost": round(oracle_cost/N,4)},
|
| 277 |
-
},
|
| 278 |
-
}
|
| 279 |
-
|
| 280 |
-
with open("/tmp/aco_e2e_v2_fixed.json","w") as f:
|
| 281 |
-
json.dump(output, f, indent=2)
|
| 282 |
-
|
| 283 |
-
from huggingface_hub import HfApi
|
| 284 |
-
api = HfApi()
|
| 285 |
-
api.upload_file(
|
| 286 |
-
path_or_fileobj="/tmp/aco_e2e_v2_fixed.json",
|
| 287 |
-
path_in_repo="eval/e2e_v2_fixed_results.json",
|
| 288 |
-
repo_id="narcolepticchicken/agent-cost-optimizer", repo_type="model",
|
| 289 |
-
)
|
| 290 |
-
print(f"\n✓ eval/e2e_v2_fixed_results.json")
|
| 291 |
-
print("DONE!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|