Skip to content

LTX-2.5 HQ: res2s_two_stage runs both stages at LoRA strength 1.0 where upstream runs 0.25/0.5, and the phase seam's boolean no-op would silently swallow a strength field #1144

Description

@localai-bot

Res2sTwoStageRecipe is this tree's port of TI2VidTwoStagesHQPipeline
(ti2vid_two_stages_hq.py:59 @ Lightricks/LTX-2 fd4ded7f), and HQ is the one
pipeline in scope that runs the SAME adapter on both stages at DIFFERENT
strengths. It builds distilled_lora_stage_1 and distilled_lora_stage_2 from
one path at distilled_lora_strength_stage_1 / _stage_2
(ti2vid_two_stages_hq.py:92-101) and hands one to each
DiffusionStage.from_checkpoint (:154, :165). The CLI defaults them 0.25
and 0.5
(utils/args.py:1174-1184).

Today res2s_two_stage runs both stages at 1.0. Neither of
Res2sTwoStageRecipe's phases sets Ltx2PhaseRecipe::loras, so both take the
kAllAdapters default, and the engine carries ONE strength for the whole load
(lora_strength absent is 1.0, include/vllm/multimodal/ltx2_video.h:214-218) —
there is no per-phase strength anywhere. Nothing refuses, nothing changes shape,
and the clip comes back at the size, frame count and sample rate asked for. The
only instrument that sees it is a render against upstream on the same checkpoint,
take and seed.

Why a new field on Ltx2PhaseRecipe does NOT close this

Row LTX25-PHASE-LORA (#1118, PR #1140) landed the per-phase adapter seam, so
the old "we hold one DiT fused at load" blocker is gone. But the seam's state is
a boolean, and adding Ltx2PhaseRecipe::lora_strength on top of it would
fail SILENTLY:

// src/vllm/model_executor/models/ltx2_loader.cpp, Ltx2RebindDitLoras
const bool currently_fused = checkpoint.lora_fused_tensors > 0;
if (currently_fused == fuse) return;

Ltx2RebindDitLoras's header calls this "a no-op it detects itself". It detects
"already FUSED", never "already fused AT THIS STRENGTH". HQ has both stages
fused, so currently_fused == fuse holds at the stage boundary, the rebind
returns having done nothing, and stage 2 renders at stage 1's strength — the
exact defect the field was added to fix, wearing a green.

What closing this needs

Two changes beyond the field, and they are modest growth rather than a redesign:

  1. bool fuse becomes a type that can carry a strength (upstream's
    LoraPathStrengthAndSDOps, effectively).
  2. Ltx2DitCheckpoint records which adapter state is applied rather than
    merely whether one is, so the no-op test compares states instead of a bit.

The re-materialize-and-write-back mechanism itself is untouched by either:
re-materializing from the pristine file already reaches any strength in one pass,
which is the property #1118 chose it for.

Also needed, and cheap once the above exists: a --distilled-lora-strength-stage-1
/ -stage-2 pair on the request surface, since one lora_strength extra cannot
spell two values.

Why this is filed rather than inherited by #921

#921 owned the per-phase strength in .agents/specs/ltx25-phase-lora.md's
## Owed, and it was closed as completed on 2026-08-17 by
LTX25-RES2S-LOOP (PR #1125, 4d7748646), which landed the res_2s sampler.
That row named "the distilled LoRA per stage" as explicitly out of scope and
"unchanged by this one" — correctly — but did not list it under its own
## Owed, so the debt outlived its issue with no open owner. This restores one.

The trap is written beside the early return in
include/vllm/model_executor/models/ltx2_loader.h so the next person to grow the
seam meets it in the header rather than in a render, and this issue is listed
under ## Owed in
.agents/specs/ltx25-phase-lora.md.

Found during the review-repair of PR #1140.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions