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>
182 lines
5.5 KiB
Python
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
|