Skip to content

Commit a8595e8

Browse files
Magnus Hartvig GrønbechCopilot
andcommitted
Align identification runner with existing conventions
Two gratuitous divergences from house patterns, surfaced in review: - Move the hardcoded IDENTIFICATION_TIMEOUT_SECONDS into TimeoutConfig (`filepath_identification`), where every other timeout lives; the runner now reads `_config.timeout.filepath_identification`. - De-duplicate the triplicated Copilot-CLI lookup into a shared `bcbench.copilot_cli.find_copilot`, now used by the identification runner, the code-review judge, and the copilot agent (the judge keeps a `_find_copilot` alias so existing test patch points still work). No behavioural change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent c5cef89 commit a8595e8

6 files changed

Lines changed: 28 additions & 21 deletions

File tree

src/bcbench/agent/copilot/agent.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""GitHub Copilot CLI Agent implementation."""
22

33
import os
4-
import shutil
54
import subprocess
65
import sys
76
from pathlib import Path
@@ -11,6 +10,7 @@
1110
from bcbench.agent.copilot.metrics import parse_metrics
1211
from bcbench.agent.shared import build_al_lsp_plugin, build_mcp_config, build_prompt, parse_tool_usage_from_hooks
1312
from bcbench.config import get_config
13+
from bcbench.copilot_cli import find_copilot
1414
from bcbench.dataset import BaseDatasetEntry
1515
from bcbench.exceptions import AgentError, AgentTimeoutError
1616
from bcbench.logger import get_logger
@@ -41,7 +41,7 @@ def run_copilot_agent(
4141

4242
# Prefer copilot.exe over copilot.bat/copilot.cmd shims on Windows: the .bat shim invokes PowerShell,
4343
# which re-parses arguments and corrupts prompts containing double quotes (e.g. JSON examples).
44-
copilot_cmd = shutil.which("copilot.exe") or shutil.which("copilot.cmd") or shutil.which("copilot")
44+
copilot_cmd = find_copilot()
4545
if not copilot_cmd:
4646
raise AgentError("Copilot CLI not found in PATH. Please ensure it is installed and available.")
4747

src/bcbench/config.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ class TimeoutConfig:
7272
test_execution: int
7373
agent_execution: int
7474
bcal_execution: int
75+
filepath_identification: int
7576

7677
@classmethod
7778
def default(cls) -> TimeoutConfig:
@@ -83,6 +84,9 @@ def default(cls) -> TimeoutConfig:
8384
agent_execution=60 * 60, # 60 minutes for coding agent (claude and copilot) execution
8485
# Total bcal CLI budget per instance.
8586
bcal_execution=25 * 60,
87+
# Context-free file-path identification; kept below the 20-min workflow step timeout
88+
# so a hung run records an error result before the CI step is force-killed.
89+
filepath_identification=15 * 60,
8690
)
8791

8892

src/bcbench/contamination/runner.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,25 @@
1010

1111
from __future__ import annotations
1212

13-
import shutil
1413
import subprocess
1514
import tempfile
1615
from pathlib import Path
1716

17+
from bcbench.config import get_config
1818
from bcbench.contamination.filepath_identification import FilePathIdentificationResult, build_identification_prompt, parse_prediction
19+
from bcbench.copilot_cli import find_copilot
1920
from bcbench.dataset import BaseDatasetEntry
2021
from bcbench.exceptions import AgentError
2122
from bcbench.logger import get_logger
2223

2324
logger = get_logger(__name__)
25+
_config = get_config()
2426

25-
__all__ = ["IDENTIFICATION_TIMEOUT_SECONDS", "load_identification_results", "run_filepath_identification", "save_identification_result"]
26-
27-
# Kept below the workflow step timeout so a hung run raises (and records an error
28-
# result) before the CI step is force-killed and loses its artifact.
29-
IDENTIFICATION_TIMEOUT_SECONDS = 15 * 60
30-
31-
32-
def _find_copilot() -> str | None:
33-
return shutil.which("copilot.exe") or shutil.which("copilot.cmd") or shutil.which("copilot")
27+
__all__ = ["load_identification_results", "run_filepath_identification", "save_identification_result"]
3428

3529

3630
def _run_copilot_context_free(prompt: str, work_dir: Path, model: str) -> str:
37-
copilot_cmd = _find_copilot()
31+
copilot_cmd = find_copilot()
3832
if not copilot_cmd:
3933
raise AgentError("Copilot CLI not found in PATH; cannot run file-path identification")
4034

@@ -57,7 +51,7 @@ def _run_copilot_context_free(prompt: str, work_dir: Path, model: str) -> str:
5751
text=True,
5852
encoding="utf-8",
5953
errors="replace",
60-
timeout=IDENTIFICATION_TIMEOUT_SECONDS,
54+
timeout=_config.timeout.filepath_identification,
6155
check=True,
6256
)
6357

src/bcbench/copilot_cli.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
"""Shared helper for locating the GitHub Copilot CLI executable."""
2+
3+
import shutil
4+
5+
__all__ = ["find_copilot"]
6+
7+
8+
def find_copilot() -> str | None:
9+
# Prefer copilot.exe over copilot.bat/copilot.cmd shims on Windows: the .bat shim invokes
10+
# PowerShell, which re-parses arguments and corrupts prompts containing double quotes.
11+
return shutil.which("copilot.exe") or shutil.which("copilot.cmd") or shutil.which("copilot")

src/bcbench/evaluate/codereview_judge.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77

88
import json
99
import re
10-
import shutil
1110
import subprocess
1211
from pathlib import Path
1312

1413
from bcbench.config import get_config
14+
from bcbench.copilot_cli import find_copilot as _find_copilot
1515
from bcbench.dataset.codereview import ReviewComment
1616
from bcbench.exceptions import LLMJudgeError
1717

@@ -81,10 +81,6 @@ def _parse_judge_results(result_path: Path, num_pairs: int, stdout: str = "") ->
8181
return [results_by_pair.get(i + 1, False) for i in range(num_pairs)]
8282

8383

84-
def _find_copilot() -> str | None:
85-
return shutil.which("copilot.exe") or shutil.which("copilot.cmd") or shutil.which("copilot")
86-
87-
8884
def _decode_stream(stream: str | bytes | None) -> str:
8985
if stream is None:
9086
return ""

tests/test_plugin_operations.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,11 @@ def test_setup_missing_required_key_raises_and_cleans_home(tmp_path):
213213
@patch("bcbench.agent.copilot.agent.build_al_lsp_plugin", return_value=None)
214214
@patch("bcbench.agent.copilot.agent.build_mcp_config", return_value=(None, None))
215215
@patch("bcbench.agent.copilot.agent.build_prompt", return_value="do the task")
216-
@patch("bcbench.agent.copilot.agent.shutil.which", return_value="copilot")
216+
@patch("bcbench.agent.copilot.agent.find_copilot", return_value="copilot")
217217
@patch("bcbench.agent.copilot.agent.subprocess.run")
218-
def test_copilot_runner_records_plugins_and_sets_home(mock_run, mock_which, mock_prompt, mock_mcp, mock_lsp, mock_instr, mock_skills, mock_agent, mock_hooks, mock_pm, mock_tu, mock_setup, tmp_path):
218+
def test_copilot_runner_records_plugins_and_sets_home(
219+
mock_run, mock_find_copilot, mock_prompt, mock_mcp, mock_lsp, mock_instr, mock_skills, mock_agent, mock_hooks, mock_pm, mock_tu, mock_setup, tmp_path
220+
):
219221
from bcbench.agent.copilot.agent import run_copilot_agent
220222
from bcbench.types import EvaluationCategory
221223

0 commit comments

Comments
 (0)