mirror of
https://github.com/vladmandic/automatic
synced 2026-09-18 16:54:33 +02:00
lora change detection improvements
Signed-off-by: Vladimir Mandic <mandic00@live.com>
This commit is contained in:
+5
-3
@@ -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
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user