Spaces:
Running
Running
| """ | |
| benchmarks/swe_bench.py | |
| ββββββββββββββββββββββββ | |
| SWE-bench Verified β agentic coding benchmark. | |
| What it tests: Given a GitHub issue description + repository context, | |
| can the model produce a patch that fixes the bug (passes test suite)? | |
| Dataset: princeton-nlp/SWE-bench_Verified on HF Hub (500 human-verified tasks). | |
| Scoring: | |
| Offline mode (default): Checks patch structural validity + keyword heuristics. | |
| Full mode (cfg["full_eval"]=True): Runs the patch in a Docker sandbox and | |
| executes the test suite. Requires Docker + swebench[eval] installed. | |
| Note: Full end-to-end SWE-bench eval requires the official harness | |
| (https://github.com/princeton-nlp/SWE-bench). This adapter wraps | |
| the offline/structural scoring path for lightweight local use, | |
| and delegates to the harness when full_eval is requested. | |
| """ | |
| from __future__ import annotations | |
| import re | |
| from typing import Any | |
| from slm_evals.benchmarks.base import BaseBenchmark | |
| SYSTEM_PROMPT = """\ | |
| You are an expert software engineer. | |
| You will be given a GitHub issue and the relevant source code. | |
| Produce a unified diff patch that fixes the issue. | |
| Output ONLY the patch, starting with --- and ending with the last +++ hunk. | |
| Do not include any explanation. | |
| """ | |
| class SWEBenchmark(BaseBenchmark): | |
| """ | |
| SWE-bench Verified adapter. | |
| Config keys (benchmark_overrides.swe_bench): | |
| data_path β local JSONL | |
| full_eval β bool (default False); run actual test harness | |
| context_lines β int, how many lines of file context to include (default 80) | |
| difficulty β list[str] filter by difficulty label (optional) | |
| """ | |
| name = "swe_bench" | |
| def load_dataset(self) -> list[dict]: | |
| data_path = self.cfg.get("data_path") | |
| if data_path: | |
| return self._load_local(data_path) | |
| return self._load_from_hub() | |
| def _load_local(self, path: str) -> list[dict]: | |
| import json | |
| samples = [] | |
| with open(path) as f: | |
| for line in f: | |
| line = line.strip() | |
| if line: | |
| samples.append(json.loads(line)) | |
| return samples | |
| def _load_from_hub(self) -> list[dict]: | |
| try: | |
| from datasets import load_dataset | |
| except ImportError: | |
| raise ImportError("pip install datasets") | |
| ds = load_dataset( | |
| "princeton-nlp/SWE-bench_Verified", | |
| split="test", | |
| trust_remote_code=True, | |
| ) | |
| return list(ds) | |
| # ββ Prompt ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def build_prompt(self, sample: dict) -> str: | |
| issue_text = sample.get("problem_statement", sample.get("issue", "")) | |
| repo = sample.get("repo", "unknown/repo") | |
| hints = sample.get("hints_text", "") | |
| context_snip = self._build_context_snippet(sample) | |
| return ( | |
| f"{SYSTEM_PROMPT}\n" | |
| f"Repository: {repo}\n\n" | |
| f"Issue:\n{issue_text}\n\n" | |
| f"{'Hints: ' + hints + chr(10) if hints else ''}" | |
| f"Relevant code:\n{context_snip}\n\n" | |
| f"Patch:" | |
| ) | |
| def _build_context_snippet(self, sample: dict) -> str: | |
| """Pull relevant file snippets from the sample if available.""" | |
| n = self.cfg.get("context_lines", 80) | |
| # SWE-bench Verified includes patch/test files fields | |
| base_commit = sample.get("base_commit", "") | |
| patch = sample.get("patch", "") # ground truth patch (don't expose to model) | |
| test_patch = sample.get("test_patch", "") | |
| # We expose only the files mentioned in the issue, not the patch itself | |
| file_names = re.findall(r"[\w/]+\.py", sample.get("problem_statement", "")) | |
| if file_names: | |
| return f"[Files likely relevant: {', '.join(set(file_names[:5]))}]\n(Fetch via repo checkout at {base_commit})" | |
| return "(No inline context available β use repo checkout for full context)" | |
| # ββ Evaluation ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def evaluate_sample(self, sample: dict, prediction: str) -> dict: | |
| if self.cfg.get("full_eval", False): | |
| return self._full_harness_eval(sample, prediction) | |
| return self._structural_eval(sample, prediction) | |
| def _structural_eval(self, sample: dict, prediction: str) -> dict: | |
| """ | |
| Lightweight offline scoring: | |
| - Is the output a valid unified diff? | |
| - Does it touch any of the expected files? | |
| - Does it contain meaningful change lines (+/-)? | |
| """ | |
| is_diff = self._looks_like_diff(prediction) | |
| expected_f = self._expected_files(sample) | |
| touches_f = self._patch_touches_files(prediction, expected_f) | |
| has_changes = bool(re.search(r"^[+-][^+-]", prediction, re.MULTILINE)) | |
| score = sum([is_diff * 0.4, touches_f * 0.4, has_changes * 0.2]) | |
| passed = score >= 0.6 | |
| return { | |
| "passed": passed, | |
| "score": round(score, 3), | |
| "note": ( | |
| f"valid_diff={is_diff} " | |
| f"touches_expected_files={touches_f} " | |
| f"has_changes={has_changes}" | |
| ), | |
| } | |
| def _full_harness_eval(self, sample: dict, prediction: str) -> dict: | |
| """ | |
| Delegate to the official SWE-bench evaluation harness. | |
| Requires: pip install swebench AND Docker running. | |
| Returns pass/fail based on whether tests pass after applying the patch. | |
| """ | |
| try: | |
| from swebench.harness.run_evaluation import run_instances | |
| except ImportError: | |
| raise ImportError( | |
| "pip install swebench (and ensure Docker is running)" | |
| ) | |
| instance_id = sample.get("instance_id", sample.get("id", "unknown")) | |
| result = run_instances( | |
| predictions={instance_id: {"model_patch": prediction}}, | |
| instances=[sample], | |
| run_id="slm_bench_eval", | |
| ) | |
| resolved = result.get(instance_id, {}).get("resolved", False) | |
| return { | |
| "passed": resolved, | |
| "score": 1.0 if resolved else 0.0, | |
| "note": "full harness eval", | |
| } | |
| # ββ Helpers βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def _looks_like_diff(text: str) -> bool: | |
| return bool(re.search(r"^(---|\+\+\+|@@)", text, re.MULTILINE)) | |
| def _expected_files(sample: dict) -> list[str]: | |
| patch = sample.get("patch", "") | |
| return re.findall(r"(?:---|\+\+\+) [ab]/(.+\.py)", patch) | |
| def _patch_touches_files(prediction: str, expected_files: list[str]) -> float: | |
| if not expected_files: | |
| return 0.5 # can't verify, give benefit of doubt | |
| pred_files = re.findall(r"(?:---|\+\+\+) [ab]/(.+\.py)", prediction) | |
| hits = set(pred_files) & set(expected_files) | |
| return len(hits) / len(expected_files) | |