Add TensorRT-LLM distilled 4-step guide - #353
Conversation
Adds the TensorRT-LLM walkthrough for the two published DMD2-distilled Cosmos3-Super students, Cosmos3-Super-Text2Image-4Step and Cosmos3-Super-Image2Video-4Step, each serving on a single GPU. A distilled checkpoint owns its sampling: its scheduler config locks the schedule to four stochastic steps and classifier-free guidance is baked into the weights. The requests therefore omit num_inference_steps and guidance_scale entirely and let the server fill both in from the checkpoint, and leave use_system_prompt unset so the image-to-video student's declared default_use_system_prompt applies. Signed-off-by: Igor Shovkun <ishovkun@nvidia.com>
…mpts The shared audiovisual negative prompts contain two substrings that the Cosmos guardrail blocklist censors, so every request carrying them is rejected with HTTP 500 "Text guardrail blocked prompt" before any denoising runs: "color bleeding between elements" -> blocklist censors "bleeding" "The scene feels lifeless and ..." -> blocklist censors "lifeless" Neither describes anything the blocklist is meant to catch; both are ordinary image-artifact vocabulary. Rephrase without changing what the negative prompt asks the model to avoid. "colors improperly mixing" matches the wording already used for the same substring in the transfer negative prompt. Verified against cosmos_guardrail 0.3.0's Blocklist: both files now report safe, as do the text2image and image2video positive prompts used by the distilled cookbook. Signed-off-by: Igor Shovkun <ishovkun@nvidia.com>
ConstBob
left a comment
There was a problem hiding this comment.
Thanks. The distilled 4-step request contract matches TensorRT-LLM (omit num_inference_steps / guidance_scale, reject mismatches, leave use_system_prompt unset; T2I as 1024x1024 one-frame /v1/videos/generations, I2V as 720p x 189 with no config yaml).
Static checks on 0e1a27a: notebook JSON, unique cell IDs, empty outputs, Python parse, assets (robot_draping, car_driving), and the shared AV negative prompts no longer contain bleeding / lifeless.
Did not re-run GPU inference (author already executed both sections as written on 1x B300).
Nits, non-blocking:
transfer/assets/negative_prompt.jsonstill has both trigger words on this base; called out in the PR.- #354 edits the same two audiovisual
neg_prompt.jsonfiles, so the later merge may need a trivial conflict resolve.
LGTM.
Review follow-up. The previous commit invented replacement wording for the two blocklisted substrings. TensorRT-LLM already ships a corrected copy of this exact prompt as examples/visual_gen/models/cosmos3/cosmos3_negative_prompt.json, and that is the default every Cosmos3 request falls back to when it sends no negative prompt of its own. Upstream's copy and the cookbook's differ in exactly four leaves, and all four are the two blocklisted substrings. Take upstream's wording rather than a local invention, so the cookbook asset and the default the model is served with cannot drift. Both files are now identical to the TensorRT-LLM default. Verified against cosmos_guardrail 0.3.0's Blocklist: "bleeding" and "lifeless" are present in NVIDIA's blocklist/exact_match word sets while "smearing" and "inert" are not, and both files report safe. Signed-off-by: Igor Shovkun <ishovkun@nvidia.com>
skasetty-Siri
left a comment
There was a problem hiding this comment.
Please fix the default TensorRT-LLM root in run_distilled_with_trt_llm.ipynb, in the “Start the Server” section. The instructions say to run from the TensorRT-LLM checkout, but ${TRTLLM_ROOT:-$PWD/TensorRT-LLM} adds another TensorRT-LLM directory when the variable is unset. The config file cannot be found.
Please use ${TRTLLM_ROOT:-$PWD} for the stated working directory, or update the instructions consistently, and verify from a fresh shell with TRTLLM_ROOT unset.
This path issue was reproduced during assisted review. GPU inference was not independently rerun.
Review follow-up. Both the distilled notebook and the shared setup guide tell you to run from inside the TensorRT-LLM checkout, then default TRTLLM_ROOT to "$PWD/TensorRT-LLM". From inside the checkout that resolves to <checkout>/TensorRT-LLM, so every --visual_gen_args path pointed at a directory that does not exist and trtllm-serve could not find the config. Default to "$PWD" instead, state the expected working directory explicitly, and check the path up front: trtllm-serve only reports a bad --visual_gen_args after it has started, which is a poor place to learn the root was wrong. This also repairs the pre-existing Nano and Super serve commands in the setup guide, which resolved their configs the same broken way. Verified from a fresh shell with TRTLLM_ROOT unset by extracting the blocks from the shipped files: all four referenced config paths resolve with the checkout as the working directory, and the new check fires from any other directory. Signed-off-by: Igor Shovkun <ishovkun@nvidia.com>
run_with_trt_llm.ipynb carries the same broken default this PR already fixed elsewhere: it defaults TRTLLM_ROOT to "$PWD/TensorRT-LLM" in both the Nano and Super serve blocks, so the --visual_gen_args config path does not resolve. It was also self-contradictory about where to stand: the section opens with "run the commands below from that checkout" and then says "From the repository root", and only the latter makes the old default correct. Settle on the checkout, matching the other two files. Three lines, no additions, to keep the overlap with NVIDIA#354 small. Verified from a fresh shell with TRTLLM_ROOT unset: the Nano and Super config paths both resolve with the checkout as the working directory. Signed-off-by: Igor Shovkun <ishovkun@nvidia.com>
|
@skasetty-Siri re: your review — confirmed, and reproduced before fixing. Inside checkout, Origin: copied from Took your first option,
Same default was wrong in two more places. Both fixed:
No Verified as asked. Fresh shell,
The fix also adds a guard so a wrong root is caught before trtllm-serve starts, rather than surfacing mid-startup. |
skasetty-Siri
left a comment
There was a problem hiding this comment.
Rechecked 314f856. The startup-path finding is resolved in the distilled notebook, shared setup, and base audiovisual notebook. Assisted offline checks and latest notebook CI passed. No remaining blocking findings from my review. GPU inference was not independently rerun.
MaciejBalaNV
left a comment
There was a problem hiding this comment.
Approving after a check from @skasetty-Siri
…uide Two content conflicts, both add/add in adjacent prose: - README.md: keep the Edge-scoped TensorRT-LLM index row and add NVIDIA#353's new distilled 4-step row beneath it. - cookbooks/cosmos3/README.md: chain the TensorRT-LLM PR references so the sentence now cites V2V (#16155), the distilled checkpoints (#16563, #16690), and Edge (#16773); place the Cosmos3-Edge launch block after Super and before the two distilled students, matching the order the vLLM-Omni section already uses. Everything else auto-merged. Verified on the merged tree: - run_with_trt_llm.ipynb parses, has 106 cells with unique ids and no syntax errors, and the three Edge cases produce byte-identical request bodies to the pre-merge branch (t2i via /v1/images/generations with output_type="image" at 640x640; t2v and i2v via /v1/videos/generations at 832x480 x 121 frames). - NVIDIA#353's negative-prompt rewording is identical to 0407123, and both files still serialize byte-for-byte to TensorRT-LLM's COSMOS3_VIDEO_NEGATIVE_PROMPT (14674 bytes). Signed-off-by: Igor Shovkun <ishovkun@nvidia.com>
## Summary Add a Cosmos3-Edge section to the TensorRT-LLM audiovisual cookbook, covering exactly what TensorRT-LLM `main` supports for that checkpoint. - add Edge text-to-image, text-to-video, and image-to-video walkthroughs to `run_with_trt_llm.ipynb`, with the Edge endpoint, launch command, and per-case request shape - send Edge's 480p-native contract explicitly: 832x480 with 121 frames, 50 UniPC steps, guidance 5.0 for video, and Edge's native 640x640 with guidance 4.0 for text-to-image. Flow shift (3.0) rides the checkpoint-declared native flow schedule, so requests do not send it - run Edge text-to-image in native image mode against `/v1/images/generations` (see below) - document the Edge launch and request contract in the audiovisual README and the shared Cosmos3 environment setup guide, and note the new section in the root notebook index - realign the shared audiovisual negative prompt with the TensorRT-LLM default (separate commit, details below) ## What TensorRT-LLM head supports for Edge Checked against `main` at `8cfa341332`. Edge support landed in [TensorRT-LLM #16773](NVIDIA/TensorRT-LLM#16773). | Mode | Edge on TensorRT-LLM | Basis | | --- | --- | --- | | Text-to-image | supported | `COSMOS3_EDGE_T2I_PARAMS` (640x640, 50 steps, guidance 4.0), via `/v1/images/generations` with `output_type="image"` | | Text-to-video | supported | `COSMOS3_EDGE_VIDEO_PARAMS` (832x480, 121 frames, 50 steps, guidance 5.0, shift 3.0) | | Image-to-video | supported | same video defaults; reference image as multipart `input_reference` | | Synchronized audio | not supported | Edge has no audio tower | | Video-to-video | not supported | V2V is validated for Nano/Super only | | Action (forward/inverse dynamics, policy) | not supported | the VisualGen pipeline has no action path on `main`, and the Edge checkpoint's action weights are explicitly unsupported | | Transfer controls | not supported | no transfer/control path exists in TensorRT-LLM `main` | | Reasoning | not supported | measured: `POST /v1/chat/completions` returns 404 against a live Edge server, and the served OpenAPI exposes only `/health`, `/metrics`, `/v1/images/*`, `/v1/videos/*`, `/v1/models`, `/version` | Edge is served with no config override — its generation defaults come from the checkpoint. That is also the invocation the upstream `test_cosmos3_edge_i2v_example` integration test exercises. For comparison, Edge coverage across the backends already in this repo: Cosmos Framework, Diffusers, and vLLM-Omni ship image-to-video; SGLang ships text-to-image, text-to-video, and image-to-video. This PR brings TensorRT-LLM to the same three modes. ## Text-to-image runs in native image mode Raised in review. The notebook's existing pattern expresses text-to-image as a one-frame video request. At `8cfa341332` that selects **video** mode, because `extra_params.output_type` defaults to `"video"`, and the pipeline keys its default negative prompt off that: ```python def default_negative_prompt(output_type: str) -> str: return (COSMOS3_DEFAULT_NEGATIVE_PROMPT if output_type == "image" else default_video_negative_prompt()) ``` The example sends no negative prompt of its own, so a still image was being steered by Cosmos3's **video** negative prompt — "Incoherent motion", "visible frame-to-frame discontinuities", "Movement appears as a slideshow" — none of which mean anything for a single frame. The Edge case now posts to `/v1/images/generations` with `output_type="image"` and decodes the base64 PNG, keeping 640x640, 50 steps, and guidance 4.0. Both halves are required: `parse_visual_gen_params` does not set `output_type` for image requests either, so hitting the image endpoint without the flag runs video mode, leaves `output.image` as `None`, and returns 500. For Edge this is the only generation-semantics change — `guidance_interval` is `None` in both `COSMOS3_EDGE_VIDEO_PARAMS` and `COSMOS3_EDGE_T2I_PARAMS`. **`t2i_nano` and `t2i_super` hit the same default and are deliberately left alone here.** They are the `qwen3` family, whose image-mode table also flips `guidance_interval` from `None` to `(400, 1000)`, so switching them changes their output in a way this PR cannot validate (Super needs four GPUs). Worth a follow-up. ## The negative-prompt commit **Update after merging `main`:** #353 landed the byte-identical rewording independently, so this change no longer appears in the diff against `main`. Commit `0407123` remains in the branch history and the explanation below still describes why it was needed at the time. A prerequisite for the Edge examples to run, but not Edge-specific — it fixes the Nano and Super video cases in the same notebook. TensorRT-LLM runs the text guardrail over the **negative** prompt as well as the positive one. The checked-in audiovisual negative prompt had drifted from the reference default in two places — `color bleeding between elements` and `the scene feels lifeless and sterile` — and the Cosmos guardrail blocklist matches `bleeding` and `lifeless`. Every video request carrying this asset therefore failed with HTTP 500 (`Text guardrail blocked prompt`) under the documented default of guardrails enabled. Text-to-image was unaffected only because the payload builder attaches no negative prompt for that mode. [TensorRT-LLM #17523](NVIDIA/TensorRT-LLM#17523) already reworded those exact phrases to `color smearing` and `feels inert` in TensorRT-LLM's own default. The cookbook ships its own copy and sends it explicitly as `negative_prompt`, so the corrected default never applied. This commit adopts the upstream wording; the serialized request body each file produces is now byte-for-byte identical to `COSMOS3_VIDEO_NEGATIVE_PROMPT` (14674 bytes, key order included). `cookbooks/cosmos3/generator/transfer/assets/negative_prompt.json` carries the same drift and is left alone — #310 already covers it. ## Validation Real Edge server, real generations, guardrails enabled throughout. **Environment.** 1x NVIDIA B200, x86_64. TensorRT-LLM built from source at `8cfa341332` (reports `1.3.0rc25+8cfa341332`). `cosmos_guardrail==0.3.0` with the pinned `nvidia/Cosmos-1.0-Guardrail` revision `cf03c0395fac8c4de386c0bdab12cc4fc8d66362`. `ffmpeg` 7.0.2 on `PATH`, so video responses take the MP4 encoder rather than the AVI fallback. The checkpoint is a local copy of `nvidia/Cosmos3-Edge`, served by path. **Server.** `trtllm-serve <Cosmos3-Edge> --port 8000`, no `--visual_gen_args`, exactly as the new section documents. **Execution.** The notebook's Edge cells were run verbatim through `jupyter nbconvert --to notebook --execute` from a checkout of this branch, so `find_repo_root`, the repo-relative asset paths, `create_payload`, `check_trtllm_server`, `run_trtllm_payload`, and `view_run` all ran as a reader hits them. `nbconvert` exited 0 with every cell executed. **Guardrail preflight.** All five prompt/negative assets the Edge cases send pass `CosmosSafetyChecker.check_text_safety`. **Endpoints actually served**, from the server log: ``` 1 "POST /v1/images/generations HTTP/1.1" 200 OK <- t2i_edge 2 "POST /v1/videos/generations HTTP/1.1" 200 OK <- t2v_edge, i2v_edge ``` **Generation parameters the server resolved**, from the server log (warmup line omitted): ``` Cosmos3 generation dims: 640x640 (WxH), num_frames=1, num_inference_steps=50, guidance_scale=4.00 Cosmos3 generation dims: 832x480 (WxH), num_frames=121, num_inference_steps=50, guidance_scale=5.00 Cosmos3 generation dims: 832x480 (WxH), num_frames=121, num_inference_steps=50, guidance_scale=5.00 ``` **Results**, decoded from the returned payloads: | Case | Decoded shape | FPS | Size | | --- | --- | --- | --- | | `t2i_edge` | `(640, 640, 3)` PNG | n/a | 514 KB | | `t2v_edge` | `(121, 480, 832, 3)` | 24 | 510 KB | | `i2v_edge` | `(121, 480, 832, 3)` | 24 | 1952 KB | Every case matches the documented contract. The run logged zero model-card envelope advisories; the one previously emitted came from the one-frame video text-to-image request and disappears in native image mode. Outputs were inspected visually as well as by shape. As a cross-check the same three cases were also run through cosmos-framework (`3845f4c`) at matched settings — same prompts, negative prompts, seed, sampler, steps and guidance. Shapes agree on all three, and after the image-mode fix the text-to-image results agree in composition as well, which they did not before: cosmos-framework already sends an empty negative prompt for text-to-image, so it was the TensorRT-LLM video-mode default that diverged. --------- Signed-off-by: Igor Shovkun <ishovkun@nvidia.com>
Summary
nvidia/Cosmos3-Super-Text2Image-4Step(text-to-image) andnvidia/Cosmos3-Super-Image2Video-4Step(image-to-video), each on a single GPUnum_inference_stepsandguidance_scaleentirely and the server fills both in; a conflicting value is rejected, not clampeduse_system_promptunset in every request so the image-to-video student's checkpoint-declareddefault_use_system_prompt: trueappliestrtllm-servelaunch commands to the shared setup guide, and index the notebook from the root READMEThis is the follow-up #310 deferred ("keep the four-step distilled T2I/I2V examples out of this PR pending separate output-quality follow-up"). The examples match the assets every other backend already uses for its distilled sections —
robot_drapingfor text-to-image andcar_drivingfor image-to-video — so the cookbook surface is consistent across Cosmos Framework, Diffusers, vLLM-Omni, SGLang, and now TensorRT-LLM.The shipped scope is text-to-image and image-to-video only. These students are task-specialized and no other backend advertises text-to-video, video-to-video, or synchronized audio for them either.
The guardrail fix, and why it is in this PR
The shared audiovisual negative prompts contained two substrings that the
cosmos_guardrail0.3.0Blocklistcensors. Any request carrying them fails with HTTP 500 andText guardrail blocked promptbefore any denoising runs:color bleeding between elementsbleedingcolor smearing between elementsThe scene feels lifeless and sterilelifelessThe scene feels inert and sterileNeither describes anything the blocklist is meant to catch; both are ordinary image-artifact vocabulary.
The replacement wording is not invented here. TensorRT-LLM already ships a corrected copy of this exact prompt as
examples/visual_gen/models/cosmos3/cosmos3_negative_prompt.json, and that is the default every Cosmos3 request falls back to when it sends no negative prompt of its own. Upstream's copy and the cookbook's differ in exactly four leaves, and all four are these two substrings — so the cookbook asset is simply stale. Both files are now identical to the TensorRT-LLM default. Kept as separate commits so they can be split out if preferred.Text-to-image was unaffected because that path sends no negative prompt — which is why only the image-to-video example hit it.
Three related issues found while diagnosing this, not changed here:
assets/prompts/image2video/humanoid_robot.jsonis blocked by a different rule (partial match, not the word blocklist). That is the prompt the mergedrun_with_trt_llm.ipynbuses fori2v_nanoandi2v_super. Left alone deliberately: rewording a positive prompt changes generated content.transfer/assets/negative_prompt.jsoncarries the same two substrings. That file belongs to Add TensorRT-LLM Transfer and Action guides #310, which now rewords both, so it is covered there rather than duplicated here.Validation
GPU inference
Both notebook sections were executed as written with
jupyter nbconvert --to notebook --executeagainst realtrtllm-serveVisualGen servers — the shipped notebook's own cells, not a re-implementation. TensorRT-LLM at505a02ce04, whosetensorrt_llm/_torch/visual_gen,tensorrt_llm/visual_genandexamples/visual_gentrees are byte-identical tomain(2dbab44d4f); the only delta is test files. Hardware: 1 x NVIDIA B300.Text-to-image —
Cosmos3-Super-Text2Image-4Stepwithconfigs/cosmos3-t2i-1gpu.yaml:Artifact: 57,617 bytes, decoded with PyAV as H.264 1024x1024, 1 frame.
Image-to-video —
Cosmos3-Super-Image2Video-4Step, no config file:Artifact: 4,890,310 bytes, decoded as H.264 1280x720, 24 fps, 189 frames. The warmup shape confirms the claim that this checkpoint deploys at the default omni shape and needs no config file.
Negative control (both servers): a request that sets
guidance_scale=6.0is rejected rather than clamped —Guardrail fix verified directly against
cosmos_guardrail0.3.0'sBlocklist: both reworded negative prompts and both positive prompts used by the notebook now reportsafe=True. The trigger words were located by diffing the censored output against the original, not guessed. Re-verified after adopting upstream's wording:bleedingandlifelessare present in NVIDIA'sblocklist/exact_matchword sets (383 and 1430 entries) whilesmearingandinertare not.Visual review: the text-to-image frame renders the prompt's robot-arm-draping-satin scene coherently. The image-to-video clip follows its
temporal_caption— coastal dashcam view, rockfall beginning a few seconds in, boulders and dust building until the road is partially blocked — with no flicker or geometry snapping across the 189 frames.Local validation
nbformat==5.11.1(the CI gate)ruff==0.16.4 check --select=E9,F63,F7,F82(the CI gate)json.toolgit diff --checkSource contract audit
The request contract is read from TensorRT-LLM
main(tensorrt_llm/_torch/visual_gen/models/cosmos3/sampling.py):load_schedulerselectsFlowMatchEulerDiscreteSchedulerfor distilled checkpoints andUniPCMultistepSchedulerfor base ones; an unknown declaration is a load-time errorCosmos3SamplingPolicy.validate_requestrejects anynum_inference_stepsother thanlen(t_list)and anyguidance_scaleother than1.0generation_default_overridessupplies both values when the request leaves them unsetset_flow_shiftis a structural no-op for distilled checkpoints, soflow_shiftdoes not applyfixed_step_sampler_config.t_list = [1.0, 0.9375, 0.8333333333333334, 0.625]withstochastic_sampling: trueDependencies and review state