2026-08-16 09:11:20 +08:00
|
|
|
# LingBot-World 2.0
|
|
|
|
|
|
|
|
|
|
> Offline and experimental realtime interactive world generation
|
|
|
|
|
|
|
|
|
|
## Summary
|
|
|
|
|
|
|
|
|
|
- Vendor: Robbyant
|
|
|
|
|
- Model: `robbyant/lingbot-world-v2-14b-causal-fast-diffusers`
|
|
|
|
|
- Task: image-conditioned interactive world generation
|
2026-09-07 13:38:23 +08:00
|
|
|
- Modes: offline trajectory replay, stepwise one-request streaming, and in-process realtime AR-Diffusion ticks (deprecated)
|
2026-08-16 09:11:20 +08:00
|
|
|
- Hardware validated: NVIDIA H200 and B200
|
|
|
|
|
- Maintainer: Community
|
|
|
|
|
|
|
|
|
|
The checkpoint is separately licensed under CC BY-NC-SA and restricted to
|
|
|
|
|
non-commercial use. The vLLM-Omni integration code remains Apache-2.0.
|
|
|
|
|
|
2026-09-07 13:38:23 +08:00
|
|
|
## Which path to use
|
|
|
|
|
|
|
|
|
|
There are three ways to drive this model, each documented in its own section
|
|
|
|
|
below.
|
|
|
|
|
|
|
|
|
|
- **Realtime stepwise** — see [Streaming video serving](#streaming-video-serving).
|
|
|
|
|
The suggested path: one `WS /v1/realtime/video` session produces the whole
|
2026-09-21 00:19:41 +08:00
|
|
|
rollout, one video chunk per AR block. Mid-session camera control uses
|
|
|
|
|
structural SE3 `session.interaction` payloads; optional
|
|
|
|
|
`camera_action_script` remains for request-scoped WASD scripts.
|
2026-09-07 13:38:23 +08:00
|
|
|
- **Realtime tick** *(deprecated)* — see
|
|
|
|
|
[Realtime in-process generation](#realtime-in-process-generation-deprecated).
|
2026-09-21 00:19:41 +08:00
|
|
|
Older one-block-per-`generate()` control plane with JSONL WASD frames; will be removed in the future.
|
2026-09-07 13:38:23 +08:00
|
|
|
- **Offline** — see [Offline generation](#offline-generation). Replays a fixed
|
|
|
|
|
pose/intrinsics trajectory in one request and writes an MP4. Use it when the
|
|
|
|
|
camera path is known up front and streaming is not needed.
|
|
|
|
|
|
2026-08-16 09:11:20 +08:00
|
|
|
## Offline generation
|
|
|
|
|
|
|
|
|
|
The offline path consumes one source image and an action directory containing
|
|
|
|
|
`poses.npy` with shape `[frames, 4, 4]` and `intrinsics.npy` with shape
|
|
|
|
|
`[frames, 4]`.
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
python examples/offline_inference/diffusion/lingbot_world_v2.py \
|
|
|
|
|
--prompt "The camera moves slowly forward through the scene." \
|
|
|
|
|
--image /path/to/first_frame.png \
|
|
|
|
|
--action-dir /path/to/actions/forward \
|
|
|
|
|
--num-frames 81 \
|
|
|
|
|
--output lingbot_world_v2.mp4
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The current causal-fast checkpoint uses four DMD sampling steps. Raw frame
|
2026-09-15 18:49:55 +08:00
|
|
|
counts must be `9 + 12k` for a non-negative integer `k`; for example, 129 frames
|
|
|
|
|
produce eleven AR blocks. Both camera files must contain the same number of
|
|
|
|
|
source frames, at least as many as requested and at most 4096. The runtime
|
|
|
|
|
consumes only the prefix needed by the request. Longer rollouts also require
|
|
|
|
|
sufficient device memory.
|
2026-08-16 09:11:20 +08:00
|
|
|
|
2026-09-21 17:02:15 +08:00
|
|
|
## Experimental KV writeback reuse
|
|
|
|
|
|
|
|
|
|
To reuse KV from the final denoising step instead of running the additional
|
|
|
|
|
clean-latent writeback, pass this option in the `model_config` supplied to
|
|
|
|
|
`Omni` or `AsyncOmni`:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
model_config = {"lingbot_reuse_last_step_kv": True}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The default is `False`. The value must be a boolean, not a string or integer.
|
|
|
|
|
It is validated and fixed when the pipeline instance is constructed; changing
|
|
|
|
|
it requires a new instance. It is not a per-request `extra_args` option.
|
|
|
|
|
The previous `VLLM_OMNI_LINGBOT_REUSE_LAST_STEP_KV` environment variable is no
|
|
|
|
|
longer read.
|
|
|
|
|
|
|
|
|
|
This experimental mode stores the final noisy probe's KV rather than clean
|
|
|
|
|
`x0` KV, so generated outputs can differ. The four denoising steps, chunk
|
|
|
|
|
boundaries, and paged-context finalization remain the same.
|
|
|
|
|
|
2026-09-07 13:38:23 +08:00
|
|
|
## Realtime in-process generation (deprecated)
|
|
|
|
|
|
2026-09-21 00:19:41 +08:00
|
|
|
Prefer [Streaming video serving](#streaming-video-serving) for mid-session
|
|
|
|
|
camera control. This tick example still works and emits no runtime warning;
|
|
|
|
|
removal is tracked as B4 of the LingBot World 2.0 roadmap
|
|
|
|
|
([#6672](https://github.com/vllm-project/vllm-omni/issues/6672)).
|
2026-08-16 09:11:20 +08:00
|
|
|
|
|
|
|
|
The realtime example keeps AR-Diffusion KV and model-owned state across
|
|
|
|
|
requests. Each JSONL line describes the prompt and/or three latent-frame camera
|
|
|
|
|
actions applied at the next chunk boundary:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{"event_id":1,"prompt":"A road through a forest","frames":[["j"],[],[]]}
|
|
|
|
|
{"event_id":2,"frames":[["w"],["w"],["w"]]}
|
|
|
|
|
{"event_id":3,"prompt":"The road enters a snowy valley","frames":[[],[],[]]}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Run:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
python examples/offline_inference/diffusion/lingbot_world_v2_realtime.py \
|
|
|
|
|
--image /path/to/first_frame.png \
|
|
|
|
|
--events /path/to/events.jsonl \
|
|
|
|
|
--output-dir /tmp/lingbot-realtime \
|
|
|
|
|
--gpu-memory-fraction 0.6
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The example writes one latent tensor and one metadata JSON file per chunk. It
|
|
|
|
|
exercises the same `ARDiffusionSessionManager -> ARDiffusionOmniTickConsumer
|
|
|
|
|
-> AsyncOmni -> ARDiffusionEngine` path used by a future HTTP or WebSocket
|
|
|
|
|
transport.
|
|
|
|
|
|
2026-09-07 13:38:23 +08:00
|
|
|
Realtime serving now goes through the generic `WS /v1/realtime/video`
|
|
|
|
|
transport, which exposes no LingBot-specific event fields; the tick loop above
|
|
|
|
|
stays available only for the older control plane.
|
|
|
|
|
|
|
|
|
|
## Streaming video serving
|
|
|
|
|
|
|
|
|
|
The stepwise path keeps AR-Diffusion paged KV but issues **one** request for
|
|
|
|
|
the whole rollout: `prepare_encode` runs once, then every AR block is four DMD
|
|
|
|
|
steps followed by one streamed chunk. Identity metadata uses
|
|
|
|
|
`session_id = request_id` with contiguous `chunk_index` values from zero.
|
2026-09-21 00:19:41 +08:00
|
|
|
Camera motion can be request-scoped (`camera_action_script` / `action_path`) or
|
|
|
|
|
updated mid-session via `session.interaction`.
|
2026-09-07 13:38:23 +08:00
|
|
|
|
|
|
|
|
Serve it with the AR-Diffusion deploy config, which selects the AR-Diffusion
|
|
|
|
|
engine and enables streamed step execution:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
vllm serve robbyant/lingbot-world-v2-14b-causal-fast-diffusers \
|
|
|
|
|
--omni \
|
|
|
|
|
--deploy-config vllm_omni/deploy/lingbot_world_v2_stepwise.yaml \
|
|
|
|
|
--port 8000
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`--deploy-config` is required: the `lingbot_world` pipeline deliberately
|
|
|
|
|
registers no default deploy config, so offline replay and the tick example
|
|
|
|
|
keep their request-mode topology when no deploy config is given, and the
|
|
|
|
|
stepwise serving topology is only ever an explicit choice.
|
|
|
|
|
|
|
|
|
|
Clients then use the generic WebSocket protocol documented in
|
2026-09-21 00:19:41 +08:00
|
|
|
[`docs/serving/streaming_video_output_api.md`](../../docs/serving/streaming_video_output_api.md):
|
2026-09-07 13:38:23 +08:00
|
|
|
`session.start` begins one rollout and each AR block arrives as a binary video
|
2026-09-21 00:19:41 +08:00
|
|
|
chunk. The model is image-conditioned, so the first frame is required
|
|
|
|
|
at `image_reference` as an `http(s)` or `data:` URL; an optional request-scoped
|
|
|
|
|
pre-scripted list of WASD actions can be provided in `extra_params`:
|
2026-09-07 13:38:23 +08:00
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{"type": "session.start", "model": "robbyant/lingbot-world-v2-14b-causal-fast-diffusers",
|
|
|
|
|
"prompt": "The camera moves slowly forward through the scene.",
|
|
|
|
|
"image_reference": {"image_url": "data:image/png;base64,<first frame>"},
|
|
|
|
|
"width": 832, "height": 480, "num_frames": 33,
|
|
|
|
|
"extra_params": {"flow_shift": 5.0,
|
|
|
|
|
"camera_action_script": [[["w"], ["w"], ["w"]], [["a"], [], []], [[], [], []]]}}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`camera_action_script` must hold exactly one action list per generated
|
|
|
|
|
chunk, and a request generates `((num_frames - 1) // 4 + 1) // 3` chunks:
|
|
|
|
|
three for `num_frames: 33`, seven for `num_frames: 81`.
|
|
|
|
|
|
2026-09-21 00:19:41 +08:00
|
|
|
Mid-session camera control uses structural SE3 on `session.interaction`
|
|
|
|
|
(Unity frame: `+X` right, `+Y` up, `+Z` forward). WASD key tokens belong in
|
|
|
|
|
clients and are required to be converted before submitting to the service:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{"type": "session.interaction",
|
|
|
|
|
"interaction": {
|
|
|
|
|
"event_id": "forward-1",
|
|
|
|
|
"event": {
|
|
|
|
|
"multi_modal_data": {
|
|
|
|
|
"camera": {
|
|
|
|
|
"mode": "velocity",
|
|
|
|
|
"data": {"translation": [0.0, 0.0, 0.05], "rotation": [0.0, 0.0, 0.0, 1.0]}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The bundled client helps translate the protocol, converting `--camera-updates` keystrokes to corresponding SE3 matrices:
|
2026-09-07 13:38:23 +08:00
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
python examples/online_serving/streaming_video_generation/streaming_video_client.py \
|
|
|
|
|
--model robbyant/lingbot-world-v2-14b-causal-fast-diffusers \
|
|
|
|
|
--prompt "The camera moves slowly forward through the scene." \
|
|
|
|
|
--image-reference /path/to/first_frame.png \
|
|
|
|
|
--width 832 --height 480 --num-frames 33 --fps 16 --seed 42 \
|
2026-09-21 00:19:41 +08:00
|
|
|
--camera-updates '[{"at": 1.0, "actions": ["w"]}, {"at": 3.0, "actions": ["a"]}]' \
|
2026-09-07 13:38:23 +08:00
|
|
|
--output lingbot_world_v2_stream.mp4
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
A served request may instead point at a pose/intrinsics trajectory with
|
|
|
|
|
`extra_params.action_path`, which is resolved inside the trusted root set by
|
|
|
|
|
`model_config.lingbot_action_root` or `VLLM_OMNI_LINGBOT_ACTION_ROOT`; a server
|
2026-09-21 00:19:41 +08:00
|
|
|
started without that root configured accepts only `camera_action_script` or
|
|
|
|
|
mid-session camera interaction.
|
2026-09-07 13:38:23 +08:00
|
|
|
|
|
|
|
|
Requested `width`/`height` must match `ar_diffusion_width`/`ar_diffusion_height`
|
|
|
|
|
in the deploy config, because the AR cache geometry is fixed at load time.
|
|
|
|
|
Blocks are decoded independently, so seams between chunks are possible; a
|
|
|
|
|
session-owned streaming decoder is tracked separately.
|
|
|
|
|
|
|
|
|
|
The deprecated tick example remains available for the older
|
|
|
|
|
one-block-per-`generate()` control plane.
|
2026-08-16 09:11:20 +08:00
|
|
|
|
|
|
|
|
## Realtime identity and controls
|
|
|
|
|
|
|
|
|
|
- `session_id` identifies the persistent world and its worker-owned state.
|
|
|
|
|
- `event_id` identifies a prompt/control update and remains monotonic across reset.
|
|
|
|
|
- `chunk_index` is contiguous from zero and restarts from zero after reset.
|
|
|
|
|
- `request_id` correlates one chunk snapshot with its output metadata.
|
|
|
|
|
- AsyncOmni uses a separate UUID-suffixed internal engine routing ID.
|
|
|
|
|
|
|
|
|
|
The generic runtime transports controls as opaque snapshots. LingBot's adapter
|
|
|
|
|
accepts:
|
|
|
|
|
|
|
|
|
|
- `lingbot.camera_actions.v1` for per-latent-frame key states such as `w`, `a`,
|
|
|
|
|
`s`, `d`, `i`, `j`, `k`, and `l`;
|
|
|
|
|
- `lingbot.camera_trajectory.v1` for explicit pose/intrinsics trajectories.
|
|
|
|
|
|
2026-09-21 20:21:46 +08:00
|
|
|
## Online FP8 linear layers (experimental)
|
|
|
|
|
|
|
|
|
|
Pass `quantization_config={"method": "fp8"}` to the existing `AsyncOmni`
|
|
|
|
|
construction to use vLLM's online FP8 linear implementation. On the tested
|
|
|
|
|
Hopper-class GPU this dispatches to CUTLASS FP8 GEMM with online per-tensor
|
|
|
|
|
activation scaling; no LingBot-specific quantization kernel is introduced.
|
|
|
|
|
|
|
|
|
|
Only the transformer's existing vLLM parallel linear layers are eligible:
|
|
|
|
|
self-attention, cross-attention, FFN, camera injectors, and C2WS projections.
|
|
|
|
|
The ordinary PyTorch linear layers (including the output head and
|
|
|
|
|
time/text embeddings), normalization, convolutions, VAE, text encoder, and
|
|
|
|
|
AR KV cache are not converted to FP8 by this option.
|
|
|
|
|
|
|
|
|
|
Quality-sensitive projections can remain BF16 through fully qualified
|
|
|
|
|
`ignored_layers` names. For example, the following configuration tests
|
|
|
|
|
retaining the camera/C2WS path in BF16:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
quantization_config = {
|
|
|
|
|
"method": "fp8",
|
|
|
|
|
"ignored_layers": [
|
|
|
|
|
f"transformer.blocks.{i}.cam_injector_layer{j}"
|
|
|
|
|
for i in range(40)
|
|
|
|
|
for j in (1, 2)
|
|
|
|
|
]
|
|
|
|
|
+ [
|
|
|
|
|
"transformer.c2ws_hidden_states_layer1",
|
|
|
|
|
"transformer.c2ws_hidden_states_layer2",
|
|
|
|
|
],
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Use `transformer.blocks.<i>.self_attn.qkv` to exclude the fused Q/K/V
|
|
|
|
|
projection as a unit. The example exclusion list is an ablation, not a
|
|
|
|
|
universal quality guarantee or a default. Validate the intended scene, seed,
|
|
|
|
|
camera trajectory, and session length against BF16 before selecting a policy.
|
|
|
|
|
An FP8 kernel speedup alone is not an end-to-end performance claim.
|
|
|
|
|
The long-session E8 quality gate in
|
|
|
|
|
[#7074](https://github.com/vllm-project/vllm-omni/issues/7074) remains required
|
|
|
|
|
for Tier-2 acceptance.
|
|
|
|
|
|
2026-08-16 09:11:20 +08:00
|
|
|
## Validation
|
|
|
|
|
|
|
|
|
|
Real-checkpoint validation uses 480x832 output, four DMD steps, and seed 42.
|
|
|
|
|
The exercised matrix includes:
|
|
|
|
|
|
|
|
|
|
- TP=1 and TP=2 execution;
|
|
|
|
|
- two interleaved resident sessions;
|
|
|
|
|
- action input and prompt switching;
|
|
|
|
|
- seven contiguous chunks crossing the sink plus recent rolling window;
|
|
|
|
|
- direct versus paged replay;
|
|
|
|
|
- CUDA Graph execution;
|
|
|
|
|
- reset, close, failure cleanup, and exact metadata matching; and
|
|
|
|
|
- nine-frame VAE decode.
|
|
|
|
|
|
|
|
|
|
Official `generate.py` accuracy and performance numbers, together with
|
|
|
|
|
generation artifacts, are recorded in the PR validation section for the exact
|
|
|
|
|
tested commit.
|
|
|
|
|
|
|
|
|
|
## Current limitations
|
|
|
|
|
|
|
|
|
|
- Only the 14B causal-fast checkpoint is supported.
|
2026-09-07 13:38:23 +08:00
|
|
|
- The tick control plane is internal; the public transport is the stepwise
|
2026-09-21 00:19:41 +08:00
|
|
|
`WS /v1/realtime/video` path with structural SE3 mid-session camera
|
|
|
|
|
interaction (WASD remains a client-side or `camera_action_script` convenience).
|
2026-09-07 13:38:23 +08:00
|
|
|
- Stepwise serving requires an explicit
|
|
|
|
|
`--deploy-config vllm_omni/deploy/lingbot_world_v2_stepwise.yaml`; there is
|
|
|
|
|
no default deploy config for this model.
|
2026-08-16 09:11:20 +08:00
|
|
|
- AR-Diffusion stages currently require one replica because session-affine
|
|
|
|
|
routing across replicas is not implemented.
|
2026-09-07 13:38:23 +08:00
|
|
|
- Tick mode generates one AR block per request. Stepwise mode generates many
|
|
|
|
|
AR blocks in one request. `max_num_seqs` must be one in both cases.
|
2026-08-16 09:11:20 +08:00
|
|
|
- Stateful streaming VAE decode is not implemented; the realtime example emits
|
|
|
|
|
latent chunks.
|
2026-09-21 20:21:46 +08:00
|
|
|
- SP/USP, pipeline/CFG parallelism, HSDP, VAE parallelism, quantization methods other than online FP8,
|
2026-08-16 09:11:20 +08:00
|
|
|
Cache-DiT, TeaCache, causal-pretrain, and the 1.3B checkpoint are not claimed.
|
|
|
|
|
- No AMD GPU, Ascend NPU, or Intel GPU support is claimed.
|
|
|
|
|
|
|
|
|
|
## References
|
|
|
|
|
|
|
|
|
|
- Checkpoint: <https://huggingface.co/robbyant/lingbot-world-v2-14b-causal-fast-diffusers>
|
|
|
|
|
- Official implementation: <https://github.com/robbyant/lingbot-world-v2>
|
|
|
|
|
- Offline example: [`examples/offline_inference/diffusion/lingbot_world_v2.py`](../../examples/offline_inference/diffusion/lingbot_world_v2.py)
|
2026-09-07 13:38:23 +08:00
|
|
|
- Streaming serving client: [`examples/online_serving/streaming_video_generation/`](../../examples/online_serving/streaming_video_generation/README.md)
|
|
|
|
|
- Streaming serving end-to-end test: [`tests/e2e/online_serving/test_lingbot_world_v2_stepwise.py`](../../tests/e2e/online_serving/test_lingbot_world_v2_stepwise.py)
|
|
|
|
|
- Stepwise latent-level end-to-end test: [`tests/e2e/offline_inference/test_lingbot_world_v2_stepwise.py`](../../tests/e2e/offline_inference/test_lingbot_world_v2_stepwise.py)
|
2026-08-16 09:11:20 +08:00
|
|
|
- Realtime design: [`docs/design/feature/realtime_ar_diffusion.md`](../../docs/design/feature/realtime_ar_diffusion.md)
|