mirror of
https://github.com/vladmandic/automatic
synced 2026-09-19 09:14:35 +02:00
Merge branch 'vladmandic:dev' into dev
This commit is contained in:
+8
-3
@@ -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,17 +115,22 @@ 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
|
||||
- update: handle git errors gracefully
|
||||
- vae: fetch scale factor from the model
|
||||
- vdm scheduler: fix steps, thanks @zjn20030811
|
||||
- xyz grid: apply bool values
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
+13
-11
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(".", "_")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import os
|
||||
from modules import shared
|
||||
from modules.logger import log
|
||||
|
||||
|
||||
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
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
+5
-2
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
+42
-25
@@ -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 = '<div style="color: var(--secondary-500)">You\'re up to date!</div>' if version.chash == version.lhash else '<div style="color: var(--secondary-500)">Update available!</div>'
|
||||
html = f'''
|
||||
<div>URL: <a href="{version.url}" target="_blank">{version.url}</a></div>
|
||||
<div>Current branch: <span style="color: var(--highlight-color)">{version.branch}</span></div>
|
||||
<div>Current version: <span style="color: var(--highlight-color)">{version.current}</span> hash <span style="color: var(--highlight-color)">{version.chash}</span></div>
|
||||
<div>Latest version: <span style="color: var(--highlight-color)">{version.latest}</span> hash <span style="color: var(--highlight-color)">{version.lhash}</span></div>
|
||||
{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 = '<div style="color: var(--secondary-500)">You\'re up to date!</div>' if version.chash == version.lhash else '<div style="color: var(--secondary-500)">Update available!</div>'
|
||||
html = f'''
|
||||
<div>URL: <a href="{version.url}" target="_blank">{version.url}</a></div>
|
||||
<div>Origin: <span style="color: var(--highlight-color)">{version.origin}</span></div>
|
||||
<div>Current branch: <span style="color: var(--highlight-color)">{version.branch}</span></div>
|
||||
<div>Current version: <span style="color: var(--highlight-color)">{version.current}</span> hash <span style="color: var(--highlight-color)">{version.chash}</span></div>
|
||||
<div>Latest version: <span style="color: var(--highlight-color)">{version.latest}</span> hash <span style="color: var(--highlight-color)">{version.lhash}</span></div>
|
||||
{latest}
|
||||
'''
|
||||
return html
|
||||
except Exception as e:
|
||||
i.log.error(f'Version: {e}')
|
||||
html = f'<div style="color: var(--error-color)">Error while detecting version<br>{str(e)}</div>'
|
||||
return html
|
||||
|
||||
|
||||
def apply_update(update_rebase, update_submodules, update_extensions):
|
||||
|
||||
@@ -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)
|
||||
|
||||
+2
-2
@@ -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():
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user