feat(videos): reference conditioning over /v1/videos, image and clip and voice - #92
Merged
Conversation
…and voice Row `SERVE-VIDEOS-REFS`, branch `row/SERVE-VIDEOS-REFS`, stacked on `row/SERVE-VIDEOS-OAI`. That row made an OpenAI client's body PARSE; this one makes its REFERENCES do something. Before it, no reference modality was reachable over HTTP at all: an image-to-video request generated silently from the prompt alone. `input_reference` -> fl2va, NOT ref2va. OpenAI documents it as the image the generated video STARTS FROM, which is exactly what fl2va expresses: MiniMaxH3EncodeKeyframeCondRows pins frame 0 OF THE OUTPUT to the supplied image. MiniMaxH3EncodeReferenceImages means something else entirely, whole reference images PREPENDED as their own blocks (subject or style guidance that never becomes a frame), so mapping input_reference there would have silently changed what the API promises. The source is a filesystem path or an RFC 2397 data: URL, decoded by the SAME DecodeDataUri the chat multimodal parts use rather than a second, subtly different decoder; an http(s) URL is refused BY NAME rather than stat-ed as a path and failing later with a confusing message. The two modalities OpenAI has no slot for ride `metadata`. H3 supports three (image, silent video, audio) and the Sora schema carries one, so the other two enter through the standard free-form string map every strict client already tolerates, rather than invented top-level fields that would fail a client's schema validation. metadata.input_reference_video is a DIRECTORY of frame_%06d.ppm (the exact layout minimax-h3-gen and this server write, so clips chain) because no demuxer is vendored; metadata.input_reference_audio is a 16-bit PCM WAV path or data: URL. A video reference is SILENT, by construction and stated: MiniMaxH3EncodeReferenceVideo emits ref_audio_t == 0. An audio reference supplied alongside ATTACHES to that same kVideoAudio block, the layout packed_sequence.py builds; alone it is its own block. Legality is enforced in the PARSER, not left to the pipeline. The rule is the pipeline's own (minimax_h3_pipeline.cpp:251: fl2va keyframes and ref2va blocks are EXCLUSIVE), so an illegal pair is a 400 naming it rather than a failed job, and never a silently dropped reference, which is the failure that looks like it worked. Two refusals up front rather than deep in the denoise: a reference image must be a binary PPM (P6), since no PNG/JPEG codec is vendored (the same NAMED residual the chat multimodal path carries), and it must already be at the resolved output geometry, since no resampler is vendored. Both VAE encoder halves load LAZILY and ONCE each under one mutex. GATE (CPU, foreground): test_video_api 14/14 (167 assertions), test_openai_api_server 41/41 (525), `server` builds clean. Each modality is gated as ARRIVING at the runner, and an illegal pair is a 400 that generates nothing. FOLLOWING_AGENTS_PROTOCOL Assisted-by: Claude Opus 5 (1M context)
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 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)
localai-bot
added a commit
that referenced
this pull request
Aug 7, 2026
…user docs (#68) 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) Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Row
SERVE-VIDEOS-REFS, stacked onSERVE-VIDEOS-OAI(#71, merged). That row made an OpenAI client's body parse; this one makes its references do something. Before it, no reference modality was reachable over HTTP at all: an image-to-video request generated silently from the prompt alone.This is the second half of the #71 split (the combined change was 1241 non-exempt lines, over the 900
check-pr-size.pycap). 552 non-exempt lines here.input_referencemaps to fl2va, not ref2vaOpenAI documents it as the image the generated video starts from, which is exactly what fl2va expresses:
MiniMaxH3EncodeKeyframeCondRowspins frame 0 of the output to the supplied image.MiniMaxH3EncodeReferenceImagesmeans something else entirely, whole reference images prepended as their own blocks (subject or style guidance that never becomes a frame), so mappinginput_referencethere would have silently changed what the API promises.The source is a filesystem path or an RFC 2397
data:URL, decoded by the sameDecodeDataUrithe chat multimodal parts use rather than a second, subtly different decoder. Anhttp(s)URL is refused by name rather than stat-ed as a path and failing later with a confusing message.The two modalities OpenAI has no slot for ride
metadataH3 supports three (image, silent video, audio) and the Sora schema carries one, so the other two enter through the standard free-form string map every strict client already tolerates, rather than invented top-level fields that would fail a client's schema validation. The whole map is kept verbatim; two keys are lifted into typed fields.
metadata.input_reference_videois a directory offrame_%06d.ppm, the exact layoutminimax-h3-genand this server write, so one run's frames chain straight into the next request. It is not a container: no demuxer is vendored, and adata:URL cannot name a directory.metadata.input_reference_audiois a 16-bit PCM WAV path ordata:URL.A video reference is silent, by construction and stated.
MiniMaxH3EncodeReferenceVideoemitsref_audio_t == 0. An audio reference supplied alongside attaches to that samekVideoAudioblock, the layoutpacked_sequence.pybuilds; alone it is its own block.Legality is enforced in the parser
The rule is the pipeline's own (
minimax_h3_pipeline.cpp:251: fl2va keyframes and ref2va blocks are exclusive), so an illegal pair is a 400 naming it rather than a failed job, and never a silently dropped reference, which is the failure that looks like it worked.Two refusals up front rather than deep in the denoise: a reference image must be a binary PPM (P6), since no PNG/JPEG codec is vendored (the same named residual the chat multimodal path carries), and it must already be at the resolved output geometry, since no resampler is vendored. Both VAE encoder halves load lazily and once each under one mutex.
Gate (CPU, foreground)
test_video_api14/14, 167 assertionstest_openai_api_server41/41, 525 assertionsserverbuilds cleancalls == 0).FOLLOWING_AGENTS_PROTOCOL