lora change detection improvements

Signed-off-by: Vladimir Mandic <mandic00@live.com>
This commit is contained in:
Vladimir Mandic
2026-05-09 09:38:52 +02:00
parent 8871f9a5a9
commit 4747ca2fc0
7 changed files with 39 additions and 22 deletions
+5 -3
View File
@@ -1,8 +1,8 @@
# Change Log for SD.Next
## Update for 2026-05-08
## Update for 2026-05-09
### Highlights for 2026-05-08
### Highlights for 2026-05-09
*What's New?*
- Image editing models now can work with multiple image inputs!
@@ -13,7 +13,7 @@ For full details, see [ChangeLog](https://github.com/vladmandic/automatic/blob/m
[ReadMe](https://github.com/vladmandic/automatic/blob/master/README.md) | [ChangeLog](https://github.com/vladmandic/automatic/blob/master/CHANGELOG.md) | [Docs](https://vladmandic.github.io/sdnext-docs/) | [WiKi](https://github.com/vladmandic/automatic/wiki) | [Discord](https://discord.com/invite/sd-next-federal-batch-inspectors-1101998836328697867) | [Sponsor](https://github.com/sponsors/vladmandic)
### Details for 2026-05-08
### Details for 2026-05-09
- **Features**
- **Multi-image** workflows!
@@ -84,6 +84,8 @@ For full details, see [ChangeLog](https://github.com/vladmandic/automatic/blob/m
- cache network thumbnails
- `scripts` corrupting control ui state
- avoid `callback` duplicate registrations
- pipeline task change causing loss of info on loaded `lora`
- `detailer` handle `lora` internally
## Update for 2026-04-28
+9
View File
@@ -37,6 +37,15 @@ class ExtraNetworkParams:
else:
self.positional.append(item)
def __eq__(self, other):
if not isinstance(other, ExtraNetworkParams):
return False
same = self.positional == other.positional and self.named == other.named
return same
def __repr__(self):
return f"NetworkParams(positional={self.positional} named={self.named})"
class ExtraNetwork:
def __init__(self, name):
+1 -1
View File
@@ -214,7 +214,7 @@ def face_id(
p.negative_prompts = p.all_negative_prompts[n * p.batch_size:(n+1) * p.batch_size]
p.seeds = p.all_seeds[n * p.batch_size:(n+1) * p.batch_size]
p.subseeds = p.all_subseeds[n * p.batch_size:(n+1) * p.batch_size]
p.prompts, p.network_data = extra_networks.parse_prompts(p.prompts)
p.prompts, p.network_data = extra_networks.parse_prompts(p.prompts, p.network_data)
extra_networks.activate(p, p.network_data)
ip_model_dict.update({
+1 -1
View File
@@ -70,7 +70,7 @@ def prepare_prompts(p, init_image, prompt:str, section_prompt:str, num_sections:
p.prompt = shared.prompt_styles.apply_styles_to_prompt(p.prompt, p.styles)
p.negative_prompt = shared.prompt_styles.apply_negative_styles_to_prompt(p.negative_prompt, p.styles)
shared.prompt_styles.apply_styles_to_extra(p)
p.prompts, p.network_data = extra_networks.parse_prompts([p.prompt])
p.prompts, p.network_data = extra_networks.parse_prompts([p.prompt], p.network_data)
extra_networks.activate(p)
prompt = p.prompts[0]
generated_prompts = [''] * num_sections
+13 -12
View File
@@ -177,11 +177,11 @@ class ExtraNetworkLora(extra_networks.ExtraNetwork):
def changed(self, requested: list[str], include: list[str] | None = None, exclude: list[str] | None = None) -> bool:
if shared.opts.lora_force_reload:
debug_log(f'Network check: type=LoRA requested={requested} status=forced')
return True
debug_log(f'Network check: type=LoRA 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:
return False
return False, "no model"
if not hasattr(sd_model, 'loaded_loras'):
sd_model.loaded_loras = {}
if include is None or len(include) == 0:
@@ -192,15 +192,15 @@ class ExtraNetworkLora(extra_networks.ExtraNetwork):
loaded = sd_model.loaded_loras.get(key, [])
if len(requested) != len(loaded):
sd_model.loaded_loras[key] = requested
debug_log(f'Network check: type=LoRA key="{key}" requested={requested} loaded={loaded} status=changed')
return True
debug_log(f'Network check: type=LoRA 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[key] = requested
debug_log(f'Network check: type=LoRA key="{key}" requested={requested} loaded={loaded} status=changed')
return True
debug_log(f'Network check: type=LoRA key="{key}" requested={requested} loaded={loaded} status=same')
return False
debug_log(f'Network check: type=LoRA 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"')
return False, "none"
def activate(self, p, params_list, step=0, include=None, exclude=None):
if exclude is None:
@@ -216,6 +216,7 @@ class ExtraNetworkLora(extra_networks.ExtraNetwork):
self.model = shared.opts.sd_model_checkpoint
names, te_multipliers, unet_multipliers, dyn_dims, lora_modules = parse(p, params_list, step)
requested = self.signature(names, te_multipliers, unet_multipliers)
reason = ''
load_method = lora_overrides.get_method()
if debug:
@@ -224,7 +225,7 @@ class ExtraNetworkLora(extra_networks.ExtraNetwork):
debug_log(f'Network load: type=LoRA include={include} exclude={exclude} method={load_method} requested={requested} fn={fn}')
if load_method == 'diffusers':
has_changed = self.changed(requested)
has_changed, reason = self.changed(requested)
if has_changed:
jobid = shared.state.begin('LoRA')
lora_load.network_load(names, te_multipliers, unet_multipliers, dyn_dims, lora_modules) # load only on first call
@@ -237,7 +238,7 @@ class ExtraNetworkLora(extra_networks.ExtraNetwork):
else: # native
lora_load.network_load(names, te_multipliers, unet_multipliers, dyn_dims) # load
has_changed = self.changed(requested, include, exclude)
has_changed, reason = self.changed(requested, include, exclude)
if has_changed:
jobid = shared.state.begin('LoRA')
if len(l.previously_loaded_networks) > 0:
@@ -254,7 +255,7 @@ class ExtraNetworkLora(extra_networks.ExtraNetwork):
prompt(p)
if has_changed and len(include) == 0: # print only once
actual_method = 'native' if any(len(n.modules) > 0 for n in l.loaded_networks) else load_method
log.info(f'Network load: type=LoRA networks={[n.name for n in l.loaded_networks]} method={actual_method} mode={"fuse" if shared.opts.lora_fuse_native else "backup"} te={te_multipliers} unet={unet_multipliers} time={l.timer.summary}')
log.info(f'Network load: type=LoRA networks={[n.name for n in l.loaded_networks]} load={load_method} method={actual_method} mode={"fuse" if shared.opts.lora_fuse_native else "backup"} te={te_multipliers} unet={unet_multipliers} time={l.timer.summary} reason={reason}')
def deactivate(self, p, force=False):
if len(lora_diffusers.diffuser_loaded) > 0 and (shared.opts.lora_force_reload or force):
+8 -4
View File
@@ -427,9 +427,12 @@ class YoloRestorer(Detailer):
pc.negative_prompt = negative_lines[i*len(items)+j]
pc.prompts = [pc.prompt]
pc.negative_prompts = [pc.negative_prompt]
pc.prompts, pc.network_data = extra_networks.parse_prompts(pc.prompts)
extra_networks.activate(pc, pc.network_data)
log.debug(f'Detail: model="{i+1}:{name}" item={j+1}/{len(items)} box={item.box} label="{item.label}" score={item.score:.2f} seg={detailer_opt(p, "detailer_segmentation")} prompt="{pc.prompt}"')
pc.prompts, pc.network_data = extra_networks.parse_prompts(pc.prompts, pc.network_data)
pc.disable_extra_networks = True # disable processing_diffusers from handling network activation since its handled here
network_same = len(p.network_data.values()) == len(pc.network_data.values()) and all(x == y for x, y in zip(p.network_data.values(), pc.network_data.values()))
if not network_same:
extra_networks.activate(pc, pc.network_data)
log.debug(f'Detail: model="{i+1}:{name}" item={j+1}/{len(items)} box={item.box} label="{item.label}" score={item.score:.2f} seg={detailer_opt(p, "detailer_segmentation")} network={network_same} prompt="{pc.prompt}"')
pc.init_images = [image]
pc.image_mask = [item.mask]
pc.overlay_images = []
@@ -443,7 +446,8 @@ class YoloRestorer(Detailer):
# process
jobid = shared.state.begin('Detailer')
pp = processing.process_images_inner(pc)
extra_networks.deactivate(pc, force=True)
if not network_same:
extra_networks.deactivate(pc, force=True)
shared.sd_model.fail_on_switch_error = False
shared.state.end(jobid)
+2 -1
View File
@@ -279,6 +279,7 @@ class StableDiffusionProcessing:
skip_processing: bool = False,
override_settings_restore_afterwards: bool = True,
override_settings: dict[str, Any] | None = None,
network_data: dict | None = None,
# metadata
# extra_generation_params: Dict[Any, Any] = {},
# task_args: Dict[str, Any] = {},
@@ -322,7 +323,7 @@ class StableDiffusionProcessing:
self.negative_prompt_attention_masks = []
self.disable_extra_networks = False
self.iteration = 0
self.network_data = {}
self.network_data = network_data or {}
# initializers
self.prompt = prompt