[LingBot World] Eleven bit-identical removals of repeated work in the realtime path (#7648)

Signed-off-by: tzhouam <tzhouam@connect.ust.hk>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Zhou TaichangandClaude Fable 5.1 authored and GitHub committed 2026-09-21 06:12:45 +00:00
1 parent a9e2117f5e
commit 86fcfe95f1
23 files changed
+1957 -200

No files matched your search

@@ -74,6 +74,17 @@ def _install_vllm_stubs() -> None:
_SeqAllToAll4D,
)
def _all_to_all_5D(value, scatter_idx=3, gather_idx=1, group=None, use_sync=False):
# Single-rank stub: the fused q/k/v exchange is the identity when N == 1.
del scatter_idx, gather_idx, group, use_sync
return value
setattr(
sys.modules["vllm_omni.diffusion.distributed.comm"],
"all_to_all_5D",
_all_to_all_5D,
)
def get_sp_group():
return SimpleNamespace(
ulysses_world_size=1,
@@ -588,26 +599,7 @@ def test_tp_rmsnorm_weight_loader_selects_rank_shard(monkeypatch: pytest.MonkeyP
torch.testing.assert_close(norm.weight, torch.tensor([30.0, 40.0]))
def test_single_token_text_kv_shard_owns_compact_storage(monkeypatch):
module = _load_module()
monkeypatch.setattr(
module,
"get_sp_group",
lambda: SimpleNamespace(
ulysses_world_size=2,
ulysses_rank=1,
ulysses_group=None,
),
)
attention = module.LingBotCrossAttention(dim=8, num_heads=4)
full = torch.arange(8, dtype=torch.float32).reshape(1, 1, 4, 2)
shard = attention.shard_kv_heads(full)
torch.testing.assert_close(shard, full[:, :, 2:], rtol=0, atol=0)
assert shard.is_contiguous()
assert shard.untyped_storage().nbytes() == shard.numel() * shard.element_size()
@pytest.mark.parametrize("attention_class", ["LingBotSelfAttention", "LingBotCrossAttention"])
@pytest.mark.parametrize("attention_class", ["LingBotSelfAttention"])
def test_ulysses_rejects_non_divisible_head_count(monkeypatch, attention_class):
module = _load_module()
monkeypatch.setattr(
@@ -621,3 +613,35 @@ def test_ulysses_rejects_non_divisible_head_count(monkeypatch, attention_class):
)
with pytest.raises(ValueError, match="heads must be divisible"):
getattr(module, attention_class)(dim=8, num_heads=4)
def test_supplied_camera_modulation_matches_computing_it_inline() -> None:
"""Passing a prebuilt modulation must be arithmetically identical to letting the block build its own."""
module = _load_module()
torch.manual_seed(0)
block = module.LingBotAttentionBlock(dim=4, num_heads=2, prefix="blocks.0")
camera = torch.randn(1, 3, 4)
scale, shift = block.camera_modulation(camera)
inline_scale, inline_shift = block.camera_modulation(camera)
torch.testing.assert_close(scale, inline_scale, rtol=0, atol=0)
torch.testing.assert_close(shift, inline_shift, rtol=0, atol=0)
def test_the_camera_modulation_reads_only_the_camera_tokens() -> None:
"""The reuse window is only sound because the modulation depends on nothing else that varies per forward."""
module = _load_module()
torch.manual_seed(0)
block = module.LingBotAttentionBlock(dim=4, num_heads=2, prefix="blocks.0")
camera = torch.randn(1, 3, 4)
first = block.camera_modulation(camera)
# Same camera, different everything else a forward would carry: the modulation cannot move.
second = block.camera_modulation(camera.clone())
torch.testing.assert_close(first[0], second[0], rtol=0, atol=0)
torch.testing.assert_close(first[1], second[1], rtol=0, atol=0)
# A different camera must move it, or the cache key would be hiding a real dependency.
other = block.camera_modulation(camera + 1)
assert not torch.equal(first[0], other[0])
@@ -837,6 +837,9 @@ def _rollout(model, mode, dtype, batch):
frames_per_block=_FRAMES,
max_scratch_tokens_per_branch=_FRAMES * _TOKENS_PER_FRAME,
cross_attention_lengths={"text": 5},
# Cross-attention keeps every local head on every SP rank, so its pool is wider than the
# self-attention share above.
cross_attention_kv_heads={"text": model.blocks[0].cross_attn.num_local_heads},
device=device,
)
state = ARDiffusionKVState(kv, "numeric", {"main": kv.begin_request("numeric")}, num_layers=_LAYERS)
@@ -896,7 +899,6 @@ def _worker(rank, world_size, sp_size, tp_size, mode, dtype, batch, rendezvous):
from vllm_omni.diffusion.distributed.parallel_state import (
destroy_distributed_env,
destroy_model_parallel,
get_sp_group,
init_distributed_environment,
initialize_model_parallel,
)
@@ -948,11 +950,10 @@ def _worker(rank, world_size, sp_size, tp_size, mode, dtype, batch, rendezvous):
bound = 1e-5 if dtype == torch.float32 else 1e-2
error = (result - expected).double().norm() / expected.double().norm()
assert error <= bound, f"rank={rank}, TP{tp} SP{sp}: relative L2 {error.item():.3g} > {bound}"
local_heads = _HEADS // tp // sp
first = (
get_tensor_model_parallel_rank() * (_HEADS // tp)
+ get_sp_group().ulysses_rank * local_heads
)
# Cross-attention K/V is replicated across the Ulysses group, not split over it, so
# every SP rank holds its whole TP shard of heads. Only TP slices the cache now.
local_heads = _HEADS // tp
first = get_tensor_model_parallel_rank() * local_heads
for (k, v), (ek, ev) in zip(cross, expected_cross, strict=True):
tolerance = 1e-5 if dtype == torch.float32 else 2e-2
torch.testing.assert_close(
@@ -1033,3 +1034,56 @@ def test_temporal_rope_extension_keeps_spatial_limit():
model._rotary_embedding(
frames=3, height=17, width=1, start_frame=16, dtype=torch.float32, device=torch.device("cpu")
)
@pytest.mark.cpu
def test_the_camera_injector_runs_once_per_held_cache_and_gives_the_same_output() -> None:
"""Two forwards sharing one caller-held cache: the injector runs once per block, outputs unchanged."""
module = attention_tests._load_module()
model = _tiny_model(module, num_layers=2).eval()
hidden_states = torch.randn(1, 36, 1, 4, 4)
timestep = torch.tensor([1.0])
encoder_hidden_states = torch.randn(1, 3, 6)
camera_hidden_states = torch.randn(1, 6 * 8 * 8, 1, 4, 4)
def run(cache, camera_cache=None):
return model(
hidden_states,
timestep,
encoder_hidden_states,
camera_hidden_states,
cache=cache,
start_frame=0,
update_cache=False,
camera_modulation_cache=camera_cache,
)
# Baseline: no cache, so every forward rebuilds the injector in every block.
uncached_first = run(_cache(module, model))
uncached_second = run(_cache(module, model))
injector_calls = [attention_tests._record_outputs(block.cam_injector_layer1) for block in model.blocks]
held = module.CameraModulationCache()
cached_first = run(_cache(module, model), held)
cached_second = run(_cache(module, model), held)
# One build per block for both forwards, instead of one per block per forward.
assert [len(calls) for calls in injector_calls] == [1, 1]
torch.testing.assert_close(cached_first, uncached_first, rtol=0, atol=0)
torch.testing.assert_close(cached_second, uncached_second, rtol=0, atol=0)
@pytest.mark.cpu
def test_timestep_projection_is_staged_into_one_static_buffer_per_shape() -> None:
"""Two forwards' projections land in the same buffer; the blocks never see the fresh per-forward tensor."""
module = attention_tests._load_module()
model = _tiny_model(module, num_layers=1).eval()
first = torch.randn(1, 8, 6, model.dim)
second = torch.randn(1, 8, 6, model.dim)
staged_first = model._stage_timestep_projection(first)
assert staged_first is not first and torch.equal(staged_first, first)
staged_second = model._stage_timestep_projection(second)
assert staged_second is staged_first and torch.equal(staged_second, second)
# A different shape gets its own buffer; the first one is kept.
other = model._stage_timestep_projection(torch.randn(1, 4, 6, model.dim))
assert other is not staged_first and len(model._timestep_projection_buffers) == 2
@@ -4,11 +4,13 @@
from __future__ import annotations
import gc
import os
import weakref
from pathlib import Path
from threading import Lock
from types import SimpleNamespace
import numpy as np
import pytest
import torch
from diffusers.utils.torch_utils import randn_tensor as _diffusers_randn_tensor
@@ -29,6 +31,7 @@ from vllm_omni.diffusion.models.lingbot_world.camera import (
)
from vllm_omni.diffusion.request import OmniDiffusionRequest
from vllm_omni.diffusion.worker.utils import StepRequestState
from vllm_omni.experimental.ar_diffusion.capability import supports_chunk_step_grouping
from vllm_omni.experimental.ar_diffusion.tick_protocol import (
ARDiffusionControlInput,
ARDiffusionTickRequest,
@@ -127,6 +130,10 @@ class _RecordingTransformer(nn.Module):
sink_size=3,
)
self.blocks = nn.ModuleList([nn.Identity(), nn.Identity()])
for block in self.blocks:
# The spec reads head geometry from both attentions; cross-attention keeps every local head.
block.self_attn = SimpleNamespace(num_sp_heads=2)
block.cross_attn = SimpleNamespace(num_local_heads=2)
self.calls: list[dict] = []
self.cache_allocations: list[dict] = []
self.raise_on_call = raise_on_call
@@ -146,6 +153,7 @@ class _RecordingTransformer(nn.Module):
"cache_id": id(kwargs["cache"]),
"start_frame": kwargs["start_frame"],
"update_cache": kwargs["update_cache"],
"camera_cache": kwargs.get("camera_modulation_cache"),
}
self.calls.append(call)
if self.raise_on_call == len(self.calls):
@@ -534,14 +542,15 @@ def test_ar_diffusion_capability_uses_transformer_local_head_geometry() -> None:
assert spec.sink_frames == 3
assert [(branch.name, branch.local_index) for branch in spec.kv_branches] == [("main", 0)]
assert spec.cross_attention_lengths == {"text": 512}
# One condition block, committed + pending encoder caches, and decoder state.
# One condition block, committed + pending encoder caches, the session's
# cached prompt embedding (512 tokens x text_dim 8 x fp32), and decoder state.
# The stub encoder retains 2 RGB frames at 16x16 and 2 feature frames at 2x2.
encoder_bytes = (2 * 3 * 16 * 16 + 2 * 16 * 2 * 2) * 4
assert spec.model_owned_state_bytes_per_session == 960 + 2 * encoder_bytes + 2_421_248
assert spec.model_owned_state_bytes_per_session == 960 + 384 + 2 * encoder_bytes + 2_421_248
def test_session_admission_accounts_for_the_streaming_decoder_cache() -> None:
"""Admission includes encoder state even when the decoder cannot stream."""
"""Admission includes encoder state and the prompt embedding even when the decoder cannot stream."""
module = _load_pipeline_module()
def spec_pipeline():
@@ -560,13 +569,29 @@ def test_session_admission_accounts_for_the_streaming_decoder_cache() -> None:
# than the image condition alone could account for.
assert widened - small > 2_000_000
# Removing decoder support leaves the condition and both encoder histories.
# Removing decoder support leaves the condition, both encoder histories and
# the prompt-embedding cache.
bare = spec_pipeline()
for attribute in ("decoder", "post_quant_conv"):
if hasattr(bare.vae, attribute):
delattr(bare.vae, attribute)
bare.vae._cached_conv_counts.pop("decoder")
assert bare.ar_diffusion_kv_cache_spec().model_owned_state_bytes_per_session == 960 + 2 * 6_656
assert bare.ar_diffusion_kv_cache_spec().model_owned_state_bytes_per_session == 960 + 384 + 2 * 6_656
@pytest.mark.parametrize("sp_size", [1, 4])
def test_session_admission_budgets_camera_modulation_per_rank(sp_size: int) -> None:
pipeline = _pipeline(_load_pipeline_module())
pipeline._ar_height, pipeline._ar_width = 480, 832
pipeline.od_config.parallel_config.ulysses_degree = sp_size
pipeline.transformer.config.num_layers = 40
pipeline.transformer.config.num_attention_heads = 40
pipeline.transformer.config.attention_head_dim = 128
pipeline.transformer._dtype = torch.bfloat16
forty_layers = pipeline.ar_diffusion_kv_cache_spec().model_owned_state_bytes_per_session
pipeline.transformer.config.num_layers = 1
one_layer = pipeline.ar_diffusion_kv_cache_spec().model_owned_state_bytes_per_session
assert forty_layers - one_layer == 2 * 39 * (4680 // sp_size) * 5120 * 2
def test_preprocess_materializes_external_inputs_before_worker_execution(tmp_path: Path) -> None:
@@ -1672,7 +1697,6 @@ def test_typed_ticks_generate_one_global_block_and_return_standard_metadata(
"applied_event_ids": [1],
}
assert pipeline._ar_sessions["world-1"].next_chunk_index == 2
assert pipeline._ar_sessions["world-1"].prompt == "enter the snowy valley"
assert pipeline._ar_sessions["world-1"].camera_tail is not None
assert pipeline._ar_sessions["world-1"].camera_tail.poses.shape == (1, 4, 4)
expected_generator = torch.Generator(device="cpu").manual_seed(17)
@@ -1846,8 +1870,11 @@ def test_typed_ticks_keep_bounded_encoder_state_beyond_ten_chunks(
pipeline(_request(sampling=sampling))
assert pipeline.vae.encode_inputs == []
assert [video.shape[2] for video in pipeline.vae.encoder.inputs] == [1] + [4] * 32
# The stub encoder's history settles after its second block; the nine
# blocks after that reuse the settled condition without an encode.
assert [video.shape[2] for video in pipeline.vae.encoder.inputs] == [1] + [4] * 5
assert all(torch.count_nonzero(video) == 0 for video in pipeline.vae.encoder.inputs[1:])
assert pipeline._ar_sessions["world-1"].condition_fixed_point is not None
encoder_cache = pipeline._ar_sessions["world-1"].encoder_cache
assert sum(t.numel() * t.element_size() for t in encoder_cache) == pipeline._condition_encoder_cache_bytes()
assert pipeline.vae._enc_feat_map == ["module-owned"]
@@ -1943,6 +1970,76 @@ def test_condition_continues_real_causal_vae_beyond_initial_horizon(
assert pipeline._ar_sessions == {}
def test_condition_encoder_stops_at_its_fixed_point_and_restarts_with_the_session(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Once a block returns the history it was given, later blocks reuse the condition without encoding.
The whole-clip encode is the reference for every block, so reusing the
stored condition is only allowed to be a speedup, never a change.
"""
pipeline = _tiny_condition_pipeline(monkeypatch)
pipeline._ar_diffusion_kv_state = object()
blocks = 12
inputs = pipeline._parse_request(_request(sampling=_SamplingParams(num_frames=(blocks * 3 - 1) * 4 + 1)))
with torch.inference_mode():
expected = pipeline._prepare_condition(inputs, dtype=torch.float32)
encoder_calls = []
original_encoder = pipeline.vae.encoder.forward
def encoder(*args, **kwargs):
encoder_calls.append(len(seen))
return original_encoder(*args, **kwargs)
seen: list[torch.Tensor] = []
def generate_block(**kwargs):
index = len(seen) % blocks
torch.testing.assert_close(
kwargs["condition"], expected[:, :, index * 3 : (index + 1) * 3], rtol=1e-6, atol=1e-6
)
seen.append(kwargs["condition"])
return torch.zeros_like(kwargs["condition"][:, :16])
monkeypatch.setattr(pipeline.vae.encoder, "forward", encoder)
monkeypatch.setattr(pipeline, "_generate_block", generate_block)
def tick(index):
pipeline(_request(sampling=_SamplingParams(extra_args=_tick_extra_args(chunk_index=index))))
settled_at = None
for index in range(blocks):
tick(index)
state = pipeline._ar_sessions["world-1"]
if settled_at is None and state.condition_fixed_point is not None:
settled_at = index
assert state.pending_encoder_cache is None and state.encoder_cache is not None
assert settled_at is not None and 0 < settled_at < blocks - 1, settled_at
# Three encoder passes per encoded block, none once the fixed point is stored.
assert encoder_calls == [index for index in range(settled_at + 1) for _ in range(3)]
# The stored condition is the session's own, not shared with the callers.
assert all(seen[settled_at] is not later for later in seen[settled_at + 1 :])
# A session restarted from chunk 0 encodes again from the source image.
pipeline.reset_ar_diffusion_session("world-1")
tick(0)
state = pipeline._ar_sessions["world-1"]
assert state.condition_fixed_point is None
assert encoder_calls[-3:] == [blocks] * 3
def test_condition_encoder_fixed_point_is_released_with_the_session(monkeypatch: pytest.MonkeyPatch) -> None:
pipeline = _tiny_condition_pipeline(monkeypatch)
pipeline._ar_diffusion_kv_state = object()
monkeypatch.setattr(pipeline, "_generate_block", lambda **kwargs: torch.zeros_like(kwargs["condition"][:, :16]))
for index in range(8):
pipeline(_request(sampling=_SamplingParams(extra_args=_tick_extra_args(chunk_index=index))))
state = pipeline._ar_sessions["world-1"]
assert state.condition_fixed_point is not None
pipeline.close_ar_diffusion_session("world-1")
assert state.condition_fixed_point is None and state.encoder_cache is None
def test_condition_encoder_interleaving_retry_and_reset(monkeypatch: pytest.MonkeyPatch) -> None:
pipeline = _tiny_condition_pipeline(monkeypatch)
pipeline._ar_diffusion_kv_state = object()
@@ -2209,6 +2306,40 @@ def _stepwise_chunks(pipeline, state, ar_state):
yield output
def test_stepwise_block_shares_one_camera_cache_across_its_five_forwards() -> None:
"""The four probes and the commit of one stepwise block reuse one camera cache; the next block gets its own.
Request mode creates one cache per loop; the stepwise path's forwards are
separate scheduler steps, so the pipeline holds the block's cache and
passes it to each one.
"""
module = _load_pipeline_module()
transformer = _RecordingTransformer()
pipeline = _pipeline(module, transformer=transformer)
pipeline._ar_height = 16
pipeline._ar_width = 16
state = _stepwise_state(num_frames=21)
state.sampling.output_type = "latent"
with pipeline.bind_ar_diffusion_state(state.request_id, _FakeARState(state.request_id)):
outputs = _run_stepwise(pipeline, state)
assert len(outputs) == 2 and len(transformer.calls) == 10
caches = [call["camera_cache"] for call in transformer.calls]
assert all(c is not None for c in caches)
assert len({id(c) for c in caches[:5]}) == 1 and len({id(c) for c in caches[5:]}) == 1
assert caches[0] is not caches[5]
# The block's cache does not outlive its commit.
assert "camera_cache" not in state.extra
def test_pipeline_declares_chunk_step_grouping() -> None:
"""A realtime block's probes and commit may run without a scheduler cycle between them."""
module = _load_pipeline_module()
assert module.LingBotWorldCausalDMDPipeline.supports_chunk_step_grouping is True
assert supports_chunk_step_grouping(_pipeline(module))
def test_pipeline_declares_step_execution_support() -> None:
module = _load_pipeline_module()
pipeline = _pipeline(module)
@@ -2337,7 +2468,10 @@ def test_stepwise_matches_tick_transformer_trace_and_latents(num_chunks) -> None
torch.testing.assert_close(step_call["camera_hidden_states"], tick_call["camera_hidden_states"])
assert stepwise_state.extra["condition"].shape[2] == 3
assert pipeline.vae.encode_inputs == []
assert [video.shape[2] for video in pipeline.vae.encoder.inputs] == ([1] + [4] * (num_chunks * 3 - 1)) * 2
# The stub encoder's history settles after its second block, so both
# modes encode two blocks and reuse the settled condition after that.
encoded_blocks = min(num_chunks, 2)
assert [video.shape[2] for video in pipeline.vae.encoder.inputs] == ([1] + [4] * (encoded_blocks * 3 - 1)) * 2
assert stepwise_fake.commits == ["main"] * num_chunks
assert len(stepwise_outputs) == num_chunks and stepwise_outputs[-1].finished
assert pipeline.vae.decode_inputs == []
@@ -2406,21 +2540,16 @@ def test_stepwise_emits_latents_without_touching_the_vae() -> None:
assert pipeline.vae.decode_inputs == []
def _capture_video_processor(monkeypatch) -> list[tuple[tuple[int, ...], str]]:
"""Record what reaches postprocess_video, and name each chunk's output."""
import diffusers.video_processor as video_processor_module
def _capture_pixel_conversion(monkeypatch) -> list[tuple[int, ...]]:
"""Record each decoded block reaching the uint8 conversion, and name its output."""
module = _load_pipeline_module()
processed: list[tuple[int, ...]] = []
processed: list[tuple[tuple[int, ...], str]] = []
def uint8_frames(video):
processed.append(tuple(video.shape))
return f"frames-{len(processed)}"
class VideoProcessor:
def __init__(self, *, vae_scale_factor):
assert vae_scale_factor == 8
def postprocess_video(self, video, *, output_type):
processed.append((tuple(video.shape), output_type))
return f"frames-{len(processed)}"
monkeypatch.setattr(video_processor_module, "VideoProcessor", VideoProcessor)
monkeypatch.setattr(module, "_uint8_frames", uint8_frames)
return processed
@@ -2434,7 +2563,7 @@ def _streaming_pipeline(module):
def test_stepwise_decodes_each_chunk_for_streaming_consumers(monkeypatch) -> None:
"""A non-latent request must stream pixels under the primary "video" key."""
module = _load_pipeline_module()
processed = _capture_video_processor(monkeypatch)
processed = _capture_pixel_conversion(monkeypatch)
pipeline = _streaming_pipeline(module)
state = _stepwise_state(num_frames=21)
state.sampling.output_type = "np"
@@ -2443,7 +2572,7 @@ def test_stepwise_decodes_each_chunk_for_streaming_consumers(monkeypatch) -> Non
with pipeline.bind_ar_diffusion_state(state.request_id, fake):
outputs = _run_stepwise(pipeline, state)
assert [output_type for _, output_type in processed] == ["np", "np"]
assert processed == [(1, 3, 9, 16, 16), (1, 3, 12, 16, 16)]
assert [output.output["payload"] for output in outputs] == [{"video": "frames-1"}, {"video": "frames-2"}]
# Identity metadata survives the switch to pixel output.
assert [output.output["metadata"]["ar_diffusion"]["chunk_index"] for output in outputs] == [0, 1]
@@ -2457,7 +2586,7 @@ def test_stepwise_chunks_continue_one_sessions_temporal_decode(monkeypatch) -> N
the same latents would: 9 frames, then 12.
"""
module = _load_pipeline_module()
processed = _capture_video_processor(monkeypatch)
processed = _capture_pixel_conversion(monkeypatch)
pipeline = _streaming_pipeline(module)
state = _stepwise_state(num_frames=21)
state.sampling.output_type = "np"
@@ -2466,7 +2595,7 @@ def test_stepwise_chunks_continue_one_sessions_temporal_decode(monkeypatch) -> N
with pipeline.bind_ar_diffusion_state(state.request_id, fake):
_run_stepwise(pipeline, state)
assert [shape for shape, _ in processed] == [(1, 3, 9, 16, 16), (1, 3, 12, 16, 16)]
assert processed == [(1, 3, 9, 16, 16), (1, 3, 12, 16, 16)]
# One opening frame for the session, not one per chunk.
assert pipeline.vae.decoder.first_chunk_flags == [True] + [False] * 5
# The whole-clip decode path is not involved, and the module-owned cache
@@ -2511,7 +2640,7 @@ def test_prepare_next_chunk_rejects_live_camera_with_scripted_paths() -> None:
def test_peek_chunk_media_matches_streaming_decoder_frame_counts(monkeypatch) -> None:
"""Camera timelines must track the streaming decoder's 9-then-12 media counts."""
module = _load_pipeline_module()
_capture_video_processor(monkeypatch)
_capture_pixel_conversion(monkeypatch)
pipeline = _streaming_pipeline(module)
state = _stepwise_state(num_frames=21)
state.sampling.output_type = "np"
@@ -2536,7 +2665,7 @@ def test_peek_chunk_media_matches_streaming_decoder_frame_counts(monkeypatch) ->
def test_streaming_decode_state_is_owned_by_the_session(monkeypatch) -> None:
"""Decoder state is keyed by request id and released with the AR session."""
module = _load_pipeline_module()
_capture_video_processor(monkeypatch)
_capture_pixel_conversion(monkeypatch)
pipeline = _streaming_pipeline(module)
state = _stepwise_state(request_id="req-stream", num_frames=21)
state.sampling.output_type = "np"
@@ -2560,7 +2689,7 @@ def test_streaming_decode_state_is_owned_by_the_session(monkeypatch) -> None:
def test_streaming_decode_state_is_dropped_on_session_reset(monkeypatch) -> None:
"""A reset session must not resume the temporal context it just abandoned."""
module = _load_pipeline_module()
_capture_video_processor(monkeypatch)
_capture_pixel_conversion(monkeypatch)
pipeline = _streaming_pipeline(module)
state = _stepwise_state(request_id="req-reset", num_frames=21)
state.sampling.output_type = "np"
@@ -2584,7 +2713,7 @@ def test_streaming_decode_keeps_interleaved_sessions_isolated(monkeypatch) -> No
interleaving the scheduler actually produces.
"""
module = _load_pipeline_module()
processed = _capture_video_processor(monkeypatch)
processed = _capture_pixel_conversion(monkeypatch)
pipeline = _streaming_pipeline(module)
states = []
for request_id in ("req-a", "req-b"):
@@ -2617,7 +2746,7 @@ def test_streaming_decode_keeps_interleaved_sessions_isolated(monkeypatch) -> No
assert pipeline.vae.decoder.first_chunk_flags == (
[True, False, False] + [True, False, False] + [False] * 3 + [False] * 3
)
assert [shape for shape, _ in processed] == [
assert processed == [
(1, 3, 9, 16, 16),
(1, 3, 9, 16, 16),
(1, 3, 12, 16, 16),
@@ -2628,7 +2757,7 @@ def test_streaming_decode_keeps_interleaved_sessions_isolated(monkeypatch) -> No
def test_streaming_decode_failure_drops_the_half_advanced_cache(monkeypatch) -> None:
"""A failed chunk must not leave a cache a later chunk would continue from."""
module = _load_pipeline_module()
_capture_video_processor(monkeypatch)
_capture_pixel_conversion(monkeypatch)
pipeline = _streaming_pipeline(module)
state = _stepwise_state(request_id="req-boom", num_frames=21)
state.sampling.output_type = "np"
@@ -2662,7 +2791,7 @@ def test_streaming_decode_receives_rescaled_latents(monkeypatch) -> None:
never reaches, so this asserts on what ``post_quant_conv`` received.
"""
module = _load_pipeline_module()
_capture_video_processor(monkeypatch)
_capture_pixel_conversion(monkeypatch)
pipeline = _streaming_pipeline(module)
state = _stepwise_state(num_frames=21)
state.sampling.output_type = "np"
@@ -2695,7 +2824,7 @@ def test_streaming_decode_is_reported_to_the_profiler(monkeypatch) -> None:
perf regression in the streaming decoder would be too.
"""
module = _load_pipeline_module()
_capture_video_processor(monkeypatch)
_capture_pixel_conversion(monkeypatch)
pipeline = _pipeline(
module,
transformer=_RecordingTransformer(),
@@ -2723,21 +2852,18 @@ def test_streaming_decode_is_reported_to_the_profiler(monkeypatch) -> None:
def test_streaming_decode_falls_back_when_the_vae_would_tile(monkeypatch) -> None:
"""Tiled decode drives its own cache, so a tiling shape keeps per-chunk decode."""
module = _load_pipeline_module()
processed = _capture_video_processor(monkeypatch)
processed = _capture_pixel_conversion(monkeypatch)
pipeline = _streaming_pipeline(module)
# A latent of 2x2 exceeds a one-latent-cell tile, so vae.decode would tile.
_enable_vae_tiling(pipeline, tile_sample_min=8)
outputs = [
pipeline._decode_chunk_to_pixels(torch.zeros(1, 16, 3, 2, 2), output_type="np", session_id="tiled")
for _ in range(2)
]
outputs = [pipeline._decode_chunk_to_pixels(torch.zeros(1, 16, 3, 2, 2), session_id="tiled") for _ in range(2)]
# One whole-clip decode per AR block, each seeing only that block's frames.
assert [tuple(latents.shape) for latents in pipeline.vae.decode_inputs] == [(1, 16, 3, 2, 2)] * 2
assert pipeline.vae.decoder.first_chunk_flags == []
assert pipeline._streaming_decode_states == {}
# Nine frames per chunk instead of 9 then 12: the frame loss being removed.
assert [shape for shape, _ in processed] == [(1, 3, 9, 16, 16), (1, 3, 9, 16, 16)]
assert processed == [(1, 3, 9, 16, 16), (1, 3, 9, 16, 16)]
assert outputs == ["frames-1", "frames-2"]
@@ -2749,7 +2875,7 @@ def test_streaming_decode_survives_tiling_enabled_below_the_tile_threshold(monke
on a configuration that would never have tiled.
"""
module = _load_pipeline_module()
processed = _capture_video_processor(monkeypatch)
processed = _capture_pixel_conversion(monkeypatch)
pipeline = _streaming_pipeline(module)
# A 2x2 latent is within a tile this size, so vae.decode would not tile.
_enable_vae_tiling(pipeline, tile_sample_min=256)
@@ -2761,7 +2887,7 @@ def test_streaming_decode_survives_tiling_enabled_below_the_tile_threshold(monke
assert pipeline.vae.decode_inputs == []
assert pipeline.vae.decoder.first_chunk_flags == [True] + [False] * 5
assert [shape for shape, _ in processed] == [(1, 3, 9, 16, 16), (1, 3, 12, 16, 16)]
assert processed == [(1, 3, 9, 16, 16), (1, 3, 12, 16, 16)]
def test_registry_and_model_exports_resolve_official_pipeline_class_name() -> None:
@@ -2904,3 +3030,193 @@ def test_stepwise_requests_with_the_same_prompt_share_one_encode() -> None:
assert encoded == ["move through the room"]
assert states[0].prompt_embeds is states[1].prompt_embeds
def test_session_text_caches_publish_key_only_after_a_successful_build() -> None:
module = _load_pipeline_module()
pipeline = _pipeline(module)
state = module._LingBotARSessionState()
invalidations: list[bool] = []
fail_next = {"raise": False}
def fake_text_caches(prompt_embeds, *, invalidate):
invalidations.append(invalidate)
if fail_next["raise"]:
fail_next["raise"] = False
raise RuntimeError("injected failure inside _ar_text_caches")
return ["caches"]
pipeline._ar_text_caches = fake_text_caches
embeds = torch.ones(1, 512, 8)
caches = lambda prompt, length=512, e=embeds: pipeline._session_text_caches( # noqa: E731
e, prompt=prompt, max_sequence_length=length, session_state=state
)
# First build of a session: runner state is empty -> no invalidation, key published.
caches("A")
assert invalidations == [False] and state.text_cache_key == ("A", 512, str(embeds.dtype))
assert state.text_cache_dirty is False
# Same inputs: reuse without invalidation.
caches("A")
assert invalidations == [False, False]
# Regression (Codex review): B's build fails after invalidating -> no key is
# left behind, so retrying A must invalidate again instead of consuming
# partial/mixed K/V.
fail_next["raise"] = True
with pytest.raises(RuntimeError, match="injected failure"):
caches("B")
assert state.text_cache_key is None and state.text_cache_dirty is True
caches("A")
assert invalidations[-2:] == [True, True] and state.text_cache_key == ("A", 512, str(embeds.dtype))
assert state.text_cache_dirty is False
# The key covers every embedding-defining input: same text, other length or dtype -> rebuild.
caches("A", length=256)
caches("A", e=embeds.to(torch.bfloat16))
assert invalidations[-2:] == [True, True]
# ---------------------------------------------------------------------------
# a source image is decoded once while the file behind it is unchanged
# ---------------------------------------------------------------------------
def _write_png(path, colour=(10, 20, 30)):
Image.new("RGB", (64, 64), colour).save(path)
return path
def _counting_decode(module):
"""Replace the file decode with a counter, so the tests measure decodes rather than pixels."""
decoded: list[str] = []
original = module._decode_source_image_file
def decode(path):
decoded.append(os.fspath(path))
return Image.new("RGB", (64, 64), (1, 2, 3))
module._decode_source_image_file = decode
return decoded, original
def test_the_same_source_image_is_decoded_once(tmp_path: Path) -> None:
module = _load_pipeline_module()
module._decode_source_image_fingerprinted.cache_clear()
path = _write_png(tmp_path / "frame.png")
decoded, original = _counting_decode(module)
try:
first = module._load_source_image(path)
second = module._load_source_image(path)
assert decoded == [os.fspath(os.path.realpath(path))] or len(decoded) == 1
# Every caller owns its copy: mutating one must not reach the next caller or the cache.
assert first is not second
first.putpixel((0, 0), (255, 255, 255))
assert module._load_source_image(path).getpixel((0, 0)) == (1, 2, 3)
assert len(decoded) == 1
finally:
module._decode_source_image_file = original
module._decode_source_image_fingerprinted.cache_clear()
def test_a_rewritten_source_image_is_decoded_again(tmp_path: Path) -> None:
module = _load_pipeline_module()
module._decode_source_image_fingerprinted.cache_clear()
path = _write_png(tmp_path / "frame.png")
decoded, original = _counting_decode(module)
try:
module._load_source_image(path)
module._load_source_image(path)
assert len(decoded) == 1
# Same path, different bytes: the mtime/size in the key must force a fresh decode.
os.utime(path, ns=(0, 0))
_write_png(path, colour=(200, 100, 50))
module._load_source_image(path)
assert len(decoded) == 2
finally:
module._decode_source_image_file = original
module._decode_source_image_fingerprinted.cache_clear()
def test_an_image_rewritten_during_the_decode_is_never_cached(tmp_path: Path) -> None:
module = _load_pipeline_module()
module._decode_source_image_fingerprinted.cache_clear()
path = _write_png(tmp_path / "frame.png")
decoded: list[str] = []
original = module._decode_source_image_file
def decode_then_rewrite(p):
decoded.append(os.fspath(p))
_write_png(path, colour=(len(decoded), 0, 0))
os.utime(path, ns=(len(decoded) * 1_000_000, len(decoded) * 1_000_000))
return Image.new("RGB", (64, 64), (1, 2, 3))
module._decode_source_image_file = decode_then_rewrite
try:
module._load_source_image(path)
# The entry is keyed by the fingerprint taken before the decode, which the rewrite invalidated: the
# next call must decode again rather than serve the image that was decoded mid-rewrite.
module._load_source_image(path)
assert len(decoded) == 2
finally:
module._decode_source_image_file = original
module._decode_source_image_fingerprinted.cache_clear()
def test_an_unstattable_path_keeps_upstream_validation(tmp_path: Path) -> None:
module = _load_pipeline_module()
module._decode_source_image_fingerprinted.cache_clear()
missing = tmp_path / "does-not-exist.png"
# The uncached path owns the error contract; caching must not change which exception a caller sees.
with pytest.raises(ValueError):
module._load_source_image(missing)
assert module._decode_source_image_fingerprinted.cache_info().currsize == 0
def test_the_text_cache_is_sized_by_cross_attention_heads_not_the_self_attention_share() -> None:
"""Cross-attention replicates heads per rank, so its pool must not inherit the self-attention head count."""
module = _load_pipeline_module()
pipeline = _pipeline(module)
pipeline.transformer.blocks[0].self_attn = SimpleNamespace(num_sp_heads=1)
pipeline.transformer.blocks[0].cross_attn = SimpleNamespace(num_local_heads=4)
spec = pipeline.ar_diffusion_kv_cache_spec()
assert spec.num_kv_heads == 1
assert spec.cross_attention_kv_heads == {"text": 4}
assert spec.cross_attention_lengths == {"text": 512}
@pytest.mark.parametrize("output_type", ["np", "pil"])
def test_streamed_chunk_is_uint8_frames_whatever_pixel_output_type_was_asked(monkeypatch, output_type) -> None:
"""A realtime tick delivers (F, H, W, 3) uint8 for every pixel output type; only latent differs."""
module = _load_pipeline_module()
pipeline = _streaming_pipeline(module)
state = _stepwise_state(num_frames=21)
state.sampling.output_type = output_type
with pipeline.bind_ar_diffusion_state(state.request_id, _FakeARState(state.request_id)):
outputs = _run_stepwise(pipeline, state)
videos = [output.output["payload"]["video"] for output in outputs]
assert [type(video) for video in videos] == [np.ndarray, np.ndarray]
assert [video.dtype for video in videos] == [np.uint8, np.uint8]
assert [video.shape for video in videos] == [(9, 16, 16, 3), (12, 16, 16, 3)]
def test_uint8_frames_match_the_pil_conversion_byte_for_byte() -> None:
"""The device-side conversion is the PIL path's arithmetic, so the bytes are the same."""
from diffusers.video_processor import VideoProcessor
module = _load_pipeline_module()
generator = torch.Generator().manual_seed(7)
# Cover the clamp on both sides and values that sit exactly on rounding edges.
video = torch.rand(1, 3, 5, 8, 8, generator=generator) * 2.4 - 1.2
video[0, :, 0, 0, :4] = torch.tensor([-1.0, 1.0, 0.0, 1 / 255 - 1.0])
reference = VideoProcessor(vae_scale_factor=8).postprocess_video(video, output_type="pil")[0]
expected = np.stack([np.asarray(frame) for frame in reference])
frames = module._uint8_frames(video)
assert frames.dtype == np.uint8 and frames.shape == (5, 8, 8, 3)
assert frames.flags["C_CONTIGUOUS"]
np.testing.assert_array_equal(frames, expected)