From 6bb444eef1845594f2406ccb18889a4ca83ac90f Mon Sep 17 00:00:00 2001 From: Aryan Kumar Date: Tue, 27 Oct 2026 02:53:15 -0700 Subject: [PATCH] [bugfix]: run the base layer while a LoRA wrapper has no adapter --- fastvideo/layers/lora/linear.py | 7 +++++-- fastvideo/tests/layers/test_lora_side_branch.py | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/fastvideo/layers/lora/linear.py b/fastvideo/layers/lora/linear.py index 655b6e556..44d4a7a38 100644 --- a/fastvideo/layers/lora/linear.py +++ b/fastvideo/layers/lora/linear.py @@ -163,8 +163,11 @@ class BaseLayerWithLoRA(nn.Module): lora_B = self.lora_B.to_local() lora_A = self.lora_A.to_local() - if self.side_branch and (self.lora_A is None or self._strength_is_zero): - # No adapter, or strength 0, is exactly the unadapted layer, down to the output dtype. + if self.lora_A is None or (self.side_branch and self._strength_is_zero): + # No adapter, or strength 0 on a side branch, is exactly the unadapted layer, down to the output dtype. + # A merge-mode wrapper reaches this branch while its first adapter is still loading or after a load + # failed: the pipeline wraps every linear before it reads the weights, so an unadapted request must + # not depend on an adapter that is not there. return self.base_layer(x) if not self.merged and not self.disable_lora: diff --git a/fastvideo/tests/layers/test_lora_side_branch.py b/fastvideo/tests/layers/test_lora_side_branch.py index 706927b7e..ca2f093f6 100644 --- a/fastvideo/tests/layers/test_lora_side_branch.py +++ b/fastvideo/tests/layers/test_lora_side_branch.py @@ -54,6 +54,20 @@ def _packed_linear(seed: int = 0) -> ReplicatedLinear: return layer +def test_a_wrapped_layer_without_an_adapter_runs_the_base_layer() -> None: + """A dense wrapper with no adapter is the base layer. + + The pipeline wraps every linear when the first runtime adapter is set. A render that then runs without one, or a + load that failed part-way, must still produce the base model's output instead of failing on the missing factors. + """ + base = _dense_linear() + wrapped = get_lora_layer(base) + assert not wrapped.side_branch and wrapped.lora_A is None + x = torch.randn(2, IN, dtype=torch.bfloat16) + with torch.no_grad(): + assert torch.equal(wrapped(x)[0], base(x)[0]) + + def _factors(seed: int = 1) -> tuple[torch.Tensor, torch.Tensor]: generator = torch.Generator().manual_seed(seed) return torch.randn(RANK, IN, generator=generator), torch.randn(OUT, RANK, generator=generator)