|
16 | 16 | from src.agents.swe_bench_pro_environment import _build_entry_script, _grade, _get_image_uri |
17 | 17 |
|
18 | 18 |
|
19 | | -_COMPILE_ERROR_PATTERNS = ( |
| 19 | +# Language detection from repo field (e.g. "gravitational/teleport") |
| 20 | +_GO_ORGS = frozenset({"gravitational", "flipt-io", "future-architect", "navidrome"}) |
| 21 | +_TS_JS_ORGS = frozenset({"NodeBB", "protonmail", "element-hq", "tutao"}) |
| 22 | + |
| 23 | +_PYTHON_COMPILE_PATTERNS = ( |
20 | 24 | "SyntaxError", |
| 25 | + "IndentationError", |
21 | 26 | "ImportError", |
22 | 27 | "ModuleNotFoundError", |
23 | 28 | "ERROR collecting", |
24 | 29 | "import file mismatch", |
25 | 30 | ) |
| 31 | +_GO_COMPILE_PATTERNS = ( |
| 32 | + "[build failed]", |
| 33 | + "build failed", |
| 34 | + "undefined:", |
| 35 | + "cannot use ", |
| 36 | + "declared and not used", |
| 37 | + "imported and not used", |
| 38 | + "has no field or method", |
| 39 | + "does not implement", |
| 40 | + "cannot convert", |
| 41 | +) |
| 42 | +_TS_JS_COMPILE_PATTERNS = ( |
| 43 | + "SyntaxError:", |
| 44 | + "error TS", |
| 45 | + "Cannot find module", |
| 46 | + "Module not found:", |
| 47 | + "Cannot find name", |
| 48 | + "is not assignable to", |
| 49 | + "has no exported member", |
| 50 | +) |
26 | 51 |
|
27 | 52 |
|
28 | | -def _compiles_from_log(log: Optional[str]) -> Optional[bool]: |
29 | | - if log is None: |
30 | | - return None |
31 | | - for pattern in _COMPILE_ERROR_PATTERNS: |
32 | | - if pattern in log: |
33 | | - return False |
34 | | - return True |
| 53 | +def _get_language(instance: Dict) -> str: |
| 54 | + org = instance.get("repo", "").split("/")[0] |
| 55 | + if org in _GO_ORGS: |
| 56 | + return "go" |
| 57 | + if org in _TS_JS_ORGS: |
| 58 | + return "ts_js" |
| 59 | + return "python" |
| 60 | + |
| 61 | + |
| 62 | +def _compiles_from_test_output( |
| 63 | + test_stdout: str, test_stderr: str, instance: Dict |
| 64 | +) -> bool: |
| 65 | + """Detect compile/build failures from the actual test runner output (stdout.log + stderr.log).""" |
| 66 | + combined = (test_stdout or "") + "\n" + (test_stderr or "") |
| 67 | + lang = _get_language(instance) |
| 68 | + if lang == "go": |
| 69 | + patterns = _GO_COMPILE_PATTERNS |
| 70 | + elif lang == "ts_js": |
| 71 | + patterns = _TS_JS_COMPILE_PATTERNS |
| 72 | + else: |
| 73 | + patterns = _PYTHON_COMPILE_PATTERNS |
| 74 | + return not any(p in combined for p in patterns) |
35 | 75 |
|
36 | 76 |
|
37 | 77 | def _create_pro_sandbox(instance: Dict, app_name: str, timeout: int) -> Any: |
@@ -116,18 +156,35 @@ def _run_pro_eval( |
116 | 156 | _write_sandbox_file(sandbox, "/workspace/entryscript.sh", entry_script) |
117 | 157 |
|
118 | 158 | process = sandbox.exec("bash", "/workspace/entryscript.sh", timeout=eval_timeout) |
119 | | - stdout = process.stdout.read() |
120 | | - stderr = process.stderr.read() if getattr(process, "stderr", None) else "" |
| 159 | + process.stdout.read() # drain to unblock |
| 160 | + if getattr(process, "stderr", None): |
| 161 | + process.stderr.read() |
121 | 162 | if hasattr(process, "wait"): |
122 | 163 | process.wait() |
123 | | - log = f"{stdout}\n{stderr}".strip() |
124 | | - print(f"[pro-labeler] eval output (last 1000 chars):\n{log[-1000:]}", flush=True) |
| 164 | + |
| 165 | + # Read the actual test runner output (stdout/stderr were redirected to files |
| 166 | + # inside the sandbox by entryscript.sh — this is the canonical source for |
| 167 | + # compile/build error detection, mirroring collect_outputs_modal() in the |
| 168 | + # official swe_bench_pro_eval.py). |
| 169 | + try: |
| 170 | + with sandbox.open("/workspace/stdout.log", "r") as f: |
| 171 | + test_stdout = f.read() or "" |
| 172 | + except Exception: |
| 173 | + test_stdout = "" |
| 174 | + try: |
| 175 | + with sandbox.open("/workspace/stderr.log", "r") as f: |
| 176 | + test_stderr = f.read() or "" |
| 177 | + except Exception: |
| 178 | + test_stderr = "" |
| 179 | + |
| 180 | + combined_preview = (test_stdout + "\n" + test_stderr).strip() |
| 181 | + print(f"[pro-labeler] test output (last 500 chars):\n{combined_preview[-500:]}", flush=True) |
125 | 182 |
|
126 | 183 | with sandbox.open("/workspace/output.json", "r") as f: |
127 | 184 | output_json = json.load(f) |
128 | 185 |
|
129 | 186 | resolved = _grade(output_json, instance) |
130 | | - compiles = _compiles_from_log(log) |
| 187 | + compiles = _compiles_from_test_output(test_stdout, test_stderr, instance) |
131 | 188 | return resolved, compiles, output_json |
132 | 189 |
|
133 | 190 | except Exception as exc: |
|
0 commit comments