Files
vllm-omni/tests/e2e/accuracy/omni_duplex_eval/README.md
T

136 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Omni-DuplexEval CI Guard
Nightly end-to-end scoring regression for [Hothan/Omni-DuplexEval]
(RTD 6 subtasks + PR 3 subtasks), driven by `vllm bench omni-duplex-eval`
(generate → evaluate → summarize).
## Files
| File | Purpose |
| ------ | --------- |
| `test_omni_duplex_eval_ci.py` | Main pytest: hardcoded thresholds + 6 assertions |
| `test_omni_duplex_eval_ci_mock.py` | CPU mock integration tests (no GPU / model needed) |
| `omni_duplex_eval_ci_config.json` | Dataset / revision / sample IDs / judge |
| `conftest.py` | `judge_server` fixture (endpoint resolution only) |
| `README.md` | This document |
| `results/.gitignore` | Track empty results dir for Buildkite artifact upload |
## Run locally
Judge server must be pre-started; this test never launches it:
```bash
# CUDA (H100 / B200), or NPU (A3)
VLLM_DUPLEX_EVAL_JUDGE_URL=http://127.0.0.1:8001 \
pytest -s -v tests/e2e/accuracy/omni_duplex_eval/ \
-m "full_model and cuda and (H100 or B200) and cards_1"
```
The `omni_server` fixture boots MiniCPM-o-4_5 with the community duplex
params (`tests/e2e/online_serving/helpers/minicpmo_4_5_duplex.py`).
`judge.base_url_env` (default `VLLM_DUPLEX_EVAL_JUDGE_URL`) overrides
`judge.base_url` from the JSON config.
## Results output
After Phase 3 (summarize), the full `summarize_scores()` dict is persisted
to `results/summary.json` (same pattern as PR#6817 perf tests) and printed
to stdout:
- `results/` is tracked in git via `results/.gitignore` (empty dir, nothing
committed inside).
- On Buildkite nightly, the step uploads `results/*.json` as artifacts
(`buildkite-agent artifact upload`) even when assertions fail, so a
failing run's scores remain downloadable.
## Judge server
Judge is a pre-started, OpenAI-compatible endpoint (never launched by this
test). It can run on a separate machine / GPU from the `omni_server`.
Current judge: **Qwen2.5-VL-7B-Instruct** (`judge.model` in the JSON
config). The 7B model is chosen for Phase A to run on a single GPU (≈16 GB
FP16, or ≈5 GB with INT4 quantization); PR 0/1 classification and temporal
0–3 scoring are robust at this size. The 0.00–3.00 content scoring is the
weakest link (fine-grained counting / color / spatial checks) — if the
guard proves insensitive to regressions, upgrade the judge (e.g.
72B on FP8 H100 or an external API) and **re-run the baseline** to reset
`_MIN_*` thresholds. Changing the judge never affects the P0 assertions
(`protocol_pin`, sample count, `by_task` keys) or the CPU mock tests.
Example (any OpenAI-compatible vLLM server):
```bash
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-VL-7B-Instruct --port 8001 --max-model-len 65536 \
--allowed-local-media-path /tmp/omni_duplex_ci
```
If the judge is started on a different host or the media temporary directory
changes, adjust `--allowed-local-media-path` to match the path used by
`vllm bench omni-duplex-eval evaluate` for materialized videos.
## CPU mock tests (no GPU / model / judge needed)
`test_omni_duplex_eval_ci_mock.py` drives the *real*
`test_omni_duplex_eval_ci()` guard on CPU by stubbing the heavy I/O:
- `_run_cli` is replaced by an argv-recording spy (never executes).
- `resolve_ref_audio` is stubbed to a local path.
- Fake score files are pre-written under `tmp_path/scores/<split>/` from
`sample_ids_per_split` (minus `exclude_ids`), so `summarize_scores()`
and the Phase-4 assertions run for real.
It validates the CLI argument assembly (generate + evaluate), the
score-file-count check, `protocol_pin`, the RTD/PR `by_task` keys, and
all threshold comparisons — including the failure paths. Runs in CI on
every PR (`core_model` + `cpu` marks):
```bash
pytest -v tests/e2e/accuracy/omni_duplex_eval/test_omni_duplex_eval_ci_mock.py
```
## Thresholds (record mode → tighten after first nightly run)
`sample_ids_per_split` contains real dataset IDs (verified against the
Hothan/Omni-DuplexEval splits; PR_correction / PR_event_reminder IDs are
inferred from the split row order and must be re-verified once the dataset
is reachable). `exclude_ids` has been removed.
All `_MIN_*` thresholds are currently **0.0 (record mode)**: the nightly run
produces real scores and persists `results/summary.json` without failing the
gate. Tighten after the first nightly run:
1. Record `summarize_scores()` output (mean content / temporal / success).
2. Set `_MIN_*` thresholds = `mean - n * std` (start `n=2`) in
`test_omni_duplex_eval_ci.py`. As a starting point, the paper's
MiniCPM-o 4.5 scores (100-point scale) map to ≈1.15 content / ≈2.40
temporal / ≈0.20 PR on the code scales (see "Reference paper" below);
~50% of those is a reasonable lower bound.
3. If needed, exclude known bad samples (generate hang / judge failure) via
`exclude_ids`.
Thresholds are bound to the judge model: **switching the judge requires
re-running steps 1–2**. If the 7B judge shows high variance (small models
are more prompt-sensitive), widen `n` (e.g. `n=3`) or increase the sample
size before tightening.
## Assertions (in order)
1. `protocol_pin == PROTOCOL_PIN` (track upstream protocol changes)
2. `summary["samples"]` == number of selected IDs
3. RTD `by_task` keys == 6 RTD subtasks
4. PR `by_task` keys == 3 PR subtasks
5. RTD mean content / temporal scores >= thresholds
6. PR `mean_all_success` >= threshold
## Reference paper & scoring scale
Reference paper: **Omni-DuplexEval** (arXiv:2605.17360). The paper reports
a 100-point scale, while the evaluation code
(`vllm_omni/benchmarks/duplex/omni_duplex_eval_metrics.py`) uses a 3-point
content scale (0.00–3.00), a 0–3 integer temporal scale, and a 0/1 PR
success flag. The CI thresholds are defined against the **code's** scales
(the source of truth for `summarize_scores()` output), not the paper's
100-point scale.