Repository navigation
[Bugfix] Stop caching synthetic videos without requested audio - #9071
Conversation
Propagate TTS and FFmpeg failures instead of caching video-only output for embed_audio=True. Regenerate legacy cached MP4s that lack the requested audio stream, and cover failure propagation, real AAC decoding, and cache reuse with CPU regressions. Refs vllm-project#8892 Signed-off-by: psv666 <2693925048@qq.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
|
This PR matches CODEOWNERS paths: /tests/. Code owners: @NickCao @yenuo26 Routing: @NickCao via CODEOWNERS; @yenuo26 via CODEOWNERS @psv666, please review your own changes and leave a short self-review comment describing what you checked. PRs without author self-review may not be assigned a reviewer. Please take a look when you have a chance. If you would like an automated review, mention @vllm-omni-review-bot in a comment. |
Omni ReviewBot triage noteAutomated triage of commit
These are automated triage suggestions only — the final decision belongs to the maintainers. |
natureofnature
left a comment
There was a problem hiding this comment.
Reviewed both changed files and the audio-in-video callers. TTS/mux failures now escape before the MP4 cache write, and legacy video-only entries are regenerated without changing valid-cache reuse. The reported 39 passing media tests on this head cover the fixture change, including real AAC decoding; the pre-rebase TTS and serving runs provide additional coverage. I haven't rerun tests locally.
【Conclusion: Found no blocking issues in commit bde3834.】
【Action required: None.】
amy-why-3459
left a comment
There was a problem hiding this comment.
Thanks, failing loudly instead of caching a video-only MP4 under the ea1 key makes sense, and the regression tests cover the main failure modes well.
One robustness point inline on the cache check. Also note that embed_audio=True is used by test_qwen2_5_omni_expansion.py and test_qwen3_omni_thinking.py as well as test_qwen3_omni_expansion.py, so those will now hard-fail on a TTS/FFmpeg problem too. It would be good to confirm their audio-in-video cases still pass, since the test plan only lists the Qwen3-Omni expansion run.
A partially written MP4 may expose audio metadata before its payload is complete. Treat probe/decode failures and incomplete cached media as cache misses, and publish completed files through a same-directory temporary file and os.replace. Cover corrupt headers, truncated audio, atomic publication, and failed replacement cleanup. Signed-off-by: psv666 <2693925048@qq.com>
amy-why-3459
left a comment
There was a problem hiding this comment.
Thanks for the follow-up. Looked at 7408f57 only: treating probe/decode errors and short frame counts as a cache miss, plus the same-directory temp file and os.replace, address the partial-write case. Two small points inline, one on the cache file permissions and one on what the truncated-audio test actually exercises.
Create the atomic cache staging file exclusively with normal umask-controlled permissions. Keep every video packet in the audio truncation regression so its failure depends on the decoded audio sample count, and verify creation permissions under shared and restrictive umasks. Signed-off-by: psv666 <2693925048@qq.com>
|
Author self-review completed at a385408: full precheck-pr plus a separate read-only review-pr pass on a detached snapshot, including affected callers and simplification. The final source fingerprint stayed unchanged across 47 passing media tests. Final real TTS/AAC repair and cache-reuse checks also passed 8/8 through PyAV and TorchCodec. The PR body has the reproduction commands and exact revisions. Additional caller verification at 7408f57 (before the final permission/test-only follow-up):
All three inline concerns are addressed with regression evidence, including umask handling and the isolated audio-tail mutation check. AI assistance: Codex assisted with the fixes, tests, validation, self-review, and this response. |
amy-why-3459
left a comment
There was a problem hiding this comment.
Looked at a385408 only. Both points are addressed. Path.open("xb") goes through os.open(O_CREAT|O_EXCL, 0o666), so the published cache now gets 0666 & ~umask like the old write_bytes, and the uuid name keeps concurrent writers from colliding before os.replace. The umask-parametrized test pins that. The corruption case now cuts at an audio packet past the last video packet and asserts six decodable frames first, so it really exercises the sample-count check. No further comments from me on this delta.
|
@yenuo26 PTAL |
yenuo26
left a comment
There was a problem hiding this comment.
Reviewed f7b36f8f statically (both files plus the embed_audio=True callers). Failing loudly before publication and the PyAV sample-count gate address #8892's video-only ea1 cache; CI images ship espeak-ng, pyttsx3, av and imageio[ffmpeg], so removing the silent fallback should not break CI on missing deps.
One non-blocking test gap inline: the new video-frame-count / decode-failure branch is not exercised by any test. I haven't run the tests locally.
Exercise empty and invalid video-only caches through the actual decode failure path, and add truncated video cases with and without audio. Assert that partial videos remain decodable and are repaired to the complete frame count, preserving audio only when requested. Signed-off-by: psv666 <2693925048@qq.com>
amy-why-3459
left a comment
There was a problem hiding this comment.
Looked at 01893cc only (skipped the merge from main). The added video-only cases are useful: empty and invalid-header now go through the real _np_array_from_mp4_bytes failure path, and the truncated video-only case is the one that actually exercises the len(cached_frames) == num_frames guard. One note inline on the with-audio truncated case.
Signed-off-by: psv666 <2693925048@qq.com>
amy-why-3459
left a comment
There was a problem hiding this comment.
Looked at 4d22141 only (test-only change). Swapping in a complete five-frame MP4 under the six-frame cache key for the with-audio case addresses my earlier note: the added precondition (samples >= duration * time_base * sample_rate) matches the runtime gate in media.py (integer samples vs. the ceil there are equivalent), so that parametrization now has to reach the len(cached_frames) == num_frames check. The video-only case still exercises physical truncation. No further comments on this delta.
Omni ReviewBot: supersededThe CI failure noted on |
Signed-off-by: psv666 <2693925048@qq.com>
Head branch was pushed to by a user without write access
|
The Buildkite #17634 failure is inherited from main and is unrelated to this PR's synthetic-video/cache changes. Both failing parameterizations are Added the narrow test-harness compatibility fix in Local validation at AI assistance: Codex assisted with diagnosis, the test-harness repair, validation, independent agent review, and writing. |
amy-why-3459
left a comment
There was a problem hiding this comment.
Looked at the delta since my last pass (ac6c5fd8, the duplex test-harness fix; the 847331e3 merge-from-main is skipped). Switching to the real AURA buffer and the real bound commit helpers is a better seam than the old _Buffer/_Reservation fakes, and the original assertions are preserved.
Two notes:
- Since this test is red on main itself (the #8828 / #8971 integration gap you described), it would be good to land the same fix on main as well, or to track it there; otherwise other open PRs stay red until they pick it up, and a different fix on main would conflict with this one.
- One inline note on how much of the commit path the
__new__-built runner actually covers.
Signed-off-by: psv666 <2693925048@qq.com>
…project#9071) Signed-off-by: psv666 <2693925048@qq.com> Co-authored-by: amy-why-3459 <wuhaiyan17@huawei.com>
Contributor intent
1. What outcome do you want to achieve?
Fix #8892 so Qwen3-Omni audio-in-video tests receive valid audio-bearing video fixtures.
2. What is the highest-leverage variable for that outcome?
Whether generated and cached test videos actually contain a decodable audio stream.
3. How will this work move that variable?
Propagate audio generation/muxing failures and regenerate cached videos missing the requested audio stream.
4. Whose customer or user needs does this address, and what are those needs?
Qwen3-Omni users and CI maintainers need audio-in-video requests and their test coverage to work reliably.
Purpose
Refs #8892.
When TTS generation or FFmpeg muxing failed,
generate_synthetic_video(..., embed_audio=True)silently cached a video-only MP4. Reusing that entry after repairing the dependency reproduced PyAV'sNo audio stream found.and TorchCodec's unknown-best-audio-stream error.Generation/muxing failures now propagate before cache publication. Cache reads regenerate missing-audio, corrupt, or incomplete MP4s: requested audio is decoded and checked against its declared sample duration, and video must decode to the requested frame count. Completed bytes are closed in a unique same-directory staging file, then published with
os.replace. Exclusive creation preserves normal0666 & ~umaskpermissions; failed replacement preserves the old entry and removes the staging file. Valid audio-bearing and video-only caches remain reusable.Regressions cover dependency failures, legacy video-only cache repair, real AAC decoding, valid reuse, empty/invalid media with and without audio, independently truncated audio/video payloads, atomic publication, failed-publication cleanup, and shared/restrictive permission masks. CPU tests substitute only TTS; MP4/AAC encoding and decoding are real. The helper docstring documents the behavior; no runtime API, configuration, or active production design contract changes.
The media regression suite in
tests/helpers/tests/test_synthetic_video.pyalso protects video frame-count validation. It isolates cached-video frame-count validation in both audio modes: the video-only case retains a physical payload truncation; the with-audio case puts a fully encoded five-frame MP4 under the six-frame cache key and explicitly verifies complete audio before invoking cache repair. The renamed incomplete-video test checks both preconditions and the restored six-frame result. This addresses test isolation, with no newly demonstrated runtime defect.tests/helpers/media.pyis unchanged fromf7b36f8f(and byte-identical toa385408e7).Main has been integrated through
ad6fc16f5, including the upstream Duplex CI repair in #9151. The overlap with this branch's earlier CI repair was resolved by retaining the upstream Duplex test unchanged. The final PR diff against main contains onlytests/helpers/media.pyandtests/helpers/tests/test_synthetic_video.py; it has no Duplex, MiniCPM E2E, or kernel delta.Test Plan
Current revision:
a7f51ee85488cabd21d76bf9899dd8439de7c7ab; merge parentsac6c5fd8eand mainad6fc16f5. Final comparison base:ad6fc16f5. The two media files are byte-identical to their validated4d2214126versions.Current CPU environment: Python 3.12.13; vLLM 0.32.0; PyTorch 2.13.0+cu130; PyAV 18.0.0; imageio 2.37.4; imageio-ffmpeg 0.6.0; NumPy 2.3.5; pytest 9.1.1. Development/media dependencies and FFmpeg are required; the CPU regression suite does not require a host TTS installation or model weights. Commands below run from the checkout and omit this host's GPU reservation wrapper.
Test Result
a7f51ee85: 1367 related Duplex/orchestrator/media CPU tests pass, including all 51 media cases and both retained-PCM cases from the upstream conflict file. The conflict file equals [CI] Fix MiniCPM-o duplex tests broken after #8971 #9151 byte-for-byte; the final two-file PR diff preserves the existing media implementation and tests unchanged.precheck-pr, applicable local pre-commit hooks, and a separate fresh-contextreview-prcompleted without actionable findings. The independent reviewer ran 21 targeted cases on its own frozen final snapshot, verified imported source identities, and checked unchanged fingerprints before/after validation. Matching-head Buildkite validation remains separate from local CPU evidence.Earlier media validation (actual revisions retained):
4d2214126: 19 fixture regressions pass within 51 related media tests. Fullprecheck-prauthor self-check and a separate fresh-context reviewer executingreview-pron the frozen final commit completed; no actionable findings. The independent reviewer ran the 51-test media group in its own detached snapshot and verified unchanged source/index fingerprints.01893cc0: adding the missing audio-completeness precondition failed for the with-audio fixture (5120 decoded samples versus 9216 declared), while video-only passed. The old sample could be rejected before video frame-count validation. This establishes a fixture-isolation gap, not a runtime regression.01893cc0failed only video-only (1 failed, 18 passed). Disabling only audio sample completeness still fails the separate truncated-audio regression (1 failed, 18 passed). Bypassing cached-videoRuntimeErrorrecovery still fails the two video-only empty/invalid-header cases (2 failed, 17 passed). Each mutation was applied independently in an isolated checkout; the unmodified helper passes all 19 cases.git diff --checkpass. No serving or model run was repeated for this test-only follow-up; the earlier results below retain their actual revisions and environments.Earlier fixture/model validation:
a385408e7: 47 media tests and 8/8 real-TTS/AAC repair/cache checks passed (128/300 frames, PyAV and TorchCodec). Environment: vLLM 0.31.0, PyTorch 2.13.0+cu129, TorchCodec 0.15.0, PyAV 18.0.0, torchaudio 2.11.0+cu129, pyttsx3 2.99.7408f57d, on two NVIDIA L20X GPUs per run: Qwen3 Instruct audio-in-video E2E 6 passed across default/async-chunk configurations. Qwen2.5 boundaries 2 passed with explicit stage KV budgets of 8 GiB/2 GiB after the unmodified deployment OOMed in startup warmup before any request. Thinking's original case generated valid MP4/AAC, then failed HTTP 400 because the client defaults to text+audio output while Thinking supports text only. Supplementary validation with explicit text output passed both the original video request and ause_audio_in_video=Truerequest. This does not turn the original Thinking case into a pass. Earlier author validation summary.a385408e7on two RTX PRO 6000 Blackwell GPUs with TorchCodec 0.17.0: Qwen3 online 11 passed, 14 skipped; online expansion 26 passed; offline 4 passed; fixture tests 15 passed; Qwen2.5 boundaries 2 passed. They reported the Thinking failure as expected. Reporter validation and full environment.The initial dependency failure in the reporter's environment remains unidentified. TTS/FFmpeg are still required for real audio generation; the helper exposes their failures instead of caching misleading input.
AI assistance: Codex assisted with diagnosis, the fix, regression tests, validation, author self-check, independent agent review, and writing.