narcolepticchicken commited on
Commit
2dad52c
·
verified ·
1 Parent(s): edbfb20

Delete router_models/baarbundle.pkl, router_models/productionbundle.pkl, router_models/routerbundle.pkl, router_models/router_bundle_v6.pkl, router_models/router_bundle_v8.pkl, router_models/router_bundle_v10_fixed.pkl, router_models/feat_keys.json, router_models/tier_config.json, eval/baar_results.json, eval/bert_vs_xgboost_results.json/*, docs/trained_router_report.md, aco/router_v10.py, aco/conformal.py, aco/learned_router.py, training/train_router_real.py, training/benchmark_final.py, training/benchmark_with_feedback.py, training/bfcl_eval.py, training/aco_eval.py, FINAL_RESULTS.md

Browse files
FINAL_RESULTS.md DELETED
@@ -1,137 +0,0 @@
1
- # ACO (Agent Cost Optimizer) — Final Results
2
-
3
- ## The Cascade Wins. Everything Else Is Seasoning.
4
-
5
- After trace analysis, anti-oracle testing, causal simulation, and multi-module benchmarking
6
- across 500 SWE-bench instances, here is the definitive ranking:
7
-
8
- ```
9
- ┌─────────────────────────────────────────┬────────┬──────────┬──────────┬──────────────┐
10
- │ Strategy │ Solved │ Cost │ $/Solved │ Ship? │
11
- ├─────────────────────────────────────────┼────────┼──────────┼──────────┼──────────────┤
12
- │ Always Frontier │ 391 │ $158.34 │ $0.405 │ baseline │
13
- │ Cascade T1→T2→T4 │ 416 │ $76.48 │ $0.184 │ ✅ PROD │
14
- │ Safe Proposal T1→T4 │ 411 │ $82.10 │ $0.200 │ 🧪 Experimental│
15
- │ Safe Proposal + T2 │ 411 │ $71.50 │ $0.174 │ 🧪 Best cost │
16
- │ Per-Step Oracle (cheating) │ 411 │ $81.78 │ $0.199 │ ❌ Lookahead │
17
- └─────────────────────────────────────────┴────────┴──────────┴──────────┴──────────────┘
18
- ```
19
-
20
- **The cascade beats frontier on BOTH cost AND quality** — 416 solved vs 391, at 51.7% less cost.
21
- This is the core result. Safe proposal variants save more money but lose 5 instances
22
- that T2 would have caught.
23
-
24
- ## 5 Things We Built and Tested
25
-
26
- ### 1. Live Cascade Causal Simulation
27
- - **Job 6a0131f2**: Causal simulation where ACOLiveAgent makes routing decisions at
28
- pre_turn(), outcomes looked up from SWE-Router oracle datasets
29
- - **Result**: 416/500 solved, $76.48, $0.184/solved, 51.7% savings
30
- - **Key insight**: Cascade solves 25 MORE instances than frontier because T1/T2
31
- catch cases Claude misses. Different models have different failure modes.
32
- - **Tier distribution**: T1=316 (63.2%), T2=43 (8.6%), T4=141 (28.2%)
33
- - **Cost per API call**: T1=$0.000277, T2=$0.002163, T4=$0.017307 (62.4x ratio)
34
-
35
- ### 2. Cache-Aware Prompt Layout
36
- - **Job 6a01332b**: Analyzed 500 traces for cacheable vs dynamic content
37
- - **Result**: Only 1.5% of tokens are static (135 of 9,066). The system prompt
38
- in SWE-bench agents is tiny. Almost everything is in the conversation transcript.
39
- - **Cache hit rate**: Already 97.6% across turns — the prior conversation IS the cache.
40
- - **Conclusion**: Cache-aware layout adds negligible value for SWE-bench agents.
41
- The prefix caching optimization matters for different workloads (RAG, long system prompts).
42
-
43
- ### 3. Macro Tool Mining
44
- - **Job 6a013424**: Extracted 109K 2-command and 147K 3-command sequences
45
- - **Result**: 6 macro tools identified, 13,578 turns saved, $44.43 saved
46
- - **Top macros**:
47
- - `repo_search`: find + cat (179 occurrences)
48
- - `locate_symbol`: grep + cat (565 occurrences)
49
- - `run_test_and_summarize`: pytest + grep failures (1,187 occurrences — most valuable)
50
- - `read_and_patch`: cat + sed (334 occurrences)
51
- - `submit_patch`: git diff + submit (123 occurrences)
52
- - **220,189 sequences are 100% exploration** — can stay entirely on T1
53
- - **Implementation**: Each macro replaces 2-3 LLM turns with one deterministic subprocess call
54
-
55
- ### 4. Doom Rescue Policy
56
- - **Job 6a0134d4**: Analyzed 1,942 error streaks across 1,000 traces
57
- - **Result**: **58-72% of "doomed" runs eventually recover and resolve the instance**
58
- - **Never terminate at 3 errors**: At streak≥3, 72% (T4) and 58% (T1) eventually succeed.
59
- Terminating is destroying value, not saving it.
60
- - **Rescue policy**: One T4 "reset + diagnose" call ($0.025) → expected value over $0.20
61
- - Summarize state into clean scratchpad
62
- - Classify error: dependency, wrong_file, bad_patch, test_env, repeating
63
- - Apply targeted recovery strategy
64
- - Terminate only if rescue produces no new plan
65
- - **Net savings**: $124.88 across 500 instances (recovers 452 solves, costs $18.40 in rescues)
66
- - **Most fatal errors**: `not_found` (55% fatality), `timeout` (51%), `syntax` (45%)
67
- vs least fatal: `permission` (11%), `other` (29%)
68
-
69
- ### 5. Provider Routing
70
- - **Job 6a0135e8**: Compared 7 providers across 3 tiers
71
- - **Per-call costs** (15K input / 2K output):
72
- - T1 DeepSeek Direct: $0.00266 (cheapest, 800ms)
73
- - T2 OpenAI Direct: $0.00345 live, $0.00255 with cache (600ms)
74
- - T4 AWS Bedrock: $0.30 live, $0.17 with cache hit (2,200ms, 20% cheaper than Anthropic)
75
- - **Batch API**: 50% off on OpenAI/Anthropic for offline workloads
76
- - Verifier 411 calls: $64.73 live → $32.37 batch (saves $32.36)
77
- - **Best provider stack**: DeepSeek (T1) + OpenAI (T2) + AWS Bedrock (T4)
78
- - **Agent loops**: Live calls only. Latency matters. Use Anthropic Direct if 2.2s is too slow.
79
- - **Eval/verifier/training**: Use Batch APIs. 50% discount.
80
-
81
- ## What We Learned (The Hard Way)
82
-
83
- 1. **ML routing is unnecessary**: The gap between cascade (83.2%) and oracle (86.8%) is 19 instances.
84
- Too narrow for ML to add value. XGBoost and BERT both failed to beat static cascade.
85
-
86
- 2. **Per-step prediction routing doesn't work**: 98.3% false positive rate on edit detection.
87
- Can't predict command type from conversation state. Safe proposal model is the fix.
88
-
89
- 3. **Cache-aware layout adds nothing for SWE-bench**: Only 1.5% static content. Different story
90
- for RAG agents with large system prompts.
91
-
92
- 4. **Never kill a run at 3 errors**: 58-72% recover. Rescue, don't terminate.
93
-
94
- 5. **Command-type prediction is a red herring**: Don't predict what the model will do.
95
- Let it propose, then gate the dangerous proposals.
96
-
97
- ## What's Production-Ready
98
-
99
- ```python
100
- from aco_live import ACOLiveAgent
101
-
102
- # Best all-around: Cascade T1→T2→T4
103
- aco = ACOLiveAgent(strategy='cascade', max_cost=2.0)
104
-
105
- # Before each turn:
106
- decision = aco.pre_turn(messages)
107
- # → {'model': 'deepseek-v4-flash', 'tier': 1}
108
-
109
- response = call_llm(model=decision['model'], messages=...)
110
-
111
- # After each turn:
112
- result = aco.post_turn(response, cost=cost, success=success)
113
- # → {'action': 'continue'|'escalate'|'review_needed'|'done'}
114
- ```
115
-
116
- ## Remaining Unknowns
117
-
118
- 1. **Causal divergence in live agents**: Would T1's exploration discover different bugs than T4's?
119
- Can only be tested in actual Docker-execution agent runs.
120
- 2. **Multi-harness validation**: Only tested on SWE-bench coding tasks. Research agents, RAG agents,
121
- personal assistants may have different optimal strategies.
122
- 3. **Model version drift**: Models improve. Cascade order needs periodic revalidation.
123
- 4. **Live safe proposal**: T1 proposing + T4 reviewing edits in a real agent loop (not simulation).
124
-
125
- ## Code Location
126
-
127
- - `aco/aco_live.py` — Drop-in ACOLiveAgent wrapper (cascade, safe_proposal, safe_proposal_t2)
128
- - `aco/per_step_router.py` — Command classifier + per-step router
129
- - `aco/classifier.py` — Task cost classifier
130
- - `aco/telemetry.py` — Cost telemetry collector
131
- - `aco/tool_gate.py` — Tool duplicate detection
132
- - `aco/doom_detector.py` — Early termination (use rescue policy, not termination!)
133
- - `aco/verifier_budgeter.py` — Selective verifier calls
134
- - `aco/retry_optimizer.py` — Error-type-specific recovery
135
- - `aco/context_budgeter.py` — Context compression
136
- - `aco/meta_tool_miner.py` — Macro tool extraction
137
- - `aco/cache_layout.py` — Cache-aware prompt layout
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
aco/conformal.py DELETED
@@ -1,112 +0,0 @@
1
- """Conformal calibration for escalation thresholds.
2
-
3
- Implements RouteNLP-style conformal risk control:
4
- P(failure AND no escalation) <= alpha
5
-
6
- Method:
7
- 1. Compute nonconformity scores from calibrated P(success)
8
- 2. Find conformal quantile threshold
9
- 3. Guarantee coverage under exchangeability
10
- """
11
- import numpy as np
12
- from typing import Dict, List, Optional, Tuple
13
-
14
-
15
- class ConformalEscalationCalibrator:
16
- """Calibrate escalation thresholds with distribution-free coverage guarantees.
17
-
18
- Based on RouteNLP (arxiv 2604.23577) and Conformal Risk Control
19
- (Angelopoulos et al., arxiv 2208.02814).
20
-
21
- Guarantee: P(y=fail AND no_escalation) <= alpha
22
- """
23
-
24
- def __init__(self, alpha: float = 0.05):
25
- self.alpha = alpha
26
- self.thresholds: Dict[int, float] = {}
27
- self.calibrated = False
28
-
29
- def calibrate(
30
- self,
31
- psuccess: Dict[int, np.ndarray],
32
- outcomes: Dict[int, np.ndarray],
33
- ) -> Dict[int, float]:
34
- """Calibrate per-tier escalation thresholds.
35
-
36
- Args:
37
- psuccess: {tier: array of calibrated P(success)}
38
- outcomes: {tier: array of binary outcomes (1=success, 0=fail)}
39
-
40
- Returns:
41
- {tier: conformal_threshold}
42
- """
43
- for tier in sorted(psuccess.keys()):
44
- p = psuccess[tier]
45
- y = outcomes[tier]
46
- n = len(y)
47
-
48
- # Nonconformity: 1 - P(success) for failed examples
49
- # These are the scores we want to bound
50
- failed_mask = y == 0
51
- if failed_mask.sum() == 0:
52
- self.thresholds[tier] = 1.0
53
- continue
54
-
55
- # Conformal risk control: find threshold lam such that
56
- # R_hat(lam) <= alpha, where R_hat = (1/n) * sum 1[p >= lam AND y=0]
57
- # This means: fraction of examples with P(success) >= lam that actually failed <= alpha
58
-
59
- # Sort P(success) values
60
- sorted_p = np.sort(p[failed_mask])
61
-
62
- # Conformal quantile: ceiling of (1-alpha)*(n+1)/n
63
- q = int(np.ceil((1 - self.alpha) * (n + 1) / n))
64
- q = min(q, len(sorted_p))
65
-
66
- # Threshold: if P(success) < this, escalate
67
- # We want the (1-alpha) quantile of failure nonconformity scores
68
- threshold = sorted_p[q - 1] if q > 0 else 0.0
69
- self.thresholds[tier] = float(threshold)
70
-
71
- self.calibrated = True
72
- return self.thresholds
73
-
74
- def should_escalate(self, tier: int, psuccess: float) -> bool:
75
- """Decide whether to escalate from this tier.
76
-
77
- Returns True if P(success) is below conformal threshold,
78
- meaning we can't guarantee success at this tier with 1-alpha coverage.
79
- """
80
- if not self.calibrated:
81
- return psuccess < 0.65 # fallback to heuristic
82
- threshold = self.thresholds.get(tier, 0.65)
83
- return psuccess < threshold
84
-
85
- def coverage_check(
86
- self,
87
- psuccess: Dict[int, np.ndarray],
88
- outcomes: Dict[int, np.ndarray],
89
- ) -> Dict[int, Dict[str, float]]:
90
- """Verify conformal coverage on test data."""
91
- results = {}
92
- for tier in sorted(psuccess.keys()):
93
- p = psuccess[tier]
94
- y = outcomes[tier]
95
- threshold = self.thresholds.get(tier, 0.65)
96
-
97
- no_escalate = p >= threshold
98
- failed_no_escalate = (y == 0) & no_escalate
99
- n_no_escalate = no_escalate.sum()
100
-
101
- violation_rate = failed_no_escalate.sum() / max(n_no_escalate, 1)
102
- escalation_rate = 1 - no_escalate.mean()
103
-
104
- results[tier] = {
105
- "violation_rate": float(violation_rate),
106
- "escalation_rate": float(escalation_rate),
107
- "threshold": float(threshold),
108
- "n_no_escalate": int(n_no_escalate),
109
- "n_violations": int(failed_no_escalate.sum()),
110
- "covered": violation_rate <= self.alpha,
111
- }
112
- return results
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
aco/learned_router.py DELETED
@@ -1,151 +0,0 @@
1
- """Trained Production Router - Replaces heuristic routing.
2
-
3
- Architecture: difficulty-first + ML confirmation + safety floors.
4
-
5
- Usage:
6
- from aco.learned_router import TrainedRouter
7
-
8
- router = TrainedRouter.from_pretrained("narcolepticchicken/agent-cost-optimizer")
9
- tier, confidence = router.predict("Write a Python function", "coding", difficulty=3)
10
- """
11
-
12
- import json
13
- import os
14
- import pickle
15
- from typing import Dict, List, Optional, Tuple
16
- from dataclasses import dataclass
17
- from collections import defaultdict
18
-
19
- try:
20
- import numpy as np
21
- import xgboost as xgb
22
- HAS_ML = True
23
- except ImportError:
24
- HAS_ML = False
25
-
26
-
27
- TASK_TYPES = ["quick_answer","coding","research","document_drafting",
28
- "legal_regulated","tool_heavy","retrieval_heavy",
29
- "long_horizon","unknown_ambiguous"]
30
- TT2IDX = {t:i for i,t in enumerate(TASK_TYPES)}
31
-
32
- CODE_KW = ["python","javascript","code","function","bug","debug","refactor",
33
- "implement","test","compile","runtime","class","module","async","thread"]
34
- LEGAL_KW = ["contract","legal","compliance","gdpr","privacy","policy","regulatory","liability"]
35
- RESEARCH_KW = ["research","find sources","literature","investigate","compare","analyze","survey"]
36
- TOOL_KW = ["search","fetch","retrieve","query","api","database","scrape","aggregate"]
37
- LONG_KW = ["plan","project","roadmap","orchestrate","multi-step","migrate","pipeline","deploy"]
38
- MATH_KW = ["calculate","compute","solve","equation","formula","optimize","probability"]
39
-
40
- # Default safety floors per task type
41
- TASK_FLOOR = {
42
- "legal_regulated":4,"long_horizon":3,"research":3,"coding":3,
43
- "unknown_ambiguous":3,"quick_answer":1,"document_drafting":2,
44
- "tool_heavy":2,"retrieval_heavy":2,
45
- }
46
-
47
-
48
- class TrainedRouter:
49
- """Production trained router: difficulty-first + ML confirmation + safety floors."""
50
-
51
- def __init__(self, tier_clfs: Dict, feat_keys: List[str],
52
- tier_config: Dict, escalation_threshold: float = 0.55):
53
- self.tier_clfs = tier_clfs
54
- self.feat_keys = feat_keys
55
- self.tier_config = tier_config
56
- self.tier_cost = {int(k):v for k,v in tier_config["tier_cost"].items()}
57
- self.task_floor = tier_config.get("task_floor", TASK_FLOOR)
58
- self.escalation_threshold = escalation_threshold
59
- self._trained = True
60
-
61
- def extract_features(self, request: str, task_type: str, difficulty: int = 3) -> Dict:
62
- r = request.lower()
63
- f = {"req_len":len(request),"num_words":len(request.split()),
64
- "has_code":int(any(k in r for k in CODE_KW)),
65
- "n_code":sum(1 for k in CODE_KW if k in r),
66
- "has_legal":int(any(k in r for k in LEGAL_KW)),
67
- "n_legal":sum(1 for k in LEGAL_KW if k in r),
68
- "has_research":int(any(k in r for k in RESEARCH_KW)),
69
- "n_research":sum(1 for k in RESEARCH_KW if k in r),
70
- "has_tool":int(any(k in r for k in TOOL_KW)),
71
- "n_tool":sum(1 for k in TOOL_KW if k in r),
72
- "has_long":int(any(k in r for k in LONG_KW)),
73
- "has_math":int(any(k in r for k in MATH_KW)),
74
- "tt_idx":TT2IDX.get(task_type,8),"difficulty":difficulty}
75
- for tt in TASK_TYPES:
76
- f[f"tt_{tt}"] = int(task_type == tt)
77
- return f
78
-
79
- def _feats_to_vec(self, feats: Dict):
80
- import numpy as np
81
- return np.array([float(feats.get(k, 0.0)) for k in self.feat_keys], dtype=np.float32)
82
-
83
- def predict(self, request: str, task_type: str, difficulty: int = 3,
84
- escalation_threshold: Optional[float] = None) -> Tuple[int, float]:
85
- """Predict optimal tier using difficulty-first + ML confirmation.
86
-
87
- Returns: (tier, confidence)
88
- """
89
- threshold = escalation_threshold or self.escalation_threshold
90
-
91
- # Step 1: difficulty -> base_tier
92
- base_tier = min(difficulty + 1, 5)
93
-
94
- # Step 2: apply safety floor
95
- floor = self.task_floor.get(task_type, 2)
96
- base_tier = max(base_tier, floor)
97
-
98
- if not HAS_ML or not self._trained:
99
- return base_tier, 0.6
100
-
101
- # Step 3: ML confirmation
102
- feats = self.extract_features(request, task_type, difficulty)
103
- x = self._feats_to_vec(feats).reshape(1, -1)
104
-
105
- p_success = self.tier_clfs[base_tier].predict_proba(x)[0, 1]
106
- confidence = p_success
107
-
108
- # Step 4: escalate if P(success) too low
109
- while p_success < threshold and base_tier < 5:
110
- base_tier += 1
111
- p_success = self.tier_clfs[base_tier].predict_proba(x)[0, 1]
112
- confidence = p_success
113
-
114
- return base_tier, float(confidence)
115
-
116
- @classmethod
117
- def from_pretrained(cls, repo_id: str, escalation_threshold: float = 0.55,
118
- cache_dir: Optional[str] = None):
119
- """Load trained router from HuggingFace Hub."""
120
- from huggingface_hub import hf_hub_download
121
-
122
- bundle_path = hf_hub_download(
123
- repo_id=repo_id, filename="router_models/router_bundle.pkl",
124
- cache_dir=cache_dir,
125
- )
126
-
127
- with open(bundle_path, "rb") as f:
128
- import pickle
129
- bundle = pickle.load(f)
130
-
131
- return cls(
132
- tier_clfs={int(k): v for k, v in bundle["tier_clfs"].items()},
133
- feat_keys=bundle["feat_keys"],
134
- tier_config=bundle["tier_config"],
135
- escalation_threshold=escalation_threshold,
136
- )
137
-
138
- @classmethod
139
- def from_local(cls, model_dir: str, escalation_threshold: float = 0.55):
140
- """Load from local directory."""
141
- bundle_path = os.path.join(model_dir, "router_bundle.pkl")
142
- with open(bundle_path, "rb") as f:
143
- import pickle
144
- bundle = pickle.load(f)
145
-
146
- return cls(
147
- tier_clfs={int(k): v for k, v in bundle["tier_clfs"].items()},
148
- feat_keys=bundle["feat_keys"],
149
- tier_config=bundle["tier_config"],
150
- escalation_threshold=escalation_threshold,
151
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
aco/router_v10.py DELETED
@@ -1,151 +0,0 @@
1
- """ACO v10 Router: Trained on REAL SWE-Router execution data.
2
-
3
- Key difference from v8: Uses XGBoost models trained on 500 real
4
- SWE-bench tasks across 8 models, not synthetic data.
5
-
6
- Routes based on problem-statement features → per-tier P(success) →
7
- optimal tier selection. Supports cascade + feedback escalation.
8
- """
9
- import numpy as np
10
- import pickle, os, json
11
- from typing import Dict, Optional, Tuple
12
- from dataclasses import dataclass
13
-
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",
18
- "safety","security"]
19
- SIMPLE_KW = ["typo","simple","quick","brief","minor","small","easy","trivial","just"]
20
- RESEARCH_KW = ["research","investigate","compare","analyze","survey","paper"]
21
- TOOL_KW = ["search","fetch","retrieve","query","api","database","scrape","aggregate"]
22
- LONG_KW = ["plan","project","roadmap","orchestrate","migrate","pipeline","deploy","architecture"]
23
-
24
- FEAT_KEYS = sorted([
25
- 'req_len','num_words','has_code','n_code','has_legal','has_research',
26
- 'has_tool','has_critical','has_simple','has_long','has_math',
27
- 'has_error_msg','has_file_path','n_lines','has_version','has_add',
28
- 'has_fix','has_change','has_remove','has_test','has_doc',
29
- 'has_see_also','has_steps_to_reproduce',
30
- ])
31
-
32
- TIER_TO_MODEL = {
33
- 1: 'deepseek-v4-flash', 2: 'gpt-5-mini',
34
- 3: 'gemini-2.5-pro', 4: 'claude-opus-4.7', 5: 'gemini-3-pro',
35
- }
36
-
37
- TIER_COST = {1:0.01, 2:0.05, 3:0.15, 4:0.30, 5:0.50}
38
-
39
- @dataclass
40
- class V10RoutingDecision:
41
- tier: int
42
- model: str
43
- confidence: float
44
- cost_estimate: float
45
- tier_probs: Dict[int, float]
46
- escalated: bool = False
47
-
48
- class V10Router:
49
- def __init__(self, model_path: str = None, success_threshold: float = 0.5):
50
- self.success_threshold = success_threshold
51
- self.tier_clfs = None
52
- self.tier_calibs = None
53
- self.opt_clf = None
54
- self.feat_keys = FEAT_KEYS
55
- if model_path and os.path.exists(model_path):
56
- self._load(model_path)
57
-
58
- def _load(self, path):
59
- bundle = pickle.load(open(path, 'rb'))
60
- self.tier_clfs = {int(k):v for k,v in bundle.get('tier_clfs',{}).items()}
61
- self.tier_calibs = {int(k):v for k,v in bundle.get('tier_calibrators',{}).items()}
62
- self.opt_clf = bundle.get('opt_clf', None)
63
- self.feat_keys = bundle.get('feat_keys', FEAT_KEYS)
64
-
65
- def _extract(self, text: str) -> np.ndarray:
66
- r = text.lower()
67
- feats = {
68
- 'req_len': len(text), 'num_words': len(text.split()),
69
- 'has_code': int(any(k in r for k in CODE_KW)),
70
- 'n_code': sum(1 for k in CODE_KW if k in r),
71
- 'has_legal': int(any(k in r for k in ["contract","legal","compliance"])),
72
- 'has_research': int(any(k in r for k in RESEARCH_KW)),
73
- 'has_tool': int(any(k in r for k in TOOL_KW)),
74
- 'has_critical': int(any(k in r for k in CRITICAL_KW)),
75
- 'has_simple': int(any(k in r for k in SIMPLE_KW)),
76
- 'has_long': int(any(k in r for k in LONG_KW)),
77
- 'has_math': int(any(k in r for k in ["calculate","compute","solve","equation"])),
78
- 'has_error_msg': int('error' in r or 'traceback' in r or 'exception' in r),
79
- 'has_file_path': int('/' in r),
80
- 'n_lines': text.count('\n') + 1,
81
- 'has_version': int('version' in r or 'update' in r),
82
- 'has_add': int('add' in r or 'new' in r or 'create' in r),
83
- 'has_fix': int('fix' in r or 'bug' in r or 'issue' in r),
84
- 'has_change': int('change' in r or 'modify' in r),
85
- 'has_remove': int('remove' in r or 'delete' in r),
86
- 'has_test': int('test' in r or 'spec' in r),
87
- 'has_doc': int('doc' in r or 'readme' in r),
88
- 'has_see_also': int('see also' in r or 'related' in r),
89
- 'has_steps_to_reproduce': int('reproduce' in r or 'steps' in r),
90
- }
91
- return np.array([float(feats.get(k,0.0)) for k in self.feat_keys], dtype=np.float32).reshape(1,-1)
92
-
93
- def route_cascade(self, text: str) -> V10RoutingDecision:
94
- """Route to cheapest tier with P(success) >= threshold."""
95
- x = self._extract(text)
96
- tier_probs = {}
97
- if self.tier_clfs:
98
- for t in range(1, 6):
99
- if t in self.tier_clfs:
100
- p_raw = self.tier_clfs[t].predict_proba(x)[0,1]
101
- p_cal = float(self.tier_calibs[t].transform([p_raw])[0])
102
- tier_probs[t] = p_cal
103
- else:
104
- tier_probs[t] = 0.5
105
- else:
106
- tier_probs = {1:0.67,2:0.72,3:0.50,4:0.84,5:0.70}
107
-
108
- # Find cheapest tier above threshold
109
- selected_tier = 5
110
- for t in range(1, 6):
111
- if tier_probs.get(t, 0) >= self.success_threshold:
112
- selected_tier = t
113
- break
114
-
115
- model = TIER_TO_MODEL.get(selected_tier, 'claude-opus-4.7')
116
- return V10RoutingDecision(
117
- tier=selected_tier, model=model,
118
- confidence=tier_probs.get(selected_tier, 0.5),
119
- cost_estimate=TIER_COST.get(selected_tier, 0.30),
120
- tier_probs=tier_probs,
121
- )
122
-
123
- def route_direct(self, text: str) -> V10RoutingDecision:
124
- """Predict optimal tier directly."""
125
- x = self._extract(text)
126
- if self.opt_clf:
127
- tier = int(self.opt_clf.predict(x)[0]) + 1
128
- else:
129
- tier = 4 # fallback
130
- model = TIER_TO_MODEL.get(tier, 'claude-opus-4.7')
131
- return V10RoutingDecision(
132
- tier=tier, model=model,
133
- confidence=0.8, cost_estimate=TIER_COST.get(tier, 0.30),
134
- tier_probs={},
135
- )
136
-
137
- def route_with_feedback(self, text: str, initial_success: bool = True) -> V10RoutingDecision:
138
- """Route with feedback: start cheap, escalate on failure."""
139
- initial = self.route_cascade(text)
140
- if initial_success:
141
- return initial
142
- # Escalate
143
- escalated_tier = min(initial.tier + 1, 5)
144
- model = TIER_TO_MODEL.get(escalated_tier, 'claude-opus-4.7')
145
- return V10RoutingDecision(
146
- tier=escalated_tier, model=model,
147
- confidence=initial.tier_probs.get(escalated_tier, 0.8),
148
- cost_estimate=TIER_COST.get(initial.tier, 0.01) + TIER_COST.get(escalated_tier, 0.30),
149
- tier_probs=initial.tier_probs,
150
- escalated=True,
151
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
docs/trained_router_report.md DELETED
@@ -1,69 +0,0 @@
1
- # Trained Router Report
2
-
3
- ## Architecture
4
-
5
- The trained router uses a **difficulty-first + ML confirmation + safety floor** architecture:
6
-
7
- 1. Map task_type → difficulty (1-5)
8
- 2. Compute base_tier = min(difficulty + 1, 5)
9
- 3. Apply safety floor per task_type (e.g., legal_regulated → tier 4)
10
- 4. Use per-tier XGBoost P(success) classifiers to confirm or escalate
11
- 5. If P(success@base_tier) < threshold, escalate one tier at a time
12
-
13
- ### Per-Tier XGBoost Classifiers
14
-
15
- 5 binary classifiers, each predicting P(task succeeds | query, tier=X).
16
-
17
- Trained on 50,000 synthetic traces with ground-truth per-tier success labels.
18
-
19
- Features: 23 (request text signals + task type one-hot + difficulty)
20
-
21
- ## Results (N=2,000 eval traces, seed=999)
22
-
23
- | Router | Success | AvgCost | CostRed vs Frontier | Unsafe | F-DONE |
24
- |--------|---------|---------|---------------------|--------|--------|
25
- | oracle | 99.8% | 0.4862 | 51.4% | 0.0% | 0.3% |
26
- | prod_t0.65 | 91.9% | 1.365 | -36.5% | 1.5% | 6.6% |
27
- | prod_t0.60 | 90.7% | 1.316 | -31.6% | 1.8% | 7.4% |
28
- | always_frontier | 88.8% | 1.000 | 0% | 2.5% | 8.7% |
29
- | prod_t0.55 | 85.5% | 1.107 | -10.7% | 4.1% | 10.4% |
30
- | heuristic_diff+1 | 83.4% | 0.940 | 6.0% | 4.9% | 11.7% |
31
- | heuristic_floor | 59.7% | 0.501 | 49.9% | 27.8% | 12.6% |
32
- | always_cheap | 20.9% | 0.050 | 95.0% | 79.0% | 0.0% |
33
-
34
- ## Key Findings
35
-
36
- 1. **Trained router at t=0.65 achieves 91.9% success — 3.1pp HIGHER than always-frontier (88.8%)**
37
- 2. The unsafe rate drops from 2.5% (frontier) to 1.5% (trained)
38
- 3. The cost is higher because the ML classifiers are conservative (they escalate more)
39
- 4. The oracle shows 51.4% cost reduction is achievable with perfect routing
40
-
41
- ## The Cost Problem
42
-
43
- The trained router OVER-ESCALATES because:
44
- - Per-tier P(success) classifiers for tiers 1-2 have low accuracy (f1 < 0.5)
45
- - They underpredict success at low tiers, causing unnecessary escalation
46
- - This is a training data problem: success at low tiers is inherently rare (22%, 40%)
47
-
48
- ## Solutions (Ordered by Expected Impact)
49
-
50
- 1. **Calibrate classifier probabilities** (Platt scaling or isotonic regression on held-out data)
51
- 2. **Add more training data** for easy tasks (oversample quick_answer successes)
52
- 3. **Use difficulty as direct feature** — already top-3 in feature importance
53
- 4. **Fine-tune escalation threshold per task type** (lower for quick_answer, higher for legal)
54
- 5. **Retrain with asymmetric sample weights** (5x penalty for underkill examples)
55
-
56
- ## Current Recommendation
57
-
58
- Use **prod_t0.55** as default: 85.5% success, 10.7% cost increase vs frontier, 4.1% unsafe.
59
- This is conservative (prefers safety over savings) which is the right default for production.
60
-
61
- For cost-sensitive deployments, use **heuristic_diff+1**: 83.4% success, 6% savings.
62
-
63
- ## Files
64
-
65
- - `router_models/router_bundle.pkl` — Pickled router with all 5 XGBoost classifiers
66
- - `router_models/tier_{1-5}_success.json` — Individual XGBoost model files
67
- - `router_models/feat_keys.json` — Feature key order
68
- - `router_models/tier_config.json` — Tier costs, strengths, task floors
69
- - `training/` — All training scripts (v1-v4)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
eval/baar_results.json DELETED
@@ -1,62 +0,0 @@
1
- {
2
- "router": "BAAR-style (BERT [CLS] + XGBoost)",
3
- "n_tasks": 500,
4
- "n_features": 791,
5
- "n_bert_dims": 768,
6
- "n_keyword_feats": 23,
7
- "policies": {
8
- "baar": {
9
- "success": 383,
10
- "success_rate": 0.766,
11
- "avg_cost": 0.1878,
12
- "cost_reduction_pct": 40.7
13
- },
14
- "baar_feedback": {
15
- "success": 426,
16
- "success_rate": 0.852,
17
- "avg_cost": 0.2485,
18
- "cost_reduction_pct": 21.5
19
- },
20
- "keyword_only": {
21
- "success": 382,
22
- "success_rate": 0.764,
23
- "avg_cost": 0.1815,
24
- "cost_reduction_pct": 42.7
25
- },
26
- "frontier": {
27
- "success": 391,
28
- "success_rate": 0.782,
29
- "avg_cost": 0.3167,
30
- "cost_reduction_pct": 0.0
31
- },
32
- "always_cheap": {
33
- "success": 316,
34
- "success_rate": 0.632,
35
- "avg_cost": 0.0142,
36
- "cost_reduction_pct": 95.5
37
- },
38
- "oracle": {
39
- "success": 435,
40
- "success_rate": 0.87,
41
- "avg_cost": 0.0586,
42
- "cost_reduction_pct": 81.5
43
- }
44
- },
45
- "cv_f1_comparison": {
46
- "keyword_only": {
47
- "1": 0.7237525635605897,
48
- "2": 0.787730931045319,
49
- "3": 0.5728946347769878,
50
- "4": 0.8946610840323498,
51
- "5": 0.7593761150692749
52
- },
53
- "baar_bert_kw": {
54
- "1": 0.7696474695238006,
55
- "2": 0.8160680568523706,
56
- "3": 0.5678540235539141,
57
- "4": 0.9130434782608695,
58
- "5": 0.8033430766327058
59
- }
60
- },
61
- "opt_tier_accuracy": 1.0
62
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
router_models/feat_keys.json DELETED
@@ -1 +0,0 @@
1
- ["difficulty", "has_code", "has_legal", "has_long", "has_math", "has_research", "has_tool", "n_code", "n_legal", "n_research", "n_tool", "num_words", "req_len", "tt_coding", "tt_document_drafting", "tt_idx", "tt_legal_regulated", "tt_long_horizon", "tt_quick_answer", "tt_research", "tt_retrieval_heavy", "tt_tool_heavy", "tt_unknown_ambiguous"]
 
 
router_models/router_bundle_v10_fixed.pkl DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:2eaf00b52c9d01e991f336518704fe2d6913ed7deb86e7b7cab0a8d31ec81f0c
3
- size 223016
 
 
 
 
router_models/router_bundle_v6.pkl DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:30e4e5548187842da8b73a96879a783f5e9b7f7d83d6be38f51be6d222585b64
3
- size 1649115
 
 
 
 
router_models/router_bundle_v8.pkl DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:3156c8b66c23af926f96a6ef146482c7ebb185877fb5277a822d50d8e7de8cee
3
- size 1649282
 
 
 
 
router_models/tier_config.json DELETED
@@ -1,27 +0,0 @@
1
- {
2
- "tier_cost": {
3
- "1": 0.05,
4
- "2": 0.15,
5
- "3": 0.75,
6
- "4": 1.0,
7
- "5": 1.5
8
- },
9
- "tier_str": {
10
- "1": 0.35,
11
- "2": 0.55,
12
- "3": 0.8,
13
- "4": 0.93,
14
- "5": 0.97
15
- },
16
- "task_floor": {
17
- "legal_regulated": 4,
18
- "long_horizon": 3,
19
- "research": 3,
20
- "coding": 3,
21
- "unknown_ambiguous": 3,
22
- "quick_answer": 1,
23
- "document_drafting": 2,
24
- "tool_heavy": 2,
25
- "retrieval_heavy": 2
26
- }
27
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
training/aco_eval.py DELETED
@@ -1,181 +0,0 @@
1
- #!/usr/bin/env python3
2
- """ACO Benchmark Evaluation: Full system test with simulated agent traces."""
3
- import sys,json,random,pickle,time
4
- sys.path.insert(0,"/app")
5
- from collections import defaultdict
6
-
7
- TIER_STR={1:0.35,2:0.55,3:0.80,4:0.93,5:0.97}
8
- TIER_COST={1:0.05,2:0.15,3:0.75,4:1.0,5:1.5}
9
- TASK_FLOOR={"legal_regulated":4,"long_horizon":3,"research":3,"coding":3,
10
- "unknown_ambiguous":3,"quick_answer":1,"document_drafting":2,
11
- "tool_heavy":2,"retrieval_heavy":2}
12
-
13
- CODE_KW=["python","javascript","code","function","bug","debug","refactor","implement","test"]
14
- CRITICAL_KW=["critical","production","urgent","now","emergency","live","deployed","safety","security"]
15
- SIMPLE_KW=["typo","simple","quick","brief","briefly","just","minor","small","easy","trivial","clarification"]
16
-
17
- from aco.classifier import TaskCostClassifier
18
- from aco.router import ModelCascadeRouter
19
- from aco.context_budgeter import ContextBudgeter
20
- from aco.tool_gate import ToolCostGate
21
- from aco.verifier_budgeter import VerifierBudgeter
22
- from aco.retry_optimizer import RetryOptimizer
23
- from aco.meta_tool_miner import MetaToolMiner
24
- from aco.doom_detector import DoomDetector
25
-
26
- TASKS={
27
- "quick_answer":["What is 2+2?","Explain quantum computing briefly.","Just tell me what 2+2 is."],
28
- "coding":["Write a Python function to reverse a linked list.","Fix a typo in the README.","Debug this critical production segfault NOW.","Just fix the typo in line 42."],
29
- "research":["Research latest transformer advances.","Find sources comparing LoRA and full FT briefly."],
30
- "document_drafting":["Draft project proposal for ML pipeline.","Write email to team about deployment."],
31
- "legal_regulated":["Review this contract for liability clauses.","Check GDPR compliance for data pipeline urgently."],
32
- "tool_heavy":["Search open issues and create summary.","Fetch API docs and generate client code."],
33
- "retrieval_heavy":["Answer based on 50-page document.","Find all payment processing mentions."],
34
- "long_horizon":["Plan 3-month roadmap.","Orchestrate complete multi-region deployment."],
35
- "unknown_ambiguous":["Help me with this thing.","I need something about the server."],
36
- }
37
-
38
- TOOL_LIST=["web_search","code_search","file_read","file_write","code_execute","verify"]
39
- TOOL_COST_ESTIMATES={"web_search":{"cost":0.01},"code_search":{"cost":0.005},"file_read":{"cost":0.001},"file_write":{"cost":0.001},"code_execute":{"cost":0.01},"verify":{"cost":0.02}}
40
- VERIFIER_COST=0.02
41
-
42
- print("="*80)
43
- print("ACO FULL SYSTEM BENCHMARK EVALUATION")
44
- print("="*80)
45
-
46
- # Initialize modules
47
- classifier=TaskCostClassifier()
48
- router=ModelCascadeRouter(model_path="/app/router_models/router_bundle_v8.pkl")
49
- context_budgeter=ContextBudgeter()
50
- tool_gate=ToolCostGate()
51
- verifier_budgeter=VerifierBudgeter()
52
- retry_optimizer=RetryOptimizer()
53
- meta_tool_miner=MetaToolMiner()
54
- doom_detector=DoomDetector()
55
-
56
- # Simulate 2000 agent runs
57
- rng=random.Random(42)
58
- N=2000
59
- results_aco=[]
60
- results_frontier=[]
61
- results_heuristic=[]
62
- results_cheap=[]
63
-
64
- for i in range(N):
65
- tt=rng.choice(list(TASKS.keys()))
66
- req=rng.choice(TASKS[tt])
67
-
68
- # Classify
69
- pred=classifier.classify(req)
70
- # Route
71
- routing=router.route(req, pred["task_type"], pred["difficulty"], pred)
72
- # Context budget
73
- budget=context_budgeter.budget(pred["task_type"],pred["difficulty"],pred["needs_retrieval"],pred["needs_tools"])
74
- # Tool decisions
75
- tool_decisions={}
76
- for tool in TOOL_LIST:
77
- if pred["needs_tools"] or tt in ("coding","tool_heavy","retrieval_heavy","research"):
78
- td=tool_gate.gate(tool,{"query":req},tt,1,5,routing.confidence)
79
- tool_decisions[tool]=td
80
- # Verifier
81
- vd=verifier_budgeter.should_verify(tt,pred["risk"],routing.confidence,False,False,routing.tier)
82
- # Simulate success
83
- ps=TIER_STR[routing.tier]**(pred["difficulty"]*0.6)
84
- success=rng.random()<ps
85
- # Compute cost
86
- model_cost=TIER_COST[routing.tier]
87
- tool_cost=sum(TOOL_COST_ESTIMATES.get(t,{}).get("cost",0.02) for t,td in tool_decisions.items() if td.action=="use")
88
- ver_cost=VERIFIER_COST if vd.should_verify else 0
89
- total_cost=model_cost+tool_cost+ver_cost
90
-
91
- results_aco.append({"tt":tt,"tier":routing.tier,"success":success,"cost":total_cost,
92
- "model_cost":model_cost,"tool_cost":tool_cost,"ver_cost":ver_cost,
93
- "context_tokens":budget.total_tokens,"verified":vd.should_verify,
94
- "tools_used":sum(1 for td in tool_decisions.values() if td.action=="use"),
95
- "escalated":routing.escalated,"downgraded":routing.downgraded})
96
-
97
- # Baseline: always frontier
98
- ps_f=TIER_STR[4]**(pred["difficulty"]*0.6)
99
- s_f=rng.random()<ps_f
100
- results_frontier.append({"tt":tt,"tier":4,"success":s_f,"cost":1.0+tool_cost+VERIFIER_COST})
101
-
102
- # Baseline: heuristic
103
- h_tier=min(pred["difficulty"]+1,5)
104
- h_tier=max(h_tier,TASK_FLOOR.get(tt,2))
105
- ps_h=TIER_STR[h_tier]**(pred["difficulty"]*0.6)
106
- s_h=rng.random()<ps_h
107
- results_heuristic.append({"tt":tt,"tier":h_tier,"success":s_h,"cost":TIER_COST[h_tier]+tool_cost+ver_cost})
108
-
109
- # Baseline: always cheap
110
- ps_c=TIER_STR[1]**(pred["difficulty"]*0.6)
111
- s_c=rng.random()<ps_c
112
- results_cheap.append({"tt":tt,"tier":1,"success":s_c,"cost":0.05+tool_cost})
113
-
114
- verifier_budgeter.reset_run()
115
-
116
-
117
- # Compute metrics
118
- def compute_metrics(results, name):
119
- n=len(results)
120
- succ=sum(1 for r in results if r["success"])
121
- cost=sum(r["cost"] for r in results)
122
- model_cost=sum(r.get("model_cost",r["cost"]) for r in results)
123
- tool_cost=sum(r.get("tool_cost",0) for r in results)
124
- ver_cost=sum(r.get("ver_cost",0) for r in results)
125
- ctx=sum(r.get("context_tokens",8000) for r in results)/n
126
- verified=sum(1 for r in results if r.get("verified",True))
127
- tools=sum(r.get("tools_used",0) for r in results)/n
128
- escalations=sum(1 for r in results if r.get("escalated",False))
129
- downgrades=sum(1 for r in results if r.get("downgraded",False))
130
- return {"name":name,"success_rate":succ/n,"avg_cost":cost/n,
131
- "model_cost":model_cost/n,"tool_cost":tool_cost/n,"ver_cost":ver_cost/n,
132
- "avg_context_tokens":ctx,"verifications":verified,
133
- "avg_tools":tools,"escalations":escalations,"downgrades":downgrades}
134
-
135
- m=compute_metrics(results_aco,"aco_v8")
136
- m_f=compute_metrics(results_frontier,"always_frontier")
137
- m_h=compute_metrics(results_heuristic,"heuristic")
138
- m_c=compute_metrics(results_cheap,"always_cheap")
139
-
140
- print(f"\n{'Router':<20} {'Success':>10} {'AvgCost':>10} {'CostRed':>10} {'ModelCost':>10} {'ToolCost':>10} {'VerCost':>10} {'Context':>10} {'Verifs':>8}")
141
- print("-"*100)
142
- for r in [m_f,m_h,m,m_c]:
143
- cr=(1-r["avg_cost"]/m_f["avg_cost"])*100
144
- print(f"{r['name']:<20} {r['success_rate']:>10.3f} {r['avg_cost']:>10.4f} {cr:>9.1f}% {r['model_cost']:>10.4f} {r['tool_cost']:>10.4f} {r['ver_cost']:>10.4f} {r['avg_context_tokens']:>10.0f} {r['verifications']:>8d}")
145
-
146
- # Per-task breakdown
147
- print(f"\n\nPer-task breakdown:")
148
- for tt in sorted(set(r["tt"] for r in results_aco)):
149
- aco_tt=[r for r in results_aco if r["tt"]==tt]
150
- front_tt=[r for r in results_frontier if r["tt"]==tt]
151
- n_tt=len(aco_tt)
152
- a_s=sum(1 for r in aco_tt if r["success"])/n_tt
153
- a_c=sum(r["cost"] for r in aco_tt)/n_tt
154
- f_c=sum(r["cost"] for r in front_tt)/n_tt
155
- f_s=sum(1 for r in front_tt if r["success"])/n_tt
156
- cr=(1-a_c/f_c)*100
157
- print(f" {tt:<20} n={n_tt:>4} aco_success={a_s:.3f} frontier_success={f_s:.3f} aco_cost={a_c:.4f} costRed={cr:.1f}%")
158
-
159
- # Cost-quality frontier
160
- print(f"\n\nCost-Quality Frontier:")
161
- frontier_points=[]
162
- for r in [m_c,m_h,m,m_f]:
163
- frontier_points.append((r["avg_cost"],r["success_rate"],r["name"]))
164
- frontier_points.sort(key=lambda x:x[0])
165
- for cost,succ,name in frontier_points:
166
- print(f" {name:<20} cost={cost:.4f} success={succ:.3f}")
167
-
168
- # Key findings
169
- print(f"\n\nKEY FINDINGS:")
170
- print(f" ACO v8 success rate: {m['success_rate']:.3f}")
171
- print(f" ACO v8 cost reduction: {(1-m['avg_cost']/m_f['avg_cost'])*100:.1f}%")
172
- print(f" ACO v8 avg context: {m['avg_context_tokens']:.0f} tokens")
173
- print(f" ACO v8 verifications: {m['verifications']}/{N}")
174
- print(f" Escalations: {m['escalations']} ({m['escalations']/N*100:.1f}%)")
175
- print(f" Downgrades: {m['downgrades']} ({m['downgrades']/N*100:.1f}%)")
176
-
177
- # Save
178
- with open("/app/aco_benchmark_results.json","w") as f:
179
- json.dump({"aco_v8":m,"frontier":m_f,"heuristic":m_h,"cheap":m_c},f,indent=2)
180
- print(f"\nSaved to /app/aco_benchmark_results.json")
181
- print("DONE!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
training/benchmark_final.py DELETED
@@ -1,143 +0,0 @@
1
- #!/usr/bin/env python3
2
- """FINAL v9 benchmark: v8 router + selective execution feedback.
3
-
4
- Strategy: Use v8 router for initial tier selection. Then for tasks
5
- where v8 chose tier < 4 (not frontier), run cheap model first and
6
- use output confidence to decide whether to escalate. This catches
7
- the ~2% of cases where cheap model fails but could be saved by
8
- escalation, without running double-inference everywhere.
9
- """
10
- import sys,random,math,pickle,json
11
- sys.path.insert(0,"/app")
12
- from collections import defaultdict
13
- from aco.classifier import TaskCostClassifier
14
- from aco.router import ModelCascadeRouter
15
- from aco.execution_feedback import ExecutionFeedbackRouter
16
-
17
- TIER_STR={1:0.35,2:0.55,3:0.80,4:0.93,5:0.97}
18
- TIER_COST={1:0.05,2:0.15,3:0.75,4:1.0,5:1.5}
19
- TASK_FLOOR={"legal_regulated":4,"long_horizon":3,"research":3,"coding":3,
20
- "unknown_ambiguous":3,"quick_answer":1,"document_drafting":2,
21
- "tool_heavy":2,"retrieval_heavy":2}
22
-
23
- TASKS = {
24
- "quick_answer":["What is 2+2?","Explain quantum computing briefly.","Convert 100F to Celsius."],
25
- "coding":["Write Python function to reverse linked list.","Fix typo in README.",
26
- "Debug critical production segfault NOW.","Just fix typo in line 42."],
27
- "research":["Research latest transformer advances."],
28
- "document_drafting":["Draft project proposal for ML pipeline."],
29
- "legal_regulated":["Review this contract for liability clauses."],
30
- "tool_heavy":["Search open issues and create summary."],
31
- "retrieval_heavy":["Answer based on 50-page document."],
32
- "long_horizon":["Plan 3-month roadmap."],
33
- "unknown_ambiguous":["Help me with this thing."],
34
- }
35
-
36
- classifier=TaskCostClassifier()
37
- router=ModelCascadeRouter(model_path="/app/router_models/router_bundle_v8.pkl")
38
-
39
- def sim_lps_correlated(tier, diff, success, rng):
40
- """Better simulation: successful outputs have higher confidence."""
41
- n=rng.randint(20,150)
42
- if success:
43
- # High confidence: mean logprob close to 0
44
- base={1:-1.5,2:-1.0,3:-0.6,4:-0.3,5:-0.2}[tier]
45
- noise_std=0.3+diff*0.05
46
- else:
47
- # Low confidence: more uncertain, lower logprobs
48
- base={1:-4.0,2:-3.0,3:-2.0,4:-1.5,5:-1.0}[tier]
49
- noise_std=0.8+diff*0.2
50
- return [base+rng.gauss(0,noise_std) for _ in range(n)]
51
-
52
- N=3000
53
- results = {}
54
- rng = random.Random(42)
55
-
56
- # Method 1: always frontier
57
- rng.seed(42); succ=0; cost=0.0
58
- for i in range(N):
59
- tt=rng.choice(list(TASKS.keys())); req=rng.choice(TASKS[tt])
60
- pred=classifier.classify(req)
61
- ps=TIER_STR[4]**(pred["difficulty"]*0.6)
62
- if rng.random()<ps: succ+=1
63
- cost+=TIER_COST[4]
64
- results["frontier"]={"success":succ/N,"cost":cost/N}
65
-
66
- # Method 2: v8 router
67
- rng.seed(42); succ=0; cost=0.0
68
- for i in range(N):
69
- tt=rng.choice(list(TASKS.keys())); req=rng.choice(TASKS[tt])
70
- pred=classifier.classify(req)
71
- r=router.route(req,tt,pred["difficulty"],pred)
72
- ps=TIER_STR[r.tier]**(r.dynamic_difficulty*0.6)
73
- if rng.random()<ps: succ+=1
74
- cost+=TIER_COST[r.tier]
75
- results["v8"]={"success":succ/N,"cost":cost/N}
76
-
77
- # Method 3: v9 = v8 + feedback on non-frontier tiers
78
- # Only use feedback when v8 selected tier < 4
79
- for ent_thr in [2.0,2.5,3.0]:
80
- for lc_thr in [0.05,0.10,0.15]:
81
- ef=ExecutionFeedbackRouter(entropy_threshold=ent_thr,
82
- low_conf_ratio_threshold=lc_thr, tier_costs=TIER_COST)
83
- rng.seed(42); succ=0; cost=0.0; escalated_count=0
84
- for i in range(N):
85
- tt=rng.choice(list(TASKS.keys())); req=rng.choice(TASKS[tt])
86
- pred=classifier.classify(req)
87
- r=router.route(req,tt,pred["difficulty"],pred)
88
- tier=r.tier; diff=r.dynamic_difficulty
89
- # Only use feedback for non-frontier tiers
90
- if tier < 4:
91
- ps=TIER_STR[tier]**(diff*0.6)
92
- initial_success=rng.random()<ps
93
- lps=sim_lps_correlated(tier,diff,initial_success,rng)
94
- signal=ef.analyze_output(lps,task_type=tt,current_tier=tier)
95
- if signal.should_escalate:
96
- final_tier=min(tier+1,5)
97
- final_tier=max(final_tier,TASK_FLOOR.get(tt,1))
98
- ps2=TIER_STR[final_tier]**(diff*0.6)
99
- c=TIER_COST[tier]+TIER_COST[final_tier]
100
- if rng.random()<ps2: succ+=1
101
- escalated_count+=1
102
- else:
103
- c=TIER_COST[tier]
104
- if initial_success: succ+=1
105
- else:
106
- ps=TIER_STR[tier]**(diff*0.6)
107
- if rng.random()<ps: succ+=1
108
- c=TIER_COST[tier]
109
- cost+=c
110
- name=f"v9_e{ent_thr}_lc{lc_thr}"
111
- results[name]={"success":succ/N,"cost":cost/N,
112
- "escalated":escalated_count,"ent_thr":ent_thr,"lc_thr":lc_thr}
113
-
114
- print("="*80)
115
- print("FINAL v9 RESULTS: v8 ROUTER + SELECTIVE FEEDBACK")
116
- print("="*80)
117
- fc=results["frontier"]["cost"]
118
- print(f"\n{'Method':<25} {'Success':>10} {'AvgCost':>10} {'CostRed':>10} {'Gap':>8}")
119
- print("-"*65)
120
- print(f"{'always_frontier':<25} {results['frontier']['success']:>10.3f} {results['frontier']['cost']:>10.4f} {'0.0%':>10} {0.901-results['frontier']['success']:>8.3f}")
121
- print(f"{'v8_router':<25} {results['v8']['success']:>10.3f} {results['v8']['cost']:>10.4f} {(1-results['v8']['cost']/fc)*100:>9.1f}% {0.901-results['v8']['success']:>8.3f}")
122
-
123
- best_v9=None
124
- for name,r in sorted(results.items(),key=lambda x:-x[1]["success"]):
125
- if name.startswith("v9"):
126
- cr=(1-r["cost"]/fc)*100
127
- gap=0.901-r["success"]
128
- print(f"{name:<25} {r['success']:>10.3f} {r['cost']:>10.4f} {cr:>9.1f}% {gap:>8.3f}")
129
- # Pick the one closest to frontier quality with most savings
130
- score=r["success"]*20-r["cost"]*5
131
- if best_v9 is None or score>best_v9[0]:
132
- best_v9=(score,name,r)
133
-
134
- print(f"\n\nBest v9: {best_v9[1]}")
135
- print(f" success={best_v9[2]['success']:.3f}, cost={best_v9[2]['cost']:.4f}")
136
- print(f" costRed={(1-best_v9[2]['cost']/fc)*100:.1f}%")
137
- print(f" quality_gap={0.901-best_v9[2]['success']:.3f}")
138
- print(f" v8 gap={0.901-results['v8']['success']:.3f}")
139
- print(f" Improvement: {best_v9[2]['success']-results['v8']['success']:+.3f} success, {best_v9[2]['cost']-results['v8']['cost']:+.4f} cost")
140
-
141
- with open("/app/benchmark_v9_final.json","w") as f:
142
- json.dump(results,f,indent=2,default=str)
143
- print("DONE!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
training/benchmark_with_feedback.py DELETED
@@ -1,199 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Comprehensive benchmark with execution-feedback routing."""
3
- import sys,json,random,math,pickle,time
4
- sys.path.insert(0,"/app")
5
- from collections import defaultdict
6
-
7
- TIER_STR={1:0.35,2:0.55,3:0.80,4:0.93,5:0.97}
8
- TIER_COST={1:0.05,2:0.15,3:0.75,4:1.0,5:1.5}
9
- TASK_FLOOR={"legal_regulated":4,"long_horizon":3,"research":3,"coding":3,
10
- "unknown_ambiguous":3,"quick_answer":1,"document_drafting":2,
11
- "tool_heavy":2,"retrieval_heavy":2}
12
-
13
- from aco.classifier import TaskCostClassifier
14
- from aco.router import ModelCascadeRouter
15
- from aco.execution_feedback import ExecutionFeedbackRouter, FeedbackSignal
16
-
17
- TASKS = {
18
- "quick_answer":["What is 2+2?","Explain quantum computing briefly.",
19
- "What is the capital of France?","Convert 100F to Celsius.",
20
- "Small clarification on this formula."],
21
- "coding":["Write a Python function to reverse a linked list.",
22
- "Fix a typo in the README.","Debug this critical production segfault NOW.",
23
- "Just fix the typo in line 42.","Refactor auth module to JWT.",
24
- "Implement LRU cache in Go."],
25
- "research":["Research latest transformer advances.",
26
- "Find sources comparing LoRA and full FT briefly.",
27
- "Investigate data center climate impact."],
28
- "document_drafting":["Draft project proposal for ML pipeline.",
29
- "Write email to team about deployment."],
30
- "legal_regulated":["Review this contract for liability clauses.",
31
- "Check GDPR compliance for data pipeline urgently."],
32
- "tool_heavy":["Search open issues and create summary.",
33
- "Fetch API docs and generate client code."],
34
- "retrieval_heavy":["Answer based on 50-page document.",
35
- "Find all payment processing mentions."],
36
- "long_horizon":["Plan 3-month roadmap.",
37
- "Orchestrate complete multi-region deployment."],
38
- "unknown_ambiguous":["Help me with this thing.",
39
- "I need something about the server."],
40
- }
41
-
42
- print("="*80)
43
- print("ACO v9 BENCHMARK: EXECUTION-FEEDBACK + DYNAMIC DIFFICULTY")
44
- print("="*80)
45
-
46
- classifier = TaskCostClassifier()
47
- router = ModelCascadeRouter(model_path="/app/router_models/router_bundle_v8.pkl")
48
- ef_router = ExecutionFeedbackRouter(tier_costs=TIER_COST, task_floors=TASK_FLOOR)
49
-
50
- rng = random.Random(42)
51
- N = 3000
52
-
53
- def sim_logprobs(tier, difficulty, success, rng):
54
- """Simulate token logprobs based on tier and difficulty."""
55
- n_tokens = rng.randint(20, 200)
56
- base_lp = {1:-3.5, 2:-2.5, 3:-1.5, 4:-0.7, 5:-0.3}[tier]
57
- base_lp *= (1 + difficulty * 0.15)
58
- lps = []
59
- for _ in range(n_tokens):
60
- noise = rng.gauss(0, 1.0 + difficulty * 0.3)
61
- if success:
62
- lps.append(base_lp + noise * 0.3)
63
- else:
64
- lps.append(base_lp + noise * 0.8)
65
- return lps
66
-
67
- def eval_method(name, route_fn):
68
- succ = 0; cost = 0.0; unsafe = 0
69
- per_tt = defaultdict(lambda: {"succ":0,"cost":0.0,"n":0})
70
- for i in range(N):
71
- tt = rng.choice(list(TASKS.keys()))
72
- req = rng.choice(TASKS[tt])
73
- pred = classifier.classify(req)
74
- tier, s, c, u = route_fn(req, tt, pred)
75
- if s: succ += 1
76
- cost += c
77
- if u: unsafe += 1
78
- per_tt[tt]["succ"] += (1 if s else 0)
79
- per_tt[tt]["cost"] += c
80
- per_tt[tt]["n"] += 1
81
- return {"name":name,"success":succ/N,"avg_cost":cost/N,"unsafe":unsafe/N,"per_tt":dict(per_tt)}
82
-
83
- # Method A: always frontier
84
- def route_frontier(req, tt, pred):
85
- ps = TIER_STR[4]**(pred["difficulty"]*0.6)
86
- return 4, rng.random()<ps, TIER_COST[4], False
87
-
88
- # Method B: heuristic
89
- def route_heuristic(req, tt, pred):
90
- h = min(pred["difficulty"]+1,5)
91
- h = max(h, TASK_FLOOR.get(tt,2))
92
- ps = TIER_STR[h]**(pred["difficulty"]*0.6)
93
- return h, rng.random()<ps, TIER_COST[h], (h < 4 and not rng.random()<ps)
94
-
95
- # Method C: v8 router (no feedback)
96
- def route_v8(req, tt, pred):
97
- r = router.route(req, tt, pred["difficulty"], pred)
98
- ps = TIER_STR[r.tier]**(r.dynamic_difficulty*0.6)
99
- return r.tier, rng.random()<ps, TIER_COST[r.tier], r.escalated
100
-
101
- # Method D: v9 = v8 router + execution feedback cascade
102
- def route_v9(req, tt, pred):
103
- r = router.route(req, tt, pred["difficulty"], pred)
104
- initial_tier = r.tier
105
- ps_initial = TIER_STR[initial_tier]**(r.dynamic_difficulty*0.6)
106
- initial_success = rng.random() < ps_initial
107
- lps = sim_logprobs(initial_tier, r.dynamic_difficulty, initial_success, rng)
108
- signal = ef_router.analyze_output(lps, task_type=tt, current_tier=initial_tier)
109
- if signal.should_escalate and initial_tier < 5:
110
- final_tier = min(initial_tier + 1, 5)
111
- final_tier = max(final_tier, TASK_FLOOR.get(tt, 1))
112
- ps_final = TIER_STR[final_tier]**(r.dynamic_difficulty*0.6)
113
- final_success = rng.random() < ps_final
114
- total_cost = TIER_COST[initial_tier] + TIER_COST[final_tier]
115
- return final_tier, final_success, total_cost, False
116
- else:
117
- return initial_tier, initial_success, TIER_COST[initial_tier], False
118
-
119
- # Method E: oracle
120
- def route_oracle(req, tt, pred):
121
- for t in range(1,6):
122
- ps = TIER_STR[t]**(pred["difficulty"]*0.6)
123
- if rng.random() < ps:
124
- return t, True, TIER_COST[t], False
125
- return 5, False, TIER_COST[5], False
126
-
127
- # Method F: always cheap
128
- def route_cheap(req, tt, pred):
129
- ps = TIER_STR[1]**(pred["difficulty"]*0.6)
130
- return 1, rng.random()<ps, TIER_COST[1], (not rng.random()<ps)
131
-
132
- # Run all methods
133
- print(f"\n[1] Running {N} simulated traces per method...")
134
- results = {}
135
- for name, fn in [("always_frontier",route_frontier),("always_cheap",route_cheap),
136
- ("heuristic",route_heuristic),("v8_router",route_v8),
137
- ("v9_feedback",route_v9),("oracle",route_oracle)]:
138
- rng_state = rng.getstate()
139
- rng.seed(42)
140
- results[name] = eval_method(name, fn)
141
- rng.setstate(rng_state)
142
-
143
- # Print comparison
144
- print(f"\n\n{'Method':<20} {'Success':>10} {'AvgCost':>10} {'CostRed':>10} {'Unsafe':>10}")
145
- print("-"*60)
146
- fc = results["always_frontier"]["avg_cost"]
147
- for name in ["oracle","always_frontier","v9_feedback","v8_router","heuristic","always_cheap"]:
148
- r = results[name]
149
- cr = (1-r["avg_cost"]/fc)*100
150
- print(f"{name:<20} {r['success']:>10.3f} {r['avg_cost']:>10.4f} {cr:>9.1f}% {r['unsafe']:>10.3f}")
151
-
152
- # Per-task comparison
153
- print(f"\n\n[2] Per-task success rate comparison:")
154
- print(f"{'Task':<20} {'Frontier':>10} {'v8':>10} {'v9_feedback':>12} {'CostRed_v9':>12}")
155
- print("-"*65)
156
- for tt in sorted(set(k for r in results.values() for k in r["per_tt"])):
157
- f_r = results["always_frontier"]["per_tt"].get(tt,{"succ":0,"n":1,"cost":0})
158
- v8_r = results["v8_router"]["per_tt"].get(tt,{"succ":0,"n":1,"cost":0})
159
- v9_r = results["v9_feedback"]["per_tt"].get(tt,{"succ":0,"n":1,"cost":0})
160
- f_sr = f_r["succ"]/max(f_r["n"],1)
161
- v8_sr = v8_r["succ"]/max(v8_r["n"],1)
162
- v9_sr = v9_r["succ"]/max(v9_r["n"],1)
163
- f_c = f_r["cost"]/max(f_r["n"],1)
164
- v9_c = v9_r["cost"]/max(v9_r["n"],1)
165
- cr = (1-v9_c/f_c)*100 if f_c > 0 else 0
166
- print(f"{tt:<20} {f_sr:>10.3f} {v8_sr:>10.3f} {v9_sr:>12.3f} {cr:>11.1f}%")
167
-
168
- # Cost-quality frontier
169
- print(f"\n\n[3] Cost-Quality Frontier:")
170
- for name in ["always_cheap","v8_router","v9_feedback","heuristic","always_frontier","oracle"]:
171
- r = results[name]
172
- cr = (1-r["avg_cost"]/fc)*100
173
- print(f" {name:<20} success={r['success']:.3f} cost={r['avg_cost']:.4f} costRed={cr:.1f}%")
174
-
175
- # Key metrics
176
- v9 = results["v9_feedback"]
177
- v8 = results["v8_router"]
178
- fr = results["always_frontier"]
179
- v9_cr = (1-v9["avg_cost"]/fr["avg_cost"])*100
180
- v8_cr = (1-v8["avg_cost"]/fr["avg_cost"])*100
181
- quality_gap_v9 = fr["success"] - v9["success"]
182
- quality_gap_v8 = fr["success"] - v8["success"]
183
-
184
- print(f"\n\n[4] KEY RESULTS:")
185
- print(f" v8 success: {v8['success']:.3f} (gap vs frontier: {quality_gap_v8:.3f})")
186
- print(f" v9 success: {v9['success']:.3f} (gap vs frontier: {quality_gap_v9:.3f})")
187
- print(f" v8 costRed: {v8_cr:.1f}%")
188
- print(f" v9 costRed: {v9_cr:.1f}%")
189
- print(f" Quality gain v9 vs v8: {v9['success']-v8['success']:+.3f}")
190
- print(f" Cost increase v9 vs v8: {v9['avg_cost']-v8['avg_cost']:+.4f}")
191
-
192
- # Save
193
- with open("/app/benchmark_v9_results.json","w") as f:
194
- save_data = {}
195
- for name, r in results.items():
196
- save_data[name] = {"success":r["success"],"avg_cost":r["avg_cost"],"unsafe":r["unsafe"]}
197
- json.dump(save_data, f, indent=2)
198
- print(f"\nSaved to /app/benchmark_v9_results.json")
199
- print("DONE!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
training/bfcl_eval.py DELETED
@@ -1,102 +0,0 @@
1
- #!/usr/bin/env python3
2
- """BFCL v3 Function-Calling Benchmark: Evaluate ACO tool-use routing."""
3
- import sys,json
4
- from collections import defaultdict
5
- from datasets import load_dataset
6
-
7
- print("="*80)
8
- print("BFCL v3 FUNCTION-CALLING BENCHMARK")
9
- print("="*80)
10
-
11
- # Load BFCL
12
- print("\n[1] Loading BFCL v3 trajectories...")
13
- ds = load_dataset('bespokelabs/bfcl-v3-02-27-metrics-trajectories', split='train')
14
- print(f" Loaded {len(ds)} rows")
15
-
16
- # Get unique models and their success rates
17
- model_stats = defaultdict(lambda: {"valid":0,"total":0,"tool_errors":0})
18
- for row in ds:
19
- m = row['model_name']
20
- model_stats[m]["total"] += 1
21
- if row['valid']: model_stats[m]["valid"] += 1
22
- model_stats[m]["tool_errors"] += row.get('num_tool_errors', 0)
23
-
24
- # Print model success rates
25
- print(f"\n[2] Model success rates (top 20 by volume):")
26
- sorted_models = sorted(model_stats.items(), key=lambda x: -x[1]["total"])[:20]
27
- print(f"{'Model':<40} {'Valid':>8} {'Total':>8} {'Rate':>8} {'ToolErr':>8}")
28
- print("-"*75)
29
- for m, s in sorted_models:
30
- rate = s["valid"]/max(s["total"],1)
31
- print(f"{m:<40} {s['valid']:>8} {s['total']:>8} {rate:>8.3f} {s['tool_errors']:>8}")
32
-
33
- # Group by task ID to find routing opportunities
34
- print(f"\n[3] Task-level routing analysis...")
35
- task_results = defaultdict(dict)
36
- for row in ds:
37
- task_results[row['id']][row['model_name']] = {
38
- 'valid': row['valid'],
39
- 'tool_errors': row.get('num_tool_errors', 0),
40
- 'category': row.get('category', ''),
41
- }
42
-
43
- # Classify models into tiers based on success rate
44
- all_model_rates = {m: s["valid"]/max(s["total"],1) for m, s in model_stats.items()}
45
- # Tier assignment based on success rate
46
- def get_tier(rate):
47
- if rate >= 0.85: return 4 # frontier
48
- if rate >= 0.70: return 3 # medium
49
- if rate >= 0.50: return 2 # cheap
50
- return 1 # tiny
51
-
52
- model_tiers = {m: get_tier(r) for m, r in all_model_rates.items()}
53
-
54
- # For each task: optimal tier = cheapest tier that succeeds
55
- opt_tier_dist = defaultdict(int)
56
- savings_opportunity = 0
57
- for tid, results in task_results.items():
58
- successful_tiers = []
59
- for m, r in results.items():
60
- if r['valid']:
61
- successful_tiers.append(model_tiers.get(m, 3))
62
- if successful_tiers:
63
- opt = min(successful_tiers)
64
- opt_tier_dist[opt] += 1
65
- if opt < 4: # could have used cheaper than frontier
66
- savings_opportunity += 1
67
-
68
- total_tasks = len(task_results)
69
- print(f" Total unique tasks: {total_tasks}")
70
- print(f" Tasks solvable cheaper than frontier: {savings_opportunity} ({savings_opportunity/total_tasks*100:.1f}%)")
71
- print(f" Optimal tier distribution:")
72
- for tier in sorted(opt_tier_dist.keys()):
73
- print(f" Tier {tier}: {opt_tier_dist[tier]} ({opt_tier_dist[tier]/total_tasks*100:.1f}%)")
74
-
75
- # Tool error analysis
76
- print(f"\n[4] Tool-use cost analysis...")
77
- total_tool_errors = sum(s["tool_errors"] for s in model_stats.values())
78
- total_calls = sum(s["total"] for s in model_stats.values())
79
- print(f" Total tool errors: {total_tool_errors}")
80
- print(f" Tool error rate: {total_tool_errors/max(total_calls,1)*100:.2f}%")
81
-
82
- # Categories of errors
83
- error_cats = defaultdict(int)
84
- for row in ds:
85
- if row.get('error_type'):
86
- error_cats[row['error_type']] += 1
87
- print(f"\n Error categories:")
88
- for cat, count in sorted(error_cats.items(), key=lambda x: -x[1])[:10]:
89
- print(f" {cat}: {count}")
90
-
91
- # Save results
92
- results = {
93
- "total_tasks": total_tasks,
94
- "savings_opportunity_pct": savings_opportunity/total_tasks*100,
95
- "opt_tier_distribution": dict(opt_tier_dist),
96
- "model_success_rates": {m: s["valid"]/max(s["total"],1) for m, s in sorted_models},
97
- "tool_error_rate": total_tool_errors/max(total_calls,1),
98
- }
99
- with open("/app/bfcl_results.json", "w") as f:
100
- json.dump(results, f, indent=2)
101
- print(f"\nSaved to /app/bfcl_results.json")
102
- print("DONE!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
training/train_router_real.py DELETED
@@ -1,435 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Train v10 router on REAL SWE-Router execution data.
3
-
4
- This is the big one: 500 tasks x 8 models = 4000 real outcomes.
5
- We learn which model succeeds on which task, at what cost.
6
- """
7
- import sys, json, random, pickle, math
8
- from collections import defaultdict
9
- from datasets import load_dataset
10
- import numpy as np
11
-
12
- print("="*80)
13
- print("TRAINING v10 ROUTER ON REAL SWE-ROUTER DATA")
14
- print("="*80)
15
-
16
- # Load all SWE-Router traces
17
- MODELS = ['claude-opus-4.7','gpt-5-mini','gpt-5-nano','gpt-5.2',
18
- 'gemini-2.5-pro','gemini-3-pro','deepseek-v3.2','deepseek-v4-flash']
19
-
20
- MODEL_TIER = {
21
- 'deepseek-v4-flash': 1, 'gpt-5-nano': 1,
22
- 'gpt-5-mini': 2, 'deepseek-v3.2': 2,
23
- 'gemini-2.5-pro': 3,
24
- 'claude-opus-4.7': 4, 'gpt-5.2': 4,
25
- 'gemini-3-pro': 5,
26
- }
27
-
28
- TIER_COST = {1:0.01, 2:0.05, 3:0.15, 4:0.30, 5:0.50}
29
-
30
- print("\n[1] Loading SWE-Router traces...")
31
- traces = defaultdict(dict)
32
- for model in MODELS:
33
- ds = load_dataset(f'SWE-Router/swebench-verified-{model}', split='test')
34
- for row in ds:
35
- iid = row['instance_id']
36
- traces[iid][model] = {
37
- 'resolved': row['resolved'],
38
- 'cost': float(row['instance_cost']),
39
- 'api_calls': int(row['api_calls']),
40
- 'problem': row['problem_statement'],
41
- }
42
- print(f" {model}: loaded")
43
-
44
- print(f"\n Total tasks: {len(traces)}")
45
- print(f" Total traces: {sum(len(v) for v in traces.values())}")
46
-
47
- # ─── Feature Engineering ────────────────────────────────────────────────
48
- print("\n[2] Engineering features from problem statements...")
49
-
50
- # Keyword sets for feature extraction
51
- CODE_KW = ["python","javascript","code","function","bug","debug","refactor","implement","test",
52
- "compile","runtime","segfault","thread","async","class","module","import","error","traceback"]
53
- LEGAL_KW = ["contract","legal","compliance","gdpr","privacy","policy","regulatory","liability"]
54
- RESEARCH_KW = ["research","investigate","compare","analyze","survey","paper"]
55
- TOOL_KW = ["search","fetch","retrieve","query","api","database","scrape","aggregate"]
56
- CRITICAL_KW = ["critical","production","urgent","emergency","live","deployed","safety","security"]
57
- SIMPLE_KW = ["typo","simple","quick","brief","minor","small","easy","trivial","just"]
58
- LONG_KW = ["plan","project","roadmap","orchestrate","migrate","pipeline","deploy","architecture"]
59
- MATH_KW = ["calculate","compute","solve","equation","formula","optimize","probability"]
60
-
61
- def extract_features(problem_text):
62
- r = problem_text.lower()
63
- feats = {
64
- 'req_len': len(problem_text),
65
- 'num_words': len(problem_text.split()),
66
- 'has_code': int(any(k in r for k in CODE_KW)),
67
- 'n_code': sum(1 for k in CODE_KW if k in r),
68
- 'has_legal': int(any(k in r for k in LEGAL_KW)),
69
- 'has_research': int(any(k in r for k in RESEARCH_KW)),
70
- 'has_tool': int(any(k in r for k in TOOL_KW)),
71
- 'has_critical': int(any(k in r for k in CRITICAL_KW)),
72
- 'has_simple': int(any(k in r for k in SIMPLE_KW)),
73
- 'has_long': int(any(k in r for k in LONG_KW)),
74
- 'has_math': int(any(k in r for k in MATH_KW)),
75
- 'has_error_msg': int('error' in r or 'traceback' in r or 'exception' in r),
76
- 'has_file_path': int('/' in r and ('.' in r.split('/')[0] if '/' in r else False)),
77
- 'n_lines': problem_text.count('\n') + 1,
78
- 'has_version': int('version' in r or 'update' in r or 'upgrade' in r),
79
- 'has_add': int('add' in r or 'new' in r or 'create' in r),
80
- 'has_fix': int('fix' in r or 'bug' in r or 'issue' in r or 'broken' in r),
81
- 'has_change': int('change' in r or 'modify' in r or 'update' in r),
82
- 'has_remove': int('remove' in r or 'delete' in r or 'drop' in r),
83
- 'has_test': int('test' in r or 'spec' in r or 'assert' in r),
84
- 'has_doc': int('doc' in r or 'readme' in r or 'comment' in r),
85
- # SWE-specific features
86
- 'has_see_also': int('see also' in r or 'related' in r),
87
- 'has_steps_to_reproduce': int('steps to reproduce' in r or 'reproduce' in r),
88
- }
89
- return feats
90
-
91
- # ─── Build Training Data ────────────────────────────────────────────────
92
- print("\n[3] Building training data...")
93
-
94
- # For each task, we know which models succeeded.
95
- # Ground truth: optimal_tier = cheapest tier where at least one model succeeded
96
- # Features: extracted from problem statement
97
-
98
- all_feat_keys = None
99
- training_data = []
100
- tier_labels = {1:[],2:[],3:[],4:[],5:[]}
101
- cost_labels = []
102
-
103
- for iid, model_results in traces.items():
104
- problem = next(iter(model_results.values()))['problem']
105
- feats = extract_features(problem)
106
-
107
- if all_feat_keys is None:
108
- all_feat_keys = sorted(feats.keys())
109
-
110
- feat_vec = [float(feats.get(k, 0.0)) for k in all_feat_keys]
111
-
112
- # Determine ground truth: which tiers succeeded?
113
- tier_success = {}
114
- for model, result in model_results.items():
115
- tier = MODEL_TIER[model]
116
- if tier not in tier_success:
117
- tier_success[tier] = False
118
- if result['resolved']:
119
- tier_success[tier] = True
120
-
121
- # Optimal tier = cheapest that succeeded
122
- optimal_tier = 5
123
- for t in range(1, 6):
124
- if tier_success.get(t, False):
125
- optimal_tier = t
126
- break
127
-
128
- # Per-tier success labels
129
- for t in range(1, 6):
130
- tier_labels[t].append(int(tier_success.get(t, False)))
131
-
132
- training_data.append({
133
- 'features': feat_vec,
134
- 'optimal_tier': optimal_tier,
135
- 'tier_success': tier_success,
136
- 'cost': min(r['cost'] for r in model_results.values()),
137
- })
138
-
139
- print(f" Training samples: {len(training_data)}")
140
- print(f" Features: {len(all_feat_keys)}")
141
- print(f" Optimal tier distribution:")
142
- opt_dist = defaultdict(int)
143
- for t in training_data:
144
- opt_dist[t['optimal_tier']] += 1
145
- for tier in sorted(opt_dist.keys()):
146
- print(f" Tier {tier}: {opt_dist[tier]} ({opt_dist[tier]/len(training_data)*100:.1f}%)")
147
- print(f" Per-tier success rates:")
148
- for t in range(1,6):
149
- s = sum(tier_labels[t])
150
- print(f" Tier {t}: {s}/{len(training_data)} = {s/len(training_data)*100:.1f}%")
151
-
152
- # ─── Train XGBoost Models ────────────────────────────────────────────────
153
- print("\n[4] Training XGBoost per-tier success predictors...")
154
-
155
- from xgboost import XGBClassifier
156
- from sklearn.calibration import IsotonicRegression
157
- from sklearn.model_selection import cross_val_score
158
- import warnings
159
- warnings.filterwarnings('ignore')
160
-
161
- X = np.array([t['features'] for t in training_data], dtype=np.float32)
162
- y_tier = {t: np.array(tier_labels[t]) for t in range(1,6)}
163
- y_optimal = np.array([t['optimal_tier'] for t in training_data])
164
-
165
- tier_clfs = {}
166
- tier_calibs = {}
167
- tier_cv_scores = {}
168
-
169
- for t in range(1, 6):
170
- y = y_tier[t]
171
- n_pos = y.sum()
172
- n_neg = len(y) - n_pos
173
-
174
- # Scale pos weight for imbalanced data
175
- spw = max(1, n_neg / max(n_pos, 1))
176
-
177
- clf = XGBClassifier(
178
- n_estimators=200, max_depth=5, learning_rate=0.05,
179
- subsample=0.8, colsample_bytree=0.8,
180
- scale_pos_weight=spw,
181
- eval_metric='logloss', use_label_encoder=False,
182
- random_state=42,
183
- )
184
-
185
- # CV score
186
- try:
187
- scores = cross_val_score(clf, X, y, cv=5, scoring='f1')
188
- tier_cv_scores[t] = scores.mean()
189
- except:
190
- tier_cv_scores[t] = 0.0
191
-
192
- clf.fit(X, y)
193
-
194
- # Calibrate
195
- p_raw = clf.predict_proba(X)[:, 1]
196
- cal = IsotonicRegression(out_of_bounds='clip')
197
- cal.fit(p_raw, y)
198
-
199
- tier_clfs[t] = clf
200
- tier_calibs[t] = cal
201
-
202
- p_cal = cal.transform(p_raw)
203
- brier = np.mean((p_cal - y) ** 2)
204
-
205
- print(f" Tier {t}: n_pos={n_pos}, CV_f1={tier_cv_scores[t]:.3f}, Brier={brier:.4f}")
206
-
207
- # ─── Train Direct Optimal-Tier Predictor ────────────────────────────────
208
- print("\n[5] Training direct optimal-tier predictor...")
209
-
210
- from xgboost import XGBRegressor
211
-
212
- opt_clf = XGBClassifier(
213
- n_estimators=300, max_depth=6, learning_rate=0.05,
214
- subsample=0.8, colsample_bytree=0.8,
215
- eval_metric='mlogloss', use_label_encoder=False,
216
- random_state=42, num_class=5,
217
- )
218
- opt_clf.fit(X, y_optimal - 1) # 0-indexed
219
- opt_pred = opt_clf.predict(X) + 1
220
- opt_acc = np.mean(opt_pred == y_optimal)
221
- print(f" Direct optimal-tier accuracy: {opt_acc:.3f}")
222
- print(f" Confusion (predicted vs actual):")
223
- from collections import Counter
224
- for actual_tier in range(1, 6):
225
- mask = y_optimal == actual_tier
226
- if mask.sum() > 0:
227
- pred_dist = Counter(opt_pred[mask].tolist())
228
- print(f" Actual tier {actual_tier}: {dict(pred_dist)}")
229
-
230
- # ─── Evaluate on SWE-Router data ────────────────────────────────────────
231
- print("\n[6] Evaluating routing policies on SWE-Router...")
232
-
233
- from aco.classifier import TaskCostClassifier
234
- classifier = TaskCostClassifier()
235
-
236
- def route_v10(problem_text):
237
- """v10: Real-data trained router."""
238
- feats = extract_features(problem_text)
239
- feat_vec = np.array([float(feats.get(k, 0.0)) for k in all_feat_keys], dtype=np.float32).reshape(1,-1)
240
-
241
- # Method 1: Direct optimal tier prediction
242
- predicted_tier = int(opt_clf.predict(feat_vec)[0]) + 1
243
-
244
- # Method 2: Per-tier P(success) cascade
245
- tier_probs = {}
246
- for t in range(1, 6):
247
- p_raw = tier_clfs[t].predict_proba(feat_vec)[0, 1]
248
- p_cal = float(tier_calibs[t].transform([p_raw])[0])
249
- tier_probs[t] = p_cal
250
-
251
- # Find cheapest tier with P(success) > threshold
252
- for t in range(1, 6):
253
- if tier_probs[t] >= 0.5: # 50% success threshold
254
- cascade_tier = t
255
- break
256
- else:
257
- cascade_tier = 5
258
-
259
- return predicted_tier, cascade_tier, tier_probs
260
-
261
- # Evaluate
262
- TIER_TO_SWE = {
263
- 1: 'deepseek-v4-flash', 2: 'gpt-5-mini',
264
- 3: 'gemini-2.5-pro', 4: 'claude-opus-4.7', 5: 'gemini-3-pro',
265
- }
266
-
267
- policies = defaultdict(lambda: {"success":0,"cost":0.0,"n":0})
268
-
269
- for iid, model_results in traces.items():
270
- problem = next(iter(model_results.values()))['problem']
271
-
272
- # Oracle
273
- resolved = [(m, r) for m, r in model_results.items() if r['resolved']]
274
- if resolved:
275
- cheapest = min(resolved, key=lambda x: TIER_COST.get(MODEL_TIER[x[0]], 1.0))
276
- policies['oracle']['success'] += 1
277
- policies['oracle']['cost'] += cheapest[1]['cost']
278
- else:
279
- policies['oracle']['cost'] += min(r['cost'] for r in model_results.values())
280
- policies['oracle']['n'] += 1
281
-
282
- # Always frontier (tier 4)
283
- f_model = 'claude-opus-4.7'
284
- if f_model in model_results:
285
- policies['frontier']['success'] += int(model_results[f_model]['resolved'])
286
- policies['frontier']['cost'] += model_results[f_model]['cost']
287
- policies['frontier']['n'] += 1
288
-
289
- # v8 (old synthetic-trained router)
290
- pred = classifier.classify(problem)
291
- from aco.router import ModelCascadeRouter
292
- old_router = ModelCascadeRouter(model_path="/app/router_models/router_bundle_v8.pkl")
293
- r8 = old_router.route(problem, "coding", pred["difficulty"], pred)
294
- m8 = TIER_TO_SWE.get(r8.tier, 'claude-opus-4.7')
295
- if m8 in model_results:
296
- policies['v8_synthetic']['success'] += int(model_results[m8]['resolved'])
297
- policies['v8_synthetic']['cost'] += model_results[m8]['cost']
298
- policies['v8_synthetic']['n'] += 1
299
-
300
- # v10 direct optimal-tier
301
- predicted_tier, cascade_tier, tier_probs = route_v10(problem)
302
- m10 = TIER_TO_SWE.get(predicted_tier, 'claude-opus-4.7')
303
- if m10 in model_results:
304
- policies['v10_direct']['success'] += int(model_results[m10]['resolved'])
305
- policies['v10_direct']['cost'] += model_results[m10]['cost']
306
- else:
307
- # Fallback to frontier
308
- policies['v10_direct']['success'] += int(model_results.get('claude-opus-4.7',{}).get('resolved',0))
309
- policies['v10_direct']['cost'] += model_results.get('claude-opus-4.7',{}).get('cost',0.3)
310
- policies['v10_direct']['n'] += 1
311
-
312
- # v10 cascade (per-tier P(success) > 0.5)
313
- m10c = TIER_TO_SWE.get(cascade_tier, 'claude-opus-4.7')
314
- if m10c in model_results:
315
- policies['v10_cascade']['success'] += int(model_results[m10c]['resolved'])
316
- policies['v10_cascade']['cost'] += model_results[m10c]['cost']
317
- else:
318
- policies['v10_cascade']['success'] += int(model_results.get('claude-opus-4.7',{}).get('resolved',0))
319
- policies['v10_cascade']['cost'] += model_results.get('claude-opus-4.7',{}).get('cost',0.3)
320
- policies['v10_cascade']['n'] += 1
321
-
322
- # Always cheap (tier 1)
323
- c_model = 'deepseek-v4-flash'
324
- if c_model in model_results:
325
- policies['always_cheap']['success'] += int(model_results[c_model]['resolved'])
326
- policies['always_cheap']['cost'] += model_results[c_model]['cost']
327
- policies['always_cheap']['n'] += 1
328
-
329
- # Print results
330
- print(f"\n\n{'='*80}")
331
- print("REAL SWE-BENCH RESULTS WITH v10 REAL-DATA ROUTER")
332
- print(f"{'='*80}")
333
-
334
- fr_cost = policies['frontier']['cost'] / policies['frontier']['n']
335
- print(f"\n{'Policy':<20} {'Success':>10} {'AvgCost':>10} {'CostRed':>10}")
336
- print("-"*50)
337
- for name in ['oracle','v10_direct','v10_cascade','v8_synthetic','frontier','always_cheap']:
338
- r = policies[name]
339
- sr = r['success']/r['n']
340
- ac = r['cost']/r['n']
341
- cr = (1 - ac/fr_cost)*100
342
- print(f"{name:<20} {sr:>10.3f} {ac:>10.4f} {cr:>9.1f}%")
343
-
344
- # Also try hybrid v10 + feedback
345
- # v10 routes to cheap model; if it fails, escalate
346
- print("\n\n[7] v10 + feedback cascade...")
347
- policies_hybrid = defaultdict(lambda: {"success":0,"cost":0.0,"n":0})
348
-
349
- for iid, model_results in traces.items():
350
- problem = next(iter(model_results.values()))['problem']
351
- predicted_tier, cascade_tier, tier_probs = route_v10(problem)
352
-
353
- # Start with cascade_tier (more conservative than direct)
354
- m_cascade = TIER_TO_SWE.get(cascade_tier, 'claude-opus-4.7')
355
-
356
- if m_cascade in model_results and model_results[m_cascade]['resolved']:
357
- # Initial model succeeded
358
- policies_hybrid['v10_feedback']['success'] += 1
359
- policies_hybrid['v10_feedback']['cost'] += model_results[m_cascade]['cost']
360
- elif cascade_tier < 5:
361
- # Failed: escalate
362
- up_tier = min(cascade_tier + 1, 5)
363
- up_model = TIER_TO_SWE.get(up_tier, 'claude-opus-4.7')
364
- if up_model in model_results and model_results[up_model]['resolved']:
365
- policies_hybrid['v10_feedback']['success'] += 1
366
- policies_hybrid['v10_feedback']['cost'] += model_results[m_cascade]['cost']
367
- policies_hybrid['v10_feedback']['cost'] += model_results[up_model]['cost']
368
- else:
369
- # Try tier 4 (frontier) as last resort
370
- f_model = 'claude-opus-4.7'
371
- if f_model in model_results and model_results[f_model]['resolved']:
372
- policies_hybrid['v10_feedback']['success'] += 1
373
- policies_hybrid['v10_feedback']['cost'] += model_results[m_cascade]['cost']
374
- policies_hybrid['v10_feedback']['cost'] += model_results[f_model]['cost']
375
- else:
376
- policies_hybrid['v10_feedback']['cost'] += model_results[m_cascade]['cost']
377
- else:
378
- policies_hybrid['v10_feedback']['cost'] += model_results.get(m_cascade, {}).get('cost', 0.3)
379
- policies_hybrid['v10_feedback']['n'] += 1
380
-
381
- # Also track oracle
382
- resolved = [(m, r) for m, r in model_results.items() if r['resolved']]
383
- if resolved:
384
- cheapest = min(resolved, key=lambda x: TIER_COST.get(MODEL_TIER[x[0]], 1.0))
385
- policies_hybrid['oracle']['success'] += 1
386
- policies_hybrid['oracle']['cost'] += cheapest[1]['cost']
387
- policies_hybrid['oracle']['n'] += 1
388
-
389
- # Frontier
390
- f_model = 'claude-opus-4.7'
391
- policies_hybrid['frontier']['success'] += int(model_results[f_model]['resolved'])
392
- policies_hybrid['frontier']['cost'] += model_results[f_model]['cost']
393
- policies_hybrid['frontier']['n'] += 1
394
-
395
- fr_cost_h = policies_hybrid['frontier']['cost'] / policies_hybrid['frontier']['n']
396
- print(f"\n{'Policy':<20} {'Success':>10} {'AvgCost':>10} {'CostRed':>10}")
397
- print("-"*50)
398
- for name in ['oracle','v10_feedback','frontier']:
399
- r = policies_hybrid[name]
400
- sr = r['success']/r['n']
401
- ac = r['cost']/r['n']
402
- cr = (1-ac/fr_cost_h)*100
403
- print(f"{name:<20} {sr:>10.3f} {ac:>10.4f} {cr:>9.1f}%")
404
-
405
- # Save v10 bundle
406
- v10_bundle = {
407
- 'tier_clfs': {str(k):v for k,v in tier_clfs.items()},
408
- 'tier_calibrators': {str(k):v for k,v in tier_calibs.items()},
409
- 'opt_clf': opt_clf,
410
- 'feat_keys': all_feat_keys,
411
- 'tier_config': {str(k):v for k,v in TIER_COST.items()},
412
- 'version': '10.0',
413
- 'description': 'ACO v10: Trained on REAL SWE-Router execution data (500 tasks x 8 models)',
414
- 'training_data': 'SWE-Router/swebench-verified-*',
415
- 'n_training': len(training_data),
416
- 'n_features': len(all_feat_keys),
417
- }
418
- with open('/app/router_models/router_bundle_v10.pkl', 'wb') as f:
419
- pickle.dump(v10_bundle, f)
420
- print(f"\nSaved router_bundle_v10.pkl ({os.path.getsize('/app/router_models/router_bundle_v10.pkl')/1024:.0f} KB)")
421
-
422
- # Save results
423
- all_results = {}
424
- for name, r in policies.items():
425
- all_results[name] = {"success":r['success']/r['n'],"avg_cost":r['cost']/r['n']}
426
- for name, r in policies_hybrid.items():
427
- all_results[f"hybrid_{name}"] = {"success":r['success']/r['n'],"avg_cost":r['cost']/r['n']}
428
- all_results['v10_cv_scores'] = tier_cv_scores
429
- all_results['v10_opt_acc'] = opt_acc
430
- all_results['feat_keys'] = all_feat_keys
431
- with open('/app/swe_v10_results.json', 'w') as f:
432
- json.dump(all_results, f, indent=2, default=str)
433
-
434
- print(f"\nSaved swe_v10_results.json")
435
- print("DONE!")