Skip to content

fix(minimax-h3): half of every video was discarded, plus the missing user docs - #68

Merged
localai-bot merged 1 commit into
mainfrom
fix/h3-audio-duration-and-readme
Aug 7, 2026
Merged

fix(minimax-h3): half of every video was discarded, plus the missing user docs#68
localai-bot merged 1 commit into
mainfrom
fix/h3-audio-duration-and-readme

Conversation

@localai-bot

@localai-bot localai-bot commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

A real bug found by rendering, not by the suite, plus the user-facing documentation the H3 lane never had.

The bug

A 124-frame render silently muxed as 61 frames. audio_t is the per-channel latent length (the planner sets it from 40 Hz * duration) and the packed layout carries audio_t * audio_channel rows, one per (channel, step). The denormalize step divided by the channel count, so the decoded audio ran half the video's duration; because the muxer passes -shortest, that silently truncated the video to half its frames too.

The fix is one line, and it now agrees with the same function's own target_audio_rows = request.audio_t * request.audio_channel a few lines above.

Why the suite was blind, which is the part worth keeping

Every existing gate asserts shape self-consistency, and a uniformly halved pipeline is perfectly self-consistent: the shapes agreed with each other, they were just half as long as the request asked for. ffprobe on the artifact exposed it.

The gate added is the one that is not self-consistency: the structural t2va e2e case now decodes audio_t latent steps independently and requires the pipeline to have produced exactly that many samples. RED-first: restoring the divide turns that case red (1 failed assertion), removing it goes green. Suite 69/69, 47416 assertions.

A second bug from the same session, dropped rather than landed twice

Conditioning prepends rows to img_pos, so the denoise loop returns condition rows followed by the targets, and unpatchify was handed the whole set. This branch fixed it by carrying the layout's own update_mask out of the loop. main fixed the same bug independently in row/H3-RENDER-CLOSE, by taking the trailing target_video_rows with a VT_CHECK on the row accounting.

On rebase the update_mask mechanism and its test were dropped: landing a second solution to a fixed bug is churn, and the positional form already ships gated. The alternative is recorded in .agents/state.md rather than silently lost.

Docs

The video+audio generation path had no user-facing documentation at all, and the multimodal input interface was documented wrong: vllm-cli --image/--video/--audio does not exist, the CLI is text-only. Multimodal input is served over the OpenAI API as image_url, video_url and input_audio/audio_url content parts on /v1/chat/completions (src/vllm/entrypoints/openai/chat_mm.cpp), which was documented nowhere, so docs/USAGE.md now states it rather than the wrong claim merely being removed.

README gains a News section led by video+audio generation, paid for inside the 30,000-char landing-page budget (29,987) rather than by raising it.

Public docs

The 19-line narrative this branch originally appended to docs/BENCHMARKS.md was a non-canonical H2 section and pushed the page over its prose budget. Converted into three keyed rows (Thor render speed, render duration, quantization floor), with the forensics moved verbatim into .agents/benchmark-record.md, which is what that page's own checker instructs.

FOLLOWING_AGENTS_PROTOCOL

