mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-08-28 05:27:41 +00:00
Python scripts for running SWE-bench Lite evals against Fabro agent in Daytona sandboxes: instance orchestration, Dockerfile generation, and result evaluation via the official swebench harness. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
158 lines
5 KiB
Python
158 lines
5 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate Dockerfiles for SWE-bench environments.
|
|
|
|
Uses swebench specs to produce one Dockerfile per unique (repo, version) pair.
|
|
These get inlined into Daytona snapshot configs in workflow.toml files.
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from swebench.harness.constants import MAP_REPO_VERSION_TO_SPECS
|
|
|
|
|
|
SNAPSHOT_VERSION = "v4"
|
|
|
|
# Pin Miniconda to a version that supports older Python versions (3.5, 3.6, etc.)
|
|
MINICONDA_URL = "https://repo.anaconda.com/miniconda/Miniconda3-py311_23.11.0-2-Linux-x86_64.sh"
|
|
|
|
|
|
def repo_version_key(repo: str, version: str) -> str:
|
|
"""Snapshot name for a (repo, version) pair."""
|
|
slug = repo.replace("/", "-").replace("_", "-").lower()
|
|
return f"swebench-{slug}-{version}-{SNAPSHOT_VERSION}"
|
|
|
|
|
|
def generate_dockerfile(repo: str, version: str) -> str:
|
|
"""Generate a Dockerfile for a (repo, version) environment.
|
|
|
|
Installs system deps, miniconda, creates a testbed conda env with the
|
|
correct Python version and pip packages from the swebench spec.
|
|
"""
|
|
specs = MAP_REPO_VERSION_TO_SPECS.get(repo, {}).get(version, {})
|
|
python_version = specs.get("python", "3.9")
|
|
|
|
# System packages
|
|
system_packages = [
|
|
"git", "curl", "build-essential", "ripgrep", "ca-certificates",
|
|
"wget", "pkg-config", "libffi-dev", "libssl-dev",
|
|
]
|
|
|
|
# Conda packages — skip file references (requirements.txt, environment.yml)
|
|
# which refer to repo files not available at Docker build time
|
|
conda_packages = specs.get("packages", "")
|
|
if conda_packages in ("requirements.txt", "environment.yml"):
|
|
conda_packages = ""
|
|
pip_packages = specs.get("pip_packages", [])
|
|
if isinstance(pip_packages, str):
|
|
pip_packages = [pip_packages] if pip_packages else []
|
|
|
|
lines = [
|
|
"FROM ubuntu:22.04",
|
|
"",
|
|
"ENV DEBIAN_FRONTEND=noninteractive",
|
|
"RUN apt-get update && apt-get install -y --no-install-recommends \\",
|
|
" " + " \\\n ".join(system_packages) + " \\",
|
|
" && rm -rf /var/lib/apt/lists/*",
|
|
"",
|
|
]
|
|
|
|
# Miniconda
|
|
lines.extend([
|
|
f"RUN curl -sL {MINICONDA_URL} -o /tmp/mc.sh && \\",
|
|
" bash /tmp/mc.sh -b -p /opt/miniconda3 && \\",
|
|
" rm /tmp/mc.sh",
|
|
"",
|
|
"ENV PATH=/opt/miniconda3/envs/testbed/bin:/opt/miniconda3/bin:$PATH",
|
|
"",
|
|
])
|
|
|
|
# Conda env with Python version — use conda-forge for old Python versions
|
|
# that are no longer available in the defaults channel
|
|
channel = "-c conda-forge" if python_version in ("3.5", "3.6") else ""
|
|
conda_create = f"RUN conda create -n testbed {channel} python={python_version} -y".replace(" ", " ")
|
|
if conda_packages:
|
|
conda_create += f" && conda install -n testbed -y {conda_packages}"
|
|
lines.append(conda_create)
|
|
lines.append("")
|
|
|
|
# Activate testbed env for subsequent RUN commands
|
|
lines.append("SHELL [\"bash\", \"-c\"]")
|
|
lines.append("ENV CONDA_DEFAULT_ENV=testbed")
|
|
lines.append("")
|
|
|
|
# Pip packages
|
|
if pip_packages:
|
|
pip_str = " ".join(pip_packages)
|
|
lines.append(f"RUN pip install {pip_str}")
|
|
lines.append("")
|
|
|
|
lines.append("WORKDIR /home/daytona/workspace")
|
|
lines.append("")
|
|
|
|
return "\n".join(lines)
|
|
|
|
|
|
def get_all_repo_versions() -> list[tuple[str, str]]:
|
|
"""Return all unique (repo, version) pairs from swebench specs."""
|
|
pairs = []
|
|
for repo, versions in MAP_REPO_VERSION_TO_SPECS.items():
|
|
for version in versions:
|
|
pairs.append((repo, version))
|
|
return sorted(pairs)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Generate SWE-bench Dockerfiles")
|
|
parser.add_argument(
|
|
"--output-dir",
|
|
type=Path,
|
|
default=Path(__file__).parent / "dockerfiles",
|
|
help="Directory to write Dockerfiles to",
|
|
)
|
|
parser.add_argument(
|
|
"--list",
|
|
action="store_true",
|
|
help="List all (repo, version) pairs and exit",
|
|
)
|
|
parser.add_argument(
|
|
"--json",
|
|
action="store_true",
|
|
dest="output_json",
|
|
help="Output snapshot name -> dockerfile mapping as JSON",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
pairs = get_all_repo_versions()
|
|
|
|
if args.list:
|
|
for repo, version in pairs:
|
|
key = repo_version_key(repo, version)
|
|
print(f"{key}: {repo} @ {version}")
|
|
print(f"\nTotal: {len(pairs)} environments")
|
|
return
|
|
|
|
dockerfiles = {}
|
|
for repo, version in pairs:
|
|
key = repo_version_key(repo, version)
|
|
dockerfile = generate_dockerfile(repo, version)
|
|
dockerfiles[key] = dockerfile
|
|
|
|
if args.output_json:
|
|
json.dump(dockerfiles, sys.stdout, indent=2)
|
|
print()
|
|
return
|
|
|
|
args.output_dir.mkdir(parents=True, exist_ok=True)
|
|
for key, dockerfile in dockerfiles.items():
|
|
path = args.output_dir / f"{key}.Dockerfile"
|
|
path.write_text(dockerfile)
|
|
print(f"Wrote {path}")
|
|
|
|
print(f"\nGenerated {len(dockerfiles)} Dockerfiles in {args.output_dir}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|