grok-record / harness /scripts /make_finish_sft.py
simonycl's picture
Upload folder using huggingface_hub
9b2f1cf verified
Raw History Blame Contribute Delete
7.53 kB
#!/usr/bin/env python3
"""Synthetic finish-the-task + no-loop trajectories in pi tool format."""
from __future__ import annotations
import json
import random
from pathlib import Path
from datasets import Dataset, load_dataset
SYS = """You are an expert coding assistant operating inside pi, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.
Guidelines:
- Be concise. Call a tool or give a short final answer and stop.
- Chain shell steps in one bash call. Do not loop ls/cd.
- When the required output exists, stop.
"""
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": "write",
"description": "Write a file",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string"},
"content": {"type": "string"},
},
"required": ["path", "content"],
},
},
},
{
"type": "function",
"function": {
"name": "read",
"description": "Read a file",
"parameters": {
"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"],
},
},
},
{
"type": "function",
"function": {
"name": "ls",
"description": "List a directory",
"parameters": {
"type": "object",
"properties": {"path": {"type": "string"}},
},
},
},
{
"type": "function",
"function": {
"name": "edit",
"description": "Edit a file",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string"},
"edits": {"type": "array"},
},
"required": ["path", "edits"],
},
},
},
]
)
def tc(i, name, args):
return {
"id": f"c{i}",
"type": "function",
"function": {"name": name, "arguments": json.dumps(args)},
}
def asst(text, calls=None):
m = {"role": "assistant", "content": text}
if calls:
m["tool_calls"] = calls
return m
def tool(i, content):
return {"role": "tool", "tool_call_id": f"c{i}", "content": content}
def pack(msgs, src):
return {"messages": [{"role": "system", "content": SYS}] + msgs, "tools": TOOLS, "source": src}
def main():
rng = random.Random(2)
rows = []
for i in range(400):
fname = f"out_{i}.txt"
body = rng.choice(["ok", "done", "42", "hello", "pass"])
rows.append(
pack(
[
{"role": "user", "content": f"Write {body} to /output/{fname} and stop."},
asst("Writing the file.", [tc(0, "bash", {"command": f"mkdir -p /output && printf '%s\\n' '{body}' > /output/{fname} && wc -c /output/{fname}"})]),
tool(0, f"4 /output/{fname}\n"),
asst(f"Wrote /output/{fname}."),
],
"fin:write",
)
)
for i in range(300):
rows.append(
pack(
[
{"role": "user", "content": "Explore the repo and set the version in pkg/__init__.py to 1.2.3."},
asst("Listing once, then editing.", [tc(0, "bash", {"command": "ls && sed -n '1,20p' pkg/__init__.py"})]),
tool(0, "__version__ = '0.1.0'\n"),
asst(
"Updating the version.",
[
tc(
1,
"edit",
{
"path": "pkg/__init__.py",
"edits": [{"oldText": "__version__ = '0.1.0'", "newText": "__version__ = '1.2.3'"}],
},
)
],
),
tool(1, "Edited pkg/__init__.py"),
asst("Set __version__ to 1.2.3."),
],
"fin:edit-once",
)
)
for i in range(250):
rows.append(
pack(
[
{"role": "user", "content": "Run the tests. If they pass, stop. Do not keep listing files."},
asst("Running tests.", [tc(0, "bash", {"command": "python -m pytest -q"})]),
tool(0, ".\n1 passed\n"),
asst("Tests passed."),
],
"fin:tests-pass",
)
)
for i in range(250):
rows.append(
pack(
[
{"role": "user", "content": "Fix add() so 2+2 is 4, then retest."},
asst("Reading and testing.", [tc(0, "bash", {"command": "cat mathlib.py && python -m pytest -q"})]),
tool(0, "def add(a,b):\n return a+b+1\nF\nassert add(2,2)==4\n"),
asst(
"Fixing the off-by-one.",
[
tc(
1,
"edit",
{
"path": "mathlib.py",
"edits": [{"oldText": " return a+b+1", "newText": " return a+b"}],
},
)
],
),
tool(1, "Edited"),
asst("Re-running tests.", [tc(2, "bash", {"command": "python -m pytest -q"})]),
tool(2, ".\n1 passed\n"),
asst("Fixed add() and tests pass."),
],
"fin:fix-retest",
)
)
# mix in shortened existing data
existing = load_dataset("/var/lib/agentptb-cache/tb1/tmp/sft-data/pi-sft-short", split="train")
idx = list(range(len(existing)))
rng.shuffle(idx)
for j in idx[:8000]:
rec = existing[j]
rows.append({"messages": rec["messages"], "tools": rec["tools"], "source": rec["source"]})
rng.shuffle(rows)
out = Path("/var/lib/agentptb-cache/tb1/tmp/sft-data/pi-sft-finish")
out.mkdir(parents=True, exist_ok=True)
ds = Dataset.from_dict(
{
"messages": [r["messages"] for r in rows],
"tools": [r["tools"] if isinstance(r["tools"], str) else json.dumps(r["tools"]) for r in rows],
"source": [r["source"] for r in rows],
}
)
ds.to_parquet(str(out / "train.parquet"))
print("wrote", len(rows), "to", out)
if __name__ == "__main__":
main()