From 0602bedd13d910f844a287c8807be5cee7d01132 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Tue, 22 Jul 2025 10:46:35 -0400 Subject: [PATCH] fix modules merge Signed-off-by: Vladimir Mandic --- CHANGELOG.md | 3 ++- modules/merging/modules_sdxl.py | 19 ++++++++++++++++--- modules/sd_samplers.py | 3 +++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2dc8fcc1..81274219b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ Feature highlights include: - [Wiki](https://github.com/vladmandic/automatic/wiki) & [Docs](https://vladmandic.github.io/sdnext-docs/) updates, especially new end-to-end [Parameters](https://vladmandic.github.io/sdnext-docs/Parameters/) page In this release we finally break with legacy with the removal of the original [A1111](https://github.com/AUTOMATIC1111/stable-diffusion-webui/) codebase which has not been maintained for a while now -This plus major cleanup of codebase and external dependencies resulted in ~53k LoC (*lines-of-code*) reduction and spread over [~720 files](https://github.com/vladmandic/sdnext/pull/4017) in ~120 commits! +This plus major cleanup of codebase and external dependencies resulted in ~55k LoC (*lines-of-code*) reduction and spread over [~750 files](https://github.com/vladmandic/sdnext/pull/4017) in ~160 commits! We also switched project license to [Apache-2.0](https://github.com/vladmandic/sdnext/blob/dev/LICENSE.txt) which means that SD.Next is now fully compatible with commercial and non-commercial use and redistribution regardless of modifications! @@ -133,6 +133,7 @@ For details, see [ChangeLog](https://github.com/vladmandic/automatic/blob/master - fix xyz with control enabled - fix control order of image save operations - fix control batch-input processing + - fix modules merge save model - fix torchvision bicubic upsample with ipex - cleanup control infotext - allow upscaling with models that have implicit VAE processing diff --git a/modules/merging/modules_sdxl.py b/modules/merging/modules_sdxl.py index ec994d567..6ddcbcd67 100644 --- a/modules/merging/modules_sdxl.py +++ b/modules/merging/modules_sdxl.py @@ -8,7 +8,7 @@ import torch from safetensors.torch import load_file import diffusers import transformers -from modules import shared, devices +from modules import shared, devices, errors class Recipe: @@ -33,6 +33,11 @@ class Recipe: lora = { } fuse = 1.0 + + def __repr__(self): + return f'Recipe(name="{self.name}" version="{self.version}" author="{self.author}" desc="{self.desc}" hint="{self.hint}" license="{self.license}" dtype="{self.dtype}" fuse={self.fuse} diffusers={self.diffusers} safetensors={self.safetensors})' + + class Test: generate = True prompt = 'astronaut in a diner drinking coffee with burger and french fries on the table' @@ -41,6 +46,8 @@ class Test: height = 1024 guidance = 4 steps = 20 + + recipe = Recipe() test = Test() pipeline: diffusers.StableDiffusionXLPipeline = None @@ -262,11 +269,13 @@ def save_model(pipe: diffusers.StableDiffusionXLPipeline): folder = os.path.join(shared.opts.diffusers_dir, f'models--{author}--{recipe.name}') if len(recipe.version) > 0: folder += f'-{recipe.version}' - if not recipe.diffusers or recipe.safetensors: + if not (recipe.diffusers or recipe.safetensors): + shared.log.debug(f'Modules merge: type=sdxl {recipe} skipping save') return try: yield msg('save') yield msg(f'pretrained={folder}') + shared.log.info(f'Modules merge save: type=sdxl diffusers="{folder}"') pipe.save_pretrained(folder, safe_serialization=True, push_to_hub=False) with open(os.path.join(folder, 'vae', 'config.json'), 'r', encoding='utf8') as f: vae_config = json.load(f) @@ -283,13 +292,16 @@ def save_model(pipe: diffusers.StableDiffusionXLPipeline): fn = os.path.join(shared.opts.ckpt_dir, fn) if not fn.endswith('.safetensors'): fn += '.safetensors' + shared.log.info(f'Modules merge save: type=sdxl safetensors="{fn}"') yield msg(f'safetensors={fn}') from modules.merging import convert_sdxl - metadata = convert_sdxl(model_path=folder, checkpoint_path=fn, metadata=get_metadata()) + metadata = convert_sdxl.convert(model_path=folder, checkpoint_path=fn, metadata=get_metadata()) if 'modelspec.thumbnail' in metadata: metadata['modelspec.thumbnail'] = f"{metadata['modelspec.thumbnail'].split(',')[0]}:{len(metadata['modelspec.thumbnail'])}" yield msg(f'metadata={metadata}') except Exception as e: + shared.log.error(f'Modules merge save: {e}') + errors.display(e, 'merge') yield msg(f'save: {e}') @@ -298,6 +310,7 @@ def merge(): yield from load_base() if pipeline is None: return + shared.log.info(f'Modules merge: type=sdxl {recipe}') pipeline = pipeline.to(device=devices.device, dtype=recipe.dtype) yield from load_scheduler(pipeline) yield from load_unet(pipeline) diff --git a/modules/sd_samplers.py b/modules/sd_samplers.py index 9b22128c5..c7502cb4b 100644 --- a/modules/sd_samplers.py +++ b/modules/sd_samplers.py @@ -54,6 +54,8 @@ def visible_sampler_names(): def restore_default(model): + if model is None: + return None if getattr(model, "default_scheduler", None) is not None: model.scheduler = copy.deepcopy(model.default_scheduler) if hasattr(model, "prior_pipe") and hasattr(model.prior_pipe, "scheduler"): @@ -67,6 +69,7 @@ def restore_default(model): shared.log.debug(f'Sampler: "Default" cls={model.scheduler.__class__.__name__} config={config}') return model.scheduler + def create_sampler(name, model): if name is None or name == 'None': return model.scheduler if model is not None else None