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>
133 lines
5.4 KiB
Python
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)
|