File size: 14,706 Bytes
4d620d9 721e233 4d620d9 721e233 4d620d9 721e233 4d620d9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 | """SWE-bench Lite loader for deepagents rollouts + official Docker eval.
Source: https://huggingface.co/datasets/SWE-bench/SWE-bench_Lite
Each row includes the prebuilt instance `image` and `eval_script` used by the
SWE-bench harness. Agent sandboxes are full git checkouts at `base_commit`
(see `deepagents_rl.harness.repo`), not sparse file dumps.
"""
from __future__ import annotations
import json
import re
from typing import Any
DIFF_PATH_RE = re.compile(r"^diff --git a/(.+?) b/(.+)$", re.MULTILINE)
def parse_paths_from_patch(patch: str) -> list[str]:
paths: list[str] = []
for m in DIFF_PATH_RE.finditer(patch or ""):
p = m.group(2).strip()
if p and p != "/dev/null" and p not in paths:
paths.append(p)
return paths
def _as_str_list(val: Any) -> list[str]:
if val is None:
return []
if isinstance(val, list):
return [str(x) for x in val]
if isinstance(val, str):
s = val.strip()
if not s:
return []
try:
parsed = json.loads(s)
if isinstance(parsed, list):
return [str(x) for x in parsed]
except json.JSONDecodeError:
pass
return [s]
return [str(val)]
AGENT_SYSTEM_PROMPT = """\
You are a software-engineering agent inside a FULL git checkout of one open-source
repository (SWE-bench). Your ONLY job is to fix the GitHub issue below by editing
files in this sandbox using tools, then stop.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ABSOLUTE RULES (violate any β reward 0)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
1) Use the structured tool / function-calling interface for EVERY action.
NEVER put tool calls inside markdown fences like ```json ... ``` unless the
runtime already wraps them β prefer native tool_calls.
2) NEVER answer with a plan-only, analysis-only, or code-only text message before
you have called at least one tool.
3) NEVER invent paths. Every path you read/edit MUST appear in a prior ls/glob
result (or be a file you yourself just created with write_file).
4) NEVER assume directories named: src/, lib/, app/, source/, sources/, code/,
project/, workspace/. Most SWE-bench repos do NOT have those. Start at ".".
5) Prefer MINIMAL diffs. Do not rewrite whole files. Do not touch tests unless the
issue explicitly requires it. Do not commit, push, or change git config.
6) Do NOT try to install packages system-wide, download the internet, or run
destructive shell (rm -rf /, sudo, reboot). execute is for small local checks.
7) When the fix is applied (or you are stuck after a thorough attempt), STOP
calling tools and reply with a short plain-text summary starting with DONE.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
TOOLS (use exact names + argument names)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β’ ls(path="." ) β list ONE directory (non-recursive). path="." or "/"
is the repo root. Returns real entry paths.
β’ glob(pattern, path=".") β find files, e.g. pattern="**/*.py"
β’ grep(pattern, path=".") β regex search file contents
β’ read_file(file_path, offset=0, limit=200)
β’ write_file(file_path, content) β create/overwrite a file
β’ edit_file(file_path, old_string, new_string) β exact string replace (preferred)
β’ execute(command) β run a shell command in the sandbox cwd (repo root)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
MANDATORY WORKFLOW (follow in order; do not skip)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Step A β Orient
Call: ls with path="."
Read the listing. Note real top-level dirs/files (e.g. django/, astropy/,
sympy/, setup.py, README.rst). IGNORE the word "src" in the issue text unless
that exact directory appeared in the listing.
Step B β Locate
Use grep / glob with path="." (or a REAL subdirectory from Step A) to find
symbols, class names, error strings, or file names mentioned in the issue.
Then read_file on the concrete paths you found.
Step C β Understand
Read enough surrounding code (use offset/limit) to see how the bug happens.
If a stack trace names a file:path β open THAT file first.
Step D β Fix
Prefer edit_file with a UNIQUE old_string that appears exactly once.
If old_string is not unique, include more context lines.
If the file does not exist and must be created, use write_file.
Keep the change minimal and consistent with existing style.
Step E β Sanity (optional but useful)
execute a cheap check if appropriate, e.g.:
python -c "import ..."
Do NOT run the full SWE-bench test suite yourself β grading is external.
Step F β Finish
Stop tools. Reply: DONE <one-sentence summary of the change>.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
EDGE CASES β handle explicitly
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β’ ls("src") / missing path:
Treat as "that folder does not exist". Immediately ls(".") and continue with
REAL paths from the root listing. Never retry the same missing path more than
once.
β’ Path formats:
Use relative paths from repo root: "pkg/mod.py" or "./pkg/mod.py".
Leading "/" means virtual-root (same as repo root), NOT the host filesystem.
Never use Windows paths, never use /home, /tmp outside the sandbox, never ~.
β’ Empty ls / unexpected layout:
Call glob with pattern="**/*" path="." (or "**/*.py") to discover files.
Many projects put code under the package name (django/, sympy/, requests/),
not under src/.
β’ edit_file fails (string not found / multiple matches):
Re-read the file around the target. Copy old_string EXACTLY (whitespace,
quotes, indentation). Widen context until unique, then retry once.
β’ Tool returns ERROR / TOOL_ERROR:
Read the error. Fix arguments. Do not loop the identical failing call >2 times.
Pivot strategy (different file, grep for another symbol).
β’ Issue mentions a test name:
You may grep for that test to understand expected behavior, but do not delete
or weaken tests to fake a pass.
β’ Multiple files needed:
OK β edit each with separate edit_file calls. Still keep the total diff small.
β’ You are unsure:
Gather more evidence with grep/read_file. Do NOT guess paths like src/main.py.
A wrong edit is worse than stopping after honest exploration.
β’ Output format traps (FORBIDDEN):
- Plain-text "I will now call ls..." without an actual tool call
- Markdown ```json {"name":"ls"...}``` instead of a real tool call
- Dumping a full rewritten file into chat without write_file/edit_file
- Asking the user questions (there is no user β only tools + DONE)
β’ Budget:
You have a limited number of tool rounds. Front-load discovery (ls β grep β
read) then one precise edit. Avoid repetitive ls on the same path.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
FIRST ACTION (hard requirement)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Your very first tool call MUST be:
ls(path=".")
Then proceed with Steps BβF using only paths that exist.
"""
def build_agent_prompt(problem_statement: str, *, instance_id: str) -> str:
issue = (problem_statement or "").strip()
return (
f"{AGENT_SYSTEM_PROMPT}\n"
f"Instance ID: {instance_id}\n"
f"{'=' * 79}\n"
f"ISSUE / PROBLEM STATEMENT\n"
f"{'=' * 79}\n"
f"{issue}\n"
)
def _row_to_task(ex: dict[str, Any], split: str) -> dict[str, Any]:
patch = ex.get("patch") or ""
test_patch = ex.get("test_patch") or ""
instance_id = str(ex["instance_id"])
problem_statement = str(ex.get("problem_statement") or "")
return {
"task_id": instance_id,
"instance_id": instance_id,
"split": split,
"repo": str(ex["repo"]),
"base_commit": str(ex["base_commit"]),
"problem_statement": problem_statement,
"prompt": build_agent_prompt(problem_statement, instance_id=instance_id),
"workspace_paths": parse_paths_from_patch(patch),
"gold_patch": patch,
"test_patch": test_patch,
"fail_to_pass": _as_str_list(ex.get("FAIL_TO_PASS")),
"pass_to_pass": _as_str_list(ex.get("PASS_TO_PASS")),
"hints_text": str(ex.get("hints_text") or ""),
"version": str(ex.get("version") or ""),
"difficulty": str(ex.get("difficulty") or ""),
"image": str(ex.get("image") or ""),
"eval_script": str(ex.get("eval_script") or ""),
"eval_type": str(ex.get("eval_type") or "pass_and_fail"),
"log_parser": str(ex.get("log_parser") or ""),
"environment_setup_commit": str(ex.get("environment_setup_commit") or ""),
"source_dataset": "SWE-bench/SWE-bench_Lite",
}
def load_swe_bench_lite(
*,
splits: tuple[str, ...] = ("dev", "test"),
max_per_split: int | None = None,
token: str | None = None,
) -> dict[str, list[dict[str, Any]]]:
from datasets import load_dataset
kwargs: dict[str, Any] = {}
if token:
kwargs["token"] = token
ds = load_dataset("SWE-bench/SWE-bench_Lite", **kwargs)
out: dict[str, list[dict[str, Any]]] = {}
for split in splits:
if split not in ds:
continue
rows = []
for i, ex in enumerate(ds[split]):
if max_per_split is not None and i >= max_per_split:
break
rows.append(_row_to_task(dict(ex), split=split))
out[split] = rows
return out
def to_hf_records(tasks: list[dict[str, Any]]) -> list[dict[str, Any]]:
records = []
for t in tasks:
records.append(
{
"task_id": t["task_id"],
"instance_id": t["instance_id"],
"split": t["split"],
"repo": t["repo"],
"base_commit": t["base_commit"],
"problem_statement": t["problem_statement"],
"prompt": t["prompt"],
"workspace_paths_json": json.dumps(t["workspace_paths"]),
"gold_patch": t["gold_patch"],
"test_patch": t["test_patch"],
"fail_to_pass_json": json.dumps(t["fail_to_pass"]),
"pass_to_pass_json": json.dumps(t["pass_to_pass"]),
"hints_text": t.get("hints_text") or "",
"version": t.get("version") or "",
"difficulty": t.get("difficulty") or "",
"image": t.get("image") or "",
"eval_script": t.get("eval_script") or "",
"eval_type": t.get("eval_type") or "pass_and_fail",
"log_parser": t.get("log_parser") or "",
"environment_setup_commit": t.get("environment_setup_commit") or "",
"source_dataset": t.get("source_dataset") or "SWE-bench/SWE-bench_Lite",
}
)
return records
def from_hf_record(rec: dict[str, Any]) -> dict[str, Any]:
# Backward compat: older sparse dataset rows may lack image/eval_script β
# callers should reload from SWE-bench/SWE-bench_Lite in that case.
return {
"task_id": rec["task_id"],
"instance_id": rec["instance_id"],
"split": rec.get("split") or "train",
"repo": rec["repo"],
"base_commit": rec["base_commit"],
"problem_statement": rec["problem_statement"],
"prompt": rec["prompt"],
"workspace_paths": json.loads(rec.get("workspace_paths_json") or "[]"),
"gold_patch": rec.get("gold_patch") or "",
"test_patch": rec.get("test_patch") or "",
"fail_to_pass": json.loads(rec.get("fail_to_pass_json") or "[]"),
"pass_to_pass": json.loads(rec.get("pass_to_pass_json") or "[]"),
"hints_text": rec.get("hints_text") or "",
"version": rec.get("version") or "",
"difficulty": rec.get("difficulty") or "",
"image": rec.get("image") or "",
"eval_script": rec.get("eval_script") or "",
"eval_type": rec.get("eval_type") or "pass_and_fail",
"log_parser": rec.get("log_parser") or "",
"environment_setup_commit": rec.get("environment_setup_commit") or "",
"source_dataset": rec.get("source_dataset") or "SWE-bench/SWE-bench_Lite",
}
def save_jsonl(tasks: list[dict[str, Any]], path) -> None:
from pathlib import Path
path = Path(path)
path.parent.mkdir(parents=True, exist_ok=True)
with path.open("w", encoding="utf-8") as f:
for t in to_hf_records(tasks):
f.write(json.dumps(t) + "\n")
|