Skip to content

Commit f95573e

Browse files
bench: add deterministic multi-task SWE-bench batches
* bench: add deterministic SWE-bench batch selection * bench: support deterministic multi-task batches
1 parent 2d9e24f commit f95573e

2 files changed

Lines changed: 89 additions & 3 deletions

File tree

‎.github/workflows/swebench-codecortex-ab.yml‎

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,20 @@ on:
44
workflow_dispatch:
55
inputs:
66
instance_ids:
7-
description: 'JSON list of SWE-bench Verified instance IDs'
7+
description: 'JSON list used when sample_size is 0'
88
required: true
99
default: '["sympy__sympy-20590"]'
1010
type: string
11+
sample_size:
12+
description: 'Deterministic fresh-task sample size; 0 uses instance_ids'
13+
required: true
14+
default: 0
15+
type: number
16+
sample_seed:
17+
description: 'Seed for deterministic unique-repository sampling'
18+
required: true
19+
default: 'codecortex-public-v1'
20+
type: string
1121
model:
1222
description: 'OpenAI model used by Codex in both arms'
1323
required: true
@@ -33,16 +43,43 @@ permissions:
3343
contents: read
3444

3545
jobs:
46+
select-instances:
47+
name: Select benchmark instances
48+
runs-on: ubuntu-latest
49+
outputs:
50+
instance_ids: ${{ steps.select.outputs.instance_ids }}
51+
steps:
52+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
53+
with:
54+
persist-credentials: false
55+
56+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
57+
with:
58+
python-version: "3.13"
59+
60+
- name: Install selector dependency
61+
run: python -m pip install "datasets>=3,<5"
62+
63+
- name: Select deterministic task set
64+
id: select
65+
run: |
66+
python scripts/swebench_ab.py select \
67+
--count "${{ inputs.sample_size }}" \
68+
--seed "${{ inputs.sample_seed }}" \
69+
--explicit '${{ inputs.instance_ids }}' \
70+
--github-output "$GITHUB_OUTPUT"
71+
3672
generate-patches:
3773
name: ${{ matrix.mode }} / ${{ matrix.instance_id }}
74+
needs: select-instances
3875
runs-on: ubuntu-latest
3976
timeout-minutes: 60
4077
strategy:
4178
fail-fast: false
4279
max-parallel: 2
4380
matrix:
4481
mode: [baseline, codecortex]
45-
instance_id: ${{ fromJSON(inputs.instance_ids) }}
82+
instance_id: ${{ fromJSON(needs.select-instances.outputs.instance_ids) }}
4683

4784
steps:
4885
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7

‎scripts/swebench_ab.py‎

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,42 @@ def load_instance(instance_id: str) -> dict[str, Any]:
4242
raise SystemExit(f"instance not found in {DATASET}: {instance_id}")
4343

4444

45+
def select_instances(count: int, seed: str, explicit: str) -> list[str]:
46+
if count <= 0:
47+
selected = json.loads(explicit)
48+
if not isinstance(selected, list) or not all(isinstance(item, str) for item in selected):
49+
raise SystemExit("--explicit must be a JSON list of instance IDs")
50+
return selected
51+
52+
from datasets import load_dataset
53+
54+
dataset = load_dataset(DATASET, split="test")
55+
ranked = sorted(
56+
dataset,
57+
key=lambda row: hashlib.sha256(
58+
f"{seed}:{row['instance_id']}".encode()
59+
).hexdigest(),
60+
)
61+
selected: list[str] = []
62+
seen_repos: set[str] = set()
63+
for row in ranked:
64+
instance_id = str(row["instance_id"])
65+
repo = str(row["repo"])
66+
if instance_id == "sympy__sympy-20590":
67+
continue
68+
if repo in seen_repos:
69+
continue
70+
selected.append(instance_id)
71+
seen_repos.add(repo)
72+
if len(selected) == count:
73+
break
74+
if len(selected) < count:
75+
raise SystemExit(
76+
f"requested {count} unique-repository tasks but only found {len(selected)}"
77+
)
78+
return selected
79+
80+
4581
def prepare(instance_id: str, workspace: Path) -> None:
4682
workspace = workspace.resolve()
4783
target = workspace / "target"
@@ -181,6 +217,12 @@ def main() -> int:
181217
parser = argparse.ArgumentParser()
182218
sub = parser.add_subparsers(dest="command", required=True)
183219

220+
sel = sub.add_parser("select")
221+
sel.add_argument("--count", type=int, required=True)
222+
sel.add_argument("--seed", required=True)
223+
sel.add_argument("--explicit", required=True)
224+
sel.add_argument("--github-output", type=Path)
225+
184226
prep = sub.add_parser("prepare")
185227
prep.add_argument("--instance-id", required=True)
186228
prep.add_argument("--workspace", type=Path, required=True)
@@ -197,7 +239,14 @@ def main() -> int:
197239
comb.add_argument("--output", type=Path, required=True)
198240

199241
args = parser.parse_args()
200-
if args.command == "prepare":
242+
if args.command == "select":
243+
selected = select_instances(args.count, args.seed, args.explicit)
244+
payload = json.dumps(selected, separators=(",", ":"))
245+
print(payload)
246+
if args.github_output:
247+
with args.github_output.open("a", encoding="utf-8") as handle:
248+
handle.write(f"instance_ids={payload}\n")
249+
elif args.command == "prepare":
201250
prepare(args.instance_id, args.workspace)
202251
elif args.command == "collect":
203252
collect(args.workspace, args.output, args.mode, args.model, args.effort)

0 commit comments

Comments
 (0)