evalstone/evalscope/tests/api/test_default_data_adapter.py
sora 13274243a0 Bump vendored EvalScope and add K3-ready DPV4 configs.
Keep K3 suite selection and report-schema scoring in bash, merge K3/vision dataset_args into dpv4 yamls, and pin EvalScope at 735d920ee911 with local patches.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-02 07:30:48 +00:00

214 lines
7.6 KiB
Python

import copy
from typing import Any, ClassVar, List, Optional
from evalscope.api.benchmark import BenchmarkMeta, DefaultDataAdapter
from evalscope.api.benchmark.statistics import SampleExample
from evalscope.api.dataset import DataLoader, DatasetDict, MemoryDataset, Sample
from evalscope.api.evaluator import TaskState
from evalscope.api.messages import ChatMessageSystem, ChatMessageUser
from evalscope.api.metric import Score
from evalscope.api.model import ModelOutput
from evalscope.config import TaskConfig
from evalscope.constants import JudgeStrategy
class CapturingDataLoader(DataLoader):
latest_limit: ClassVar[Optional[int]] = None
latest_repeats: ClassVar[Optional[int]] = None
latest_version: ClassVar[Optional[str]] = None
latest_seed: ClassVar[Optional[int]] = None
def load(self) -> MemoryDataset:
self.__class__.latest_limit = self.limit
self.__class__.latest_repeats = self.repeats
self.__class__.latest_version = self.version
self.__class__.latest_seed = self.seed
samples: List[Sample] = [
Sample(input='question-1', target='answer-1', subset_key='subset-a'),
Sample(input='question-2', target='answer-2', subset_key='subset-a'),
]
if self.limit is not None:
samples = samples[:self.limit]
if self.repeats > 1:
samples = [copy.deepcopy(sample) for sample in samples for _ in range(self.repeats)]
dataset = MemoryDataset(samples=samples, name='dummy')
dataset.reindex(group_size=self.repeats)
return dataset
class DummyReformatAdapter(DefaultDataAdapter):
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
self.reformat_subset = True
def record_to_sample(self, record: Any) -> Sample:
return Sample(input=str(record), target='', subset_key='subset-a')
class DummyLLMJudgeAdapter(DefaultDataAdapter):
llm_judge_default = True
def record_to_sample(self, record: Any) -> Sample:
return Sample(input=str(record), target='')
def make_adapter(
repeats: int = 3,
limit: Optional[int] = None,
system_prompt: Optional[str] = None,
dataset_revision: Optional[str] = None,
) -> DummyReformatAdapter:
task_config = TaskConfig(datasets=['dummy'], repeats=repeats, limit=limit)
benchmark_meta = BenchmarkMeta(
name='dummy',
dataset_id='dummy',
subset_list=['subset-a'],
default_subset='default',
eval_split='test',
prompt_template='{question}',
system_prompt=system_prompt,
dataset_revision=dataset_revision,
)
return DummyReformatAdapter(benchmark_meta=benchmark_meta, task_config=task_config)
def test_reformat_subset_repeats_are_applied_once_after_grouping() -> None:
CapturingDataLoader.latest_limit = None
CapturingDataLoader.latest_repeats = None
adapter = make_adapter(repeats=3)
dataset_dict: DatasetDict = adapter.load_subsets(
lambda subset: adapter.load_subset(subset=subset, data_loader=CapturingDataLoader)
)
assert CapturingDataLoader.latest_limit is None
assert CapturingDataLoader.latest_repeats == 1
assert len(dataset_dict['subset-a']) == 6
assert [sample.group_id for sample in dataset_dict['subset-a']] == [0, 0, 0, 1, 1, 1]
def test_load_subset_passes_the_resolved_dataset_revision() -> None:
adapter = make_adapter(dataset_revision='2026-08-21')
adapter.load_subset(subset='subset-a', data_loader=CapturingDataLoader)
assert CapturingDataLoader.latest_version == '2026-08-21'
assert CapturingDataLoader.latest_seed == 42
def test_shuffle_choices_configuration_survives_adapter_initialization() -> None:
meta = BenchmarkMeta(
name='choice_shuffle',
dataset_id='dummy',
eval_split='test',
shuffle_choices=True,
)
adapter = DummyReformatAdapter(benchmark_meta=meta, task_config=TaskConfig(datasets=['choice_shuffle']))
assert adapter.shuffle_choices is True
user_configured_meta = BenchmarkMeta(name='user_choice_shuffle', dataset_id='dummy', eval_split='test')
user_configured_meta._update({'shuffle_choices': True})
user_configured_adapter = DummyReformatAdapter(
benchmark_meta=user_configured_meta,
task_config=TaskConfig(datasets=['user_choice_shuffle']),
)
assert user_configured_adapter.shuffle_choices is True
def test_auto_judge_strategy_uses_adapter_class_default() -> None:
benchmark_meta = BenchmarkMeta(name='dummy_judge', dataset_id='dummy', eval_split='test')
adapter = DummyLLMJudgeAdapter(
benchmark_meta=benchmark_meta,
task_config=TaskConfig(datasets=['dummy_judge'], judge={'strategy': JudgeStrategy.AUTO}),
)
assert adapter.use_llm_judge is True
adapter._task_config.judge.strategy = JudgeStrategy.RULE
assert adapter.use_llm_judge is False
adapter._task_config.judge.strategy = JudgeStrategy.LLM
assert adapter.use_llm_judge is True
def test_llm_recall_skips_judge_when_rule_score_is_perfect(monkeypatch) -> None:
benchmark_meta = BenchmarkMeta(name='dummy_judge', dataset_id='dummy', eval_split='test')
adapter = DummyLLMJudgeAdapter(
benchmark_meta=benchmark_meta,
task_config=TaskConfig(
datasets=['dummy_judge'],
judge={'strategy': 'llm_recall', 'models': {'model_id': 'judge'}},
),
)
state = TaskState(
model='m',
sample=Sample(input='question', target='answer'),
output=ModelOutput(model='m', completion='answer'),
completed=True,
)
monkeypatch.setattr(adapter, 'match_score', lambda **_: Score(value={'acc': 1.0}, main_score_name='acc'))
monkeypatch.setattr(adapter, 'score_with_judge_contracts', lambda **_: (_ for _ in ()).throw(AssertionError()))
score = adapter.calculate_metrics(state).score
assert score.value == {'acc': 1.0}
def test_sample_score_preserves_repeat_position() -> None:
adapter = make_adapter(repeats=3)
state = TaskState(
model='m',
sample=Sample(id=4, group_id=1, input='question', target='answer'),
output=ModelOutput(model='m', completion='answer'),
completed=True,
)
sample_score = adapter.calculate_metrics(state)
assert sample_score.generation_index == 1
def test_sample_example_detects_parameterized_truncation_marker() -> None:
sample = Sample(input='prefix ... [TRUNCATED 123 chars] ... suffix', target='answer')
sample_example = SampleExample.from_sample(sample=sample)
assert sample_example.truncated is True
def test_empty_system_prompt_is_added_to_string_input() -> None:
adapter = make_adapter(system_prompt='')
messages = adapter.process_sample_str_input(Sample(input='question', target='answer'), subset='subset-a')
assert [message.role for message in messages] == ['system', 'user']
assert messages[0].content == ''
def test_empty_system_prompt_is_added_once_to_message_input() -> None:
adapter = make_adapter(system_prompt='')
existing_system = ChatMessageSystem(content='existing')
messages = adapter.process_sample_messages_input(
Sample(input=[ChatMessageUser(content='question')], target='answer'), subset='subset-a'
)
existing_messages = adapter.process_sample_messages_input(
Sample(input=[existing_system, ChatMessageUser(content='question')], target='answer'), subset='subset-a'
)
assert [message.role for message in messages] == ['system', 'user']
assert messages[0].content == ''
assert [message.role for message in existing_messages] == ['system', 'user']
assert len(existing_messages) == 2
assert existing_messages[0] is existing_system
assert existing_messages[0].content == 'existing'