mirror of
https://github.com/vladmandic/automatic
synced 2026-09-19 17:24:32 +02:00
+4
-2
@@ -1,8 +1,8 @@
|
||||
# Change Log for SD.Next
|
||||
|
||||
## Update for 2026-07-12
|
||||
## Update for 2026-07-13
|
||||
|
||||
### Highlights for 2026-07-12
|
||||
### Highlights for 2026-07-13
|
||||
|
||||
*What's New?* Full week(!) since there release, we're bringing a service pack update:
|
||||
**Anima** has new *Aesthetic* and *Turbo* variants
|
||||
@@ -38,6 +38,7 @@ But also:
|
||||
- log view copy server log and copy client log buttons
|
||||
copies current log to clipboard for easy sharing
|
||||
- collapsible input & output panels for *video*, *caption*, *process* tabs
|
||||
- video add immediate load
|
||||
- **Experimental**
|
||||
- support for `comfy_quant` models
|
||||
- **Fixes**
|
||||
@@ -50,6 +51,7 @@ But also:
|
||||
- server-info: handle multiple gpus
|
||||
- load: skip redundant model reload
|
||||
- sdnq: fix hadamard on sdnq atten with sd15
|
||||
- download: improved progress tracking
|
||||
|
||||
## Update for 2026-07-07
|
||||
|
||||
|
||||
+1
-1
@@ -549,7 +549,7 @@ def check_diffusers():
|
||||
t_start = time.time()
|
||||
if args.skip_all:
|
||||
return
|
||||
target_commit = "ea802951f5fb235b6af8fe9247f56187d49748b2" # diffusers commit hash == 0.39.0.dev0 == 06-29-2026
|
||||
target_commit = "01969142b55379991fee07608c9e7e8f80afced0" # diffusers commit hash == 0.39.0.dev0 == 06-29-2026
|
||||
# if args.use_rocm or args.use_zluda or args.use_directml:
|
||||
# sha = '043ab2520f6a19fce78e6e060a68dbc947edb9f9' # lock diffusers versions for now
|
||||
pkg = package_spec('diffusers')
|
||||
|
||||
@@ -4,7 +4,7 @@ from modules import ui_sections, ui_symbols
|
||||
from modules.ui_components import ToolButton
|
||||
from modules.logger import log
|
||||
from modules.video_models.models_def import models
|
||||
from modules.ltx import ltx_process, ltx_capabilities
|
||||
from modules.ltx import ltx_process, ltx_capabilities, ltx_util
|
||||
|
||||
|
||||
debug = log.trace if os.environ.get('SD_VIDEO_DEBUG', None) is not None else lambda *args, **kwargs: None
|
||||
@@ -63,6 +63,7 @@ def create_ui(prompt, negative, styles, overrides, mp4_fps, mp4_interpolate, mp4
|
||||
with gr.Row():
|
||||
ltx_models = [m.name for m in models['LTX Video']] if 'LTX Video' in models else ['None']
|
||||
model = gr.Dropdown(label='LTX model', choices=ltx_models, value=ltx_models[0], elem_id="ltx_model")
|
||||
btn_load = ToolButton(ui_symbols.loading, elem_id="video_model_load_ltx")
|
||||
with gr.Accordion(open=False, label='Size', elem_id='ltx_size_accordion'):
|
||||
width, height = ui_sections.create_resolution_inputs('ltx', default_width=832, default_height=480)
|
||||
with gr.Row():
|
||||
@@ -119,6 +120,12 @@ def create_ui(prompt, negative, styles, overrides, mp4_fps, mp4_interpolate, mp4
|
||||
with gr.Row():
|
||||
text = gr.HTML('', elem_id='ltx_generation_info', show_label=False)
|
||||
|
||||
|
||||
def load_model(model_name: str):
|
||||
ltx_util.load_model('LTX Video', model_name)
|
||||
|
||||
btn_load.click(fn=load_model, inputs=[model], outputs=[])
|
||||
|
||||
model.change(
|
||||
fn=_model_change,
|
||||
inputs=[model],
|
||||
|
||||
@@ -28,7 +28,7 @@ def load_model(engine: str, model: str):
|
||||
selected: models_def.Model = [m for m in models_def.models[engine] if m.name == model][0]
|
||||
# video_load owns the cache; pipe-class mismatch inside it invalidates the name-based hit
|
||||
# when Unload Models (or any external swap) silently replaced shared.sd_model.
|
||||
log.info(f'Video load: engine="{engine}" selected="{model}" {selected}')
|
||||
log.info(f'Load video: engine="{engine}" selected="{model}" {selected}')
|
||||
video_load.load_model(selected)
|
||||
t1 = time.time()
|
||||
shared.sd_model = sd_models.apply_balanced_offload(shared.sd_model)
|
||||
@@ -41,7 +41,7 @@ def load_upsample(upsample_pipe, upsample_repo_id):
|
||||
if upsample_pipe is None:
|
||||
t0 = time.time()
|
||||
from diffusers.pipelines.ltx.pipeline_ltx_latent_upsample import LTXLatentUpsamplePipeline
|
||||
log.info(f'Video load: cls={LTXLatentUpsamplePipeline.__name__} repo="{upsample_repo_id}"')
|
||||
log.info(f'Load video: cls={LTXLatentUpsamplePipeline.__name__} repo="{upsample_repo_id}"')
|
||||
upsample_pipe = LTXLatentUpsamplePipeline.from_pretrained(
|
||||
upsample_repo_id,
|
||||
vae=shared.sd_model.vae,
|
||||
@@ -61,7 +61,7 @@ def load_upsample_2x(upsample_pipe, upsample_repo_id):
|
||||
from diffusers.pipelines.ltx2.pipeline_ltx2_latent_upsample import LTX2LatentUpsamplePipeline
|
||||
from diffusers.pipelines.ltx2.latent_upsampler import LTX2LatentUpsamplerModel
|
||||
from modules import sd_checkpoint
|
||||
log.info(f'Video load: cls={LTX2LatentUpsamplePipeline.__name__} repo="{upsample_repo_id}"')
|
||||
log.info(f'Load video: cls={LTX2LatentUpsamplePipeline.__name__} repo="{upsample_repo_id}"')
|
||||
latent_upsampler = LTX2LatentUpsamplerModel.from_pretrained(
|
||||
upsample_repo_id,
|
||||
subfolder='latent_upsampler',
|
||||
|
||||
@@ -331,7 +331,7 @@ def load_diffuser_initial(diffusers_load_config: dict, op='model'):
|
||||
return sd_model, checkpoint_info
|
||||
|
||||
|
||||
def hf_prefetch_configs(checkpoint_info: CheckpointInfo, diffusers_load_config: dict, op='model'):
|
||||
def hf_prefetch_configs(checkpoint_info: CheckpointInfo | str, diffusers_load_config: dict, op='model'):
|
||||
# diffusers pipeline downloads build subfolder config allow-patterns with os.path.join, and huggingface_hub>=1.22
|
||||
# matches patterns with fnmatchcase which does not normalize separators (huggingface/huggingface_hub#4435),
|
||||
# so on windows component config.json files are never downloaded and the incomplete snapshot
|
||||
@@ -932,7 +932,9 @@ def load_diffuser(checkpoint_info: CheckpointInfo | None = None, op='model', rev
|
||||
if model_type is None:
|
||||
log.error(f'Load {op}: pipeline={shared.opts.diffusers_pipeline} not detected')
|
||||
return
|
||||
|
||||
hf_prefetch_configs(checkpoint_info, diffusers_load_config, op)
|
||||
|
||||
vae_file = None
|
||||
if model_type.startswith('Stable Diffusion') and (op == 'model' or op == 'refiner'): # preload vae for sd models
|
||||
vae_file, vae_source = sd_vae.resolve_vae(checkpoint_info.filename)
|
||||
|
||||
@@ -58,7 +58,7 @@ def get_call(cls):
|
||||
return signature.parameters
|
||||
|
||||
|
||||
def path_to_repo(checkpoint_info):
|
||||
def path_to_repo(checkpoint_info: CheckpointInfo | str):
|
||||
if isinstance(checkpoint_info, CheckpointInfo):
|
||||
if os.path.exists(checkpoint_info.path) and 'models--' not in checkpoint_info.path:
|
||||
return checkpoint_info.path # local models
|
||||
|
||||
@@ -37,7 +37,7 @@ def getpipe(package, name, _default=None):
|
||||
cls = getattr(package, name, _default)
|
||||
return cls
|
||||
except Exception as e:
|
||||
log.error(f'Video load: error loading class "{name}" from package "{package.__name__}": {e}')
|
||||
log.error(f'Load video: error loading class "{name}" from package "{package.__name__}": {e}')
|
||||
return None
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ loaded_model = None
|
||||
|
||||
|
||||
def load_custom(model_name: str):
|
||||
log.debug(f'Video load: module=pipe repo="{model_name}" cls=Custom')
|
||||
log.debug(f'Load video: module=pipe repo="{model_name}" cls=Custom')
|
||||
if 'veo-3.1' in model_name:
|
||||
from modules.video_models.google_veo import load_veo
|
||||
pipe = load_veo(model_name)
|
||||
@@ -48,7 +48,7 @@ def load_model(selected: models_def.Model):
|
||||
# shared.sd_model auto-reloads the default checkpoint when model_data.sd_model is None,
|
||||
# which silently swaps the pipe class behind the name-based cache. Pipe-class mismatch
|
||||
# is the reliable signal that the cached name no longer maps to the cached object.
|
||||
log.warning(f'Video load: cached model="{selected.name}" cls={type(shared.sd_model).__name__} mismatch forcing reload')
|
||||
log.warning(f'Load video: cached model="{selected.name}" cls={type(shared.sd_model).__name__} mismatch forcing reload')
|
||||
loaded_model = None
|
||||
if loaded_model == selected.name:
|
||||
return ''
|
||||
@@ -104,7 +104,7 @@ def load_model(selected: models_def.Model):
|
||||
selected.te_folder = 'text_encoder'
|
||||
selected.te_revision = None
|
||||
|
||||
log.debug(f'Video load: module=te repo="{selected.te or selected.repo}" folder="{selected.te_folder}" cls={selected.te_cls.__name__} quant={model_quant.get_quant_type(quant_args)} loader={_loader("transformers")}')
|
||||
log.debug(f'Load video: module=te repo="{selected.te or selected.repo}" folder="{selected.te_folder}" cls={selected.te_cls.__name__} quant={model_quant.get_quant_type(quant_args)} loader={_loader("transformers")}')
|
||||
kwargs["text_encoder"] = selected.te_cls.from_pretrained(
|
||||
pretrained_model_name_or_path=selected.te or selected.repo,
|
||||
subfolder=selected.te_folder,
|
||||
@@ -125,7 +125,7 @@ def load_model(selected: models_def.Model):
|
||||
if dit_folder is not None and dit_folder not in kwargs:
|
||||
# get a new quant arg on every loop to prevent the quant config classes getting entangled
|
||||
load_args, quant_args = model_quant.get_dit_args({}, module='Model', device_map=True)
|
||||
log.debug(f'Video load: module=transformer repo="{selected.dit or selected.repo}" module="{dit_folder}" folder="{dit_folder}" cls={selected.dit_cls.__name__} quant={model_quant.get_quant_type(quant_args)} loader={_loader("diffusers")}')
|
||||
log.debug(f'Load video: module=transformer repo="{selected.dit or selected.repo}" module="{dit_folder}" folder="{dit_folder}" cls={selected.dit_cls.__name__} quant={model_quant.get_quant_type(quant_args)} loader={_loader("diffusers")}')
|
||||
kwargs[dit_folder] = selected.dit_cls.from_pretrained(
|
||||
pretrained_model_name_or_path=selected.dit or selected.repo,
|
||||
subfolder=dit_folder,
|
||||
@@ -136,7 +136,7 @@ def load_model(selected: models_def.Model):
|
||||
**offline_args,
|
||||
)
|
||||
else:
|
||||
log.debug(f'Video load: module=transformer repo="{selected.dit or selected.repo}" module="{dit_folder}" folder="{dit_folder}" cls={selected.dit_cls.__name__} loader={_loader("diffusers")} skip')
|
||||
log.debug(f'Load video: module=transformer repo="{selected.dit or selected.repo}" module="{dit_folder}" folder="{dit_folder}" cls={selected.dit_cls.__name__} loader={_loader("diffusers")} skip')
|
||||
|
||||
if selected.dit_folder is None:
|
||||
selected.dit_folder = ['transformer']
|
||||
@@ -154,7 +154,10 @@ def load_model(selected: models_def.Model):
|
||||
if selected.repo_cls is None:
|
||||
shared.sd_model = load_custom(selected.repo)
|
||||
else:
|
||||
log.debug(f'Video load: module=pipe repo="{selected.repo}" cls={selected.repo_cls.__name__}')
|
||||
log.debug(f'Load video: module=pipe repo="{selected.repo}" cls={selected.repo_cls.__name__}')
|
||||
print('HERE1')
|
||||
sd_models.hf_prefetch_configs(selected.repo, {}, 'video')
|
||||
print('HERE2')
|
||||
shared.sd_model = selected.repo_cls.from_pretrained(
|
||||
pretrained_model_name_or_path=selected.repo,
|
||||
revision=selected.repo_revision,
|
||||
@@ -168,7 +171,7 @@ def load_model(selected: models_def.Model):
|
||||
errors.display(e, 'video')
|
||||
|
||||
if shared.sd_model is None:
|
||||
msg = f'Video load: model="{selected.name}" failed'
|
||||
msg = f'Load video: model="{selected.name}" failed'
|
||||
log.error(msg)
|
||||
return msg
|
||||
|
||||
@@ -211,7 +214,7 @@ def load_model(selected: models_def.Model):
|
||||
sd_models.set_diffuser_offload(shared.sd_model)
|
||||
|
||||
loaded_model = selected.name
|
||||
msg = f'Video load: cls={shared.sd_model.__class__.__name__} model="{selected.name}" time={t1-t0:.2f}'
|
||||
msg = f'Load video: cls={shared.sd_model.__class__.__name__} model="{selected.name}" time={t1-t0:.2f}'
|
||||
log.info(msg)
|
||||
log.debug(f'Video hijacks: decode={decode} text={text} image={image} slicing={slicing} tiling={tiling} framewise={framewise}')
|
||||
shared.state.end(jobid)
|
||||
|
||||
@@ -36,10 +36,10 @@ def load_override(selected: Model, **load_args):
|
||||
from diffusers.pipelines.ltx2 import LTX2TextConnectors
|
||||
ltx2_connectors_cls = LTX2TextConnectors
|
||||
except ImportError as e:
|
||||
log.warning(f'Video load: LTX2TextConnectors unavailable ({e}); dedup of LTX-2.3 connectors disabled')
|
||||
log.warning(f'Load video: LTX2TextConnectors unavailable ({e}); dedup of LTX-2.3 connectors disabled')
|
||||
if ('LTXVideo 2.3' in selected.name and shared.opts.te_shared_te and ltx2_connectors_cls is not None):
|
||||
conn_repo = 'OzzyGT/LTX-2.3-sdnq-dynamic-int4' if 'SDNQ' in selected.name else 'OzzyGT/LTX-2.3'
|
||||
log.debug(f'Video load: module=connectors repo="{conn_repo}" cls={ltx2_connectors_cls.__name__} shared={shared.opts.te_shared_te}')
|
||||
log.debug(f'Load video: module=connectors repo="{conn_repo}" cls={ltx2_connectors_cls.__name__} shared={shared.opts.te_shared_te}')
|
||||
kwargs['connectors'] = ltx2_connectors_cls.from_pretrained(
|
||||
conn_repo,
|
||||
subfolder='connectors',
|
||||
|
||||
@@ -41,7 +41,7 @@ def model_change(engine, model):
|
||||
|
||||
|
||||
def model_load(engine, model):
|
||||
debug(f'Video load: engine="{engine}" model="{model}"')
|
||||
debug(f'Load video: engine="{engine}" model="{model}"')
|
||||
selected = get_selected(engine, model)
|
||||
yield f'Video model loading: {selected.name}'
|
||||
if selected:
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ requests==2.34.2
|
||||
tqdm==4.68.3
|
||||
accelerate==1.14.0
|
||||
einops==0.8.2
|
||||
huggingface_hub==1.22.0
|
||||
huggingface_hub==1.23.0
|
||||
hf_xet==1.5.1
|
||||
numpy==2.1.2
|
||||
pandas==2.3.1
|
||||
|
||||
Reference in New Issue
Block a user