Skip to content
Merged
29 changes: 22 additions & 7 deletions docs/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -331,13 +331,17 @@ In particular, the encoders being present does NOT mean image, keyframe,
reference-video or reference-audio conditioning is usable: the video engine
still refuses every one of those by name, because the request-side work between
a file on disk and a tensor the encoder accepts — image decode, aspect-fill
resize, and the H.264 CRF re-compression upstream performs before encoding — is
not ported. Two encoder-level limits are worth stating in advance because they
are refusals rather than approximations. A reference waveform whose sample rate
differs from the audio VAE's is refused rather than resampled, since upstream
uses a polyphase kaiser resampler this project does not carry. And a VAE
configured with `latent_log_var: none` is refused, because upstream itself
raises on it.
resize, and the H.264 CRF re-compression upstream performs before encoding
whenever the resolved CRF is not `0` and the image is at least 2 pixels on its
shorter side — is not ported. The engine also holds no
encoder to call: it materializes the VAE DECODER key filters only, so no
encoder weights are ever in memory, and the refusal names that rather than
claiming the encoder itself is missing. Two encoder-level limits are worth
stating in advance because they are refusals rather than approximations. A
reference waveform whose sample rate differs from the audio VAE's is refused
rather than resampled, since upstream uses a polyphase kaiser resampler this
project does not carry. And a VAE configured with `latent_log_var: none` is
refused, because upstream itself raises on it.

