narcolepticchicken commited on
Commit
c2d60f8
Β·
verified Β·
1 Parent(s): 46d62fa

Upload build_traces.py

Browse files
Files changed (1) hide show
  1. build_traces.py +241 -0
build_traces.py ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Build and publish the ACO training traces dataset.
2
+
3
+ Loads the same three source datasets used for training:
4
+ - lockon/ToolACE → tool_gater traces (query→tool_called binary)
5
+ - RouteWorks/RouterArena → tier_router traces (question→difficulty tier)
6
+ - R2E-Gym/R2EGym-Verifier-Trajectories → verifier_gater traces (patch→verified binary)
7
+
8
+ Applies identical preprocessing to verify_v2.py training.
9
+ Publishes train/test splits to narcolepticchicken/aco-traces.
10
+
11
+ Usage:
12
+ uv run --with transformers,torch,datasets,huggingface_hub build_traces.py
13
+ """
14
+ import json
15
+ import os
16
+ import re
17
+ from datasets import Dataset, load_dataset
18
+ from huggingface_hub import HfApi
19
+
20
+ # ═══════════════════════════════════════════════════════════════
21
+ # 1. Tool Gater: ToolACE β†’ binary tool-call classification
22
+ # ═══════════════════════════════════════════════════════════════
23
+
24
+ def build_tool_gater():
25
+ ds = load_dataset("lockon/ToolACE", split="train")
26
+ texts, labels = [], []
27
+ skipped = 0
28
+ for row in ds:
29
+ conv = row.get("conversations", [])
30
+ q = ""
31
+ for turn in conv:
32
+ if turn.get("from") == "user":
33
+ q = turn.get("value", "")[:1500]
34
+ break
35
+ if not q:
36
+ skipped += 1
37
+ continue
38
+ # Detect if any assistant turn includes a tool-call pattern
39
+ called = any(
40
+ re.search(r'\[[A-Z][a-zA-Z]+\s*\(', turn["value"])
41
+ for turn in conv if turn.get("from") == "assistant"
42
+ )
43
+ text = f"Query: {q}"
44
+ if row.get("system"):
45
+ text = f"System: {row['system'][:500]}\n\n{text}"
46
+ texts.append(text[:2000])
47
+ labels.append(1 if called else 0)
48
+ ds = Dataset.from_dict({"text": texts, "labels": labels})
49
+ print(f" ToolACE: {len(texts)} samples, {skipped} skipped, "
50
+ f"pos={sum(labels)} ({sum(labels)/len(labels)*100:.1f}%)")
51
+ return ds.train_test_split(test_size=0.15, seed=42)
52
+
53
+ # ═══════════════════════════════════════════════════════════════
54
+ # 2. Tier Router: RouterArena β†’ 3-class difficulty classification
55
+ # ═══════════════════════════════════════════════════════════════
56
+
57
+ def build_tier_router():
58
+ ds = load_dataset("RouteWorks/RouterArena", "default", split="full")
59
+ tmap = {"easy": 0, "medium": 1, "hard": 2}
60
+ texts, labels = [], []
61
+ skipped = 0
62
+ for row in ds:
63
+ d = row.get("Difficulty", "").strip().lower()
64
+ if d not in tmap:
65
+ skipped += 1
66
+ continue
67
+ parts = []
68
+ if row.get("Domain"):
69
+ parts.append(f"[{row['Domain']}]")
70
+ if row.get("Context"):
71
+ parts.append(f"Context: {row['Context']}")
72
+ parts.append(row.get("Question", ""))
73
+ o = row.get("Options", "")
74
+ if o:
75
+ parts.append(f"Options: {'; '.join(o) if isinstance(o, list) else o}")
76
+ texts.append(" ".join(parts)[:2000])
77
+ labels.append(tmap[d])
78
+ ds = Dataset.from_dict({"text": texts, "labels": labels})
79
+ dist = {k: labels.count(k) for k in [0,1,2]}
80
+ print(f" RouterArena: {len(texts)} samples, {skipped} skipped, "
81
+ f"dist={dist}")
82
+ return ds.train_test_split(test_size=0.15, seed=42)
83
+
84
+ # ═══════════════════════════════════════════════════════════════
85
+ # 3. Verifier Gater: R2E-Gym β†’ binary verification classification
86
+ # ═══════════════════════════════════════════════════════════════
87
+
88
+ def build_verifier_gater():
89
+ ds = load_dataset("R2E-Gym/R2EGym-Verifier-Trajectories", split="train")
90
+ texts, labels = [], []
91
+ for row in ds:
92
+ messages = row["messages"]
93
+ fl = messages[1]["content"] if len(messages) > 1 else ""
94
+
95
+ # Extract github issue text
96
+ task_text = ""
97
+ for msg in messages:
98
+ if msg["role"] == "user" and "INTERACTION LOG" in msg["content"]:
99
+ m = re.search(r'<github_issue>(.*?)</github_issue>', msg["content"], re.DOTALL)
100
+ if m:
101
+ task_text = m.group(1).strip()[:1000]
102
+ break
103
+ if not task_text:
104
+ for msg in messages:
105
+ if msg["role"] == "system":
106
+ task_text = msg["content"][:500]
107
+ break
108
+
109
+ # Extract agent action summary from last 3 turns
110
+ ab = re.findall(r'\[ASSISTANT\](.*?)(?:\[USER\]|\[STEP\]|$)', fl, re.DOTALL)
111
+ agent_sum = " ".join(b.strip()[:200] for b in ab[-3:])
112
+
113
+ # Extract patch
114
+ pm = re.search(r'=== FINAL PATCH ===\s*\n(.*?)\n=== END FINAL PATCH ===', fl, re.DOTALL)
115
+ patch = pm.group(1)[:500] if pm else ""
116
+
117
+ text = f"TASK: {task_text[:600]}\nAGENT_ACTIONS: {agent_sum[:600]}\nPATCH: {patch[:400]}"
118
+ texts.append(text[:2000])
119
+ labels.append(1 if row["rewards"] >= 1.0 else 0)
120
+
121
+ ds = Dataset.from_dict({"text": texts, "labels": labels})
122
+ print(f" R2E-Gym: {len(texts)} samples, pos={sum(labels)} ({sum(labels)/len(labels)*100:.1f}%)")
123
+ return ds.train_test_split(test_size=0.15, seed=42)
124
+
125
+ # ═══════════════════════════════════════════════════════════════
126
+ # Build and publish
127
+ # ═══════════════════════════════════════════════════════════════
128
+
129
+ def main():
130
+ api = HfApi()
131
+ repo = "narcolepticchicken/aco-traces"
132
+
133
+ builders = {
134
+ "tool_gater": build_tool_gater,
135
+ "tier_router": build_tier_router,
136
+ "verifier_gater": build_verifier_gater,
137
+ }
138
+
139
+ metadata = {
140
+ "description": "Agent Cost Optimizer training traces. Preprocessed from "
141
+ "lockon/ToolACE, RouteWorks/RouterArena, and "
142
+ "R2E-Gym/R2EGym-Verifier-Trajectories.",
143
+ "license": "apache-2.0",
144
+ "citation_sources": [
145
+ "ToolACE (arXiv:2409.00920)",
146
+ "RouteWorks RouterArena (arXiv:2510.00202)",
147
+ "R2E-Gym Verifier Trajectories",
148
+ ],
149
+ "build_date": None, # will be set
150
+ "preprocessing": "Identical to verify_v2.py loaders. "
151
+ "Tool gater: regex tool-call detection in ToolACE conversations. "
152
+ "Tier router: RouterArena Difficulty field β†’ 3 classes. "
153
+ "Verifier gater: R2E-Gym rewards threshold 1.0, "
154
+ "600-char github issue + agent action summary + patch snippet.",
155
+ }
156
+
157
+ from datetime import datetime
158
+ metadata["build_date"] = datetime.utcnow().isoformat()
159
+
160
+ for task_name, builder_fn in builders.items():
161
+ print(f"\n{'='*60}")
162
+ print(f"Building: {task_name}")
163
+ print(f"{'='*60}")
164
+
165
+ splits = builder_fn()
166
+ for split_name in ["train", "test"]:
167
+ ds = splits[split_name]
168
+ path = f"data/{task_name}/{split_name}.parquet"
169
+ ds.to_parquet(f"/tmp/{task_name}_{split_name}.parquet")
170
+
171
+ api.upload_file(
172
+ path_or_fileobj=f"/tmp/{task_name}_{split_name}.parquet",
173
+ path_in_repo=path,
174
+ repo_id=repo,
175
+ repo_type="dataset",
176
+ )
177
+ print(f" Uploaded: {path} ({len(ds)} rows)")
178
+
179
+ # Upload metadata / data card
180
+ api.upload_file(
181
+ path_or_fileobj=json.dumps(metadata, indent=2).encode(),
182
+ path_in_repo="metadata.json",
183
+ repo_id=repo,
184
+ repo_type="dataset",
185
+ )
186
+
187
+ # Upload README
188
+ readme = f"""---
189
+ license: apache-2.0
190
+ task_categories:
191
+ - text-classification
192
+ language:
193
+ - en
194
+ tags:
195
+ - agent-traces
196
+ - cost-optimization
197
+ - model-routing
198
+ - tool-calling
199
+ - verifier
200
+ pretty_name: ACO Training Traces
201
+ ---
202
+
203
+ # ACO Training Traces
204
+
205
+ Training data for the Agent Cost Optimizer's specialist classifiers.
206
+
207
+ ## Source Datasets
208
+
209
+ | Split | Source | Task | Classes |
210
+ |-------|--------|------|---------|
211
+ | tool_gater | lockon/ToolACE | Predict whether a tool call is needed | binary: no_tool (0) / call_tool (1) |
212
+ | tier_router | RouteWorks/RouterArena | Predict difficulty tier | 3-class: easy (0) / medium (1) / hard (2) |
213
+ | verifier_gater | R2E-Gym/R2EGym-Verifier-Trajectories | Predict whether patch passes verification | binary: fail (0) / pass (1) |
214
+
215
+ ## Preprocessing
216
+
217
+ Identical to the loaders in `verify_v2.py`. See `metadata.json` for details.
218
+
219
+ ## Usage
220
+
221
+ ```python
222
+ from datasets import load_dataset
223
+
224
+ ds = load_dataset("narcolepticchicken/aco-traces", "tool_gater")
225
+ # Contains 'train' and 'test' splits with 'text' and 'labels' columns
226
+ ```
227
+
228
+ Built: {metadata['build_date']}
229
+ """
230
+ api.upload_file(
231
+ path_or_fileobj=readme.encode(),
232
+ path_in_repo="README.md",
233
+ repo_id=repo,
234
+ repo_type="dataset",
235
+ )
236
+
237
+ print(f"\nDataset published to https://huggingface.co/datasets/{repo}")
238
+ print(f"Total splits: {len(builders) * 2}")
239
+
240
+ if __name__ == "__main__":
241
+ main()