[None][feat] Wan 2.2 serve: extend video generation API#13783
Conversation
📝 WalkthroughWalkthroughThis PR adds MLPerf-deterministic video generation support via fixed latent tensors, extends the video generation API with diffusion controls (guidance_scale_2, boundary_ratio, num_frames), and introduces raw RGB binary video export. Changes flow through config, model pipeline, OpenAI protocol, server, and utilities layers. ChangesFixed Latent Support
Extended Diffusion Controls Protocol
Raw Video Export
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
tensorrt_llm/serve/openai_server.py (2)
1951-1957:⚠️ Potential issue | 🟠 Major | ⚡ Quick winHandle raw outputs with the correct content type.
resolve_video_format("raw")produces a.rgb.binfile, but this branch still maps everything except.mp4tovideo/x-msvideo. Foroutput_format="raw"the sync endpoint will stream arbitrary RGB bytes with an AVI media type.Suggested fix
actual_path = Path(actual_output_path) - media_type = "video/mp4" if actual_path.suffix == ".mp4" else "video/x-msvideo" + if actual_path.suffix == ".mp4": + media_type = "video/mp4" + elif actual_path.suffix == ".avi": + media_type = "video/x-msvideo" + else: + media_type = "application/octet-stream" return FileResponse( actual_output_path, media_type=media_type, filename=actual_path.name,🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tensorrt_llm/serve/openai_server.py` around lines 1951 - 1957, The current FileResponse branch always uses "video/x-msvideo" for non-.mp4 files, causing raw outputs like ".rgb.bin" (from resolve_video_format("raw")) to be served with the wrong MIME type; update the logic around actual_path / actual_output_path to detect raw output (e.g., check actual_path.suffix == ".rgb.bin" or the local variable output_format == "raw") and set media_type to a generic binary type such as "application/octet-stream" (or a more specific raw/RGB MIME if desired) before returning FileResponse so raw RGB byte streams are served with the correct content type.
1997-2000:⚠️ Potential issue | 🟠 Major | ⚡ Quick winParse
response_formatfrom multipart requests too.The new
response_format="video_path"mode is only honored for JSON bodies right now. Multipart requests never copyresponse_formatintodata, so they always fall back to the model default and cannot opt into the new path-returning behavior.Suggested fix
- for field in ["model", "size", "negative_prompt", "output_format"]: + for field in ["model", "size", "negative_prompt", "output_format", "response_format"]: if field in form and form[field]: data[field] = form[field]🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tensorrt_llm/serve/openai_server.py` around lines 1997 - 2000, The multipart parsing loop that copies optional string fields into data (for field in ["model", "size", "negative_prompt", "output_format"]) omits the new response_format option, so multipart requests never get data["response_format"]; update that loop to include "response_format" (or otherwise copy form["response_format"] into data when present) so the response_format="video_path" mode is honored for multipart requests; look for the variables form and data in openai_server.py to apply the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tensorrt_llm/_torch/visual_gen/models/wan/pipeline_wan.py`:
- Around line 483-495: The loaded fixed latent (in the _use_fixed_latent branch
where self.fixed_latent_path is used to set self.fixed_latent) must be validated
against the current expected latent shape (batch, channels, latent_frames,
latent_h, latent_w) before reuse; after torch.load and before assigning latents,
check self.fixed_latent.shape matches the computed target shape (derived from
the current batch size, self.latent_channels, computed latent_frames from
num_frames, and latent spatial dims from input resolution) and if it mismatches
either raise a clear ValueError indicating the expected vs actual shape or
optionally reshape/replicate the tensor only if a safe, documented
transformation is possible; update the logic around
self.fixed_latent.to(device=self.device, dtype=self.dtype) to run after
validation so you never pass an incompatible tensor into the denoiser/decoder
(refer to symbols: _use_fixed_latent, fixed_latent_path, fixed_latent, latents,
device, dtype).
In `@tensorrt_llm/serve/openai_server.py`:
- Around line 2013-2020: The multipart parsing currently treats form values by
truthiness and thus drops valid zero values (e.g., "0" or "0.0"); update the
checks for guidance_scale_2, boundary_ratio, guidance_rescale, and num_frames so
they only skip when the form key is missing or the value is an empty string (not
when the value is "0" or "0.0"), then parse and assign into the data dict as
before; locate the block in openai_server.py that sets data["guidance_scale_2"],
data["boundary_ratio"], data["guidance_rescale"], and data["num_frames"] and
replace the truthiness guards with explicit existence/empty-string checks before
casting.
---
Outside diff comments:
In `@tensorrt_llm/serve/openai_server.py`:
- Around line 1951-1957: The current FileResponse branch always uses
"video/x-msvideo" for non-.mp4 files, causing raw outputs like ".rgb.bin" (from
resolve_video_format("raw")) to be served with the wrong MIME type; update the
logic around actual_path / actual_output_path to detect raw output (e.g., check
actual_path.suffix == ".rgb.bin" or the local variable output_format == "raw")
and set media_type to a generic binary type such as "application/octet-stream"
(or a more specific raw/RGB MIME if desired) before returning FileResponse so
raw RGB byte streams are served with the correct content type.
- Around line 1997-2000: The multipart parsing loop that copies optional string
fields into data (for field in ["model", "size", "negative_prompt",
"output_format"]) omits the new response_format option, so multipart requests
never get data["response_format"]; update that loop to include "response_format"
(or otherwise copy form["response_format"] into data when present) so the
response_format="video_path" mode is honored for multipart requests; look for
the variables form and data in openai_server.py to apply the change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 98e8c215-c95f-4c65-9382-a6fe519d8698
📒 Files selected for processing (6)
tensorrt_llm/_torch/visual_gen/config.pytensorrt_llm/_torch/visual_gen/models/wan/pipeline_wan.pytensorrt_llm/serve/media_storage.pytensorrt_llm/serve/openai_protocol.pytensorrt_llm/serve/openai_server.pytensorrt_llm/serve/visual_gen_utils.py
|
@wu6u3tw Can you clarify the motivation of this change? We are refactoring the Python APIs, once we have a "beta" python API, we plan to get back to the serving endpoint changes. |
|
@zhenhuaw-me |
|
@zhenhuaw-me Let's sync offline for the api change plan. |
a460b90 to
65ed452
Compare
65ed452 to
43ac610
Compare
…terministic + filesystem-shared deployments Adds four serve-side capabilities to the Wan 2.2 video generation pipeline. They are independent extensions to existing public-facing schemas (no internal API changes). 1. Extra Wan 2.2 generation knobs in VideoGenerationRequest: - guidance_scale_2 (second CFG scale for two-stage denoising) - boundary_ratio (timestep ratio for switching guidance scales) - num_frames (overrides implicit int(seconds * fps)) guidance_scale_2 / boundary_ratio are routed through extra_params to match pipeline_wan.WanPipeline.infer's extra.get(...) reads. Also defaults guidance_scale_2 to 3.0 on Wan 2.2 A14B when unset (was = guidance_scale, which is wrong for two-stage CFG). 2. fixed_latent_path: VisualGenArgs option to load a pre-computed latent tensor from disk and substitute it for sampled latents at request time. Used for MLPerf-deterministic generation across runs. Lazy-loaded on first inference (pipeline construction runs inside MetaInitMode, where torch.load on a meta tensor would raise). Mirrors the existing extra_attrs propagation pattern used by spatial_upsampler_path / distilled_lora_path. Warmup passes _use_fixed_latent=False so the multi-shape warmup grid is not forced through the fixed_latent shape. 3. response_format = "video_path": when set, the server returns JSON with the on-disk path to the generated video instead of streaming bytes back. Useful when client and server share a filesystem (e.g. Lustre) - avoids holding the HTTP connection open through encode + transfer of multi-MB payloads, and avoids the per-replica streaming-tail effect that bytes mode exhibits at high concurrency. Backward-compatible: default is "video_bytes". 4. output_format = "raw": uncompressed uint8 RGB byte dump (T*H*W*3 layout, .rgb.bin extension). No ffmpeg dependency, no encode latency. Worst-case payload ~224 MB at 720x1280x81. Backward-compatible: default remains "auto". Files touched ------------- * tensorrt_llm/serve/openai_protocol.py * tensorrt_llm/serve/openai_server.py * tensorrt_llm/serve/visual_gen_utils.py * tensorrt_llm/serve/media_storage.py * tensorrt_llm/_torch/visual_gen/config.py * tensorrt_llm/_torch/visual_gen/models/wan/pipeline_wan.py Signed-off-by: Tin-Yin Lai <tinyinl@nvidia.com>
Signed-off-by: Tin-Yin Lai <tinyinl@nvidia.com>
249bdc9 to
8e0a070
Compare
… (.pt) Per review feedback on NVIDIA#13783: swap output_format='raw' (uint8 RGB byte stream, .rgb.bin) for output_format='pt' (torch.save of the video tensor, .pt). The .pt format is symmetric with fixed_latent_path's torch.load consumer and makes cross-framework comparisons trivial. Drops the dtype-cast-to- uint8 step since torch.save preserves the original tensor. Signed-off-by: Tin-Yin Lai <tinyinl@nvidia.com>
…_routes Three independent fixes on the new multipart/JSON video-generation path, per CodeRabbit feedback on PR NVIDIA#13783: 1. response_format on multipart: add 'response_format' to the optional string-field copy loop so multipart clients can opt into the new response_format='video_path' mode. Without this it was silently dropped and the request fell back to the model default. 2. Numeric zero values: replace truthiness guards with form.get(...) not in (None, '') for the three PR-added numeric fields (guidance_scale_2, boundary_ratio, num_frames). boundary_ratio explicitly allows 0.0 per its Pydantic constraint (ge=0.0, le=1.0); the old guard silently dropped a valid request. The pre-existing pattern for older fields (seconds/fps/guidance_scale) is left alone to keep the diff surgical. 3. media_type for .pt: when output_format='pt' and response_format='video_bytes', the FileResponse was returning the tensor with 'video/x-msvideo'. Map .pt to application/octet-stream. Signed-off-by: Tin-Yin Lai <tinyinl@nvidia.com>
Per CodeRabbit review on PR NVIDIA#13783: the fixed_latent tensor is loaded once and reused across requests, but each request may carry a different batch_size / num_frames / resolution. Without an upfront shape check, a mismatch surfaces deep inside denoising or decoding with an unactionable error. Compare the loaded tensor against the same shape formula _prepare_latents uses (in_channels, (num_frames-1)/temporal_scale + 1, spatial dims) and raise a ValueError pointing at the latent path so operators know to regenerate it. Signed-off-by: Tin-Yin Lai <tinyinl@nvidia.com>
|
Fixed based on the reviews. |
…e for fixed_latent_path Allows per-run override of the MLPerf deterministic fixed latent path without editing YAML configs. Env var takes precedence over VisualGenArgs.fixed_latent_path; both still work, addressing reviewer feedback that the path may be used as a debug/benchmark knob. Signed-off-by: Tin-Yin Lai <tinyinl@nvidia.com>
Make the VideoGenerationRequest.num_frames docstring explicit that when set, num_frames takes precedence over both seconds and fps (matches reviewer's request that the override behavior be documented). Signed-off-by: Tin-Yin Lai <tinyinl@nvidia.com>
…ssignment Single-line assignment satisfies the ruff-format hook that flagged the multi-line form added in a42d32d. Signed-off-by: Tin-Yin Lai <tinyinl@nvidia.com>
|
@zhenhuaw-me friendly ping — when you have a moment, could you take another look? Since your last round of feedback, I've pushed:
All CI checks are green. Thanks! |
chang-l
left a comment
There was a problem hiding this comment.
Approve to unblock — please also wait for @zhenhuaw-me to sign off as well
…-var only Per reviewer feedback, the fixed-latent path is purely a debug/benchmark knob for MLPerf-deterministic generation and does not belong on the public VisualGenArgs config surface. Remove the field and its extra_attrs propagation; the WAN pipeline now reads the path solely from the TLLM_MLPERF_FIXED_LATENT_PATH environment variable. Signed-off-by: Tin-Yin Lai <tinyinl@nvidia.com>
|
@zhenhuaw-me I am wondering in you API change, is the extra param wired into your new API? |
|
Covered in other PRs |
Description
Adds four serve-side capabilities to the Wan 2.2 video generation pipeline.
All are independent, backward-compatible additive extensions to existing
public-facing schemas (no internal API changes).
VideoGenerationRequest knobs:
guidance_scale_2,boundary_ratio,num_frames.guidance_scale_2/boundary_ratioare routed throughextra_paramsto matchWanPipeline.infer'sextra.get(...)reads.Also defaults
guidance_scale_2 = 3.0on Wan 2.2 A14B when unset(was
= guidance_scale, which is wrong for two-stage CFG).fixed_latent_path:VisualGenArgsoption to load a pre-computedlatent tensor and substitute it for sampled latents at request time —
used for MLPerf-deterministic generation. Lazy-loaded on first
inference (pipeline construction runs inside
MetaInitMode, wheretorch.loadon a meta tensor would raise). Mirrors the existingextra_attrspattern used byspatial_upsampler_path/distilled_lora_path. Warmup passes_use_fixed_latent=False.response_format = "video_path": server returns JSON with theon-disk path instead of streaming bytes — for filesystem-shared
deployments (e.g. Lustre). Avoids holding HTTP open through encode
"video_bytes".output_format = "raw": uncompressed uint8 RGB byte dump(T·H·W·3 layout,
.rgb.bin). No ffmpeg dependency, no encodelatency. Worst-case payload ~224 MB at 720x1280x81. Default remains
"auto".Test Coverage
tests/unittest/_torch/visual_gen/test_wan22_t2v_pipeline.pyexercises the WAN pipeline path; new fields are additive and do not
change existing test behavior.
trtllm-serve <Wan2.2-T2V-A14B-Diffusers>thenPOST
VideoGenerationRequestwith each new field combination.PR Checklist
Please review the following before submitting your PR:
PR description clearly explains what and why.
PR Follows TRT-LLM CODING GUIDELINES.
Test cases provided for new code paths (additive backward-compatible fields; no new code paths require dedicated tests beyond manual server verification).
Any new dependencies have been scanned for license and vulnerabilities (none added).
CODEOWNERS updated if ownership changes (no ownership change).
Documentation updated as needed.
Update tava architecture diagram if there is a significant design change in PR (n/a).
The reviewers assigned automatically/manually are appropriate for the PR.
Please check this after reviewing the above items as appropriate for this PR.
GitHub Bot Help
To see a list of available CI bot commands, please comment
/bot help.