One behaviour is worth stating in advance, because it decides what you get when
the entrypoint does arrive. LTX-2.5 ships two video decoders behind one
Expand Down Expand Up @@ -1557,6 +1561,17 @@ python3 scripts/gen-ltx2-pipeline-goldens.py \
cmake --build build --target test_ltx2_pipeline && ./build/tests/test_ltx2_pipeline
```

If you regenerate that `.inc` against a moved upstream, expect the goldens to
carry the change rather than only the pin cases. The pipeline goldens reach the
GroupNorm eps and group count in the latent upsampler, the connector's
`rms_norm` eps, the `BlurDownsample` width (on the 1.5 arm only, since the blur
runs on the rational denominator) and the Res2s `sigma_up` clamp — that last one
on the eta = 1 arm, where the clamp binds on every step. A regeneration that
moves one of those constants alone reds a value comparison; one that moves the
constant AND the tensors together passes it, and is caught only by the cases that
compare each constant against upstream's own signature. Both layers are there
deliberately, and neither is redundant.

Recipes resolve on an EXACT `(pipeline_kind, model_version)` pair and refuse
anything else by name rather than defaulting, because a plausible but wrong sigma
schedule or guidance scale renders a video instead of failing. The pairs that
Expand Down
8 changes: 8 additions & 0 deletions include/vllm/model_executor/models/ltx2.h
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,14 @@ struct Ltx2AttentionArgs {
int64_t context_dim = 0;
int64_t heads = 0;
int64_t dim_head = 0;
// The q/k RMSNorm eps: `Attention.__init__`'s `norm_eps: float = 1e-6`
// (attention.py:485), handed to both RMSNorms (attention.py:505-506).
//
// This DEFAULT is read by nothing today — every construction of this struct
// assigns it — and a 10^6 mutation of it leaves every suite green. It is a
// latent trap rather than live code, so the only instrument that can hold it is
// the pin in tests/vllm/models/test_ltx2.cpp. Keep it equal to
// `Ltx2DitParams::norm_eps`, which is where every real call site sources it.
double norm_eps = 1e-6;
Ltx2RopeType rope_type = Ltx2RopeType::kSplit;
const Ltx2FreqsCis* pe = nullptr;
Expand Down
35 changes: 28 additions & 7 deletions include/vllm/model_executor/models/ltx2_audio_vae.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,22 @@ struct Ltx2AudioDecoderConfig {
// The decoder's TARGET mel-bin count. 0 keeps whatever the latent carried,
// mirroring `mel_bins=None` (audio_vae.py:422).
int64_t mel_bins = 0;
// Only read on the GroupNorm arm; PixelNorm has no parameters at all.
// Only read on the GroupNorm arm; PixelNorm has no parameters at all. Neither
// is reachable from a checkpoint: `build_normalization_layer` passes `eps=1e-6`
// as a LITERAL and forwards its own `num_groups` keyword, whose default is 32
// (normalization.py:44, 56), and no audio_vae call site passes `num_groups`.
// They are fields here so the gate can pin them.
//
// `norm_type = kGroup` is not a dead arm, but it is not what pure defaults give
// you either. `AudioDecoder.__init__` declares `norm_type = GROUP`
// (audio_vae.py:294) and, on the very next line, `causality_axis = WIDTH`
// (audio_vae.py:295) — and `ResnetBlock` refuses that combination with
// `ValueError: Causal ResnetBlock with GroupNorm is not supported`
// (resnet.py:130-131), verified by construction against the pinned upstream. A
// group-norm checkpoint is therefore one that declares `causality_axis: none`
// alongside it, which is legal and is what the group-norm golden in
// test_ltx2_vae.cpp runs. Before that arm existed this eps was never READ on
// any path, and a 100x change moved nothing.
int64_t num_groups = 32;
double norm_eps = 1e-6;
// The audio VAE reaches PixelNorm through `build_normalization_layer`, which
Expand Down Expand Up @@ -188,12 +203,18 @@ std::vector<float> Ltx2VocoderForward(const Ltx2VocoderConfig& config,
// ---------------------------------------------------------------------------

// The floor under the BWE mel BEFORE its log: `torch.clamp(mel, min=1e-5)`
// (vocoder.py:516). Named so it can be pinned, because it is the member of the
// invisible-constant class that actually bites in production: it sets the floor of
// the log-mel fed to the bwe_generator, and REAL SILENCE reaches it. A
// reduced-dimension golden built from the deterministic stream cannot, because
// that stream's mel_basis is non-negative and well-scaled and nothing saturates —
// mutation proves 1e-5 -> 1e-8 leaves every tensor golden green.
// (vocoder.py:515). Named so it can be pinned, because it sets the floor of the
// log-mel fed to the bwe_generator and REAL SILENCE reaches it in production.
//
// It is NOT a member of the invisible-constant class described in
// ltx2_video_vae.h, and the line here that said it was is corrected rather than
// carried: the ORDINARY BWE arm's mel_basis is non-negative and well-scaled and
// its raw minimum is ~4.4e-3, so that arm alone cannot move under a mutation.
// "ltx2 vae: the BWE mel log clamp is gated where it actually binds" attenuates
// mel_basis until every bin saturates the floor, and against it 1e-5 -> 1e-8 REDS
// at max|diff| = 0.144965 versus the 5e-6 band. The pin below stays anyway, for
// what no golden can see: a regeneration that moves the constant and the expected
// tensors together.
inline constexpr double kLtx2BweMelLogClamp = 1e-5;

struct Ltx2VocoderBweConfig {
Expand Down
17 changes: 16 additions & 1 deletion include/vllm/model_executor/models/ltx2_audio_vae_encoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,22 @@ struct Ltx2AudioEncoderConfig {
bool mid_block_add_attention = true;
Ltx2NormType norm_type = Ltx2NormType::kPixel;
Ltx2CausalityAxis causality_axis = Ltx2CausalityAxis::kHeight;
// Only read on the GroupNorm arm; PixelNorm is parameter-free.
// Only read on the GroupNorm arm; PixelNorm is parameter-free. Neither is
// reachable from a checkpoint: `build_normalization_layer` passes `eps=1e-6` as
// a LITERAL and forwards its own `num_groups` keyword, whose default is 32
// (normalization.py:44, 56), and no audio_vae call site passes `num_groups` at
// all — so they are fields only so the gate can pin them.
//
// `norm_type = kGroup` is `AudioEncoder.__init__`'s declared default
// (audio_vae.py:82), but it is NOT what pure defaults give you: the paired
// default is `causality_axis = WIDTH` (audio_vae.py:83), and `ResnetBlock`
// refuses GroupNorm on any causal axis with
// `ValueError: Causal ResnetBlock with GroupNorm is not supported`
// (resnet.py:130-131) — verified by construction against the pinned upstream.
// So a group-norm checkpoint is one that declares `causality_axis: none`
// alongside it, which is legal and is what the group-norm golden in
// test_ltx2_vae.cpp runs. That arm is what stopped this eps from being a
// constant no arm ever read.
int64_t num_groups = 32;
double norm_eps = 1e-6;
// Reached through `build_normalization_layer`, which passes eps=1e-6
Expand Down
15 changes: 10 additions & 5 deletions include/vllm/model_executor/models/ltx2_connector.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,16 @@ namespace vllm {

// utils.py:7-12 — `torch.nn.functional.rms_norm`'s eps as `rms_norm` passes it.
// The connector uses the WEIGHTLESS form, so this is the only stabilizer in the
// residual path. A member of the invisible-constant class (the fixture's rows are
// never near-zero), so the value comparison does NOT gate it. What gates it is
// test_ltx2_pipeline.cpp, case "the constants the headers call pinned are
// actually pinned", which compares this against upstream's own `rms_norm`
// signature default rather than a retyped literal.
// residual path. NOT a member of the invisible-constant class, however near-zero
// the fixture's rows are: `rms_norm` adds the epsilon to the MEAN SQUARE, not to
// a row minimum, so it perturbs every row it normalizes. At the class's own 100x
// bar (1e-6 -> 1e-4) it REDS 5 of the arms in "ltx2 the Embeddings1DConnector
// reproduces upstream on every arm" — Split 0.0558581, Interleaved 0.104284,
// Float64 0.140343, NoRegisters 0.000542641, GatedNoBias 0.0892045.
// It is pinned as well as gated, in test_ltx2_pipeline.cpp, case "the constants
// the headers call pinned are actually pinned", which compares this against
// upstream's own `rms_norm` signature default rather than a retyped literal —
// the one check a regenerated golden cannot satisfy by moving with it.
inline constexpr double kLtx2ConnectorRmsNormEps = 1e-6;

// Embeddings1DConnector.__init__ defaults (:95-108), which are also both
Expand Down
17 changes: 13 additions & 4 deletions include/vllm/model_executor/models/ltx2_pipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,19 @@ struct Ltx2SdeCoeff {
double sigma_up = 0.0;
};

// diffusion_steps.py:138. The fallback that keeps `sqrt(sigma_next^2 - sigma_up^2)`
// off zero. Pinned because it never binds on a well-formed schedule (eta <= 1
// keeps sigma_up <= sigma_next), so no value comparison can see it — a member of
// the invisible-constant class the spec's section 7.0(a) names.
// diffusion_steps.py:138. What keeps `sqrt(sigma_next^2 - sigma_up^2)` off zero,
// and it BINDS on the ordinary eta = 1 schedule rather than only on a malformed
// one. `step` forms `sigma_up = sigma_next * eta`, so eta <= 1 gives
// sigma_up <= sigma_next — but <= includes ==, and at equality `min` takes
// `sigma_next * 0.9999`, which is the whole point: without it the residual is
// exactly 0 and `sigma_down` collapses. The earlier note reasoned from the
// inequality and skipped its boundary, calling the constant invisible; it is not.
// A 1% move (0.9999 -> 0.99) REDS the Eta1 arm the suite already runs, at
// max|diff| = 0.086 (index 0) and 0.130563 (index 1), because the residual scales
// as sqrt(1 - clamp^2) and that is 10x larger at 0.99. The EtaHalf arm stays green
// (0.5 * sigma_next is below the clamp) and Eta1 index 2 stays green
// (sigma_next == 0 returns the denoised prediction unchanged, :181-182). Pinned
// as well, because a regenerated golden would move with the constant.
inline constexpr double kLtx2Res2sSigmaUpClamp = 0.9999;

Ltx2SdeCoeff Ltx2Res2sSdeCoeff(double sigma_next, double sigma_up);
Expand Down
36 changes: 29 additions & 7 deletions include/vllm/model_executor/models/ltx2_text_encoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,20 @@
// `Ltx2TextFeatureExtractorForward`.
//
// ─── AND THE EPSILONS, WHICH ARE A CLASS AND NOT ONE INSTANCE ────────────────
// A constant that only changes the answer on a DEGENERATE input is invisible to
// any golden built from random values. Both normalizations have one, they are
// named below, and each is held two ways: its VALUE against upstream measured by
// probe, and the degenerate input on which it is the only thing between the port
// and a division by zero. When a fourth epsilon arrives, it owes the same pair —
// A constant that only changes the answer on a DEGENERATE input is invisible to a
// golden built from random values — but NEITHER of the two below is that
// constant, and the detailed note at kLtx2TextNormV1Eps already says why for the
// V1 half. Both are additive terms on an O(1) denominator against a 1e-5 band, so
// at the 100x bar they move ORDINARY random-value goldens: V1 1e-6 -> 1e-4 REDS
// "`_norm_and_concat_padded_batch`, both padding sides" at 0.000524044 and
// "FeatureExtractorV1" at 7.53999e-05 / 6.61612e-05; V2 1e-6 -> 1e-4 REDS
// "`norm_and_concat_per_token_rms`, both padding sides" at 0.00232971 and
// carries into "FeatureExtractorV2" and the hand-off at 0.000344872 /
// 0.000259042 / 0.00039053. They are held two ways all the same: their VALUE
// against upstream measured by probe, and the degenerate input on which each is
// the only thing between the port and a division by zero — that input is what
// makes a division-by-zero visible, not what makes the constant visible at all.
// When a fourth epsilon arrives, it owes the same pair —
// not a comment saying it matches upstream.
//
// ─── DTYPE ───────────────────────────────────────────────────────────────────
Expand Down Expand Up @@ -161,10 +170,23 @@ struct Ltx2TextHiddenStates {
// lands around 2e-12 at the output — small, but not the "any input" the earlier
// comment claimed. Both the value and the arithmetic width are gated on the
// degenerate inputs, where they are visible.
//
// That is the ONE-ULP dtype perturbation. A VALUE move of the size the pin
// exists to catch is far louder and needs no degenerate input at all: 1e-6 ->
// 1e-4 REDS "`_norm_and_concat_padded_batch`, both padding sides" at 0.000524044
// and "FeatureExtractorV1" at 7.53999e-05 / 6.61612e-05, on the ordinary
// random-value goldens.
inline constexpr double kLtx2TextNormV1Eps = 1e-6;

// feature_extractor.py:61 — `torch.rsqrt(variance + 1e-6)`. Reachable only when a
// token's whole hidden slice is zero.
// feature_extractor.py:61 — `torch.rsqrt(variance + 1e-6)`. DIVISION-BY-ZERO-
// reachable only when a token's whole hidden slice is zero, which is the narrow
// claim the earlier "reachable only when" was making and the wrong one to state
// alone: the epsilon is added to an O(1) variance, so it is OUTPUT-observable on
// ordinary random values too. 1e-6 -> 1e-4 REDS
// "`norm_and_concat_per_token_rms`, both padding sides" at 0.00232971, and the
// perturbation carries through the projections into "FeatureExtractorV2" and
// "the encoder -> conditioning hand-off" at 0.000344872 / 0.000259042 /
// 0.00039053 against the suite's 1e-5 kTol. Same shape as the V1 half above.
inline constexpr float kLtx2TextNormV2Eps = 1e-6f;

// feature_extractor.py:12-64. Which of the two normalizations runs.
Expand Down
17 changes: 12 additions & 5 deletions include/vllm/model_executor/models/ltx2_upsampler.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,14 @@ namespace vllm {
// literal on all three sites.
inline constexpr int64_t kLtx2UpsamplerNormGroups = 32;
// torch's `nn.GroupNorm` default `eps` (it is not passed at any of the three
// construction sites). A member of the invisible-constant class: the reduced
// fixture's variances are O(1), so a mutation of it leaves every golden green,
// and it is therefore pinned here against the upstream default rather than by the
// value comparison.
// construction sites). NOT a member of the invisible-constant class. It was
// recorded as one on a mutation that happened not to move anything, and a
// mutation that moves nothing proves nothing; at the class's OWN 100x bar
// (1e-5 -> 1e-3) it REDS all three arms of
// "ltx2 the latent spatial upsampler reproduces upstream" —
// PixelShuffle 0.0289409, Rational2 0.0347079, Rational1p5 0.0649014. The pin
// below still earns its place, because a golden regenerated with a moved eps
// moves with it and only the pin compares against torch's own default.
inline constexpr double kLtx2UpsamplerNormEps = 1e-5;

// LatentUpsampler.__init__ defaults (model.py:25-35), which are also
Expand Down Expand Up @@ -99,7 +103,10 @@ std::vector<float> Ltx2BlurKernel(int64_t kernel_size);
// `SpatialRationalResampler` never overrides (:38) — so this default IS the
// shipped kernel width. Gated against upstream's own signature by
// test_ltx2_pipeline.cpp, case "the constants the headers call pinned are
// actually pinned".
// actually pinned", and reached NUMERICALLY as well: 5 -> 3 REDS the Rational1p5
// arm of "ltx2 the latent spatial upsampler reproduces upstream" at 0.689782.
// Only that arm, because the blur runs on the rational `den` and 1.5 -> {3, 2} is
// the one scale the suite covers with den != 1.
inline constexpr int64_t kLtx2BlurKernelSize = 5;

// The parameter contract: every tensor `LatentUpsampler(config)` creates, in
Expand Down
Loading