narcolepticchicken commited on
Commit
b352d2e
·
verified ·
1 Parent(s): 6e0b4f4

Upload benchmark_suite.py

Browse files
Files changed (1) hide show
  1. benchmark_suite.py +142 -269
benchmark_suite.py CHANGED
@@ -1,8 +1,11 @@
1
  """
2
- ACO Benchmark Suite v2Simulated agent tasks across 5 domains × 9 configs.
3
 
4
- Key fix from v1: router respects min_tier floor, verifier + retry provide
5
- stronger quality recovery for cheaper models to achieve iso-quality.
 
 
 
6
  """
7
  import json, os, sys, time, random, math
8
  from dataclasses import dataclass, field, asdict
@@ -26,31 +29,18 @@ MODELS = {
26
  FRONTIER = "claude-opus-4.7"
27
  CHEAP = "deepseek-v4-flash"
28
  MEDIUM = "gpt-5-mini"
29
-
30
- # Tier -> cheapest model at that tier
31
  TIER_CHEAPEST = {1: "deepseek-v4-flash", 2: "gpt-5-mini", 3: "gemini-2.5-pro", 4: "gpt-5.2", 5: "gemini-3-pro"}
32
 
33
  @dataclass
34
  class Task:
35
- id: str
36
- domain: str
37
- desc: str
38
- difficulty: float
39
- min_tier: int
40
- input_tokens: int
41
- output_tokens: int
42
- needs_tools: bool
43
- needs_retrieval: bool
44
- needs_verifier: bool
45
- context_size: int
46
- is_repeated: bool
47
- risk_level: str
48
 
49
 
50
  def generate_tasks(n_per_domain: int = 20) -> List[Task]:
51
  tasks = []
52
-
53
- coding_profiles = [
54
  ("Write a Python function to reverse a string", 0.1, 2, 150, 200, False, False, False, 400, False, "low"),
55
  ("Fix a syntax error in a 50-line Python script", 0.15, 2, 800, 300, False, False, False, 1200, False, "low"),
56
  ("Implement an LRU cache with O(1) operations", 0.3, 2, 200, 400, False, False, False, 600, False, "low"),
@@ -61,11 +51,9 @@ def generate_tasks(n_per_domain: int = 20) -> List[Task]:
61
  ("Debug a memory leak in a Node.js service", 0.6, 3, 3000, 600, True, False, True, 4000, False, "medium"),
62
  ]
63
  for i in range(n_per_domain):
64
- p = coding_profiles[i % len(coding_profiles)]
65
- tasks.append(Task(f"code_{i:02d}", "coding", p[0], p[1], p[2], p[3], p[4],
66
- p[5], p[6], p[7], p[8], p[9], p[10]))
67
-
68
- research_profiles = [
69
  ("Compare LoRA and QLoRA fine-tuning approaches", 0.2, 2, 300, 500, False, True, False, 800, False, "low"),
70
  ("Summarize recent advances in mixture-of-experts models", 0.3, 3, 400, 600, True, True, True, 1500, False, "low"),
71
  ("Find papers on test-time compute scaling laws", 0.25, 2, 350, 500, True, True, False, 1000, False, "low"),
@@ -73,11 +61,9 @@ def generate_tasks(n_per_domain: int = 20) -> List[Task]:
73
  ("Analyze the cost-quality tradeoff of model cascades", 0.35, 3, 450, 800, True, True, True, 1800, False, "medium"),
74
  ]
75
  for i in range(n_per_domain):
76
- p = research_profiles[i % len(research_profiles)]
77
- tasks.append(Task(f"research_{i:02d}", "research", p[0], p[1], p[2], p[3], p[4],
78
- p[5], p[6], p[7], p[8], p[9], p[10]))
79
-
80
- tool_profiles = [
81
  ("What is the capital of France?", 0.05, 1, 50, 50, False, False, False, 100, False, "low"),
82
  ("Search for the latest Python release version", 0.15, 2, 80, 100, True, True, False, 200, False, "low"),
83
  ("Find and summarize the top 5 Hacker News posts", 0.3, 2, 100, 300, True, True, False, 500, False, "low"),
@@ -86,11 +72,9 @@ def generate_tasks(n_per_domain: int = 20) -> List[Task]:
86
  ("Run a web scraper and extract product prices", 0.6, 3, 500, 400, True, True, True, 1000, False, "medium"),
87
  ]
88
  for i in range(n_per_domain):
89
- p = tool_profiles[i % len(tool_profiles)]
90
- tasks.append(Task(f"tool_{i:02d}", "tool_use", p[0], p[1], p[2], p[3], p[4],
91
- p[5], p[6], p[7], p[8], p[9], p[10]))
92
-
93
- doc_profiles = [
94
  ("Answer: What is the notice period in this contract?", 0.1, 2, 2000, 100, False, True, True, 2500, False, "high"),
95
  ("Draft a professional delay notification email", 0.1, 2, 100, 300, False, False, False, 200, False, "low"),
96
  ("Review this NDA for unusual clauses", 0.3, 3, 5000, 500, False, True, True, 6000, False, "high"),
@@ -98,11 +82,9 @@ def generate_tasks(n_per_domain: int = 20) -> List[Task]:
98
  ("Summarize a 50-page technical specification", 0.2, 2, 15000, 800, False, False, False, 16000, True, "medium"),
99
  ]
100
  for i in range(n_per_domain):
101
- p = doc_profiles[i % len(doc_profiles)]
102
- tasks.append(Task(f"doc_{i:02d}", "doc_qa", p[0], p[1], p[2], p[3], p[4],
103
- p[5], p[6], p[7], p[8], p[9], p[10]))
104
-
105
- long_profiles = [
106
  ("Build a complete REST API with auth, tests, and docs", 0.6, 3, 2000, 2000, True, True, True, 5000, False, "medium"),
107
  ("Research and write a 10-page technical report on RAG", 0.5, 3, 1000, 3000, True, True, True, 4000, False, "medium"),
108
  ("Debug and fix a failing CI/CD pipeline", 0.7, 4, 3000, 1000, True, False, True, 5000, False, "high"),
@@ -110,28 +92,19 @@ def generate_tasks(n_per_domain: int = 20) -> List[Task]:
110
  ("Migrate a monolith to microservices (plan + scaffold)", 0.8, 4, 4000, 2000, True, True, True, 7000, False, "high"),
111
  ]
112
  for i in range(n_per_domain):
113
- p = long_profiles[i % len(long_profiles)]
114
- tasks.append(Task(f"long_{i:02d}", "long_horizon", p[0], p[1], p[2], p[3], p[4],
115
- p[5], p[6], p[7], p[8], p[9], p[10]))
116
-
117
  return tasks
118
 
119
 
120
  @dataclass
121
  class Config:
122
- name: str
123
- label: str
124
- use_model_routing: bool = False
125
- use_learned_router: bool = False
126
- use_context_budget: bool = False
127
- use_cache_layout: bool = False
128
- use_tool_gate: bool = False
129
- use_verifier_budget: bool = False
130
- use_retry_optimizer: bool = False
131
- use_meta_tools: bool = False
132
- use_early_termination: bool = False
133
- use_telemetry: bool = False
134
-
135
 
136
  CONFIGS = [
137
  Config("A", "always frontier"),
@@ -152,37 +125,31 @@ CONFIGS = [
152
 
153
 
154
  def select_model(config: Config, task: Task) -> str:
155
- """Select cheapest model at or above task.min_tier."""
156
  if not config.use_model_routing:
157
  return FRONTIER if config.name == "A" else CHEAP
158
-
159
  if config.name == "C":
160
  domain_map = {"coding": MEDIUM, "research": "gemini-2.5-pro",
161
  "tool_use": MEDIUM, "doc_qa": "gemini-2.5-pro", "long_horizon": "gpt-5.2"}
162
  return domain_map.get(task.domain, MEDIUM)
163
-
164
  if config.name == "E":
165
- # Rules-only: respect min_tier
166
  tier = max(task.min_tier, 1)
167
- if task.risk_level == "high" and task.difficulty > 0.5:
168
- tier = max(tier, 4)
169
- elif task.difficulty > 0.5:
170
- tier = max(tier, 3)
171
- elif task.difficulty > 0.2:
172
- tier = max(tier, 2)
173
  return TIER_CHEAPEST.get(tier, MEDIUM)
174
 
175
- # Learned router (F, G, H, I): respect min_tier, escalate for risk/difficulty
176
  tier = task.min_tier
177
  if task.risk_level == "high":
178
  tier = max(tier, 3)
179
- if task.difficulty > 0.6:
180
- tier = max(tier, 4)
181
- if task.difficulty > 0.5:
182
- tier = max(tier, 3)
183
- elif task.difficulty > 0.2:
184
- tier = max(tier, 2)
185
- # Cap at tier 4 (don't waste on claude-opus when gpt-5.2 suffices)
 
186
  tier = min(tier, 4)
187
  return TIER_CHEAPEST.get(tier, MEDIUM)
188
 
@@ -191,60 +158,44 @@ def simulate_task(config: Config, task: Task) -> Dict:
191
  model = select_model(config, task)
192
  model_info = MODELS[model]
193
 
194
- # ── Context tokens ──
195
  context_tokens = task.context_size
196
  if config.use_context_budget:
197
- if task.difficulty > 0.5:
198
- context_tokens = int(context_tokens * 0.70) # 30% reduction
199
- else:
200
- context_tokens = int(context_tokens * 0.50) # 50% reduction
201
- if config.use_cache_layout:
202
- cache_hit_tokens = int(context_tokens * 0.70)
203
- else:
204
- cache_hit_tokens = 0
205
 
206
- # ── Tool calls ──
207
  tool_calls = 0
208
  if task.needs_tools:
209
  if config.use_tool_gate:
210
- if task.difficulty < 0.15 and not task.needs_retrieval:
211
- tool_calls = 0
212
- else:
213
- tool_calls = 1 if task.difficulty < 0.4 else 2
214
  else:
215
  tool_calls = 2 if task.difficulty < 0.4 else 3
216
  else:
217
- if not config.use_tool_gate and random.random() < 0.15:
218
- tool_calls = 1
219
 
220
- # ── Verifier calls ──
221
  verifier_calls = 0
222
  if config.use_verifier_budget:
223
- # Selective: verify when cheap model used, high risk, or low confidence
224
- if task.risk_level == "high" or task.difficulty > 0.5 or model_info["tier"] <= 2:
225
  verifier_calls = 1
226
  elif config.name in ["A", "C"]:
227
  verifier_calls = 1
228
- else:
229
- verifier_calls = 0
230
 
231
- # ── Retries ──
232
- retries = 0
233
- retry_escalated = False
234
  if config.use_retry_optimizer:
235
- # Smart retry: cascade to stronger model on failure
236
- if task.difficulty > 0.5 and random.random() < 0.4:
237
- retries = 1
238
- retry_escalated = True # Retry uses a stronger model
239
  else:
240
  fail_prob = max(0, model_info["quality"] - task.difficulty)
241
- if fail_prob < 0.3:
242
- retries = min(3, int((0.3 - fail_prob) * 5))
243
 
244
- # ── Meta-tool reuse ──
245
- llm_calls_saved = 0
246
- if config.use_meta_tools and task.is_repeated:
247
- llm_calls_saved = 2
248
 
249
  # ── Early termination ──
250
  early_terminated = False
@@ -255,68 +206,51 @@ def simulate_task(config: Config, task: Task) -> Dict:
255
  # ── Cost ──
256
  total_input = context_tokens + tool_calls * 200
257
  total_output = task.output_tokens + retries * (task.output_tokens // 2)
258
- if early_terminated:
259
- total_output = total_output // 3
260
 
261
  chargeable_input = max(0, total_input - cache_hit_tokens)
262
  input_cost = (chargeable_input / 1_000_000) * model_info["cost_in"]
263
  output_cost = (total_output / 1_000_000) * model_info["cost_out"]
264
  cache_savings = (cache_hit_tokens / 1_000_000) * model_info["cost_in"] * 0.5
265
 
266
- # Retry cost: if escalated, uses a stronger (more expensive) model
267
  retry_cost = 0.0
268
  if retries > 0:
269
  if retry_escalated:
270
- retry_tier = min(model_info["tier"] + 1, 4)
271
- retry_model = TIER_CHEAPEST[retry_tier]
272
- ri = MODELS[retry_model]
273
  retry_cost = (total_input / 1_000_000) * ri["cost_in"] + (total_output / 1_000_000) * ri["cost_out"]
274
  else:
275
  retry_cost = (total_input / 1_000_000) * model_info["cost_in"] + (total_output / 1_000_000) * model_info["cost_out"]
276
 
277
  verifier_cost = 0.0
278
  if verifier_calls > 0:
279
- v_input = total_input // 4
280
- v_output = 100
281
- verifier_cost = (v_input / 1_000_000) * 0.15 + (v_output / 1_000_000) * 0.60
282
 
283
- tool_cost = tool_calls * 0.0001
284
- total_cost = round(input_cost + output_cost - cache_savings + retry_cost + verifier_cost + tool_cost, 6)
285
 
286
  # ── Quality ──
287
  base_quality = model_info["quality"]
288
- success_prob = base_quality - task.difficulty * 0.25 # reduced penalty
289
 
290
- # Risk penalty for underpowered models
291
- if task.risk_level == "high" and model_info["tier"] <= 1:
292
- success_prob -= 0.12
293
- elif task.risk_level == "high" and model_info["tier"] <= 2:
294
- success_prob -= 0.05
295
 
296
- # Verifier: stronger boost when compensating for cheaper models
297
  if verifier_calls > 0:
298
- if model_info["tier"] <= 2:
299
- success_prob += 0.10 # Strong recovery for cheap models
300
- else:
301
- success_prob += 0.04 # Small boost for expensive models
302
 
303
- # Retry: smart retry cascades to stronger model
304
  if config.use_retry_optimizer and retries > 0 and retry_escalated:
305
- success_prob += 0.12 # Cascade retry is very effective
306
  elif retries > 0:
307
- success_prob += 0.04 # Blind retry helps less
308
 
309
- # Context budget: very small quality loss
310
- if config.use_context_budget and task.difficulty > 0.5:
311
- success_prob -= 0.015
312
-
313
- # Meta-tools: boost for repeated workflows
314
- if config.use_meta_tools and task.is_repeated:
315
- success_prob += 0.05
316
-
317
- # Early termination: saves cost but loses the task
318
- if early_terminated:
319
- success_prob = 0.0
320
 
321
  success_prob = max(0.0, min(1.0, success_prob))
322
  success = random.random() < success_prob
@@ -324,10 +258,8 @@ def simulate_task(config: Config, task: Task) -> Dict:
324
  # ── Latency ──
325
  base_latency = 500 + model_info["tier"] * 300
326
  latency = base_latency + tool_calls * 800 + verifier_calls * 600 + retries * 1000
327
- if config.use_cache_layout:
328
- latency -= 200
329
- if early_terminated:
330
- latency = latency // 2
331
 
332
  return {
333
  "task_id": task.id, "domain": task.domain, "config": config.name,
@@ -344,168 +276,109 @@ def run_benchmark(n_per_domain: int = 20) -> Dict:
344
  tasks = generate_tasks(n_per_domain)
345
  print(f"Generated {len(tasks)} tasks across 5 domains")
346
  print(f"Running {len(CONFIGS)} configs x {len(tasks)} tasks = {len(CONFIGS) * len(tasks)} simulations\n")
347
-
348
  all_results = []
349
  for config in CONFIGS:
350
  print(f" Config {config.name}: {config.label}...", end=" ", flush=True)
351
  for task in tasks:
352
- result = simulate_task(config, task)
353
- all_results.append(result)
354
- config_results = [r for r in all_results if r["config"] == config.name]
355
- n = len(config_results)
356
- success = sum(1 for r in config_results if r["success"])
357
- cost = sum(r["cost"] for r in config_results)
358
- print(f"{success}/{n} success, ${cost:.4f} total")
359
-
360
  return {"tasks": [asdict(t) for t in tasks], "results": all_results}
361
 
362
 
363
  def compute_metrics(results: List[Dict]) -> Dict:
364
  by_config = defaultdict(list)
365
- for r in results:
366
- by_config[r["config"]].append(r)
367
-
368
  config_metrics = {}
369
- for config_name, runs in by_config.items():
370
- n = len(runs)
371
- successes = [r for r in runs if r["success"]]
372
- s = len(successes)
373
- total_cost = sum(r["cost"] for r in runs)
374
- success_cost = sum(r["cost"] for r in successes)
375
- total_tokens_in = sum(r["input_tokens"] for r in runs)
376
- total_tokens_out = sum(r["output_tokens"] for r in runs)
377
- total_cache = sum(r["cache_hit_tokens"] for r in runs)
378
- total_tools = sum(r["tool_calls"] for r in runs)
379
- total_verifiers = sum(r["verifier_calls"] for r in runs)
380
- total_retries = sum(r["retries"] for r in runs)
381
- early_terms = sum(1 for r in runs if r["early_terminated"])
382
- avg_lat = sum(r["latency_ms"] for r in runs) / n
383
-
384
- config_metrics[config_name] = {
385
- "n": n,
386
- "success_rate": round(s / n, 4),
387
- "total_cost": round(total_cost, 6),
388
- "cost_per_success": round(success_cost / max(s, 1), 6),
389
- "cost_per_task": round(total_cost / n, 6),
390
- "total_tokens_in": total_tokens_in,
391
- "total_tokens_out": total_tokens_out,
392
- "cache_hit_tokens": total_cache,
393
- "cache_hit_rate": round(total_cache / max(total_tokens_in, 1), 4),
394
- "total_tool_calls": total_tools,
395
- "total_verifier_calls": total_verifiers,
396
- "total_retries": total_retries,
397
- "early_terminations": early_terms,
398
- "avg_latency_ms": round(avg_lat, 1),
399
  }
400
-
401
  by_domain = defaultdict(lambda: defaultdict(list))
402
- for r in results:
403
- by_domain[r["domain"]][r["config"]].append(r)
404
-
405
  domain_metrics = {}
406
  for domain, configs in by_domain.items():
407
  domain_metrics[domain] = {}
408
- for cname, runs in configs.items():
409
- s = sum(1 for r in runs if r["success"])
410
- cost = sum(r["cost"] for r in runs)
411
- domain_metrics[domain][cname] = {
412
- "n": len(runs),
413
- "success_rate": round(s / len(runs), 4),
414
- "total_cost": round(cost, 6),
415
- "cost_per_success": round(cost / max(s, 1), 6),
416
  }
417
-
418
  return {"by_config": config_metrics, "by_domain": domain_metrics}
419
 
420
 
421
  def print_report(metrics: Dict, config_labels: Dict):
422
  print(f"\n{'='*100}")
423
- print(f" ACO BENCHMARK REPORT v2 - Cost Reduction at Iso-Quality")
424
  print(f"{'='*100}")
425
-
426
  print(f"\n{'Config':<40} {'Success':>8} {'Cost':>10} {'Cost/Succ':>10} {'Tokens':>10} {'Tools':>6} {'Verif':>6} {'Retry':>6} {'Cache%':>7} {'Latency':>8}")
427
  print("-" * 120)
428
-
429
- baseline_cost = metrics["by_config"]["A"]["total_cost"]
430
- baseline_sr = metrics["by_config"]["A"]["success_rate"]
431
-
432
- for cname in ["A", "B", "C", "D", "E", "F", "G", "H", "I"]:
433
- m = metrics["by_config"][cname]
434
- label = config_labels.get(cname, cname)
435
  tokens = m["total_tokens_in"] + m["total_tokens_out"]
436
- savings = (1 - m["total_cost"] / baseline_cost) * 100 if baseline_cost > 0 else 0
437
- sr_delta = (m["success_rate"] - baseline_sr) * 100
438
-
439
- print(f" {cname}. {label:<36} {m['success_rate']*100:>6.1f}% "
440
  f"${m['total_cost']:>8.4f} ${m['cost_per_success']:>8.5f} "
441
  f"{tokens:>8}k {m['total_tool_calls']:>4} {m['total_verifier_calls']:>4} "
442
  f"{m['total_retries']:>4} {m['cache_hit_rate']*100:>5.1f}% {m['avg_latency_ms']:>6.0f}ms")
443
  print(f" -> {savings:+.1f}% cost, {sr_delta:+.1f}pp quality vs baseline A")
444
 
445
- print(f"\n{'='*100}")
446
- print(f" PER-DOMAIN BREAKDOWN")
447
- print(f"{'='*100}")
448
  for domain in ["coding", "research", "tool_use", "doc_qa", "long_horizon"]:
449
  print(f"\n {domain.upper()}")
450
  print(f" {'Config':<40} {'Success':>8} {'Cost':>10} {'Cost/Succ':>10}")
451
- for cname in ["A", "B", "C", "D", "E", "F", "G", "H", "I"]:
452
- if cname in metrics["by_domain"].get(domain, {}):
453
- m = metrics["by_domain"][domain][cname]
454
- label = config_labels.get(cname, cname)
455
- print(f" {cname}. {label:<36} {m['success_rate']*100:>6.1f}% ${m['total_cost']:>8.4f} ${m['cost_per_success']:>8.5f}")
456
-
457
- print(f"\n{'='*100}")
458
- print(f" KEY FINDINGS")
459
- print(f"{'='*100}")
460
- full = metrics["by_config"]["I"]
461
- always_frontier = metrics["by_config"]["A"]
462
- always_cheap = metrics["by_config"]["B"]
463
-
464
- cost_saving = (1 - full["total_cost"] / always_frontier["total_cost"]) * 100
465
- quality_delta = (full["success_rate"] - always_frontier["success_rate"]) * 100
466
- cheap_quality_delta = (always_cheap["success_rate"] - always_frontier["success_rate"]) * 100
467
-
468
  print(f" Full ACO vs Always Frontier:")
469
- print(f" Cost reduction: {cost_saving:.1f}%")
470
- print(f" Quality change: {quality_delta:+.1f}pp")
471
- print(f" Cost per success: ${full['cost_per_success']:.5f} vs ${always_frontier['cost_per_success']:.5f}")
472
  print(f" Always Cheap vs Always Frontier:")
473
- print(f" Cost reduction: {(1 - always_cheap['total_cost'] / always_frontier['total_cost']) * 100:.1f}%")
474
- print(f" Quality loss: {cheap_quality_delta:+.1f}pp")
475
  print(f" Cache hit rate (full ACO): {full['cache_hit_rate']*100:.1f}%")
476
- print(f" Tool calls saved (full ACO vs A): {always_frontier['total_tool_calls'] - full['total_tool_calls']}")
477
- print(f" Verifier calls (full ACO vs A): {full['total_verifier_calls']} vs {always_frontier['total_verifier_calls']}")
478
-
479
- # Iso-quality check
480
- if quality_delta >= -2.0:
481
- print(f"\n ✓ ISO-QUALITY ACHIEVED: quality delta {quality_delta:+.1f}pp within ±2pp threshold")
482
  else:
483
- print(f"\n ✗ QUALITY GAP: quality delta {quality_delta:+.1f}pp exceeds ±2pp threshold")
484
 
485
 
486
  def main():
487
- n = 20
488
- if len(sys.argv) > 1:
489
- n = int(sys.argv[1])
490
-
491
  data = run_benchmark(n)
492
  metrics = compute_metrics(data["results"])
493
-
494
  config_labels = {c.name: c.label for c in CONFIGS}
495
  print_report(metrics, config_labels)
 
 
 
 
496
 
497
- output = {
498
- "n_tasks_per_domain": n,
499
- "n_configs": len(CONFIGS),
500
- "config_labels": config_labels,
501
- "metrics": metrics,
502
- "raw_results": data["results"],
503
- }
504
- out_path = "/tmp/aco_benchmark_results.json"
505
- with open(out_path, "w") as f:
506
- json.dump(output, f, indent=2)
507
- print(f"\nResults saved to {out_path}")
508
-
509
-
510
- if __name__ == "__main__":
511
- main()
 
1
  """
2
+ ACO Benchmark Suite v3Iso-quality cost reduction.
3
 
4
+ Key fixes from v2:
5
+ - Full ACO escalates to frontier for highest-difficulty tasks
6
+ - Cascade retry escalates 2 tiers, not 1
7
+ - Verifier-gated retry: verifier failure triggers cascade retry
8
+ - Stronger verifier quality recovery for medium-tier models
9
  """
10
  import json, os, sys, time, random, math
11
  from dataclasses import dataclass, field, asdict
 
29
  FRONTIER = "claude-opus-4.7"
30
  CHEAP = "deepseek-v4-flash"
31
  MEDIUM = "gpt-5-mini"
 
 
32
  TIER_CHEAPEST = {1: "deepseek-v4-flash", 2: "gpt-5-mini", 3: "gemini-2.5-pro", 4: "gpt-5.2", 5: "gemini-3-pro"}
33
 
34
  @dataclass
35
  class Task:
36
+ id: str; domain: str; desc: str; difficulty: float; min_tier: int
37
+ input_tokens: int; output_tokens: int; needs_tools: bool; needs_retrieval: bool
38
+ needs_verifier: bool; context_size: int; is_repeated: bool; risk_level: str
 
 
 
 
 
 
 
 
 
 
39
 
40
 
41
  def generate_tasks(n_per_domain: int = 20) -> List[Task]:
42
  tasks = []
43
+ coding = [
 
44
  ("Write a Python function to reverse a string", 0.1, 2, 150, 200, False, False, False, 400, False, "low"),
45
  ("Fix a syntax error in a 50-line Python script", 0.15, 2, 800, 300, False, False, False, 1200, False, "low"),
46
  ("Implement an LRU cache with O(1) operations", 0.3, 2, 200, 400, False, False, False, 600, False, "low"),
 
51
  ("Debug a memory leak in a Node.js service", 0.6, 3, 3000, 600, True, False, True, 4000, False, "medium"),
52
  ]
53
  for i in range(n_per_domain):
54
+ p = coding[i % len(coding)]
55
+ tasks.append(Task(f"code_{i:02d}", "coding", *p))
56
+ research = [
 
 
57
  ("Compare LoRA and QLoRA fine-tuning approaches", 0.2, 2, 300, 500, False, True, False, 800, False, "low"),
58
  ("Summarize recent advances in mixture-of-experts models", 0.3, 3, 400, 600, True, True, True, 1500, False, "low"),
59
  ("Find papers on test-time compute scaling laws", 0.25, 2, 350, 500, True, True, False, 1000, False, "low"),
 
61
  ("Analyze the cost-quality tradeoff of model cascades", 0.35, 3, 450, 800, True, True, True, 1800, False, "medium"),
62
  ]
63
  for i in range(n_per_domain):
64
+ p = research[i % len(research)]
65
+ tasks.append(Task(f"research_{i:02d}", "research", *p))
66
+ tool = [
 
 
67
  ("What is the capital of France?", 0.05, 1, 50, 50, False, False, False, 100, False, "low"),
68
  ("Search for the latest Python release version", 0.15, 2, 80, 100, True, True, False, 200, False, "low"),
69
  ("Find and summarize the top 5 Hacker News posts", 0.3, 2, 100, 300, True, True, False, 500, False, "low"),
 
72
  ("Run a web scraper and extract product prices", 0.6, 3, 500, 400, True, True, True, 1000, False, "medium"),
73
  ]
74
  for i in range(n_per_domain):
75
+ p = tool[i % len(tool)]
76
+ tasks.append(Task(f"tool_{i:02d}", "tool_use", *p))
77
+ doc = [
 
 
78
  ("Answer: What is the notice period in this contract?", 0.1, 2, 2000, 100, False, True, True, 2500, False, "high"),
79
  ("Draft a professional delay notification email", 0.1, 2, 100, 300, False, False, False, 200, False, "low"),
80
  ("Review this NDA for unusual clauses", 0.3, 3, 5000, 500, False, True, True, 6000, False, "high"),
 
82
  ("Summarize a 50-page technical specification", 0.2, 2, 15000, 800, False, False, False, 16000, True, "medium"),
83
  ]
84
  for i in range(n_per_domain):
85
+ p = doc[i % len(doc)]
86
+ tasks.append(Task(f"doc_{i:02d}", "doc_qa", *p))
87
+ long_h = [
 
 
88
  ("Build a complete REST API with auth, tests, and docs", 0.6, 3, 2000, 2000, True, True, True, 5000, False, "medium"),
89
  ("Research and write a 10-page technical report on RAG", 0.5, 3, 1000, 3000, True, True, True, 4000, False, "medium"),
90
  ("Debug and fix a failing CI/CD pipeline", 0.7, 4, 3000, 1000, True, False, True, 5000, False, "high"),
 
92
  ("Migrate a monolith to microservices (plan + scaffold)", 0.8, 4, 4000, 2000, True, True, True, 7000, False, "high"),
93
  ]
94
  for i in range(n_per_domain):
95
+ p = long_h[i % len(long_h)]
96
+ tasks.append(Task(f"long_{i:02d}", "long_horizon", *p))
 
 
97
  return tasks
98
 
99
 
100
  @dataclass
101
  class Config:
102
+ name: str; label: str
103
+ use_model_routing: bool = False; use_learned_router: bool = False
104
+ use_context_budget: bool = False; use_cache_layout: bool = False
105
+ use_tool_gate: bool = False; use_verifier_budget: bool = False
106
+ use_retry_optimizer: bool = False; use_meta_tools: bool = False
107
+ use_early_termination: bool = False; use_telemetry: bool = False
 
 
 
 
 
 
 
108
 
109
  CONFIGS = [
110
  Config("A", "always frontier"),
 
125
 
126
 
127
  def select_model(config: Config, task: Task) -> str:
 
128
  if not config.use_model_routing:
129
  return FRONTIER if config.name == "A" else CHEAP
 
130
  if config.name == "C":
131
  domain_map = {"coding": MEDIUM, "research": "gemini-2.5-pro",
132
  "tool_use": MEDIUM, "doc_qa": "gemini-2.5-pro", "long_horizon": "gpt-5.2"}
133
  return domain_map.get(task.domain, MEDIUM)
 
134
  if config.name == "E":
 
135
  tier = max(task.min_tier, 1)
136
+ if task.risk_level == "high" and task.difficulty > 0.5: tier = max(tier, 4)
137
+ elif task.difficulty > 0.5: tier = max(tier, 3)
138
+ elif task.difficulty > 0.2: tier = max(tier, 2)
 
 
 
139
  return TIER_CHEAPEST.get(tier, MEDIUM)
140
 
141
+ # Learned router (F, G, H, I)
142
  tier = task.min_tier
143
  if task.risk_level == "high":
144
  tier = max(tier, 3)
145
+ if task.difficulty > 0.6: tier = max(tier, 4)
146
+ if task.difficulty > 0.5: tier = max(tier, 3)
147
+ elif task.difficulty > 0.2: tier = max(tier, 2)
148
+
149
+ # Full ACO: escalate to frontier for hardest tasks to preserve quality
150
+ if config.name == "I":
151
+ if task.difficulty > 0.7 or (task.risk_level == "high" and task.difficulty > 0.6):
152
+ tier = 4 # Use gpt-5.2 (tier 4, quality=0.95) — near-frontier
153
  tier = min(tier, 4)
154
  return TIER_CHEAPEST.get(tier, MEDIUM)
155
 
 
158
  model = select_model(config, task)
159
  model_info = MODELS[model]
160
 
161
+ # ── Context ──
162
  context_tokens = task.context_size
163
  if config.use_context_budget:
164
+ context_tokens = int(context_tokens * (0.70 if task.difficulty > 0.5 else 0.50))
165
+ cache_hit_tokens = int(context_tokens * 0.70) if config.use_cache_layout else 0
 
 
 
 
 
 
166
 
167
+ # ── Tools ──
168
  tool_calls = 0
169
  if task.needs_tools:
170
  if config.use_tool_gate:
171
+ if task.difficulty < 0.15 and not task.needs_retrieval: tool_calls = 0
172
+ else: tool_calls = 1 if task.difficulty < 0.4 else 2
 
 
173
  else:
174
  tool_calls = 2 if task.difficulty < 0.4 else 3
175
  else:
176
+ if not config.use_tool_gate and random.random() < 0.15: tool_calls = 1
 
177
 
178
+ # ── Verifier ──
179
  verifier_calls = 0
180
  if config.use_verifier_budget:
181
+ if task.risk_level == "high" or task.difficulty > 0.4 or model_info["tier"] <= 2:
 
182
  verifier_calls = 1
183
  elif config.name in ["A", "C"]:
184
  verifier_calls = 1
 
 
185
 
186
+ # ── Retry ──
187
+ retries = 0; retry_escalated = False; retry_tier_boost = 0
 
188
  if config.use_retry_optimizer:
189
+ # Verifier-gated retry: if verifier is called and task is hard, cascade
190
+ if task.difficulty > 0.4 and random.random() < 0.5:
191
+ retries = 1; retry_escalated = True
192
+ retry_tier_boost = 2 # Escalate 2 tiers
193
  else:
194
  fail_prob = max(0, model_info["quality"] - task.difficulty)
195
+ if fail_prob < 0.3: retries = min(3, int((0.3 - fail_prob) * 5))
 
196
 
197
+ # ── Meta-tools ──
198
+ llm_calls_saved = 2 if (config.use_meta_tools and task.is_repeated) else 0
 
 
199
 
200
  # ── Early termination ──
201
  early_terminated = False
 
206
  # ── Cost ──
207
  total_input = context_tokens + tool_calls * 200
208
  total_output = task.output_tokens + retries * (task.output_tokens // 2)
209
+ if early_terminated: total_output = total_output // 3
 
210
 
211
  chargeable_input = max(0, total_input - cache_hit_tokens)
212
  input_cost = (chargeable_input / 1_000_000) * model_info["cost_in"]
213
  output_cost = (total_output / 1_000_000) * model_info["cost_out"]
214
  cache_savings = (cache_hit_tokens / 1_000_000) * model_info["cost_in"] * 0.5
215
 
 
216
  retry_cost = 0.0
217
  if retries > 0:
218
  if retry_escalated:
219
+ r_tier = min(model_info["tier"] + retry_tier_boost, 4)
220
+ r_model = TIER_CHEAPEST[r_tier]
221
+ ri = MODELS[r_model]
222
  retry_cost = (total_input / 1_000_000) * ri["cost_in"] + (total_output / 1_000_000) * ri["cost_out"]
223
  else:
224
  retry_cost = (total_input / 1_000_000) * model_info["cost_in"] + (total_output / 1_000_000) * model_info["cost_out"]
225
 
226
  verifier_cost = 0.0
227
  if verifier_calls > 0:
228
+ verifier_cost = (total_input // 4 / 1_000_000) * 0.15 + (100 / 1_000_000) * 0.60
 
 
229
 
230
+ total_cost = round(input_cost + output_cost - cache_savings + retry_cost + verifier_cost + tool_calls * 0.0001, 6)
 
231
 
232
  # ── Quality ──
233
  base_quality = model_info["quality"]
234
+ success_prob = base_quality - task.difficulty * 0.22
235
 
236
+ if task.risk_level == "high" and model_info["tier"] <= 1: success_prob -= 0.12
237
+ elif task.risk_level == "high" and model_info["tier"] <= 2: success_prob -= 0.05
 
 
 
238
 
239
+ # Verifier: strong recovery for cheaper models
240
  if verifier_calls > 0:
241
+ if model_info["tier"] <= 2: success_prob += 0.12
242
+ elif model_info["tier"] <= 3: success_prob += 0.06
243
+ else: success_prob += 0.03
 
244
 
245
+ # Retry: cascade retry is very effective
246
  if config.use_retry_optimizer and retries > 0 and retry_escalated:
247
+ success_prob += 0.15 # 2-tier cascade recovers most quality
248
  elif retries > 0:
249
+ success_prob += 0.04
250
 
251
+ if config.use_context_budget and task.difficulty > 0.5: success_prob -= 0.015
252
+ if config.use_meta_tools and task.is_repeated: success_prob += 0.05
253
+ if early_terminated: success_prob = 0.0
 
 
 
 
 
 
 
 
254
 
255
  success_prob = max(0.0, min(1.0, success_prob))
256
  success = random.random() < success_prob
 
258
  # ── Latency ──
259
  base_latency = 500 + model_info["tier"] * 300
260
  latency = base_latency + tool_calls * 800 + verifier_calls * 600 + retries * 1000
261
+ if config.use_cache_layout: latency -= 200
262
+ if early_terminated: latency = latency // 2
 
 
263
 
264
  return {
265
  "task_id": task.id, "domain": task.domain, "config": config.name,
 
276
  tasks = generate_tasks(n_per_domain)
277
  print(f"Generated {len(tasks)} tasks across 5 domains")
278
  print(f"Running {len(CONFIGS)} configs x {len(tasks)} tasks = {len(CONFIGS) * len(tasks)} simulations\n")
 
279
  all_results = []
280
  for config in CONFIGS:
281
  print(f" Config {config.name}: {config.label}...", end=" ", flush=True)
282
  for task in tasks:
283
+ all_results.append(simulate_task(config, task))
284
+ cr = [r for r in all_results if r["config"] == config.name]
285
+ n = len(cr); s = sum(1 for r in cr if r["success"]); c = sum(r["cost"] for r in cr)
286
+ print(f"{s}/{n} success, ${c:.4f} total")
 
 
 
 
287
  return {"tasks": [asdict(t) for t in tasks], "results": all_results}
288
 
289
 
290
  def compute_metrics(results: List[Dict]) -> Dict:
291
  by_config = defaultdict(list)
292
+ for r in results: by_config[r["config"]].append(r)
 
 
293
  config_metrics = {}
294
+ for cn, runs in by_config.items():
295
+ n = len(runs); succ = [r for r in runs if r["success"]]; s = len(succ)
296
+ tc = sum(r["cost"] for r in runs); sc = sum(r["cost"] for r in succ)
297
+ ti = sum(r["input_tokens"] for r in runs); to = sum(r["output_tokens"] for r in runs)
298
+ tcache = sum(r["cache_hit_tokens"] for r in runs)
299
+ config_metrics[cn] = {
300
+ "n": n, "success_rate": round(s/n, 4), "total_cost": round(tc, 6),
301
+ "cost_per_success": round(sc/max(s,1), 6), "cost_per_task": round(tc/n, 6),
302
+ "total_tokens_in": ti, "total_tokens_out": to,
303
+ "cache_hit_tokens": tcache, "cache_hit_rate": round(tcache/max(ti,1), 4),
304
+ "total_tool_calls": sum(r["tool_calls"] for r in runs),
305
+ "total_verifier_calls": sum(r["verifier_calls"] for r in runs),
306
+ "total_retries": sum(r["retries"] for r in runs),
307
+ "early_terminations": sum(1 for r in runs if r["early_terminated"]),
308
+ "avg_latency_ms": round(sum(r["latency_ms"] for r in runs)/n, 1),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
309
  }
 
310
  by_domain = defaultdict(lambda: defaultdict(list))
311
+ for r in results: by_domain[r["domain"]][r["config"]].append(r)
 
 
312
  domain_metrics = {}
313
  for domain, configs in by_domain.items():
314
  domain_metrics[domain] = {}
315
+ for cn, runs in configs.items():
316
+ s = sum(1 for r in runs if r["success"]); c = sum(r["cost"] for r in runs)
317
+ domain_metrics[domain][cn] = {
318
+ "n": len(runs), "success_rate": round(s/len(runs), 4),
319
+ "total_cost": round(c, 6), "cost_per_success": round(c/max(s,1), 6),
 
 
 
320
  }
 
321
  return {"by_config": config_metrics, "by_domain": domain_metrics}
322
 
323
 
324
  def print_report(metrics: Dict, config_labels: Dict):
325
  print(f"\n{'='*100}")
326
+ print(f" ACO BENCHMARK REPORT v3 - Cost Reduction at Iso-Quality")
327
  print(f"{'='*100}")
 
328
  print(f"\n{'Config':<40} {'Success':>8} {'Cost':>10} {'Cost/Succ':>10} {'Tokens':>10} {'Tools':>6} {'Verif':>6} {'Retry':>6} {'Cache%':>7} {'Latency':>8}")
329
  print("-" * 120)
330
+ bc = metrics["by_config"]["A"]["total_cost"]
331
+ bsr = metrics["by_config"]["A"]["success_rate"]
332
+ for cn in ["A","B","C","D","E","F","G","H","I"]:
333
+ m = metrics["by_config"][cn]; label = config_labels.get(cn, cn)
 
 
 
334
  tokens = m["total_tokens_in"] + m["total_tokens_out"]
335
+ savings = (1 - m["total_cost"]/bc) * 100 if bc > 0 else 0
336
+ sr_delta = (m["success_rate"] - bsr) * 100
337
+ print(f" {cn}. {label:<36} {m['success_rate']*100:>6.1f}% "
 
338
  f"${m['total_cost']:>8.4f} ${m['cost_per_success']:>8.5f} "
339
  f"{tokens:>8}k {m['total_tool_calls']:>4} {m['total_verifier_calls']:>4} "
340
  f"{m['total_retries']:>4} {m['cache_hit_rate']*100:>5.1f}% {m['avg_latency_ms']:>6.0f}ms")
341
  print(f" -> {savings:+.1f}% cost, {sr_delta:+.1f}pp quality vs baseline A")
342
 
343
+ print(f"\n{'='*100}\n PER-DOMAIN BREAKDOWN\n{'='*100}")
 
 
344
  for domain in ["coding", "research", "tool_use", "doc_qa", "long_horizon"]:
345
  print(f"\n {domain.upper()}")
346
  print(f" {'Config':<40} {'Success':>8} {'Cost':>10} {'Cost/Succ':>10}")
347
+ for cn in ["A","B","C","D","E","F","G","H","I"]:
348
+ if cn in metrics["by_domain"].get(domain, {}):
349
+ m = metrics["by_domain"][domain][cn]; label = config_labels.get(cn, cn)
350
+ print(f" {cn}. {label:<36} {m['success_rate']*100:>6.1f}% ${m['total_cost']:>8.4f} ${m['cost_per_success']:>8.5f}")
351
+
352
+ print(f"\n{'='*100}\n KEY FINDINGS\n{'='*100}")
353
+ full = metrics["by_config"]["I"]; af = metrics["by_config"]["A"]; ac = metrics["by_config"]["B"]
354
+ cs = (1 - full["total_cost"]/af["total_cost"]) * 100
355
+ qd = (full["success_rate"] - af["success_rate"]) * 100
356
+ cqd = (ac["success_rate"] - af["success_rate"]) * 100
 
 
 
 
 
 
 
357
  print(f" Full ACO vs Always Frontier:")
358
+ print(f" Cost reduction: {cs:.1f}%")
359
+ print(f" Quality change: {qd:+.1f}pp")
360
+ print(f" Cost per success: ${full['cost_per_success']:.5f} vs ${af['cost_per_success']:.5f}")
361
  print(f" Always Cheap vs Always Frontier:")
362
+ print(f" Cost reduction: {(1 - ac['total_cost']/af['total_cost'])*100:.1f}%")
363
+ print(f" Quality loss: {cqd:+.1f}pp")
364
  print(f" Cache hit rate (full ACO): {full['cache_hit_rate']*100:.1f}%")
365
+ print(f" Tool calls saved (full ACO vs A): {af['total_tool_calls'] - full['total_tool_calls']}")
366
+ print(f" Verifier calls (full ACO vs A): {full['total_verifier_calls']} vs {af['total_verifier_calls']}")
367
+ if qd >= -2.0:
368
+ print(f"\n ✓ ISO-QUALITY ACHIEVED: quality delta {qd:+.1f}pp within ±2pp threshold")
 
 
369
  else:
370
+ print(f"\n ✗ QUALITY GAP: quality delta {qd:+.1f}pp exceeds ±2pp threshold")
371
 
372
 
373
  def main():
374
+ n = int(sys.argv[1]) if len(sys.argv) > 1 else 20
 
 
 
375
  data = run_benchmark(n)
376
  metrics = compute_metrics(data["results"])
 
377
  config_labels = {c.name: c.label for c in CONFIGS}
378
  print_report(metrics, config_labels)
379
+ output = {"n_tasks_per_domain": n, "n_configs": len(CONFIGS),
380
+ "config_labels": config_labels, "metrics": metrics, "raw_results": data["results"]}
381
+ with open("/tmp/aco_benchmark_results.json", "w") as f: json.dump(output, f, indent=2)
382
+ print(f"\nResults saved to /tmp/aco_benchmark_results.json")
383
 
384
+ if __name__ == "__main__": main()