From b0aa61ff21c843a0353c3ffa6c0408f26a260032 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 10 Sep 2026 18:33:47 +0200 Subject: [PATCH 1/7] cleanup Signed-off-by: Vladimir Mandic --- modules/ui_settings.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ui_settings.py b/modules/ui_settings.py index d8033642f..4d793eece 100644 --- a/modules/ui_settings.py +++ b/modules/ui_settings.py @@ -258,9 +258,9 @@ def create_ui(disabled_tabs=None): item for item in shared.opts.data_labels.items() if item[1].section is not None and item[1].section[0] == section_id ] # find all items in this section - hidden = (section_id is None) or ('hidden' in section_id.lower()) or ('hidden' in section_text.lower()) - for (key, _item) in items: - log.trace(f'Settings: id={section_id} text={section_text} key={key} hidden={hidden}') + hidden = (section_id is None) or ('hidden' in section_id.lower()) or ('hidden' in section_text.lower()) or ('legacy' in section_id.lower()) or ('legacy' in section_text.lower()) + # for (key, _item) in items: + # log.trace(f'Settings: id={section_id} text={section_text} key={key} hidden={hidden}') if hidden: for (key, _item) in items: hidden_list.append(key) From 9a9c70c6d33c737d889232e75d0b971019987f7f Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 10 Sep 2026 19:59:34 +0200 Subject: [PATCH 2/7] lora debug logging and support minimax-ref2va Signed-off-by: Vladimir Mandic --- modules/lora/lora_convert.py | 15 +++++++++++---- modules/lora/lora_load.py | 29 ++++++++++++++++++++--------- modules/lora/lora_overrides.py | 19 +++++++++++++++++-- modules/lora/networks.py | 2 +- 4 files changed, 49 insertions(+), 16 deletions(-) diff --git a/modules/lora/lora_convert.py b/modules/lora/lora_convert.py index 2c5455c00..2e565b183 100644 --- a/modules/lora/lora_convert.py +++ b/modules/lora/lora_convert.py @@ -487,28 +487,35 @@ def assign_network_names_to_compvis_modules(sd_model): sd_model = getattr(shared.sd_model, "pipe", shared.sd_model) # wrapped model compatibility network_layer_mapping = {} if hasattr(sd_model, 'text_encoder') and sd_model.text_encoder is not None: - for name, module in sd_model.text_encoder.named_modules(): + for name, module in sd_model.text_encoder.named_modules() : prefix = "lora_te1_" if hasattr(sd_model, 'text_encoder_2') else "lora_te_" network_name = prefix + name.replace(".", "_") network_layer_mapping[network_name] = module module.network_layer_name = network_name - if hasattr(sd_model, 'text_encoder_2'): + if hasattr(sd_model, 'text_encoder_2') and sd_model.text_encoder_2 is not None: for name, module in sd_model.text_encoder_2.named_modules(): network_name = "lora_te2_" + name.replace(".", "_") network_layer_mapping[network_name] = module module.network_layer_name = network_name - if hasattr(sd_model, 'unet'): + if hasattr(sd_model, 'unet') and sd_model.unet is not None: for name, module in sd_model.unet.named_modules(): network_name = "lora_unet_" + name.replace(".", "_") network_layer_mapping[network_name] = module module.network_layer_name = network_name - if hasattr(sd_model, 'transformer'): + if hasattr(sd_model, 'transformer') and sd_model.transformer is not None: for name, module in sd_model.transformer.named_modules(): network_name = "lora_transformer_" + name.replace(".", "_") network_layer_mapping[network_name] = module if "norm" in network_name and "linear" not in network_name and shared.sd_model_type != "sd3": continue module.network_layer_name = network_name + if hasattr(sd_model, 'transformer_ref') and sd_model.transformer_ref is not None: + for name, module in sd_model.transformer_ref.named_modules(): + network_name = "lora_transformer_" + name.replace(".", "_") + network_layer_mapping[network_name] = module + if "norm" in network_name and "linear" not in network_name and shared.sd_model_type != "sd3": + continue + module.network_layer_name = network_name if hasattr(sd_model, 'llm_adapter') and sd_model.llm_adapter is not None: for name, module in sd_model.llm_adapter.named_modules(): network_name = "lora_llm_adapter_" + name.replace(".", "_") diff --git a/modules/lora/lora_load.py b/modules/lora/lora_load.py index 7b9a0f918..41764b32f 100644 --- a/modules/lora/lora_load.py +++ b/modules/lora/lora_load.py @@ -56,12 +56,19 @@ def lora_dump(lora, dct): def load_safetensors(name, network_on_disk: network.NetworkOnDisk) -> network.Network | None: if not shared.sd_loaded: return None - sd_model = getattr(shared.sd_model, "pipe", shared.sd_model) + + # cached cached = lora_cache.get(name, None) if cached is not None: + if l.debug: + log.trace(f'LoRA load: name="{name}" fn="{network_on_disk.filename}" cache=True') return cached + + # native dispatch native_module = NATIVE_DISPATCH.get(shared.sd_model_type) + if l.debug: + log.trace(f'LoRA load: name="{name}" fn="{network_on_disk.filename}" native={native_module}') if native_module is not None: import importlib mod = importlib.import_module(native_module) @@ -69,6 +76,10 @@ def load_safetensors(name, network_on_disk: network.NetworkOnDisk) -> network.Ne if net is not None: lora_cache[name] = net return net + + # fallback to standard network loading + if l.debug: + log.trace(f'LoRA load: name="{name}" network_on_disk="{network_on_disk.filename}" safetensors') net = network.Network(name, network_on_disk) net.mtime = os.path.getmtime(network_on_disk.filename) state_dict = sd_models.read_state_dict(network_on_disk.filename, what='network') @@ -96,7 +107,7 @@ def load_safetensors(name, network_on_disk: network.NetworkOnDisk) -> network.Ne emb_dict[vec_name] = weight bundle_embeddings[emb_name] = emb_dict continue - if parts[0] in ["clip_l","clip_g","t5","unet","transformer"]: + if parts[0] in ["clip_l", "clip_g", "t5", "unet", "transformer", "transformer_2"]: network_part = [] while parts and parts[-1] in ["alpha","weight","lora_up","lora_down"]: network_part.insert(0,parts[-1]) @@ -147,7 +158,7 @@ def load_safetensors(name, network_on_disk: network.NetworkOnDisk) -> network.Ne if len(keys_failed_to_match) > 0: log.warning(f'Network load: type=LoRA name="{name}" type={set(network_types)} unmatched={len(keys_failed_to_match)} matched={len(matched_networks)}') if l.debug: - log.debug(f'Network load: type=LoRA name="{name}" unmatched={keys_failed_to_match}') + log.trace(f'Network load: type=LoRA name="{name}" unmatched={keys_failed_to_match}') else: log.debug(f'Network load: type=LoRA name="{name}" type={set(network_types)} keys={len(matched_networks)} dtypes={dtypes} fuse={lora_overrides.fuse_native()}:{shared.opts.lora_fuse_diffusers}') if len(matched_networks) == 0: @@ -172,13 +183,13 @@ def maybe_recompile_model(names, te_multipliers): if not recompile_model: skip_lora_load = True if len(l.loaded_networks) > 0 and l.debug: - log.debug('Model Compile: Skipping LoRa loading') + log.trace('LoRA: recompile required, skip loading') return recompile_model, skip_lora_load else: recompile_model = True shared.compiled_model_state.lora_model = [] if l.debug: - log.debug(f'Model recompile check: task={sd_models.get_diffusers_task(shared.sd_model)} recompile={recompile_model} load={skip_lora_load}') + log.trace(f'LoRA recompile check: task={sd_models.get_diffusers_task(shared.sd_model)} recompile={recompile_model} load={skip_lora_load}') if recompile_model: current_task = sd_models.get_diffusers_task(shared.sd_model) log.debug(f'Compile: task={current_task} force model reload') @@ -285,7 +296,7 @@ def network_load(names, te_multipliers=None, unet_multipliers=None, dyn_dims=Non if network_on_disk is not None: shorthash = getattr(network_on_disk, 'shorthash', '').lower() if l.debug: - log.debug(f'Network load: type=LoRA name="{name}" file="{network_on_disk.filename}" hash="{shorthash}" cached={name in lora_cache}') + log.trace(f'LoRA: name="{name}" fn="{network_on_disk.filename}" hash="{shorthash}" cached={name in lora_cache}') try: lora_scale = te_multipliers[i] if te_multipliers else shared.opts.extra_networks_default_multiplier lora_module = lora_modules[i] if lora_modules and len(lora_modules) > i else None @@ -330,8 +341,8 @@ def network_load(names, te_multipliers=None, unet_multipliers=None, dyn_dims=Non try: t1 = time.time() if l.debug: - log.trace(f'Network load: type=LoRA list={sd_model.get_list_adapters()}') - log.trace(f'Network load: type=LoRA active={sd_model.get_active_adapters()}') + log.trace(f'LoRA: list={sd_model.get_list_adapters()}') + log.trace(f'LoRA: active={sd_model.get_active_adapters()}') sd_model.set_adapters(adapter_names=lora_diffusers.diffuser_loaded, adapter_weights=lora_diffusers.diffuser_scales) sd_model.enable_lora() # set_adapters does not clear the disabled flag left by a prior removal except Exception as e: @@ -359,7 +370,7 @@ def network_load(names, te_multipliers=None, unet_multipliers=None, dyn_dims=Non networks.network_activate() if len(l.loaded_networks) > 0 and l.debug: - log.debug(f'Network load: type=LoRA loaded={[n.name for n in l.loaded_networks]} cache={list(lora_cache)} fuse={lora_overrides.fuse_native()}:{shared.opts.lora_fuse_diffusers}') + log.trace(f'LoRA: loaded={[n.name for n in l.loaded_networks]} cache={list(lora_cache)} fuse={lora_overrides.fuse_native()}:{shared.opts.lora_fuse_diffusers}') if recompile_model: log.info("Network load: type=LoRA model recompile required") diff --git a/modules/lora/lora_overrides.py b/modules/lora/lora_overrides.py index c7141d06f..d50229b10 100644 --- a/modules/lora/lora_overrides.py +++ b/modules/lora/lora_overrides.py @@ -1,4 +1,9 @@ +import os from modules import shared +from modules.logger import log + + +debug_log = log.debug if os.environ.get('SD_LORA_DEBUG', None) is not None else lambda *args, **kwargs: None force_hashes_diffusers = [ # forced always @@ -116,15 +121,22 @@ def disable_fuse(): from modules.lora import lora_common as l from modules.lora import lora_stack if lora_stack.select_possible(len(l.loaded_networks)) or lora_stack.select_engaged(): + debug_log('LoRA: fuse=False reason="active select mode"') return True # select flips per-layer winners against the pristine backup; a dormant select mode leaves fuse alone sd_model = getattr(shared.sd_model, 'pipe', shared.sd_model) if is_quantized(sd_model): + debug_log('LoRA: fuse=False reason="model is quantized"') return True if any(is_quantized(getattr(sd_model, name, None)) for name in fuse_components(sd_model)): + debug_log('LoRA: fuse=False reason="component is quantized"') return True if hasattr(sd_model, '_lora_partial'): + debug_log('LoRA: fuse=False reason="partial lora applied"') return True - return shared.sd_model_type in fuse_ignore + if shared.sd_model_type in fuse_ignore: + debug_log(f'LoRA: fuse=False reason="model type {shared.sd_model_type} in fuse_ignore"') + return True + return False def fuse_native(): @@ -134,4 +146,7 @@ def fuse_native(): the backup, activate and deactivate passes, since backup mode restores from a stored tensor while fuse mode restores by subtracting the delta. """ - return shared.opts.lora_fuse_native and not disable_fuse() + result = shared.opts.lora_fuse_native and not disable_fuse() + force = os.environ.get('SD_LORA_FUSE', None) is not None + debug_log(f'LoRA: native fuse={result} force={force}') + return (result or force) diff --git a/modules/lora/networks.py b/modules/lora/networks.py index 4595bbb00..87dde344f 100644 --- a/modules/lora/networks.py +++ b/modules/lora/networks.py @@ -56,7 +56,7 @@ from modules.logger import log, console applied_layers: list[str] = [] refused_writes: int = 0 # deltas the modules would not take on the last activate pass; infotext reports the network as partial native_active: bool = False -default_components = ['text_encoder', 'text_encoder_2', 'text_encoder_3', 'text_encoder_4', 'unet', 'transformer', 'transformer_2', 'llm_adapter'] +default_components = ['text_encoder', 'text_encoder_2', 'text_encoder_3', 'text_encoder_4', 'unet', 'transformer', 'transformer_2', 'transformer_ref', 'llm_adapter'] class ActivationPass: From a4aefdb0935de10d0014e1c70b8552863f078113 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 10 Sep 2026 21:17:45 +0200 Subject: [PATCH 3/7] minimax crop init/last to aspect ratio, update logging, prompt strip on lora parse, video prompt enhance consider image Signed-off-by: Vladimir Mandic --- modules/extra_networks.py | 1 + modules/image/sharpfin.py | 24 +++++++++++++----------- modules/lora/extra_networks_lora.py | 14 +++++++------- modules/lora/lora_load.py | 6 +++--- modules/lora/lora_overrides.py | 2 +- modules/minimax/minimax_video.py | 19 +++++++++++++++---- modules/styles.py | 7 +++++-- modules/video_models/video_minimax.py | 2 +- scripts/prompt_enhance_ext.py | 25 ++++++++++++++++--------- 9 files changed, 62 insertions(+), 38 deletions(-) diff --git a/modules/extra_networks.py b/modules/extra_networks.py index 89acf5a4e..3d57d4d7f 100644 --- a/modules/extra_networks.py +++ b/modules/extra_networks.py @@ -164,6 +164,7 @@ def parse_prompt(prompt: str | None) -> tuple[str, defaultdict[str, list[ExtraNe return "" updated_prompt = re.sub(re_extra_net, found, prompt) + updated_prompt = updated_prompt.strip(', ') return updated_prompt, res diff --git a/modules/image/sharpfin.py b/modules/image/sharpfin.py index d8c8ef4e7..dbb1a7ab5 100644 --- a/modules/image/sharpfin.py +++ b/modules/image/sharpfin.py @@ -7,6 +7,7 @@ and Triton GPU acceleration when available. Non-CUDA devices fall back to PIL/torch.nn.functional automatically. """ +import os import sys import torch from PIL import Image @@ -17,6 +18,7 @@ from modules.image.convert import to_tensor, to_pil _sharpfin_checked = False _sharpfin_ok = False _triton_ok = False +debug = log.trace if os.environ.get('SD_PROCESS_DEBUG', None) is not None else lambda *args, **kwargs: None def check_sharpfin(): @@ -104,7 +106,7 @@ def _scale_pil(scale_fn, tensor, out_res, rk, dev, dt, do_linear, src_h, src_w, return scale_fn(tensor, out_res, resize_kernel=rk, device=dev, dtype=dt, do_srgb_conversion=do_linear, use_sparse=True) except Exception: _triton_ok = False - log.info("Sharpfin: Triton sparse disabled, using dense path") + log.debug("Sharpfin: Triton sparse disabled, using dense path") return scale_fn(tensor, out_res, resize_kernel=rk, device=dev, dtype=dt, do_srgb_conversion=do_linear, use_sparse=False) # Mixed axis: split into two single-axis resizes if h > src_h: # H up, W down @@ -115,7 +117,7 @@ def _scale_pil(scale_fn, tensor, out_res, rk, dev, dt, do_linear, src_h, src_w, return scale_fn(intermediate, (h, w), resize_kernel=rk, device=dev, dtype=dt, do_srgb_conversion=do_linear, use_sparse=True) except Exception: _triton_ok = False - log.info("Sharpfin: Triton sparse disabled, using dense path") + log.debug("Sharpfin: Triton sparse disabled, using dense path") return scale_fn(intermediate, (h, w), resize_kernel=rk, device=dev, dtype=dt, do_srgb_conversion=do_linear, use_sparse=False) # H down, W up use_sparse = _want_sparse(dev, rk, True) @@ -125,7 +127,7 @@ def _scale_pil(scale_fn, tensor, out_res, rk, dev, dt, do_linear, src_h, src_w, return scale_fn(intermediate, (h, w), resize_kernel=rk, device=dev, dtype=dt, do_srgb_conversion=do_linear, use_sparse=False) except Exception: _triton_ok = False - log.info("Sharpfin: Triton sparse disabled, using dense path") + log.debug("Sharpfin: Triton sparse disabled, using dense path") intermediate = scale_fn(tensor, (h, src_w), resize_kernel=rk, device=dev, dtype=dt, do_srgb_conversion=do_linear, use_sparse=False) return scale_fn(intermediate, (h, w), resize_kernel=rk, device=dev, dtype=dt, do_srgb_conversion=do_linear, use_sparse=False) @@ -137,24 +139,24 @@ def resize_pil(image: Image.Image, target_size: tuple[int, int], *, kernel=None, is_mask = image.mode == 'L' if (image.width == w) and (image.height == h): - log.debug(f'Resize image: skip={w}x{h} fn={fn}') + # log.debug(f'Resize image: skip={w}x{h} fn={fn}') return image from modules import devices dev = device if device is not None else devices.device if not allow_sharpfin(dev): - log.debug(f'Resize image: method=PIL source={image.width}x{image.height} target={w}x{h} device={dev} fn={fn}') + debug(f'Resize image: method=PIL source={image.width}x{image.height} target={w}x{h} device={dev} fn={fn}') return image.resize((w, h), resample=Image.Resampling.LANCZOS) rk = get_kernel(kernel) if rk is None: - log.debug(f'Resize image: method=PIL source={image.width}x{image.height} target={w}x{h} kernel=None fn={fn}') + debug(f'Resize image: method=PIL source={image.width}x{image.height} target={w}x{h} kernel=None fn={fn}') return image.resize((w, h), resample=Image.Resampling.LANCZOS) from modules.sharpfin.functional import scale dt = dtype or torch.float16 do_linear = get_linearize(linearize, is_mask=is_mask) - log.debug(f'Resize image: method=sharpfin source={image.width}x{image.height} target={w}x{h} kernel={rk} device={dev} linearize={do_linear} fn={fn}') + debug(f'Resize image: method=sharpfin source={image.width}x{image.height} target={w}x{h} kernel={rk} device={dev} linearize={do_linear} fn={fn}') tensor = to_tensor(image) if tensor.dim() == 3: tensor = tensor.unsqueeze(0) @@ -182,14 +184,14 @@ def resize_tensor(tensor: torch.Tensor, target_size: tuple[int, int], *, kernel= dev = devices.device if not allow_sharpfin(dev): mode = 'bilinear' if (target_size[0] * target_size[1]) > (tensor.shape[-2] * tensor.shape[-1]) else 'area' - log.debug(f'Resize tensor: method=torch mode={mode} shape={tensor.shape} target={target_size} fn={fn}') + debug(f'Resize tensor: method=torch mode={mode} shape={tensor.shape} target={target_size} fn={fn}') inp = tensor if tensor.dim() == 4 else tensor.unsqueeze(0) result = torch.nn.functional.interpolate(inp, size=target_size, mode=mode, antialias=mode != 'area') return result.squeeze(0) if tensor.dim() == 3 else result rk = get_kernel(kernel) if rk is None: mode = 'bilinear' if (target_size[0] * target_size[1]) > (tensor.shape[-2] * tensor.shape[-1]) else 'area' - log.debug(f'Resize tensor: method=torch mode={mode} shape={tensor.shape} target={target_size} kernel=None fn={fn}') + debug(f'Resize tensor: method=torch mode={mode} shape={tensor.shape} target={target_size} kernel=None fn={fn}') inp = tensor if tensor.dim() == 4 else tensor.unsqueeze(0) result = torch.nn.functional.interpolate(inp, size=target_size, mode=mode, antialias=mode != 'area') return result.squeeze(0) if tensor.dim() == 3 else result @@ -206,10 +208,10 @@ def resize_tensor(tensor: torch.Tensor, target_size: tuple[int, int], *, kernel= both_up = (th >= src_h and tw >= src_w) if both_down or both_up: use_sparse = _triton_ok and dev.type == 'cuda' and rk.value == 'magic_kernel_sharp_2021' and both_down - log.debug(f'Resize tensor: method=sharpfin shape={tensor.shape} target={target_size} direction={both_up}:{both_down} kernel={rk} sparse={use_sparse} fn={fn}') + debug(f'Resize tensor: method=sharpfin shape={tensor.shape} target={target_size} direction={both_up}:{both_down} kernel={rk} sparse={use_sparse} fn={fn}') result = scale(tensor, target_size, resize_kernel=rk, device=dev, dtype=dt, do_srgb_conversion=linearize, use_sparse=use_sparse) else: - log.debug(f'Resize tensor: method=sharpfin shape={tensor.shape} target={target_size} direction={both_up}:{both_down} kernel={rk} sparse=False fn={fn}') + debug(f'Resize tensor: method=sharpfin shape={tensor.shape} target={target_size} direction={both_up}:{both_down} kernel={rk} sparse=False fn={fn}') intermediate = scale(tensor, (th, src_w), resize_kernel=rk, device=dev, dtype=dt, do_srgb_conversion=linearize, use_sparse=False) result = scale(intermediate, (th, tw), resize_kernel=rk, device=dev, dtype=dt, do_srgb_conversion=linearize, use_sparse=False) if squeezed: diff --git a/modules/lora/extra_networks_lora.py b/modules/lora/extra_networks_lora.py index e85771e3f..14e99351f 100644 --- a/modules/lora/extra_networks_lora.py +++ b/modules/lora/extra_networks_lora.py @@ -27,7 +27,7 @@ def get_stepwise(param, step, steps): # from https://github.com/cheald/sd-webui- if m[1][-1] <= 1.0: step = step / (max_steps - step_offset) if max_steps > 0 else 1.0 v = np.interp(step, m[1], m[0]) - debug_log(f"Network load: type=LoRA step={step} steps={max_steps} v={v}") + debug_log(f"LoRA: stepwise step={step} steps={max_steps} v={v}") return v else: return m @@ -184,7 +184,7 @@ class ExtraNetworkLora(extra_networks.ExtraNetwork): from modules.lora import lora_sdnq, lora_stack requested = requested + [f'stack={lora_stack.signature()}{lora_sdnq.signature()}'] # settings-only stack or mechanism changes must re-trigger activation if shared.opts.lora_force_reload: - debug_log(f'Network check: type=LoRA requested={requested} status="forced"') + debug_log(f'LoRA check requested={requested} status="forced"') return True, "forced" sd_model = shared.sd_model.pipe if hasattr(shared.sd_model, 'pipe') else shared.sd_model if sd_model is None: @@ -200,15 +200,15 @@ class ExtraNetworkLora(extra_networks.ExtraNetwork): if len(requested) != len(loaded): sd_model.loaded_loras.clear() # single-entry cache: any activation invalidates state recorded under other filter keys sd_model.loaded_loras[key] = requested - debug_log(f'Network check: type=LoRA key="{key}" requested={requested} loaded={loaded} status="num changed"') + debug_log(f'LoRA check key="{key}" requested={requested} loaded={loaded} status="num changed"') return True, "num changed" for req, load in zip(requested, loaded, strict=False): if req != load: sd_model.loaded_loras.clear() sd_model.loaded_loras[key] = requested - debug_log(f'Network check: type=LoRA key="{key}" requested={requested} loaded={loaded} status="content changed"') + debug_log(f'LoRA check key="{key}" requested={requested} loaded={loaded} status="content changed"') return True, "content changed" - debug_log(f'Network check: type=LoRA key="{key}" requested={requested} loaded={loaded} status="same"') + debug_log(f'LoRA check key="{key}" requested={requested} loaded={loaded} status="same"') return False, "none" def activate(self, p, params_list, step=0, include=None, exclude=None): # pylint: disable=arguments-differ @@ -236,7 +236,7 @@ class ExtraNetworkLora(extra_networks.ExtraNetwork): if debug: import sys fn = f'{sys._getframe(2).f_code.co_name}:{sys._getframe(1).f_code.co_name}' # pylint: disable=protected-access - debug_log(f'Network load: type=LoRA include={include} exclude={exclude} method={load_method} reason="{load_reason}" requested={requested} fn={fn}') + debug_log(f'LoRA load: include={include} exclude={exclude} method={load_method} reason="{load_reason}" requested={requested} fn={fn}') if load_method == 'diffusers': has_changed, reason = self.changed(requested) @@ -267,7 +267,7 @@ class ExtraNetworkLora(extra_networks.ExtraNetwork): log.info(f'Network unload: type=LoRA networks={[n.name for n in l.previously_loaded_networks]} mode={networks.effective_mode()}') networks.network_deactivate(include, exclude) networks.network_activate(include, exclude) - debug_log(f'Network change: type=LoRA previous={[n.name for n in l.previously_loaded_networks]} current={[n.name for n in l.loaded_networks]}') + debug_log(f'LoRA change: previous={[n.name for n in l.previously_loaded_networks]} current={[n.name for n in l.loaded_networks]}') if len(include) == 0: l.previously_loaded_networks = l.loaded_networks.copy() shared.state.end(jobid) diff --git a/modules/lora/lora_load.py b/modules/lora/lora_load.py index 41764b32f..6eb0fd5b1 100644 --- a/modules/lora/lora_load.py +++ b/modules/lora/lora_load.py @@ -62,13 +62,13 @@ def load_safetensors(name, network_on_disk: network.NetworkOnDisk) -> network.Ne cached = lora_cache.get(name, None) if cached is not None: if l.debug: - log.trace(f'LoRA load: name="{name}" fn="{network_on_disk.filename}" cache=True') + log.trace(f'LoRA: load name="{name}" fn="{network_on_disk.filename}" cache=True') return cached # native dispatch native_module = NATIVE_DISPATCH.get(shared.sd_model_type) if l.debug: - log.trace(f'LoRA load: name="{name}" fn="{network_on_disk.filename}" native={native_module}') + log.trace(f'LoRA: load name="{name}" fn="{network_on_disk.filename}" native={native_module}') if native_module is not None: import importlib mod = importlib.import_module(native_module) @@ -79,7 +79,7 @@ def load_safetensors(name, network_on_disk: network.NetworkOnDisk) -> network.Ne # fallback to standard network loading if l.debug: - log.trace(f'LoRA load: name="{name}" network_on_disk="{network_on_disk.filename}" safetensors') + log.trace(f'LoRA: load name="{name}" network_on_disk="{network_on_disk.filename}" safetensors') net = network.Network(name, network_on_disk) net.mtime = os.path.getmtime(network_on_disk.filename) state_dict = sd_models.read_state_dict(network_on_disk.filename, what='network') diff --git a/modules/lora/lora_overrides.py b/modules/lora/lora_overrides.py index d50229b10..cad8b514a 100644 --- a/modules/lora/lora_overrides.py +++ b/modules/lora/lora_overrides.py @@ -3,7 +3,7 @@ from modules import shared from modules.logger import log -debug_log = log.debug if os.environ.get('SD_LORA_DEBUG', None) is not None else lambda *args, **kwargs: None +debug_log = log.trace if os.environ.get('SD_LORA_DEBUG', None) is not None else lambda *args, **kwargs: None force_hashes_diffusers = [ # forced always diff --git a/modules/minimax/minimax_video.py b/modules/minimax/minimax_video.py index 5e4592328..85d86fd73 100644 --- a/modules/minimax/minimax_video.py +++ b/modules/minimax/minimax_video.py @@ -49,23 +49,28 @@ def unwrap_file(entry): return entry -def prepare_inputs(workflow: str | None, init_image: Image.Image | None, last_image: Image.Image | None, reference_media: list | None) -> dict: +def prepare_inputs(workflow: str | None, init_image: Image.Image | None, last_image: Image.Image | None, reference_media: list | None, width: int | None = None, height: int | None = None) -> dict: """The task args a workflow conditions on, resolved before the model load so a rejected request costs nothing.""" t_inputs = time.time() + from modules.image.resize import resize_image from modules.minimax import minimax_references if minimax_references.get_reference_caps(workflow) is not None: entries = [unwrap_file(entry) for entry in (reference_media or [])] references = minimax_references.resolve(workflow, entries, init_image) - log.debug(f'Prepare inputs: workflow={workflow} references={len(references)}') + log.debug(f'Video inputs: workflow={workflow} references={len(references)}') return {'references': references} task_args = {} if init_image is not None: + if width is not None and height is not None: + init_image = resize_image(2, init_image, width, height) # crop to aspect ratio task_args['image'] = init_image if last_image is not None: + if width is not None and height is not None: + last_image = resize_image(2, last_image, width, height) # crop to aspect ratio task_args['last_image'] = last_image if reference_media: log.warning(f'Video: op=reference workflow={workflow} references not supported, ignoring: count={len(reference_media)}') - log.debug(f'Prepare inputs: workflow={workflow} first={init_image} last={last_image}') + log.debug(f'Video inputs: workflow={workflow} first={init_image} last={last_image}') timer.video.ts('inputs', t_inputs) return task_args @@ -111,18 +116,24 @@ def generate(task_id, _ui_state, # resolved off the registry row so a bad reference is rejected before the load, the same as on the api path selected = models_def.find(engine, model) workflow = getattr(selected, 'workflow', None) - task_args = prepare_inputs(workflow, init_image, last_image, reference_media) + task_args = prepare_inputs(workflow, init_image, last_image, reference_media, width=width, height=height) workflow = load_model(model) # override workflow based on loaded model if not workflow: progress.finish_task(task_id) log.error('Video: model not loaded') return None, 'Model not loaded' + init_images = [] # only so they are available for inspection by rest of the processing + if init_image is not None: + init_images.append(init_image) + if last_image is not None: + init_images.append(last_image) p = processing.StableDiffusionProcessingVideo( sd_model=shared.sd_model, video_engine=engine, video_model=model, prompt=prompt, styles=styles, + init_images=init_images, seed=int(seed) if seed is not None else -1, steps=int(steps), width=width, diff --git a/modules/styles.py b/modules/styles.py index 6fdbb672d..f5136f1a5 100644 --- a/modules/styles.py +++ b/modules/styles.py @@ -243,12 +243,15 @@ def apply_styles_to_extra(p, style: Style): 'size', ] reference_style = get_reference_style() - extra = infotext.parse(reference_style) if shared.opts.extra_network_reference_values else {} + reference = infotext.parse(reference_style) if shared.opts.extra_network_reference_values else {} + extra = reference.copy() style_extra = apply_wildcards_to_prompt(style.extra, [style.wildcards], silent=True, p=p) style_extra = ' ' + style_extra.lower() extra.update(infotext.parse(style_extra)) extra.pop('Prompt', None) extra.pop('Negative prompt', None) + has_prompt = (style.prompt is not None) and len(style.prompt) > 2 + has_negative = (style.negative_prompt is not None) and len(style.negative_prompt) > 2 if debug_enabled: log.trace(f'Apply style extra: {extra}') @@ -284,7 +287,7 @@ def apply_styles_to_extra(p, style: Style): if debug_enabled: log.trace(f'Apply style skip: {k}={v}') skipped.append(f'{k}={v}') - log.debug(f'Apply style: name="{style.name}" params={params} settings={settings} unknown={skipped} reference={True if reference_style else False}') + log.debug(f'Apply style: name="{style.name}" prompt={has_prompt} negative={has_negative} params={params} settings={settings} unknown={skipped} reference={reference}') class StyleDatabase: diff --git a/modules/video_models/video_minimax.py b/modules/video_models/video_minimax.py index 0f69af878..658ac1c75 100644 --- a/modules/video_models/video_minimax.py +++ b/modules/video_models/video_minimax.py @@ -25,7 +25,7 @@ def apply_overrides(p, pipe, still: bool = False, audio: bool = True): while frames > max_frames: frames -= pipe.vae_frames_per_chunk if frames != getattr(p, 'frames', None): - log.debug(f'Pipeline: cls={pipe.__class__.__name__} frames={getattr(p, "frames", None)} aligned={frames}') + log.debug(f'Pipeline: cls={pipe.__class__.__name__} frames requested={getattr(p, "frames", None)} aligned={frames}') p.frames = frames p.task_args['num_frames'] = frames p.steps = max(2, p.steps) diff --git a/scripts/prompt_enhance_ext.py b/scripts/prompt_enhance_ext.py index 1386f38bc..dfb4edba6 100644 --- a/scripts/prompt_enhance_ext.py +++ b/scripts/prompt_enhance_ext.py @@ -279,14 +279,16 @@ class PromptEnhanceScript(scripts_manager.Script): def get_image(self, image): current_image = None try: - if image is not None and isinstance(image, gr.Image): + if (image is not None) and isinstance(image, list) and len(image) > 0: + current_image = image[0] + if (image is not None) and isinstance(image, gr.Image): current_image = image.value - elif image is not None and isinstance(image, Image.Image): # if image is already a PIL image + elif (image is not None) and isinstance(image, Image.Image): # if image is already a PIL image current_image = image - if current_image is not None and (current_image.width <= 64 or current_image.height <= 64): + if (current_image is not None) and (current_image.width <= 64 or current_image.height <= 64): current_image = None # Fallback to Kanvas/Control input if no image from Gradio component (e.g., when Kanvas is active) - if current_image is None and ui_control_helpers.input_source is not None: + if (current_image is None) and (ui_control_helpers.input_source is not None): if isinstance(ui_control_helpers.input_source, list) and len(ui_control_helpers.input_source) > 0: current_image = ui_control_helpers.input_source[0] elif isinstance(ui_control_helpers.input_source, Image.Image): @@ -327,6 +329,8 @@ class PromptEnhanceScript(scripts_manager.Script): prompt = prompt or (self.prompt.value if self.prompt else "") # Check if self.prompt is None if use_vision and is_vision_model(model): # handle vision toggle image = image or self.image + else: + image = None prefix = prefix or '' suffix = suffix or '' min_tokens = min_tokens or self.options.min_tokens @@ -338,7 +342,7 @@ class PromptEnhanceScript(scripts_manager.Script): thinking = thinking or self.options.thinking_mode sample = sample if sample is not None else self.options.do_sample nsfw = nsfw if nsfw is not None else True # Default nsfw to True if not provided - debug_log(f'Prompt enhance: model="{model}" model_class="{self.llm.__class__.__name__ if self.llm is not None else "not loaded"}" nsfw={nsfw} thinking={thinking} prefill="{prefill[:30] if prefill else ""}" use_vision={use_vision} image={image is not None}') + debug_log(f'Prompt enhance: model="{model}" model_class="{self.llm.__class__.__name__ if self.llm is not None else "not loaded"}" nsfw={nsfw} thinking={thinking} prefill="{prefill[:30] if prefill else ""}" vision={use_vision} image={image}') while self.busy: time.sleep(0.1) @@ -395,8 +399,10 @@ class PromptEnhanceScript(scripts_manager.Script): self.busy = True if is_cloud_model(model): - has_prompt = prompt_text is not None and len(prompt_text) > 4 + has_prompt = (prompt_text is not None) and (len(prompt_text) > 4) + has_prefill = (prefill_text is not None) and (len(prefill_text) > 4) system = get_system_prompt(system, self.options, nsfw, has_prompt=has_prompt, is_video=self.parent=='video', is_image=current_image is not None) + debug_log(f'Prompt enhance: prompt="{prompt_text}"') if 'gemini' in model: from modules.caption import gemini kwargs = { @@ -407,7 +413,7 @@ class PromptEnhanceScript(scripts_manager.Script): model_name = model.replace('google/', '') response = gemini.predict(prompt_text, current_image, model_name, system, prefill_text, thinking, kwargs) t1 = time.time() - log.info(f'Prompt enhance: model="{model}" nsfw={nsfw} time={t1-t0:.2f} prefill="{prefill_text[:20] if prefill_text else None}" response={len(response)}') + log.info(f'Prompt enhance: model="{model}" nsfw={nsfw} time={t1-t0:.2f} prompt={has_prompt} prefill={has_prefill} image={current_image} thinking={thinking} response={len(response)}') debug_log(f'Prompt enhance: response="{response}"') self.busy = False return response @@ -419,7 +425,7 @@ class PromptEnhanceScript(scripts_manager.Script): model_name = model.replace('xai/', '') response = grok.predict(prompt_text, current_image, model_name, system, prefill_text, thinking, kwargs) t1 = time.time() - log.info(f'Prompt enhance: model="{model}" nsfw={nsfw} time={t1-t0:.2f} prefill="{prefill_text[:20] if prefill_text else None}" response={len(response)}') + log.info(f'Prompt enhance: model="{model}" nsfw={nsfw} time={t1-t0:.2f} prompt={has_prompt} prefill={has_prefill} image={current_image} thinking={thinking} response={len(response)}') debug_log(f'Prompt enhance: response="{response}"') self.busy = False return response @@ -730,10 +736,11 @@ class PromptEnhanceScript(scripts_manager.Script): jobid = shared.state.begin('LLM') p.extra_generation_params['LLM'] = get_model_repo_from_display(llm_model) p.extra_generation_params['Original'] = p.prompt + image = self_image or p.init_images p.prompt = self.enhance( prompt=p.prompt, seed=p.seed, - image=self_image, + image=image, prefix=prompt_prefix, suffix=prompt_suffix, model=llm_model, From b0b0a2eb2269ec80963274117ea64e2a59a2d134 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 10 Sep 2026 21:20:44 +0200 Subject: [PATCH 4/7] update changelog Signed-off-by: Vladimir Mandic --- CHANGELOG.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 019c22a42..4390e66eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ # Change Log for SD.Next -## Update for 2026-09-10 +## Update for 2026-09-11 -### Highlights for 2026-09-10 +### Highlights for 2026-09-11 *What's New*? Well, code-wise, this is a big one... First, a-lot-of-optimizations: @@ -23,7 +23,7 @@ Plus inevitable bug-fixes... [Home](https://vladmandic.github.io/sdnext/) | [ChangeLog](https://github.com/vladmandic/automatic/blob/master/CHANGELOG.md) | [Docs](https://vladmandic.github.io/sdnext-docs/) | [Discord](https://discord.com/invite/sd-next-federal-batch-inspectors-1101998836328697867) | [Sponsor](https://github.com/sponsors/vladmandic) -### Details for 2026-09-10 +### Details for 2026-09-11 - **Models** - [Anima 2.9B Preview v1](https://huggingface.co/yeoj34760/Anima-2.9B) @@ -115,14 +115,18 @@ Plus inevitable bug-fixes... - detailer: handling of stop/skip/pause - log: ansi color handling - lora: cleanup tags + - lora: support transformer ref models - lucida: handle requirements - lumina-dimoo: attention-kwargs, thanks @Anai-Guo + - minimax: crop image to video aspect ratio - network: improve type/version lookup - offline: honor offline mode for more models, thanks @ryanmeador - openvino: optimize recompile checks and lora loading - prompt enhance: cloud models use correct system prompt + - prompt enhance: use init image for video - prompt: cache checks when cfg changes - prompt: unnecessary secondary prompt if same + - prompt: clean prompt after network parsing - rife: cleanup dead code, thanks @Anai-Guo - todo: remove dead code, thanks @Anai-Guo - ui: js fetch exception handling From 82810b6c4abb6a32884c47d713288e35b6d71ff9 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 10 Sep 2026 21:36:41 +0200 Subject: [PATCH 5/7] update handle git errors Signed-off-by: Vladimir Mandic --- CHANGELOG.md | 1 + modules/update.py | 67 +++++++++++++++++++++++++++++------------------ 2 files changed, 43 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4390e66eb..e74ba6e4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -130,6 +130,7 @@ Plus inevitable bug-fixes... - rife: cleanup dead code, thanks @Anai-Guo - todo: remove dead code, thanks @Anai-Guo - ui: js fetch exception handling + - update: handle git errors gracefully - vae: fetch scale factor from the model - vdm scheduler: fix steps, thanks @zjn20030811 - xyz grid: apply bool values diff --git a/modules/update.py b/modules/update.py index d958f3e7d..92f8693e9 100644 --- a/modules/update.py +++ b/modules/update.py @@ -6,6 +6,7 @@ import installer as i version = SimpleNamespace(**{ 'url': '', 'branch': '', + 'origin': '', 'current': '0000-00-00', 'chash': '0000000', 'latest': '0000-00-00', @@ -14,34 +15,50 @@ version = SimpleNamespace(**{ def get_version(): - # try: - origin = i.git('remote get-url origin') - origin = origin.splitlines()[0] - version.branch = i.git('rev-parse --abbrev-ref HEAD') - version.branch = version.branch.splitlines()[0] - version.url = origin.removesuffix('.git') + '/tree/' + version.branch + try: + origin = i.git('remote get-url origin') + origin = origin.splitlines() + if len(origin) > 0: + version.origin = origin[0] + version.url = version.origin.removesuffix('.git') + '/tree/' + version.branch + else: + version.origin = 'unknown' + i.log.warning('Version: origin URL not found') - ver = i.git('log --pretty=format:"%h %ad" -1 --date=short') - ver = ver.splitlines()[0] - version.chash, version.current = ver.split(' ') + branch = i.git('rev-parse --abbrev-ref HEAD') + branch = branch.splitlines() + if len(branch) > 0: + version.branch = branch[0] + else: + version.branch = 'unknown' + i.log.warning('Version: branch not found') - i.git('fetch') - ver = i.git(f'log origin/{version.branch} --pretty=format:"%h %ad" -1 --date=short') - ver = ver.splitlines()[0] - version.lhash, version.latest = ver.split(' ') + gitlog = i.git('log --pretty=format:"%h %ad" -1 --date=short') + gitlog = gitlog.splitlines() + if len(gitlog) > 0: + version.chash, version.current = gitlog[0].split(' ') - # except Exception as e: - # i.log.error(f'Version check failed: {e}') - i.log.info(f'Version: {vars(version)}') - latest = '
You\'re up to date!
' if version.chash == version.lhash else '
Update available!
' - html = f''' - -
Current branch: {version.branch}
-
Current version: {version.current} hash {version.chash}
-
Latest version: {version.latest} hash {version.lhash}
- {latest} - ''' - return html + i.git('fetch') + ver = i.git(f'log origin/{version.branch} --pretty=format:"%h %ad" -1 --date=short') + ver = ver.splitlines() + if len(ver) > 0 and ' ' in ver[0]: + version.lhash, version.latest = ver[0].split(' ') + + i.log.info(f'Version: {vars(version)}') + latest = '
You\'re up to date!
' if version.chash == version.lhash else '
Update available!
' + html = f''' + +
Origin: {version.origin}
+
Current branch: {version.branch}
+
Current version: {version.current} hash {version.chash}
+
Latest version: {version.latest} hash {version.lhash}
+ {latest} + ''' + return html + except Exception as e: + i.log.error(f'Version: {e}') + html = f'
Error while detecting version
{str(e)}
' + return html def apply_update(update_rebase, update_submodules, update_extensions): From 59bb85408e509d5989dab17b224941f56b82fe01 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 10 Sep 2026 22:06:11 +0200 Subject: [PATCH 6/7] dlss change defaults Signed-off-by: Vladimir Mandic --- scripts/dlss_ext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/dlss_ext.py b/scripts/dlss_ext.py index f7a4d7580..fc0a96053 100644 --- a/scripts/dlss_ext.py +++ b/scripts/dlss_ext.py @@ -29,7 +29,7 @@ def create_ui(parent): with gr.Accordion('DLSS NeuralRender', open=False, elem_id='dlss_nn'): with gr.Row(): nr_enabled = gr.Checkbox(label='NR enable', value=False, elem_id='dlss_nr_enabled') - nr_append = gr.Checkbox(label='NR append result', value=True, elem_id='dlss_nr_append') + nr_append = gr.Checkbox(label='NR append result', value=False, elem_id='dlss_nr_append') with gr.Row(): nr_style = gr.Dropdown(label='NR style', choices=NR_STYLES, value='Default', elem_id='dlss_nr_style') nr_preset = gr.Dropdown(label='NR preset', choices=NR_PRESETS, value='Default', elem_id='dlss_nr_preset') @@ -47,7 +47,7 @@ def create_ui(parent): with gr.Accordion('DLSS SuperSample', open=False, elem_id='dlss_ss'): with gr.Row(): ss_enabled = gr.Checkbox(label='SS enable', value=False, elem_id='dlss_ss_enabled') - ss_append = gr.Checkbox(label='SS append result', value=True, elem_id='dlss_ss_append') + ss_append = gr.Checkbox(label='SS append result', value=False, elem_id='dlss_ss_append') with gr.Row(): ss_vsr_quality = gr.Dropdown(label='SS VSR quality', choices=["1: Low", "2: Medium", "3: High", "4: Ultra"], value="4: Ultra", type='value', elem_id='dlss_ss_vsr_quality') with gr.Row(): From 5c7e018fb8597fd87677ac63d16032570ed9a8a7 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 10 Sep 2026 22:40:14 +0200 Subject: [PATCH 7/7] change prompt enhance defaults Signed-off-by: Vladimir Mandic --- scripts/prompt_enhance/options.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/prompt_enhance/options.py b/scripts/prompt_enhance/options.py index 785b2af80..933782fc4 100644 --- a/scripts/prompt_enhance/options.py +++ b/scripts/prompt_enhance/options.py @@ -219,10 +219,10 @@ class Options: max_delim_index: int = 60 min_tokens: int = 0 - max_tokens: int = 256 + max_tokens: int = 512 do_sample: bool = True - temperature: float = 0.6 - repetition_penalty: float = 1.2 + temperature: float = 0.75 + repetition_penalty: float = 1.05 top_k: int = 0 top_p: float = 0.0 thinking_mode: bool = False