evalstone/evalscope/tests/benchmark/test_omnidoc_bench.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

133 lines
5.4 KiB
Python

import json
from pathlib import Path
from types import SimpleNamespace
from unittest import mock
import pytest
from evalscope.api.dataset import DictDataLoader
from evalscope.api.metric import MetricSelector, SampleScore, Score
from evalscope.api.registry import get_benchmark
from evalscope.benchmarks.omnidoc_bench.legacy.omnidoc_bench_adapter import OmniDocBenchAdapter
from evalscope.benchmarks.omnidoc_bench.v1_6.omnidoc_bench_v1_6_adapter import OmniDocBenchV16Adapter
from evalscope.benchmarks.omnidoc_bench.v1_6.sandbox_scorer import RESULT_SENTINEL, parse_scoring_result
from evalscope.config import TaskConfig
from evalscope.constants import ScoreStatus
def _sandbox_result(metrics: dict[str, float]) -> dict[str, str]:
return {'status': 'success', 'output': RESULT_SENTINEL + json.dumps(metrics)}
def _v16_adapter_with_result(result: dict[str, str]) -> OmniDocBenchV16Adapter:
adapter = OmniDocBenchV16Adapter.__new__(OmniDocBenchV16Adapter)
adapter._task_config = SimpleNamespace(sandbox=SimpleNamespace(enabled=True))
adapter._benchmark_meta = SimpleNamespace(review_timeout=1)
adapter.execute_code_in_sandbox = mock.Mock(return_value=result)
return adapter
@pytest.mark.parametrize(
('metrics', 'expected'),
[({}, {}), ({'text_block_Edit_dist': 0.25}, {'text_block_Edit_dist': 0.25})],
)
def test_v16_parse_scoring_result_accepts_empty_and_normal_results(
metrics: dict[str, float], expected: dict[str, float]
) -> None:
assert parse_scoring_result(_sandbox_result(metrics)) == expected
@pytest.mark.parametrize(
('metrics', 'status', 'main_score_name'),
[({}, ScoreStatus.EXCLUDED, None), ({'text_block_Edit_dist': 0.25}, ScoreStatus.SUCCESS, 'text_block_Edit_dist')],
)
def test_v16_match_score_handles_pages_without_supported_metrics(
metrics: dict[str, float], status: ScoreStatus, main_score_name: str | None
) -> None:
adapter = _v16_adapter_with_result(_sandbox_result(metrics))
task_state = SimpleNamespace(metadata={'image_name': 'page.png'})
score = adapter.match_score('prediction', 'prediction', '{}', task_state)
assert score.value == metrics
assert score.status is status
assert score.main_score_name == main_score_name
if not metrics:
assert score.metadata == {'scoring_excluded_reason': 'no_page_metrics'}
def test_v16_aggregation_excludes_unscored_pages_from_normalized_score_count() -> None:
adapter = OmniDocBenchV16Adapter.__new__(OmniDocBenchV16Adapter)
sample_scores = [
SampleScore(
sample_id=1,
score=Score(
value={
'text_block_Edit_dist': 0.2,
'display_formula_CDM': 0.8,
'table_TEDS': 0.7,
}
),
),
SampleScore(sample_id=2, score=Score(status=ScoreStatus.EXCLUDED)),
]
aggregated = adapter.aggregate_scores(sample_scores)
normalized_score = next(score for score in aggregated if score.metric_name == 'normalized_score')
assert normalized_score.num == 1
assert normalized_score.ids == [1]
def test_v16_loads_snapshot_once_and_reads_images_locally(tmp_path: Path) -> None:
image_names = ['page.png', 'long_' + '页面' * 36 + '.png']
records = [{'page_info': {'image_path': image_name}} for image_name in image_names]
(tmp_path / 'OmniDocBench.json').write_text(json.dumps(records), encoding='utf-8')
image_dir = tmp_path / 'images'
image_dir.mkdir()
for image_name in image_names:
(image_dir / image_name).write_bytes(b'image')
adapter = get_benchmark(
'omni_doc_bench_v1_6',
TaskConfig(datasets=['omni_doc_bench_v1_6']),
)
with mock.patch(
'evalscope.benchmarks.omnidoc_bench.v1_6.omnidoc_bench_v1_6_adapter.resolve_snapshot_or_local_path',
return_value=str(tmp_path),
) as resolve_snapshot:
dataset = adapter.load_subset('default', DictDataLoader)
resolve_snapshot.assert_called_once_with(adapter)
assert [sample.metadata['image_name'] for sample in dataset] == image_names
assert all(sample.input[0].content[0].image.startswith('data:image/png;base64,') for sample in dataset)
def test_legacy_omnidoc_aggregates_canonical_metrics() -> None:
selector = MetricSelector(name='normalized_score', aggregation='macro_mean')
adapter = OmniDocBenchAdapter.__new__(OmniDocBenchAdapter)
adapter._benchmark_meta = SimpleNamespace(metric_list=[], primary_metric=selector)
adapter.match_method = 'quick_match'
sample_scores = [SampleScore(sample_id=1, score=Score(prediction='markdown', metadata={'reference': {}}))]
raw_scores = {
'text_block_Edit_dist_EN': 0.2,
'table_TEDS_CH': 0.8,
'overall_EN': 0.7,
'overall_CH': 0.9,
}
with mock.patch(
'evalscope.benchmarks.omnidoc_bench.legacy.end2end_eval.End2EndEvaluator.score',
return_value=raw_scores,
):
aggregated = adapter.aggregate_scores(sample_scores)
identities = [score.identity for score in aggregated]
assert any(
identity.name == 'text_block_edit_dist' and identity.dimensions == {'language': 'en'} for identity in identities
)
assert any(identity.name == 'table_teds' and identity.dimensions == {'language': 'ch'} for identity in identities)
primary_matches = [score for score in aggregated if selector.matches(score.identity)]
assert len(primary_matches) == 1
assert primary_matches[0].score == pytest.approx(0.8)