Automate construction of GitHub issue resolution benchmarks via multi-agent LLM coordination, binary file recovery, and exit-code-based validation, achieving 337 valid instances at $0.047 per instance.
Automate construction of GitHub issue resolution benchmarks via multi-agent LLM coordination, binary file recovery, and exit-code-based validation, achieving 337 valid instances at $0.047 per instance.
SWE-Factory: Automated Benchmark Generation
Core Concept
SWE-Factory fully automates GitHub issue resolution dataset construction through a multi-agent LLM system that coordinates environment setup, binary file recovery, and fail2pass validation. Instead of manual inspection and custom log parsing, the system uses exit-code-based testing (standardized Unix return codes), successfully generating 337 valid benchmarks from 671 issues across Python, Java, JavaScript, and TypeScript at $0.047 per instance and 96% quality.
Architecture Overview
SWE-Builder Multi-Agent System: Four collaborative agents (Repository Explorer, Environment Manager, Test Manager, Test Analyst) with shared Environment Memory Pool for configuration reuse
Binary File Recovery: Automatically downloads missing test binaries and removes incomplete hunks from patches
Exit-Code-Based Validation: Standardized fail2pass testing via Unix exit codes ($rc) instead of manual log parsing, achieving F1=0.99
Environment Memory Pool: Caches successful Docker configurations from similar repository versions, reducing redundant iterations
Language Support: Python, Java, JavaScript, TypeScript with unified testing interface
Implementation
Step 1: Binary File Recovery System
import os
import subprocess
from pathlib import Path
classBinaryFileRecoverer:
"""
Automatically downloads missing binary test files from repositories
and removes incomplete binary hunks from patches.
"""def__init__(self, repo_path, repo_url):
self.repo_path = repo_path
self.repo_url = repo_url
defidentify_missing_binaries(self, patch_content):
"""
Parse patch file to identify missing binary files.
Binary patches show "Binary files ... differ" marker.
"""
missing_binaries = []
lines = patch_content.split('\n')
for i, line (lines):
line line:
j (i-, (, i-), -):
lines[j].startswith():
filename = lines[j].split()[].replace(, )
missing_binaries.append(filename)
missing_binaries
():
downloaded = []
filename filenames:
raw_url = .repo_url.replace(, )
raw_url =
:
result = subprocess.run(
[, , , filename, raw_url],
cwd=.repo_path,
timeout=,
capture_output=
)
result.returncode == os.path.exists(os.path.join(.repo_path, filename)):
downloaded.append(filename)
()
Exception e:
()
downloaded
():
lines = patch_content.split()
cleaned = []
skip_binary_section =
i, line (lines):
line line:
skip_binary_section =
cleaned.append(line)
line.startswith() line.startswith():
skip_binary_section =
cleaned.append(line)
skip_binary_section line.startswith((, )):
cleaned.append(line)
.join(cleaned)
in
enumerate
if
'Binary files'
in
and
'differ'
in
# Extract filename from patch header
for
in
range
1
max
0
10
1
if
'+++'
'\t'
0
'+++ b/'
''
break
return
def
download_binaries_from_repo
self, filenames
"""
Download binary files from original repository.
Handles different VCS (GitHub, GitLab, etc.)
"""
for
in
# Construct raw file URL
self
'github.com'
'raw.githubusercontent.com'
f"{raw_url}/HEAD/{filename}"
try
'curl'
'-L'
'-o'
self
30
True
if
0
and
self
print
f"Downloaded: {filename}"
except
as
print
f"Failed to download {filename}: {e}"
return
def
clean_patch_binaries
self, patch_content
"""
Remove incomplete binary hunks from patches.
Keeps only well-formed binary sections.
"""
'\n'
False
for
in
enumerate
if
'Binary files'
in
and
'differ'
in
True
# Keep the "differs" marker
elif
'+++'
or
'---'
False
elif
not
or
'GIT binary patch'
'literal'
return
'\n'
Step 2: Multi-Agent Environment Builder
classSWEBuilderMultiAgent:
"""
Orchestrates four collaborative agents to construct evaluation environment.
Uses shared memory pool for configuration reuse.
"""def__init__(self, repo_path, model_name='gpt-4-mini'):
self.repo_path = repo_path
self.model = model_name
self.env_memory_pool = {}
defrepository_explorer(self):
"""Agent 1: Collects environment dependencies and test commands."""
exploration_prompt = f"""
Analyze repository at {self.repo_path}:
1. Identify all dependency files (requirements.txt, pom.xml, package.json, etc.)
2. Extract all test commands (pytest, maven, npm test, etc.)
3. List any configuration files (setup.cfg, tsconfig.json, etc.)
4. Identify language versions needed (Python 3.9+, Java 11+, etc.)
Return structured JSON with sections: dependencies, test_commands, configs, versions
"""returnself._llm_call(exploration_prompt)
defenvironment_manager(self, exploration_result):
"""Agent 2: Generates Dockerfile configuration."""
docker_prompt = f"""
Based on repository analysis:
{exploration_result}
Generate complete Dockerfile that:
1. Installs base image matching language version requirements
2. Installs all dependencies in correct order
3. Sets environment variables
4. Copies repository code
5. Runs any setup scripts
Output valid Dockerfile content.
"""
docker_content = self._llm_call(docker_prompt)
# Check memory pool for similar configurations
similar = self._find_similar_config(exploration_result)
if similar:
print(f"Found similar configuration: {similar}")
docker_content = self._merge_configs(docker_content, similar)
return docker_content
deftest_manager(self, exploration_result):
"""Agent 3: Creates executable test scripts with exit code reporting."""
test_prompt = f"""
Based on identified test commands:
{exploration_result['test_commands']}
Create a master test script that:
1. Sets up test environment
2. Runs each test command sequentially
3. Captures exit codes
4. Appends standardized marker: OMNIGRIL_EXIT_CODE=$rc
5. Returns appropriate exit code (0=pass, 1=fail)
Language: {self._detect_language()}
"""
test_script = self._llm_call(test_prompt)
return test_script
deftest_analyst(self, docker_content, test_script):
"""Agent 4: Validates environment and orchestrates refinement."""# Try to build Docker image
build_success = self._build_docker(docker_content)
ifnot build_success:
# Iteratively refine via LLM
refinement_prompt = f"""
Docker build failed. Previous Dockerfile:
{docker_content}
Common issues:
- Missing base image layer
- Dependency installation order
- Working directory not set
Provide corrected Dockerfile.
"""
docker_content = self._llm_call(refinement_prompt)
returnself.test_analyst(docker_content, test_script)
# Build passed; store in memory poolself.env_memory_pool[hash(docker_content)] = {
'dockerfile': docker_content,
'test_script': test_script,
'success': True
}
return {'dockerfile': docker_content, 'test_script': test_script}
def_find_similar_config(self, exploration_result):
"""Find similar configurations in memory pool."""
repo_hash = hash(exploration_result.get('dependencies', ''))
# Simple similarity: same language/versionfor stored_hash, config inself.env_memory_pool.items():
if config['success']:
return config
returnNonedef_merge_configs(self, new_config, similar_config):
"""Merge new config with similar successful one."""# Simplified: prefer new, fallback to similarreturn new_config
def_llm_call(self, prompt):
"""Call LLM with prompt."""# Simplified placeholderreturnf"Generated from: {prompt[:50]}..."def_detect_language(self):
"""Detect primary language of repository."""if os.path.exists(os.path.join(self.repo_path, 'requirements.txt')):
return'python'elif os.path.exists(os.path.join(self.repo_path, 'pom.xml')):
return'java'elif os.path.exists(os.path.join(self.repo_path, 'package.json')):
return'javascript'return'unknown'def_build_docker(self, dockerfile_content):
"""Test build Docker image."""# Simplified: return True for nowreturnTrue
Step 3: Exit-Code-Based Fail2Pass Validation
import re
import subprocess
classExitCodeValidator:
"""
Standardized fail2pass validation using Unix exit codes.
Eliminates custom log parsing; achieves 100% precision, 98.3% recall.
"""defcreate_test_wrapper(self, original_test_command, language):
"""
Wraps test command to append standardized exit code marker.
Format: OMNIGRIL_EXIT_CODE=$rc
"""if language == 'python':
wrapper = f"""#!/bin/bash
set -e
bash -c '{original_test_command}'
rc=$?
echo "OMNIGRIL_EXIT_CODE=$rc"
exit $rc
"""elif language == 'java':
wrapper = f"""#!/bin/bash
set -e
{original_test_command}
rc=$?
echo "OMNIGRIL_EXIT_CODE=$rc"
exit $rc
"""else:
wrapper = f"""#!/bin/bash
set -e
{original_test_command}
rc=$?
echo "OMNIGRIL_EXIT_CODE=$rc"
exit $rc
"""return wrapper
defextract_exit_code(self, test_output):
"""
Parse test output and extract exit code marker.
Returns (exit_code, is_valid).
"""
pattern = r'OMNIGRIL_EXIT_CODE=(\d+)'match = re.search(pattern, test_output)
ifmatch:
exit_code = int(match.group(1))
return exit_code, TruereturnNone, Falsedefvalidate_fail2pass(self, buggy_version_output, fixed_version_output):
"""
Verify that test failed on buggy version and passed on fixed version.
Returns (is_valid, buggy_code, fixed_code).
"""
buggy_code, buggy_valid = self.extract_exit_code(buggy_version_output)
fixed_code, fixed_valid = self.extract_exit_code(fixed_version_output)
ifnot (buggy_valid and fixed_valid):
returnFalse, None, None# Fail2pass: buggy version failed (non-zero), fixed version passed (zero)
is_fail2pass = (buggy_code != 0) and (fixed_code == 0)
return is_fail2pass, buggy_code, fixed_code
Practical Guidance
Dataset Construction Workflow:
Collect GitHub issues (use GitHub API with issue labels like "bug", "fix")
Extract patches from issue comments or linked PRs
Run Binary Recovery to handle test fixtures
Execute SWE-Builder multi-agent pipeline to construct environment
Validate with Exit-Code-Based testing
Cost Optimization:
Average: $0.047 per valid instance with GPT-4 mini
Batch processing: Process 50+ issues in parallel to amortize LLM calls
Memory pool: Reuse successful configurations reduces total cost by 30%
Quality Assurance:
Validate exit codes independently with 2+ test runners
Manual review of first 50 instances to verify quality
Monitor F1 score (0.99 target) on exit code extraction
Supported Languages:
Python: pytest, unittest, nose
Java: Maven, Gradle
JavaScript/TypeScript: Jest, Mocha, npm test
Others: Extensible via custom test runners
Reference
Unix exit codes: Standard convention (0=success, non-zero=failure) across all languages
Docker layer caching: Significantly speeds up repeated builds for similar configurations
Environment Memory Pool: Inspired by case-based reasoning; dramatically improves efficiency