8.9 KiB
LingBot-Video
Native dense and MoE text-to-video serving for LingBot-Video
Summary
- Vendor: Robbyant
- Models:
robbyant/lingbot-video-dense-1.3bandrobbyant/lingbot-video-moe-30b-a3b - Task: Text-to-video generation
- Mode: Offline generation and online serving with the OpenAI-compatible
/v1/videosAPI - Maintainer: Community
When to use this recipe
Use this recipe when you want to run a dense or MoE LingBot-Video checkpoint
with vLLM-Omni's native pipeline. The runtime path does not import the upstream
lingbot_video Python package; it loads checkpoint components directly with
the in-tree LingBotVideoPipeline, dense or routed-MoE DiT blocks, shared
FlowUniPC scheduler, Qwen3-VL text encoder, and Wan VAE.
This first MoE integration is intentionally text-to-video and single-GPU only. It targets the base BF16 checkpoint without its optional refiner or additional parallel, cache, quantized, or expert-kernel backends.
References
- Dense checkpoint: https://huggingface.co/robbyant/lingbot-video-dense-1.3b
- MoE checkpoint: https://huggingface.co/robbyant/lingbot-video-moe-30b-a3b
- Upstream project: https://github.com/Robbyant/lingbot-video
- Related offline example:
examples/offline_inference/text_to_video/text_to_video.py - Related online video API docs:
docs/serving/videos_api.md
Hardware Support
This recipe documents the CUDA single-GPU dense and BF16 MoE checkpoint paths. Multi-GPU parallelism, Cache-DiT, quantization, and CPU offload are not validated for LingBot-Video in this PR.
GPU
1 x NVIDIA L20X
Both checkpoints have been smoke-tested on one NVIDIA L20X at 192x320,
9 frames, and 2 steps. The MoE smoke reserved approximately 67.70 GiB of GPU
memory, so use a GPU with at least about 70 GiB of available memory for this
small validation shape. Larger resolutions, frame counts, or concurrent
requests require additional headroom.
The MoE path is validated with BF16 expert weights.
Dense offline T2V
CUDA_VISIBLE_DEVICES=0 \
python examples/offline_inference/text_to_video/text_to_video.py \
--model robbyant/lingbot-video-dense-1.3b \
--prompt "a robotic arm picks up a red block" \
--output lingbot_t2v.mp4 \
--height 192 \
--width 320 \
--num-frames 9 \
--num-inference-steps 2 \
--guidance-scale 3.0 \
--flow-shift 3.0 \
--seed 42 \
--fps 24
MoE offline T2V
CUDA_VISIBLE_DEVICES=0 \
python examples/offline_inference/text_to_video/text_to_video.py \
--model robbyant/lingbot-video-moe-30b-a3b \
--prompt "a robotic arm picks up a red block" \
--output lingbot_moe_t2v.mp4 \
--height 192 \
--width 320 \
--num-frames 9 \
--num-inference-steps 2 \
--guidance-scale 3.0 \
--flow-shift 3.0 \
--seed 42 \
--fps 24
Online serving
CUDA_VISIBLE_DEVICES=0 \
vllm serve robbyant/lingbot-video-dense-1.3b \
--omni \
--model-class-name LingBotVideoPipeline \
--default-sampling-params \
'{"0":{"num_frames":81,"num_inference_steps":40,"guidance_scale":6.0}}' \
--port 8091
For the MoE checkpoint, use the same single-GPU pipeline:
CUDA_VISIBLE_DEVICES=0 \
vllm serve robbyant/lingbot-video-moe-30b-a3b \
--omni \
--model-class-name LingBotVideoPipeline \
--default-sampling-params \
'{"0":{"num_frames":81,"num_inference_steps":40,"guidance_scale":6.0}}' \
--port 8091
These stage defaults match the LingBot reference pipeline. Request-level values continue to override them, so the smaller smoke request below remains unchanged.
When serving MoE, replace the request's model form value below with
robbyant/lingbot-video-moe-30b-a3b.
After the server is ready, submit a text-to-video job:
create_response=$(curl -s http://localhost:8091/v1/videos \
-F "model=robbyant/lingbot-video-dense-1.3b" \
-F "prompt=a robotic arm picks up a red block" \
-F "width=320" \
-F "height=192" \
-F "num_frames=9" \
-F "fps=24" \
-F "num_inference_steps=2" \
-F "guidance_scale=3.0" \
-F "flow_shift=3.0" \
-F "seed=42")
video_id=$(echo "${create_response}" | jq -r '.id')
while true; do
status=$(curl -s "http://localhost:8091/v1/videos/${video_id}" | jq -r '.status')
if [ "${status}" = "completed" ]; then
break
fi
if [ "${status}" = "failed" ]; then
curl -s "http://localhost:8091/v1/videos/${video_id}" | jq .
exit 1
fi
sleep 2
done
curl -L "http://localhost:8091/v1/videos/${video_id}/content" -o lingbot_t2v.mp4
Key Parameters
| Parameter | Suggested smoke value | Notes |
|---|---|---|
height |
192 |
Must be a multiple of 16 |
width |
320 |
Must be a multiple of 16 |
num_frames |
9 |
Must be 1 or 4n + 1; this PR validates T2V with video outputs |
num_inference_steps |
2 |
Use more steps for quality sweeps |
guidance_scale |
3.0 |
CFG is active when this is greater than 1.0 |
flow_shift |
3.0 |
Scheduler flow-shift; aliases the pipeline's internal shift |
negative_prompt |
model default | Optional text describing artifacts to avoid |
fps |
24 |
Output MP4 frame rate |
Validation
Local dense smoke run:
- Shape: 9 frames at
192x320 - Steps: 2
- Request generation time:
0.2923s - Peak reserved GPU memory:
14548 MiB
Local dense parity harness against the upstream repository:
- Shape:
[9, 192, 320, 3] - MAE:
0.0065238 - MSE:
0.00006650 - PSNR:
41.77 dB - Native request time:
0.2875s
Local MoE validation on one NVIDIA L20X used checkpoint revision
f2e538f64afe00cc4ae674db2aeb52e2945edfd5:
- Loaded all
977transformer state keys and30,084,506,176parameters. - Router weights and correction biases stayed in FP32; routed and shared expert weights loaded in BF16.
- The complete 48-layer transformer forward returned finite
[1, 16, 1, 8, 8]BF16 output. - With both implementations using matched math SDPA backends, the complete
native transformer matched the upstream implementation bitwise
(
max_abs=0,mean_abs=0). - An isolated sparse MoE block exercising the router, grouped experts, scatter restore, and shared expert also matched the upstream implementation bitwise.
- The native pipeline generated a 9-frame
192x320MP4 in 2 steps with69326 MiB(67.70 GiB) peak reserved GPU memory. - The online
/v1/videossmoke created, polled, and downloaded the generated MP4 successfully (1 passed).
Do not treat these as production benchmarks; they are functional smoke plus controlled numerical-parity evidence for small validation inputs.
Reproducing MoE numerical parity
The in-tree parity harness compares the native MoE implementation with a local checkout of the upstream LingBot-Video implementation. It uses local files only and does not download checkpoints.
Set paths for the upstream checkout and the cached MoE checkpoint:
export LINGBOT_VIDEO_REPO=/path/to/lingbot-video
export LINGBOT_VIDEO_MOE_MODEL=/path/to/lingbot-video-moe-30b-a3b
Run the lightweight sparse-block comparison:
CUDA_VISIBLE_DEVICES=0 \
python benchmarks/lingbot_video/moe_transformer_parity.py \
--scope block \
--official-repo "${LINGBOT_VIDEO_REPO}" \
--output-json /tmp/lingbot_moe_block_parity.json
This path covers bias-corrected router selection, group-limited top-k, routed experts, FP32 scatter-weighted restore, padding masks, and the shared expert. It exits with a nonzero status unless the upstream and native block outputs are bitwise equal.
The real-checkpoint transformer comparison requires one GPU with enough memory for the 30B checkpoint. Models are loaded sequentially, so two copies are not resident on the GPU at the same time:
CUDA_VISIBLE_DEVICES=0 \
python benchmarks/lingbot_video/moe_transformer_parity.py \
--scope transformer \
--official-repo "${LINGBOT_VIDEO_REPO}" \
--model "${LINGBOT_VIDEO_MOE_MODEL}" \
--output-json /tmp/lingbot_moe_transformer_parity.json
Transformer parity fixes the official implementation to
diffusers:_native_math and the native implementation to
TORCH_SDPA + SDPBackend.MATH. The expected result is exact=true,
output.equal=true, and zero max/mean/RMSE error. Different fused attention
kernels can introduce BF16 rounding differences before MoE routing, so
automatic attention-backend comparisons are diagnostic only and are not the
bitwise correctness oracle.
Known Limitations
- T2V base-transformer inference only. T2I, I2V, TI2V, and the checkpoint's
optional
refiner/transformer are not supported by this PR. - Only the BF16 MoE checkpoint is validated in this first integration.
- No HSDP, tensor, sequence, expert, or CFG parallelism is claimed.
- No Cache-DiT, TeaCache, CPU offload, VAE patch parallelism, or quantized inference is claimed.
- Optional Triton, SGLang, FP8, and alternative fused-expert backends from the upstream project are not included.
- Only one request per LingBot pipeline batch is currently supported.