SWE-bench
[TOC]
1. Code Analysis
1.1 Architecture
The architecture of this benchmark can be divided into 4 layer:
- Entry Layer
- Inference Layer & Harness Layer
- Core Engine Layer
- Data Collection Layer
1 | ┌───────────────────────────────────────────────────────────────┐ |
1.2 Core Data Structure
1.2.1 SWEbenchInstance (SWE-bench Instance)
1 | class SWEbenchInstance(TypedDict): |
The core judgement of the evaluation process are
FAIL_TO_PASS and PASS_TO_PASS.
FAIL_TO_PASS: Tests that fail when the bug exists and
pass when the bug is repaired.
PASS_TO_PASS: Tests that should pass when the bug both
exists and is repaired.
1.2.2 TestSpec (Test Specification)
1 |
|
1.3.3 Three-Layer Structure of Docker Image
1 | sweb.eval.x86_64.sympy__sympy-20590:latest ← Instance Image |
Image Naming Rule :
sweb.{Layer}.{Language}.{Arch}.{Hash}:{Lable}
e.g. Test 3 different Django Issues
1 | django__django-15022 (Django 4.1, Python 3.11) |
If the docker is not layered, it is needed to create 3 different docker images:
1 | django-15022 image ──── Ubuntu + Python + Django 4.1 + Code → 2GB |
Totally 6 GB needed.
But if we layer the image:
- Base Layer: we need 2 images for Python 3.9 and Python 3.11, total 2 * 1 = 2GB.
- Env Layer: we need 2 images for Django 3.2 and Django 4.1, total 2 * 2 = 4GB
- Instance Layer: we need 3 images for each issue, total 50MB × 3 ≈ 0.15GB
- total 6.15 GB
It seems that the cost of 2 plans seem similar. But if we have 2294 instance, what will happen ?
- No layer: 2294 * 2GB = 4588 GB
- 8 images of Base Layer + 200 images of Env Layer + 2294 images of instance Layer = 8 * 1GB + 200 * 2GB + 0.15GB * 2294 ≈ 523 GB
- 88.60% Memory are saved !
1.3 Inference Pipeline
1.3.1 Default Inference (Not Available Now)
In fact, the main function of SWE-bench is to evaluate the score of patch generated by LLM. It doesn’t care about how you generate the patch. There are three original scripts to call LLM to generate patch. However there are a lot of limits of these scripts because they were just created for the paper demonstration.
There are three ways of run interference command:
Call cloud LLM API (OpenAI ChatGPT / Ahthropic Claude)
1
2
3
4python -m swebench.inference.run_api \
--dataset_name_or_path princeton-nlp/SWE-bench_oracle \
--model_name_or_path gpt-4 \
--output_dir ./outputsLocal LLM
1
2
3
4python -m swebench.inference.run_llama \
--model_name_or_path princeton-nlp/SWE-Llama-13b \
--dataset_path princeton-nlp/SWE-bench_oracle \
--output_dir ./outputsReason about issues that appear in real-time on GitHub
1
2
3python -m swebench.inference.run_live \
--model_name gpt-3.5-turbo-1106 \
--issue_url https://github.com/some/repo/issues/123
1.3.1.1 How Did The Original Inference Scripts Work?
1.3.2 How to Inference Personally
1.3.2.2 Prepare for the Env
1 | conda activate / |
1.3.2.1 Start a local LLM by vLLM (This section should be moved to Part 2 )
A local LLM has been deployed and is driven by vLLM:
1 | docker run -d --name vllm_server --gpus all \ |
1.3.2.2 Full run_local_vllm.py file
A inference script has been added:
1 | swebench/inference/run_local_vllm.py |
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
| –dataset_name | str | princeton-nlp/SWE-bench_Lite | Dataset: princeton-nlp/SWE-bench_Lite (300) princeton-nlp/SWE-bench_verified (500) princeton-nlp/SWE-bench (2249) |
| –split | str | test | 数据集分割:test |
| –output_dir | str | /data/disk2/wye/SWE-bench/patches_by_llm | JSONL output directory, auto-create if not exist |
| –model_short_name | str | None (Auto-Detect) | If not setted, auto-get from vLLM/v1/models |
| –max_instances | int | None(Run all instances) | Limit to only run the first N instances; 3 = test |
| –vllm_url | str | http://localhost:8000/v1 | vLLM server URL |
| –model_name | str | /models/Qwen2.5-Coder-7B-Instruct | the model’s name for vLLM |
| –max_context_files | int | 5 | context file nums for phase2 |
1.3.2.3 Inference Commnds
Start vLLM:
1 | docker run -d --name vllm_wye_swebench --gpus all \ |
Check if the vLLM is started:
1 | docker logs -f vllm_wye_swebench |
1 | cd /data/disk2/wye/SWE-bench |
Run test:
1 | python -m swebench.inference.run_local_vllm --max_instances 3 |
Run SWE-bench_Lite(default):
1 | python -m swebench.inference.run_local_vllm |
Run SWE-bench_Verified:
1 | python -m swebench.inference.run_local_vllm \ |
Run Full:
1 | python -m swebench.inference.run_local_vllm \ |
[!NOTE]
The inference script will catch the dataset automatically.
1
2
3
4
5 (base) ubuntu@ubuntu:/data/disk2/wye/SWE-bench$ ll /home/ubuntu/.cache/huggingface/datasets/
...
drwxrwxr-x 3 ubuntu ubuntu 4096 Jul 1 12:06 princeton-nlp___swe-bench_lite/
...
1.4 Evaluation Pipeline
If you run the main test command:
1 | python -m swebench.harness.run_evaluation \ |
The actual functions call chain:
1 | main() # run_evaluation.py:main() |
1.4.1 Main
test split of SWE-bench Multimodal does not support the
local evaluation because of the vast cost of memory.
1 | if dataset_name == "SWE-bench/SWE-bench_Multimodal" and split == "test": |
Check ID length:
1 | assert len(run_id) > 0, "Run ID must be provided" |
Check / Create report_path:
1 | if report_dir is not None: |
SWE-bench’s docker image has 2 pulling ways (DockerHub or Local):
| namespace | Image Source | Action |
|---|---|---|
| “swebench” (default) | DockerHub | docker pull swebench/sweb.eval.xxx |
| None (pass -n none) | Local | docker build -t sweb.eval.xxx . |
force_rebuild: Only works when you use local build.
It will delete the old image and re-run docker build
command.
Load predictions (records including patches generated by
LLM for solving the issues).
And trans the list into hash map:
1 | predictions = get_predictions_from_file(predictions_path, dataset_name, split) |
Import dataset tested:
1 | dataset = get_dataset_from_preds( |
Full dataset including skipped test instances:
1 | full_dataset = load_swebench_dataset(dataset_name, split, instance_ids) |
Evaluating in the cloud:
1 | if modal: |
Evaluating locally:
Set the maximum number of file descriptors (fds) that can be opened by a process. Docker APi opens several sockets/fds for each container. The default fds num 1024 might be insufficient.
1
2
3
4if platform.system() == "Linux":
# resource.setrlimit: equal to setrlimit(2) sys call
resource.setrlimit(resource.RLIMIT_NOFILE, (open_file_limit, open_file_limit))
client = docker.from_env()recording the existing images before evaluating:
1
existing_images = list_images(client)
Dataset comes from
get_dataset_from_preds, it might be empty. The evaluation will go on only when it’s not empty. If it’s local docker image and it’s not a re-grading process, create shared dockers(base + env) for all instances:1
2
3
4
5
6
7
8
9build_env_images(
client,
dataset,
force_rebuild,
max_workers,
namespace,
instance_image_tag,
env_image_tag,
)Run all the instances(Here, each instance will generate an independent docker based on the env_images):
1
2
3
4
5
6
7
8
9
10
11
12
13
14run_instances(
predictions,
dataset,
cache_level,
clean,
force_rebuild,
max_workers,
run_id,
timeout,
namespace=namespace,
instance_image_tag=instance_image_tag,
env_image_tag=env_image_tag,
rewrite_reports=rewrite_reports,
)Clean images and generate report:
1
2
3
4
5
6
7
8
9
10clean_images(client, existing_images, cache_level, clean)
return make_run_report(
predictions,
full_dataset,
run_id,
client,
namespace,
instance_image_tag,
env_image_tag,
)
1.4.2 Run_Instance
1.4.2.1 Preparation
入参:
1 | def run_instance( |
Initialization:
1 | instance_id = test_spec.instance_id |
rewrite_reports method (existing test_output.txt) :
1 | if rewrite_reports: |
If evaluation is interrupted mid-run, any instances whose report.json has already been written are automatically skipped on restart. This means you can safely kill the process (Ctrl+C, crash, etc.) and rerun it later — only unfinished instances will be evaluated, without duplicating completed work.
1 | if report_path.exists(): |
Create a shortcut in the logs directory, pointing to the image’s build directory. The purpose is to facilitate debugging - when you view the logs of a specific instance, you can follow this link to directly locate the corresponding Docker build files (Dockerfile, build logs, etc.).
1 | if not test_spec.is_remote_image: |
log:
1 | log_dir.mkdir(parents=True, exist_ok=True) |
1.4.2.2 Apply Patch & Run eval.sh
Env & Variable:
1 | container = None |
Prepare the eval.sh file:
1 | # eval.sh is generated by properites of TestSpec.eval_script, including: |
Run eval.sh:
1 | # record the test_output, timed_out, total_runtime |
Record the repo status( Check if the eval.sh changed the repo accidently):
1 | git_diff_output_after = ( |
1 | if git_diff_output_after != git_diff_output_before: |
1.4.2.3 Grading
1 | logger.info(f"Grading answer for {instance_id}...") |
1.4.2.4 Exception Dealing & Clear Env
Exception:
1 | except (EvaluationError, BuildImageError) as e: |
Clear Env:
1 | finally: |