evalstone/evalscope/tests/benchmark/test_terminal_bench.py
sora ad61a2c44e Fix DeepSWE Pier routing and Terminal-Bench context/apt setup.
Keep generation max_tokens from being used as terminus-2 context, rewrite Debian/Ubuntu .sources to the Tsinghua mirror, and skip injecting truncation_tokens into DeepSWE extra_params.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-08 07:32:07 +00:00

182 lines
5.5 KiB
Python

import json
import math
from types import SimpleNamespace
from typing import Any
import pytest
from evalscope.api.messages.perf_metrics import PerformanceMetrics
from evalscope.api.metric import Score
from evalscope.benchmarks.terminal_bench.terminal_bench_adapter import _phase_timeout_options, _TerminalBenchBase
from evalscope.benchmarks.terminal_bench.utils import HARBOR_DEFAULT_CONTEXT_LIMIT, resolve_terminus_context_limit
TRIAL_URI = 'file:///tmp/terminal-bench-trial'
def _score(result: dict[str, Any]) -> Score:
adapter = object.__new__(_TerminalBenchBase)
task_state = SimpleNamespace(metadata={'result': result})
return adapter.match_score('raw', 'filtered', 'target', task_state)
@pytest.mark.parametrize('reward', [0, 1])
def test_terminal_bench_scores_valid_binary_reward(reward: int) -> None:
result = {
'trial_uri': TRIAL_URI,
'verifier_result': {
'rewards': {
'reward': reward
}
},
}
score = _score(result)
assert score.value == {'acc': float(reward)}
assert score.metadata == result
@pytest.mark.parametrize(
('verifier_result', 'expected_context'),
[
(None, 'verifier_result'),
({}, 'rewards'),
({'rewards': None}, 'rewards'),
({'rewards': {}}, 'reward'),
],
)
def test_terminal_bench_rejects_missing_reward(verifier_result: Any, expected_context: str) -> None:
result = {
'trial_uri': TRIAL_URI,
'verifier_result': verifier_result,
}
with pytest.raises(RuntimeError) as exc_info:
_score(result)
assert TRIAL_URI in str(exc_info.value)
assert expected_context in str(exc_info.value)
@pytest.mark.parametrize(
'reward',
[
pytest.param(None, id='null'),
pytest.param('1', id='string'),
pytest.param(True, id='bool'),
pytest.param(math.nan, id='nan'),
pytest.param(math.inf, id='infinity'),
pytest.param(-0.1, id='below-range'),
pytest.param(1.1, id='above-range'),
],
)
def test_terminal_bench_rejects_invalid_reward(reward: Any) -> None:
result = {
'trial_uri': TRIAL_URI,
'verifier_result': {
'rewards': {
'reward': reward
}
},
}
with pytest.raises(RuntimeError) as exc_info:
_score(result)
assert TRIAL_URI in str(exc_info.value)
assert 'invalid reward' in str(exc_info.value)
def test_terminal_bench_rejects_trial_exception_even_with_reward() -> None:
result = {
'trial_uri': TRIAL_URI,
'exception_info': {
'exception_type': 'RewardFileNotFoundError',
'message': 'reward.json is missing',
},
'verifier_result': {
'rewards': {
'reward': 0
}
},
}
with pytest.raises(RuntimeError) as exc_info:
_score(result)
error = str(exc_info.value)
assert TRIAL_URI in error
assert 'RewardFileNotFoundError' in error
assert 'reward.json is missing' in error
def test_terminal_bench_absolute_timeout_disables_global_multiplier_for_that_phase() -> None:
assert _phase_timeout_options(10_800, None, 'agent') == (10_800.0, 1.0)
def test_terminal_bench_rejects_conflicting_phase_timeout_options() -> None:
with pytest.raises(ValueError, match='agent_timeout_sec'):
_phase_timeout_options(10_800, 2.0, 'agent')
def test_terminal_bench_trace_preserves_request_perf_metrics(tmp_path) -> None:
trial_dir = tmp_path / 'trial'
trajectory_dir = trial_dir / 'agent'
trajectory_dir.mkdir(parents=True)
(trajectory_dir / 'trajectory.json').write_text(
json.dumps({
'agent': {
'name': 'terminus-2',
'model_name': 'test-model',
},
'steps': [{
'source': 'agent',
'message': 'answer',
'step_id': 0,
}],
})
)
adapter = object.__new__(_TerminalBenchBase)
adapter.environment_type = 'docker'
_, messages = adapter._load_harbor_trace(
{'trial_uri': f'file://{trial_dir}'},
[PerformanceMetrics(latency=1.0, input_tokens=3, output_tokens=2)],
)
assert messages[0].perf_metrics.latency == 1.0
assert messages[0].perf_metrics.input_tokens == 3
@pytest.mark.parametrize(
('kwargs', 'expected'),
[
({'context_limit': 65536, 'max_tokens': 8192, 'truncation_tokens': 131072}, 65536),
({'max_tokens': 8192, 'truncation_tokens': 131072}, 8192),
({'truncation_tokens': 65536}, 65536),
({}, HARBOR_DEFAULT_CONTEXT_LIMIT),
({'context_limit': 0, 'max_tokens': 8192, 'truncation_tokens': 65536}, 8192),
({'context_limit': '', 'max_tokens': None, 'truncation_tokens': 65536}, 65536),
],
)
def test_terminus_context_limit_priority(kwargs, expected) -> None:
assert resolve_terminus_context_limit(**kwargs) == expected
def test_adapter_resolves_context_limit_from_extra_params() -> None:
adapter = object.__new__(_TerminalBenchBase)
adapter.context_limit = 65536
adapter.extra_params = {'truncation_tokens': 131072}
model = SimpleNamespace(config=SimpleNamespace(max_tokens=8192))
assert adapter._resolve_context_limit(model) == 65536
adapter.context_limit = None
assert adapter._resolve_context_limit(model) == 8192
model.config.max_tokens = None
assert adapter._resolve_context_limit(model) == 131072
adapter.extra_params = {}
assert adapter._resolve_context_limit(model) == HARBOR_DEFAULT_CONTEXT_LIMIT