@mudler
mudler force-pushed the fix/h3-audio-duration-and-readme branch from 458c813 to a07403d Compare August 6, 2026 16:41
mudler added a commit that referenced this pull request Aug 6, 2026
…lash-attn build (#68 W2/W3)

QUANT-CT-MXFP4-FLASH-AUDIT names the flash +807us/step (+22.4us/call) MXFP4-8B
decode term: vLLM's flash-attn (_vllm_fa2_C) is compiled --use_fast_math; our
vendored FA2 TUs (same 2c839c33 source) were not. Without it nvcc emits the
precise div/rsqrt sequences: 5448 SASS instrs / REG246 vs 4832 / REG255 with it
— matching vLLM's 4880 / REG255, HMMA(768)/LDSM(408)/LDGSTS(120) byte-identical.
At the tiny latency-bound decode tile that ~11.6% scalar bloat ≈ the +14%
per-call time. Non-byte-exact (fast-math div/rsqrt) => SACRED battery owed
before any default claim; measured recovery + gates in follow-up.

FOLLOWING_AGENTS_PROTOCOL
Assisted-by: Claude Code:claude-opus-4-8 [ClaudeCode]
mudler added a commit that referenced this pull request Aug 6, 2026
…4 flash +807us decode term (#68)

QUANT-CT-MXFP4-FLASH-AUDIT closes the #67-OWED flash audit: why the IDENTICAL-grid
flash_fwd_splitkv MXFP4-8B decode kernel runs ~+22us/call slower than the vLLM oracle.

W1 (fresh same-tool reconciliation, analyze_decode.py on the #57 c8 traces): flash MAIN
ours 178.8 vs vLLM 156.3 us/call (+807us/step), combine tied, totals reconcile — REAL,
not a reconciliation artifact.

W2 (cuobjdump/ptxas arbiter, nvcc/ptxas 13.0.88 both sides): kernel-version REFUTED —
vLLM v0.25.0 pins vllm-flash-attn @ 2c839c33, the exact commit we vendored, so flash
SOURCE is byte-identical. The real difference is a BUILD FLAG: vLLM's flash-attn is
compiled --use_fast_math; our vendored FA2 TUs were not. Decode kernel <128,64,128,4>:
ours 5448 instrs/REG246 -> 4832/REG255 with fast-math (= vLLM's 4880/REG255), with
HMMA(768)/LDSM(408)/LDGSTS(120) byte-identical. The +616 scalar instrs (precise
div/rsqrt) = +11.6% ~ the +14.3% per-call time at the latency-bound decode tile. hd256
(27B/35B) also leans 4928->4560. Ruled out: -maxrregcount=255 (5424/246); our own
nvcc-13 compute_80 PTX (5440).

FIX: CMakeLists flash TU COMPILE_OPTIONS += -use_fast_math (mirrors flash-attn's own
build). Real sm_121a build's flash object VERIFIED = 4832 instrs/REG255. Non-byte-exact
(fast-math div/rsqrt) => runtime nsys A/B + SACRED battery OWED (box-contended by the
active H3 campaign); NO default flipped. Full forensics in .agents/benchmark-record.md
(QUANT-CT-MXFP4-FLASH-AUDIT) + spec; STATUS/BENCHMARKS/NOW/state updated.

FOLLOWING_AGENTS_PROTOCOL
Assisted-by: Claude Code:claude-opus-4-8 [ClaudeCode]
mudler added a commit that referenced this pull request Aug 6, 2026
…ash regression); gap is occupancy/L2, not the SASS

QUANT-CT-MXFP4-FLASH-AUDIT runs the #67-OWED flash audit to a MEASURED verdict.
W1: fresh same-tool c8 decode-window — the flash gap on CURRENT main is +12.5us/call
(+450us/step) ours 168.8 vs vLLM 156.3, SMALLER than #57's +807 (leaner marlin/glue).
W2 lens 1 (cuobjdump HYPOTHESIS): vLLM's flash-attn is --use_fast_math, ours was not
(same 2c839c33 src; +fast-math -> 4832/REG255 = vLLM's 4880/255). W2 lens 2 (MEASURED,
the arbiter): a controlled same-build nsys A/B REFUTES it — -use_fast_math makes flash
+21us/call SLOWER (168.8->189.8); the kernel is memory-latency-bound (ncu occ 8.3%,
register-limited), so the higher reg count (246->255) lowers occupancy, which dominates.
cuobjdump was necessary but NOT sufficient. So the flag is REVERTED (a CMakeLists NOTE
records why); no functional code ships. Real residual = occupancy/L2 (ncu: 38%
smem-scoreboard + 37% barrier stalls); vLLM-side ncu owed (box OOM-rebooted twice under
3-way contention). No default flip (no throughput win). Records + spec updated.

FOLLOWING_AGENTS_PROTOCOL
Assisted-by: Claude Code:claude-opus-4-8 [ClaudeCode]
mudler added a commit that referenced this pull request Aug 6, 2026
…IDENTICAL, gap is an irreducible-for-us ptxas SASS-quality difference; no lever (#75)

Runs the #69-owed ours-vs-vLLM flash decode ncu diff to a MEASURED verdict on an
idle box, and CORRECTS #69's "occupancy/L2-bound" framing.

W1 (matched-c8 ncu, both engines, grid 1x3x64, full section set):
- vLLM runs at the SAME 8.33% occupancy as ours — both smem-limited to 1 CTA/SM
  by the byte-identical 81.92 KB kSmemSize (the 216-vs-241 reg delta is moot).
- L2 hit ~1% on BOTH (KV streamed from DRAM, no adjacency/warmth to win),
  identical short-scoreboard stall structure. Only measured diff: +13% instr.
- The prior #69 "8.3% occupancy" was the batch-1 short-context num_splits=1
  kernel; the prior ncu pair was context-mismatched. Now measured on the real
  c8 kernel with everything else held identical.

W2 (mirror-first levers, all refuted DECISIVELY by building vLLM's exact recipe):
- arch-mirror (compute_80 PTX driver-JIT): neutral (no change from native).
- compute_80 + -use_fast_math (vLLM's EXACT recipe): reproduces vLLM's SASS
  profile EXACTLY (241 reg, 17,008 instr vs vLLM 241/17,020) and is STILL ~167us
  = the same as native ~165us and ~10us slower than vLLM's ~157us. So matching
  vLLM's arch, fast-math, register AND instruction count does NOT close the gap:
  the kernel is not instruction-bound; the residual is vLLM's wheel-ptxas
  SASS-scheduling quality, un-reachable from nvcc 13.0. (native+fast-math
  REGRESSES to 189.8us per #68; compute_80+fast-math is neutral.)

W3 VERDICT: MXFP4 stays BELOW-FLOOR at c2-c8 (binding unchanged
1.020/0.962/0.966/0.969). NO lever exists on our stack — occupancy, L2,
num_splits, reg count, instruction count, __launch_bounds__, arch-mirror and
fast-math ALL refuted by measurement. The flash term (+450us/step, ~40% of the
c8 residual) is an irreducible-for-us ptxas quality gap. NO default flip owed; no
functional code ships. CMakeLists NOTE + benchmark-record #75 record the closed
levers so they are not re-tried. No byte-exact razor / SACRED battery owed
(nothing shipped to gate).

check-fusion-consistency (minimax_h3_video_vae_device) is a pre-existing red,
untouched by this change.

FOLLOWING_AGENTS_PROTOCOL
Assisted-by: Claude Code:claude-opus-4-8 [ClaudeCode]
@mudler
mudler force-pushed the fix/h3-audio-duration-and-readme branch from f032e0b to ae47f25 Compare August 6, 2026 20:47
mudler added a commit that referenced this pull request Aug 7, 2026
…user docs

PR #68. A real bug found by RENDERING, not by the suite, plus the user-facing
documentation the H3 lane never had.

THE BUG. A 124-frame render silently muxed as 61 frames. `audio_t` is the
PER-CHANNEL latent length (the planner sets it from 40 Hz * duration) and the
packed layout carries `audio_t * audio_channel` ROWS, one per (channel, step).
The denormalize step divided by the channel count, so the decoded audio ran half
the video's duration; because the muxer passes `-shortest`, that silently
truncated the VIDEO to half its frames too. The fix is one line, and it now
agrees with the same function's own `target_audio_rows = request.audio_t *
request.audio_channel` a few lines above.

WHY THE SUITE WAS BLIND, which is the part worth keeping. Every existing gate
asserts shape SELF-CONSISTENCY, and a uniformly halved pipeline is perfectly
self-consistent: the shapes agreed with each other, they were just half as long
as the request asked for. ffprobe on the artifact exposed it. The gate added is
the one that is NOT self-consistency: the structural t2va e2e case now decodes
`audio_t` latent steps INDEPENDENTLY and requires the pipeline to have produced
exactly that many samples. RED-first: restoring the divide turns that case RED
(1 failed assertion), removing it goes GREEN. Suite 68/68, 46586 assertions.

A SECOND BUG FROM THE SAME SESSION, DROPPED RATHER THAN LANDED TWICE.
Conditioning PREPENDS rows to img_pos, so the denoise loop returns condition rows
followed by the targets, and unpatchify was handed the whole set. This branch
fixed it by carrying the layout's own `update_mask` out of the loop; main fixed
the SAME bug independently in row/H3-RENDER-CLOSE by taking the TRAILING
target_video_rows with a VT_CHECK on the row accounting. On rebase the
update_mask mechanism and its test were DROPPED: landing a second solution to a
fixed bug is churn, and the positional form already ships gated. The alternative
is recorded in .agents/state.md rather than silently lost.

DOCS. The video+audio generation path had no user-facing documentation at all,
and the multimodal INPUT interface was documented WRONG: `vllm-cli
--image/--video/--audio` does not exist, the CLI is text-only. Multimodal input
is served over the OpenAI API as `image_url`, `video_url` and
`input_audio`/`audio_url` content parts on /v1/chat/completions
(src/vllm/entrypoints/openai/chat_mm.cpp), which was documented NOWHERE, so
docs/USAGE.md now states it rather than the wrong claim merely being removed.
README gains a News section led by video+audio generation, paid for INSIDE the
30,000-char landing-page budget (29,997) rather than by raising it.

PUBLIC DOCS. The 19-line narrative this branch originally appended to
docs/BENCHMARKS.md was a non-canonical H2 section and pushed the page over its
prose budget: converted into three keyed ROWS (Thor render speed, render
duration, quantization floor), with the forensics moved verbatim into
.agents/benchmark-record.md, which is what that page's own checker instructs.

FOLLOWING_AGENTS_PROTOCOL
Assisted-by: Claude Opus 5 (1M context)
@mudler
mudler force-pushed the fix/h3-audio-duration-and-readme branch from ae47f25 to 8f4dd83 Compare August 7, 2026 03:28
@localai-bot localai-bot changed the title fix(minimax-h3): half of every video was being discarded, plus the missing user-facing docs fix(minimax-h3): half of every video was discarded, plus the missing user docs Aug 7, 2026
mudler added a commit that referenced this pull request Aug 7, 2026
…user docs

PR #68. A real bug found by RENDERING, not by the suite, plus the user-facing
documentation the H3 lane never had.

THE BUG. A 124-frame render silently muxed as 61 frames. `audio_t` is the
PER-CHANNEL latent length (the planner sets it from 40 Hz * duration) and the
packed layout carries `audio_t * audio_channel` ROWS, one per (channel, step).
The denormalize step divided by the channel count, so the decoded audio ran half
the video's duration; because the muxer passes `-shortest`, that silently
truncated the VIDEO to half its frames too. The fix is one line, and it now
agrees with the same function's own `target_audio_rows = request.audio_t *
request.audio_channel` a few lines above.

WHY THE SUITE WAS BLIND, which is the part worth keeping. Every existing gate
asserts shape SELF-CONSISTENCY, and a uniformly halved pipeline is perfectly
self-consistent: the shapes agreed with each other, they were just half as long
as the request asked for. ffprobe on the artifact exposed it. The gate added is
the one that is NOT self-consistency: the structural t2va e2e case now decodes
`audio_t` latent steps INDEPENDENTLY and requires the pipeline to have produced
exactly that many samples. RED-first: restoring the divide turns that case RED
(1 failed assertion), removing it goes GREEN. Suite 68/68, 46586 assertions.

A SECOND BUG FROM THE SAME SESSION, DROPPED RATHER THAN LANDED TWICE.
Conditioning PREPENDS rows to img_pos, so the denoise loop returns condition rows
followed by the targets, and unpatchify was handed the whole set. This branch
fixed it by carrying the layout's own `update_mask` out of the loop; main fixed
the SAME bug independently in row/H3-RENDER-CLOSE by taking the TRAILING
target_video_rows with a VT_CHECK on the row accounting. On rebase the
update_mask mechanism and its test were DROPPED: landing a second solution to a
fixed bug is churn, and the positional form already ships gated. The alternative
is recorded in .agents/state.md rather than silently lost.

DOCS. The video+audio generation path had no user-facing documentation at all,
and the multimodal INPUT interface was documented WRONG: `vllm-cli
--image/--video/--audio` does not exist, the CLI is text-only. Multimodal input
is served over the OpenAI API as `image_url`, `video_url` and
`input_audio`/`audio_url` content parts on /v1/chat/completions
(src/vllm/entrypoints/openai/chat_mm.cpp), which was documented NOWHERE, so
docs/USAGE.md now states it rather than the wrong claim merely being removed.
README gains a News section led by video+audio generation, paid for INSIDE the
30,000-char landing-page budget (29,997) rather than by raising it.

PUBLIC DOCS. The 19-line narrative this branch originally appended to
docs/BENCHMARKS.md was a non-canonical H2 section and pushed the page over its
prose budget: converted into three keyed ROWS (Thor render speed, render
duration, quantization floor), with the forensics moved verbatim into
.agents/benchmark-record.md, which is what that page's own checker instructs.

FOLLOWING_AGENTS_PROTOCOL
Assisted-by: Claude Opus 5 (1M context)
@mudler
mudler force-pushed the fix/h3-audio-duration-and-readme branch from 8f4dd83 to 7f44da1 Compare August 7, 2026 03:31
…user docs

PR #68. A real bug found by RENDERING, not by the suite, plus the user-facing
documentation the H3 lane never had.

THE BUG. A 124-frame render silently muxed as 61 frames. `audio_t` is the
PER-CHANNEL latent length (the planner sets it from 40 Hz * duration) and the
packed layout carries `audio_t * audio_channel` ROWS, one per (channel, step).
The denormalize step divided by the channel count, so the decoded audio ran half
the video's duration; because the muxer passes `-shortest`, that silently
truncated the VIDEO to half its frames too. The fix is one line, and it now
agrees with the same function's own `target_audio_rows = request.audio_t *
request.audio_channel` a few lines above.

WHY THE SUITE WAS BLIND, which is the part worth keeping. Every existing gate
asserts shape SELF-CONSISTENCY, and a uniformly halved pipeline is perfectly
self-consistent: the shapes agreed with each other, they were just half as long
as the request asked for. ffprobe on the artifact exposed it. The gate added is
the one that is NOT self-consistency: the structural t2va e2e case now decodes
`audio_t` latent steps INDEPENDENTLY and requires the pipeline to have produced
exactly that many samples. RED-first: restoring the divide turns that case RED
(1 failed assertion), removing it goes GREEN. Suite 69/69, 47416 assertions.

A SECOND BUG FROM THE SAME SESSION, DROPPED RATHER THAN LANDED TWICE.
Conditioning PREPENDS rows to img_pos, so the denoise loop returns condition rows
followed by the targets, and unpatchify was handed the whole set. This branch
fixed it by carrying the layout's own `update_mask` out of the loop; main fixed
the SAME bug independently in row/H3-RENDER-CLOSE by taking the TRAILING
target_video_rows with a VT_CHECK on the row accounting. On rebase the
update_mask mechanism and its test were DROPPED: landing a second solution to a
fixed bug is churn, and the positional form already ships gated. The alternative
is recorded in .agents/state.md rather than silently lost.

DOCS. The video+audio generation path had no user-facing documentation at all,
and the multimodal INPUT interface was documented WRONG: `vllm-cli
--image/--video/--audio` does not exist, the CLI is text-only. Multimodal input
is served over the OpenAI API as `image_url`, `video_url` and
`input_audio`/`audio_url` content parts on /v1/chat/completions
(src/vllm/entrypoints/openai/chat_mm.cpp), which was documented NOWHERE, so
docs/USAGE.md now states it rather than the wrong claim merely being removed.
README gains a News section led by video+audio generation, paid for INSIDE the
30,000-char landing-page budget rather than by raising it.

PUBLIC DOCS. The 19-line narrative this branch originally appended to
docs/BENCHMARKS.md was a non-canonical H2 section and pushed the page over its
prose budget: converted into three keyed ROWS (Thor render speed, render
duration, quantization floor), with the forensics moved verbatim into
.agents/benchmark-record.md, which is what that page's own checker instructs.

ALSO REPAIRS RECORD DAMAGE THAT LEFT check-agent-record RED ON MAIN. #93's
squash-merge removed 130 lines from specs/minimax-h3.md: its branch predated
#71/#92, so landing it DELETED sections 9 and 10 (the /v1/videos rows) and left
two orphaned table fragments with no heading and no separator. The checker
reports that as "table has 4 pipes; expected 3" plus "no linked spec names exact
stable token SERVE-VIDEOS-REFS", the engine-matrix row pointing at a section that
no longer existed. Restored sections 9 and 10 VERBATIM from 548b000 and cut the
orphans; #93's own content is preserved byte-for-byte.

FOLLOWING_AGENTS_PROTOCOL
Assisted-by: Claude Opus 5 (1M context)
@mudler
mudler force-pushed the fix/h3-audio-duration-and-readme branch from 7f44da1 to 10b4b62 Compare August 7, 2026 04:20
@localai-bot
localai-bot merged commit 6a69045 into main Aug 7, 2026
11 checks passed
@localai-bot
localai-bot deleted the fix/h3-audio-duration-and-readme branch August 7, 2026 05:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants