mirror of
https://github.com/vladmandic/automatic
synced 2026-08-27 15:41:00 +02:00
5814d5c4b3
2.5 reuses the LTX-2 pipeline classes, so it is described through the capability table: Gemma 4 text encoder, cross-timestep conditioning, and the upsampler and stage 2 LoRA that now ship inside the model repo. The repo carries the distilled scheduler config, so Dev rows restore the terminal shift, and the Dev transformer sits in transformer_full. Distilled rows stop forcing dynamic shifting on, which remapped their sigma schedule. Auto duration hands the clip length to the duration head.
168 lines
6.3 KiB
Python
168 lines
6.3 KiB
Python
from dataclasses import dataclass, field
|
|
from typing import Optional
|
|
|
|
from modules.logger import log
|
|
|
|
|
|
@dataclass
|
|
class LTXCaps:
|
|
name: str
|
|
repo_cls_name: str
|
|
family: str # '0.9' or '2.x'
|
|
variant: str # '0.9', '2.0', '2.3', '2.5' (finer-grained sub-variant)
|
|
is_distilled: bool
|
|
is_i2v: bool
|
|
supports_input_media: bool
|
|
supports_multi_condition: bool
|
|
supports_image_cond_noise_scale: bool
|
|
supports_decode_timestep: bool
|
|
supports_stg: bool
|
|
supports_audio: bool
|
|
supports_frame_rate_kwarg: bool
|
|
# 2.3 transformer cross-attn reads the other modality's sigma; unset falls back to 2.0's
|
|
# independent-sigma path, which is a joint-distribution mismatch for 2.3 weights.
|
|
use_cross_timestep: bool
|
|
default_cfg: float
|
|
default_steps: int
|
|
default_sampler_shift: float
|
|
default_dynamic_shift: bool
|
|
default_width: int
|
|
default_height: int
|
|
default_frames: int
|
|
default_frame_rate: int
|
|
stg_default_scale: float = 0.0
|
|
stg_default_blocks: list = field(default_factory=list)
|
|
# Dev 2.x trained under cfg + stg + modality + rescale four-way composition;
|
|
# distilled bakes these into its sigma schedule and stays at pipeline identity.
|
|
modality_default_scale: float = 1.0
|
|
guidance_rescale_default: float = 0.0
|
|
supports_canonical_stage2: bool = False
|
|
# Stage 1 at target/2 -> 2x upsample -> Stage 2 at target. Distilled needs upsampled latents
|
|
# for refine; same-res refine oversaturates. Condition variants rebuild conditions per stage.
|
|
supports_two_stage_refine: bool = False
|
|
stage2_dev_lora_repo: Optional[str] = None
|
|
# 2.5 keeps the stage 2 LoRA in the model repo; 2.0 and 2.3 each have their own
|
|
stage2_dev_lora_weight: Optional[str] = None
|
|
# tied to the family VAE: the wrong one drifts per-channel latent statistics
|
|
upsample_repo: Optional[str] = None
|
|
# 2.5 ships the distilled scheduler config, so its Dev rows restore the terminal shift
|
|
scheduler_shift_terminal: Optional[float] = None
|
|
supports_auto_duration: bool = False
|
|
|
|
|
|
CONDITION_CLASSES = {'LTXConditionPipeline', 'LTX2ConditionPipeline'}
|
|
LTX2_CLASSES = {'LTX2Pipeline', 'LTX2ImageToVideoPipeline', 'LTX2ConditionPipeline'}
|
|
ALL_LTX_CLASSES = {
|
|
'LTXPipeline',
|
|
'LTXImageToVideoPipeline',
|
|
'LTXConditionPipeline',
|
|
'LTX2Pipeline',
|
|
'LTX2ImageToVideoPipeline',
|
|
'LTX2ConditionPipeline',
|
|
}
|
|
|
|
|
|
def _repo_cls_name(model_name: str) -> Optional[str]:
|
|
from modules.video_models.models_def import models
|
|
entries = models.get('LTX Video', [])
|
|
for m in entries:
|
|
if m.name == model_name:
|
|
if m.repo_cls is None:
|
|
return None
|
|
elif isinstance(m.repo_cls, str):
|
|
return m.repo_cls
|
|
else:
|
|
return m.repo_cls.__name__
|
|
return None
|
|
|
|
|
|
def get_caps(model_name: str) -> Optional[LTXCaps]:
|
|
if not model_name or model_name == 'None':
|
|
return None
|
|
cls_name = _repo_cls_name(model_name)
|
|
if cls_name is None:
|
|
log.warning(f'LTX caps: model="{model_name}" has no repo_cls registered')
|
|
return None
|
|
if cls_name not in ALL_LTX_CLASSES:
|
|
log.warning(f'LTX caps: model="{model_name}" repo_cls="{cls_name}" is not an LTX pipeline')
|
|
return None
|
|
|
|
is_ltx2 = cls_name in LTX2_CLASSES
|
|
family = '2.x' if is_ltx2 else '0.9'
|
|
# 2.x sub-variant detection: unknown 2.x mirrors fall through to '2.0' (conservative default).
|
|
if is_ltx2:
|
|
if '2.5' in model_name:
|
|
variant = '2.5'
|
|
elif '2.3' in model_name:
|
|
variant = '2.3'
|
|
else:
|
|
variant = '2.0'
|
|
else:
|
|
variant = '0.9'
|
|
is_distilled = 'Distilled' in model_name
|
|
is_i2v = 'I2V' in model_name or cls_name in ('LTXImageToVideoPipeline', 'LTX2ImageToVideoPipeline')
|
|
is_condition_cls = cls_name in CONDITION_CLASSES
|
|
supports_input_media = is_i2v or is_condition_cls
|
|
|
|
caps = LTXCaps(
|
|
name=model_name,
|
|
repo_cls_name=cls_name,
|
|
family=family,
|
|
variant=variant,
|
|
is_distilled=is_distilled,
|
|
is_i2v=is_i2v,
|
|
supports_input_media=supports_input_media,
|
|
supports_multi_condition=is_condition_cls,
|
|
supports_image_cond_noise_scale=(cls_name == 'LTXConditionPipeline'),
|
|
supports_decode_timestep=(family == '0.9'),
|
|
supports_stg=is_ltx2,
|
|
supports_audio=is_ltx2,
|
|
supports_frame_rate_kwarg=is_ltx2,
|
|
use_cross_timestep=variant in ('2.3', '2.5'),
|
|
default_cfg=3.0,
|
|
default_steps=30 if is_ltx2 else 50,
|
|
default_sampler_shift=-1.0,
|
|
# distilled ships use_dynamic_shifting=False and runs explicit sigmas, which shifting remaps
|
|
default_dynamic_shift=is_ltx2 and not is_distilled,
|
|
default_width=768,
|
|
default_height=512,
|
|
default_frames=121 if is_ltx2 else 161,
|
|
default_frame_rate=24 if is_ltx2 else 25,
|
|
)
|
|
|
|
if is_distilled:
|
|
caps.default_cfg = 1.0
|
|
caps.default_steps = 8
|
|
|
|
if is_ltx2 and not is_distilled:
|
|
if variant == '2.5':
|
|
caps.stage2_dev_lora_repo = 'Lightricks/LTX-2.5-Diffusers'
|
|
caps.stage2_dev_lora_weight = 'ltx-2.5-22b-distilled-lora-450-bf16.safetensors'
|
|
elif variant == '2.3':
|
|
caps.stage2_dev_lora_repo = 'CalamitousFelicitousness/LTX-2.3-distilled-lora-384-Diffusers'
|
|
elif variant == '2.0':
|
|
caps.stage2_dev_lora_repo = 'CalamitousFelicitousness/LTX-2.0-distilled-lora-384-Diffusers'
|
|
caps.supports_canonical_stage2 = caps.stage2_dev_lora_repo is not None
|
|
caps.supports_two_stage_refine = is_ltx2
|
|
|
|
if is_ltx2:
|
|
if variant == '2.5':
|
|
caps.upsample_repo = 'Lightricks/LTX-2.5-Diffusers'
|
|
caps.stg_default_blocks = [28]
|
|
caps.supports_auto_duration = True
|
|
elif variant == '2.3':
|
|
caps.upsample_repo = 'CalamitousFelicitousness/LTX-2.3-Spatial-Upsampler-x2-1.1-Diffusers'
|
|
caps.stg_default_blocks = [28]
|
|
else:
|
|
caps.upsample_repo = 'Lightricks/LTX-2'
|
|
caps.stg_default_blocks = [29]
|
|
if not is_distilled:
|
|
# canonical T2V composition from huggingface/diffusers#13217
|
|
caps.stg_default_scale = 1.0
|
|
caps.modality_default_scale = 3.0
|
|
caps.guidance_rescale_default = 0.7
|
|
if variant == '2.5':
|
|
caps.scheduler_shift_terminal = 0.1
|
|
|
|
return caps
|