grok-record / harness /scripts /build_solved_sft.py
simonycl's picture
Upload folder using huggingface_hub
9b2f1cf verified
Raw History Blame Contribute Delete
6.02 kB
#!/usr/bin/env python3
"""Convert our own solved R2E rollouts to pi-format SFT. Not eval-suite items."""
from __future__ import annotations
import json
from pathlib import Path
from datasets import Dataset
OUT = Path("/var/lib/agentptb-cache/tb1/tmp/sft-data/pi-sft-solved2")
ROOTS = [
Path("/mnt/pvc/users/simon/agentptb/runs/grok/workspace/outputs/rl-r2e4/run_default/rollouts"),
Path("/mnt/pvc/users/simon/agentptb/runs/grok/workspace/outputs/rl-r2e7/run_default/rollouts"),
Path("/mnt/pvc/users/simon/agentptb/runs/grok/workspace/outputs/rl-r2e8/run_default/rollouts"),
]
TOOLS = json.dumps(
[
{
"type": "function",
"function": {
"name": "bash",
"description": "Execute a bash command",
"parameters": {
"type": "object",
"properties": {"command": {"type": "string"}},
"required": ["command"],
},
},
},
{
"type": "function",
"function": {
"name": "read",
"description": "Read a file",
"parameters": {
"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"],
},
},
},
{
"type": "function",
"function": {
"name": "write",
"description": "Write a file",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string"},
"content": {"type": "string"},
},
"required": ["path", "content"],
},
},
},
{
"type": "function",
"function": {
"name": "edit",
"description": "Edit a file",
"parameters": {
"type": "object",
"properties": {"path": {"type": "string"}, "edits": {"type": "array"}},
"required": ["path", "edits"],
},
},
},
]
)
def score(tr, key):
v = (tr.get("rewards") or {}).get(key)
return float((v.get("score") if isinstance(v, dict) else v) or 0)
def norm_tc(tc: dict) -> dict:
if "function" in tc:
return {
"id": tc.get("id") or "call_0",
"type": "function",
"function": {
"name": (tc.get("function") or {}).get("name") or "bash",
"arguments": (tc.get("function") or {}).get("arguments") or "{}",
},
}
args = tc.get("arguments")
if not isinstance(args, str):
args = json.dumps(args or {})
return {
"id": tc.get("id") or "call_0",
"type": "function",
"function": {"name": tc.get("name") or "bash", "arguments": args},
}
def has_edit(msgs) -> bool:
for m in msgs:
if m.get("role") != "assistant":
continue
for tc in m.get("tool_calls") or []:
fn = (tc.get("function") or {}).get("name") or tc.get("name")
if fn in {"edit", "write"}:
return True
args = (tc.get("function") or {}).get("arguments") or tc.get("arguments") or ""
if isinstance(args, dict):
args = json.dumps(args)
if any(tok in str(args) for tok in ("sed -i", "tee ", "cat >", "git apply", "printf ")):
return True
return False
def to_msgs(tr) -> list[dict] | None:
nodes = tr.get("nodes") or []
if not (16 <= len(nodes) <= 200):
return None
msgs = []
for n in nodes:
m = n.get("message") or {}
role = m.get("role")
if role not in {"system", "user", "assistant", "tool"}:
continue
out = {"role": role}
content = m.get("content")
if isinstance(content, list):
parts = []
for p in content:
if isinstance(p, dict) and p.get("type") == "text":
parts.append(p.get("text") or "")
elif isinstance(p, str):
parts.append(p)
content = "\n".join(parts)
if content is None:
content = ""
if not isinstance(content, str):
content = str(content)
out["content"] = content
if role == "assistant" and m.get("tool_calls"):
out["tool_calls"] = [norm_tc(tc) for tc in m["tool_calls"]]
if role == "tool":
if m.get("tool_call_id"):
out["tool_call_id"] = m["tool_call_id"]
elif m.get("name"):
out["name"] = m["name"]
msgs.append(out)
if len(msgs) < 8:
return None
return msgs
def main():
rows = []
seen = set()
for root in ROOTS:
if not root.exists():
continue
for f in root.rglob("traces.jsonl"):
for line in f.open():
rec = json.loads(line)
traces = rec.get("traces") or [rec]
for tr in traces:
if not isinstance(tr, dict):
continue
if score(tr, "solved") < 1:
continue
msgs = to_msgs(tr)
if not msgs:
continue
key = json.dumps([m.get("content", "")[:80] for m in msgs[:6]], sort_keys=True)
if key in seen:
continue
seen.add(key)
rows.append({"messages": msgs, "tools": TOOLS, "source": "r2e-solved"})
print("rows", len(rows))
OUT.mkdir(parents=True, exist_ok=True)
Dataset.from_list(rows).to_parquet(str(OUT / "train.parquet"))
print("wrote", OUT / "train.parquet")
if __name__ == "__main__":
main()