diff --git a/CHANGELOG.md b/CHANGELOG.md index ae2981e6a..d69b653fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,12 @@ # Change Log for SD.Next -## Update for 2024-09-17 +## Update for 2024-09-18 - **flux** - avoid unet load if unchanged - mark specific unet as unavailable if load failed - fix diffusers local model name parsing + - full prompt parser will auto-select `xhinker` for flux models - **xyz grid** full refactor - multi-mode: *selectable-script* and *alwayson-script* - allow usage combined with other scripts @@ -29,9 +30,20 @@ - if prompt contains `_tags_` it will be used as placeholder for replacement, otherwise tags will be appended - used tags are also logged and registered in image metadata - correct using of `extra_networks_default_multiplier` if not scale is specified -- **hf** force logout/login on token change +- **text encoder**: + - allow loading different custom text encoders: *clip-vit-l, clip-vit-g, t5* + will automatically find appropriate encoder in the loaded model and replace it with loaded text encoder + download text encoders into folder set in settings -> system paths -> text encoders + default `models/Text-encoder` folder is used if no custom path is set + example *clip-vit-l* models: [Detailed & Smooth](https://huggingface.co/zer0int/CLIP-GmP-ViT-L-14), [LongCLIP](https://huggingface.co/zer0int/LongCLIP-GmP-ViT-L-14) + - xyz grid support for text encoder + - full prompt parser now correctly works with different prompts in batch +- **huggingface**: + - force logout/login on token change + - unified handling of cache folder: set via `HF_HUB` or `HF_HUB_CACHE` or via settings -> system paths - **backend=original** is now marked as in maintenance-only mode - **python 3.12** improved compatibility, automatically handle `setuptools` +- massive log cleanup - minor ui optimizations ## Update for 2024-09-13 diff --git a/extensions-builtin/Lora/extra_networks_lora.py b/extensions-builtin/Lora/extra_networks_lora.py index ba583b450..0bb7511af 100644 --- a/extensions-builtin/Lora/extra_networks_lora.py +++ b/extensions-builtin/Lora/extra_networks_lora.py @@ -61,7 +61,7 @@ class ExtraNetworkLora(extra_networks.ExtraNetwork): loaded.tags = loaded.tags[:shared.opts.lora_apply_tags] all_tags.extend(loaded.tags) if len(all_tags) > 0: - shared.log.debug(f"LoRA apply: max={shared.opts.lora_apply_tags} tags{all_tags}") + shared.log.debug(f"Load network: type=LoRA max={shared.opts.lora_apply_tags} tags={all_tags} apply") all_tags = ', '.join(all_tags) p.extra_generation_params["LoRA tags"] = all_tags if p.all_prompts is not None: @@ -129,7 +129,7 @@ class ExtraNetworkLora(extra_networks.ExtraNetwork): if len(names) > 0 and step == 0: self.infotext(p) self.prompt(p) - shared.log.info(f'LoRA apply: {names} patch={t1-t0:.2f} te={te_multipliers} unet={unet_multipliers} dims={dyn_dims} load={t2-t1:.2f}') + shared.log.info(f'Load network: type=LoRA apply={names} patch={t1-t0:.2f} te={te_multipliers} unet={unet_multipliers} dims={dyn_dims} load={t2-t1:.2f}') elif self.active: self.active = False diff --git a/extensions-builtin/Lora/networks.py b/extensions-builtin/Lora/networks.py index f6bc0dfa0..12be93091 100644 --- a/extensions-builtin/Lora/networks.py +++ b/extensions-builtin/Lora/networks.py @@ -86,25 +86,24 @@ def load_diffusers(name, network_on_disk, lora_scale=shared.opts.extra_networks_ t0 = time.time() name = name.replace(".", "_") #cached = lora_cache.get(name, None) - shared.log.debug(f'LoRA load: name="{name}" file="{network_on_disk.filename}" type=diffusers scale={lora_scale} fuse={shared.opts.lora_fuse_diffusers}') + shared.log.debug(f'Load network: type=LoRA name="{name}" file="{network_on_disk.filename}" type=diffusers scale={lora_scale} fuse={shared.opts.lora_fuse_diffusers}') # if cached is not None: # return cached if not shared.native: return None if not hasattr(shared.sd_model, 'load_lora_weights'): - shared.log.error(f'LoRA load failed: class={shared.sd_model.__class__} does not implement load lora') + shared.log.error(f'Load network: type=LoRA class={shared.sd_model.__class__} does not implement load lora') return None try: shared.sd_model.load_lora_weights(network_on_disk.filename, adapter_name=name) except Exception as e: if 'already in use' in str(e): - # shared.log.warning(f'LoRA load failed: file={network_on_disk.filename} {e}') pass else: if 'The following keys have not been correctly renamed' in str(e): - shared.log.error(f'LoRA load failed: file="{network_on_disk.filename}" diffusers unsupported format') + shared.log.error(f'Load network: type=LoRA file="{network_on_disk.filename}" diffusers unsupported format') else: - shared.log.error(f'LoRA load failed: file="{network_on_disk.filename}" {e}') + shared.log.error(f'Load network: type=LoRA file="{network_on_disk.filename}" {e}') if debug: errors.display(e, "LoRA") return None @@ -123,7 +122,7 @@ def load_network(name, network_on_disk) -> network.Network: t0 = time.time() cached = lora_cache.get(name, None) if debug: - shared.log.debug(f'LoRA load: name="{name}" file="{network_on_disk.filename}" type=lora {"cached" if cached else ""}') + shared.log.debug(f'Load network: type=LoRA name="{name}" file="{network_on_disk.filename}" type=lora {"cached" if cached else ""}') if cached is not None: return cached net = network.Network(name, network_on_disk) @@ -148,8 +147,6 @@ def load_network(name, network_on_disk) -> network.Network: network_part = '.'.join(parts[-2:]).replace('lora_A', 'lora_down').replace('lora_B', 'lora_up') else: key_network_without_network_parts, network_part = key_network.split(".", 1) - # if debug: - # shared.log.debug(f'LoRA load: name="{name}" full={key_network} network={network_part} key={key_network_without_network_parts}') key, sd_module = convert(key_network_without_network_parts) # Now returns lists if sd_module[0] is None: if "bundle_emb" not in key_network: @@ -222,7 +219,7 @@ def load_networks(names, te_multipliers=None, unet_multipliers=None, dyn_dims=No if network_on_disk is not None: shorthash = getattr(network_on_disk, 'shorthash', '').lower() if debug: - shared.log.debug(f'LoRA load: name="{name}" file="{network_on_disk.filename}" hash="{shorthash}"') + shared.log.debug(f'Load network: type=LoRA name="{name}" file="{network_on_disk.filename}" hash="{shorthash}"') try: if recompile_model: shared.compiled_model_state.lora_model.append(f"{name}:{te_multipliers[i] if te_multipliers else shared.opts.extra_networks_default_multiplier}") @@ -234,13 +231,13 @@ def load_networks(names, te_multipliers=None, unet_multipliers=None, dyn_dims=No net.mentioned_name = name network_on_disk.read_hash() except Exception as e: - shared.log.error(f'LoRA load failed: file="{network_on_disk.filename}" {e}') + shared.log.error(f'Load network: type=LoRA file="{network_on_disk.filename}" {e}') if debug: errors.display(e, 'LoRA') continue if net is None: failed_to_load_networks.append(name) - shared.log.error(f'LoRA unknown type: network="{name}"') + shared.log.error(f'Load network: type=LoRA network="{name}" unknown type') continue if shared.native: shared.sd_model.embedding_db.load_diffusers_embedding(None, net.bundle_embeddings) @@ -253,17 +250,17 @@ def load_networks(names, te_multipliers=None, unet_multipliers=None, dyn_dims=No name = next(iter(lora_cache)) lora_cache.pop(name, None) if len(diffuser_loaded) > 0: - shared.log.debug(f'LoRA loaded={diffuser_loaded} scales={diffuser_scales}') + shared.log.debug(f'Load network: type=LoRA loaded={diffuser_loaded} scales={diffuser_scales}') shared.sd_model.set_adapters(adapter_names=diffuser_loaded, adapter_weights=diffuser_scales) if shared.opts.lora_fuse_diffusers: shared.sd_model.fuse_lora(adapter_names=diffuser_loaded, lora_scale=1.0, fuse_unet=True, fuse_text_encoder=True) # fuse uses fixed scale since later apply does the scaling shared.sd_model.unload_lora_weights() if len(loaded_networks) > 0 and debug: - shared.log.debug(f'LoRA loaded={len(loaded_networks)} cache={list(lora_cache)}') + shared.log.debug(f'Load network: type=LoRA loaded={len(loaded_networks)} cache={list(lora_cache)}') devices.torch_gc() if recompile_model: - shared.log.info("LoRA recompiling model") + shared.log.info("Load network: type=LoRA recompiling model") backup_lora_model = shared.compiled_model_state.lora_model if 'Model' in shared.opts.cuda_compile: shared.sd_model = sd_models_compile.compile_diffusers(shared.sd_model) @@ -532,7 +529,7 @@ def list_available_networks(): with concurrent.futures.ThreadPoolExecutor(max_workers=shared.max_workers) as executor: for fn in candidates: executor.submit(add_network, fn) - shared.log.info(f'LoRA networks: available={len(available_networks)} folders={len(forbidden_network_aliases)}') + shared.log.info(f'Available LoRAs: items={len(available_networks)} folders={len(forbidden_network_aliases)}') def infotext_pasted(infotext, params): # pylint: disable=W0613 diff --git a/extensions-builtin/Lora/ui_extra_networks_lora.py b/extensions-builtin/Lora/ui_extra_networks_lora.py index 60e655fa2..d35c9406b 100644 --- a/extensions-builtin/Lora/ui_extra_networks_lora.py +++ b/extensions-builtin/Lora/ui_extra_networks_lora.py @@ -110,7 +110,7 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage): return item except Exception as e: - shared.log.error(f"Networks: type=lora file={name} {e}") + shared.log.error(f'Networks: type=lora file="{name}" {e}') if debug: from modules import errors errors.display('e', 'Lora') diff --git a/installer.py b/installer.py index 25c105e12..766eb6660 100644 --- a/installer.py +++ b/installer.py @@ -88,6 +88,7 @@ def setup_logging(): from rich.theme import Theme from rich.logging import RichHandler from rich.console import Console + from rich import print as rprint from rich.pretty import install as pretty_install from rich.traceback import install as traceback_install @@ -102,6 +103,7 @@ def setup_logging(): level = logging.DEBUG if args.debug else logging.INFO log.setLevel(logging.DEBUG) # log to file is always at level debug for facility `sd` + log.print = rprint global console # pylint: disable=global-statement console = Console(log_time=True, log_time_format='%H:%M:%S-%f', theme=Theme({ "traceback.border": "black", @@ -789,7 +791,7 @@ def run_extension_installer(folder): env['PYTHONPATH'] = os.path.abspath(".") result = subprocess.run(f'"{sys.executable}" "{path_installer}"', shell=True, env=env, check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=folder) txt = result.stdout.decode(encoding="utf8", errors="ignore") - debug(f'Extension installer: file={path_installer} {txt}') + debug(f'Extension installer: file="{path_installer}" {txt}') if result.returncode != 0: global errors # pylint: disable=global-statement errors += 1 @@ -975,7 +977,6 @@ def set_environment(): os.environ.setdefault('KINETO_LOG_LEVEL', '3') os.environ.setdefault('DO_NOT_TRACK', '1') os.environ.setdefault('HF_HUB_CACHE', opts.get('hfcache_dir', os.path.join(os.path.expanduser('~'), '.cache', 'huggingface', 'hub'))) - log.debug(f'Huggingface folders: home="{os.environ.get("HF_HUB")}" cache="{os.environ.get("HF_HUB_CACHE")}"') allocator = f'garbage_collection_threshold:{opts.get("torch_gc_threshold", 80)/100:0.2f},max_split_size_mb:512' if opts.get("torch_malloc", "native") == 'cudaMallocAsync': allocator += ',backend:cudaMallocAsync' diff --git a/modules/deepbooru.py b/modules/deepbooru.py index 23ec10caa..509762c35 100644 --- a/modules/deepbooru.py +++ b/modules/deepbooru.py @@ -16,7 +16,7 @@ class DeepDanbooru: if self.model is not None: return model_path = os.path.join(paths.models_path, "DeepDanbooru") - shared.log.debug(f'Loading interrogate model: type=DeepDanbooru folder={model_path}') + shared.log.debug(f'Loading interrogate model: type=DeepDanbooru folder="{model_path}"') files = modelloader.load_models( model_path=model_path, model_url='https://github.com/AUTOMATIC1111/TorchDeepDanbooru/releases/download/v1/model-resnet_custom_v3.pt', diff --git a/modules/face/faceid.py b/modules/face/faceid.py index ec25e2f3d..754ce59a3 100644 --- a/modules/face/faceid.py +++ b/modules/face/faceid.py @@ -80,13 +80,13 @@ def face_id( basename, _ext = os.path.splitext(filename) model_path = hf.hf_hub_download(repo_id=folder, filename=filename, cache_dir=shared.opts.diffusers_dir) if model_path is None: - shared.log.error(f"FaceID download failed: model={model} file={ip_ckpt}") + shared.log.error(f'FaceID download failed: model={model} file="{ip_ckpt}"') return None if faceid_model_weights is None or faceid_model_name != model or not cache: - shared.log.debug(f"FaceID load: model={model} file={ip_ckpt}") + shared.log.debug(f'FaceID load: model={model} file="{ip_ckpt}"') faceid_model_weights = torch.load(model_path, map_location="cpu") else: - shared.log.debug(f"FaceID cached: model={model} file={ip_ckpt}") + shared.log.debug(f'FaceID cached: model={model} file="{ip_ckpt}"') if "XL Plus" in model and shared.sd_model_type == 'sd': image_encoder_path = "laion/CLIP-ViT-H-14-laion2B-s32B-b79K" diff --git a/modules/ipadapter.py b/modules/ipadapter.py index 111b39d98..de8522195 100644 --- a/modules/ipadapter.py +++ b/modules/ipadapter.py @@ -240,5 +240,5 @@ def apply(pipe, p: processing.StableDiffusionProcessing, adapter_names=[], adapt t1 = time.time() shared.log.info(f'IP adapter: {ip_str} image={adapter_images} mask={adapter_masks is not None} time={t1-t0:.2f}') except Exception as e: - shared.log.error(f'IP adapter failed to load: repo={base_repo} folder={ip_subfolder} weights={adapters} names={adapter_names} {e}') + shared.log.error(f'IP adapter failed to load: repo="{base_repo}" folder="{ip_subfolder}" weights={adapters} names={adapter_names} {e}') return True diff --git a/modules/lama.py b/modules/lama.py index b7611770b..b02522140 100644 --- a/modules/lama.py +++ b/modules/lama.py @@ -72,7 +72,7 @@ def download_model(): filename = os.path.basename(parts.path) cached_file = os.path.join(model_dir, filename) if not os.path.exists(cached_file): - log.info(f'LaMa download: url={LAMA_MODEL_URL} file={cached_file}') + log.info(f'LaMa download: url="{LAMA_MODEL_URL}" file="{cached_file}"') hash_prefix = None download_url_to_file(LAMA_MODEL_URL, cached_file, hash_prefix, progress=True) return cached_file diff --git a/modules/model_flux.py b/modules/model_flux.py index 270599771..5dd54e164 100644 --- a/modules/model_flux.py +++ b/modules/model_flux.py @@ -139,7 +139,7 @@ def load_transformer(file_path): # triggered by opts.sd_unet change "torch_dtype": devices.dtype, "cache_dir": shared.opts.hfcache_dir, } - shared.log.info(f'Loading UNet: type=FLUX file="{file_path}" offload={shared.opts.diffusers_offload_mode} quant={quant} dtype={devices.dtype}') + shared.log.info(f'Load module: type=UNet/Transformer file="{file_path}" offload={shared.opts.diffusers_offload_mode} quant={quant} dtype={devices.dtype}') if quant == 'qint8' or quant == 'qint4': _transformer, _text_encoder_2 = load_flux_quanto(file_path) if _transformer is not None: @@ -191,7 +191,7 @@ def load_flux(checkpoint_info, diffusers_load_config): # triggered by opts.sd_ch if shared.opts.sd_text_encoder != 'None': try: debug(f'Loading FLUX: t5="{shared.opts.sd_text_encoder}"') - from modules.model_t5 import load_t5 + from modules.model_te import load_t5 _text_encoder_2 = load_t5(t5=shared.opts.sd_text_encoder, cache_dir=shared.opts.diffusers_dir) if _text_encoder_2 is not None: text_encoder_2 = _text_encoder_2 diff --git a/modules/model_pixart.py b/modules/model_pixart.py index 560fcccd9..a0e55ba82 100644 --- a/modules/model_pixart.py +++ b/modules/model_pixart.py @@ -2,13 +2,13 @@ import diffusers def load_pixart(checkpoint_info, diffusers_load_config={}): - from modules import shared, devices, modelloader, model_t5 + from modules import shared, devices, modelloader, model_te modelloader.hf_login() # shared.opts.data['cuda_dtype'] = 'FP32' # override # shared.opts.data['diffusers_offload_mode}'] = "model" # override # devices.set_cuda_params() fn = checkpoint_info.path.replace('huggingface/', '') - t5 = model_t5.load_t5(shared.opts.sd_text_encoder, cache_dir=shared.opts.diffusers_dir) + t5 = model_te.load_t5(shared.opts.sd_text_encoder, cache_dir=shared.opts.diffusers_dir) transformer = diffusers.PixArtTransformer2DModel.from_pretrained( fn, subfolder = 'transformer', diff --git a/modules/model_t5.py b/modules/model_te.py similarity index 53% rename from modules/model_t5.py rename to modules/model_te.py index 4d082d2d4..013806d18 100644 --- a/modules/model_t5.py +++ b/modules/model_te.py @@ -3,19 +3,21 @@ import json import torch import transformers from safetensors.torch import load_file -from modules import shared, devices, files_cache +from modules import shared, devices, files_cache, errors from installer import install -t5_dict = {} +te_dict = {} +debug = os.environ.get('SD_LOAD_DEBUG', None) is not None +loaded_te = None def load_t5(t5=None, cache_dir=None): from modules import modelloader modelloader.hf_login() repo_id = 'stabilityai/stable-diffusion-3-medium-diffusers' - fn = t5_dict.get(t5) if t5 in t5_dict else None - if fn is not None: + fn = te_dict.get(t5) if t5 in te_dict else None + if fn is not None and 'fp8' in t5.lower(): from accelerate.utils import set_module_tensor_to_device with open(os.path.join('configs', 'flux', 'text_encoder_2', 'config.json'), encoding='utf8') as f: t5_config = transformers.T5Config(**json.load(f)) @@ -35,6 +37,11 @@ def load_t5(t5=None, cache_dir=None): except Exception: shared.log.error(f"FLUX: Failed to cast text encoder to {devices.dtype}, set dtype to {t5.dtype}") raise + elif fn is not None: + with open(os.path.join('configs', 'flux', 'text_encoder_2', 'config.json'), encoding='utf8') as f: + t5_config = transformers.T5Config(**json.load(f)) + state_dict = load_file(fn) + t5 = transformers.T5EncoderModel.from_pretrained(None, state_dict=state_dict, config=t5_config) elif 'fp16' in t5.lower(): t5 = transformers.T5EncoderModel.from_pretrained(repo_id, subfolder='text_encoder_3', cache_dir=cache_dir, torch_dtype=devices.dtype) elif 'fp4' in t5.lower(): @@ -67,11 +74,22 @@ def load_t5(t5=None, cache_dir=None): def set_t5(pipe, module, t5=None, cache_dir=None): + global loaded_te # pylint: disable=global-statement + if loaded_te == shared.opts.sd_text_encoder: + return if pipe is None or not hasattr(pipe, module): return pipe - t5 = load_t5(t5=t5, cache_dir=cache_dir) - if module == "text_encoder_2" and t5 is None: # do not unload te2 + t5 = None + try: + t5 = load_t5(t5=t5, cache_dir=cache_dir) + except Exception as e: + shared.log.error(f'Load module: type={module} class="T5" file="{shared.opts.sd_text_encoder}" {e}') + if debug: + errors.display(e, 'TE:') + t5 = None + if t5 is None: return None + loaded_te = shared.opts.sd_text_encoder setattr(pipe, module, t5) if shared.opts.diffusers_offload_mode == "sequential": from accelerate import cpu_offload @@ -86,9 +104,48 @@ def set_t5(pipe, module, t5=None, cache_dir=None): return pipe -def refresh_t5_list(): - t5_dict.clear() - for file in files_cache.list_files(shared.opts.t5_dir, ext_filter=[".safetensors"]): +def set_te(pipe): + global loaded_te # pylint: disable=global-statement + if loaded_te == shared.opts.sd_text_encoder: + return + from modules.sd_models import move_model + if 'vit-l' in shared.opts.sd_text_encoder.lower() and hasattr(shared.sd_model, 'text_encoder') and shared.sd_model.text_encoder.__class__.__name__ == 'CLIPTextModel': + try: + config = transformers.PretrainedConfig.from_json_file('configs/sdxl/text_encoder/config.json') + state_dict = load_file(os.path.join(shared.opts.te_dir, f'{shared.opts.sd_text_encoder}.safetensors')) + te = transformers.CLIPTextModel.from_pretrained(pretrained_model_name_or_path=None, state_dict=state_dict, config=config) + except Exception as e: + shared.log.error(f'Load module: type="text_encoder" class="ViT-L" file="{shared.opts.sd_text_encoder}" {e}') + if debug: + errors.display(e, 'TE:') + state_dict = None + te = None + if te is not None: + loaded_te = shared.opts.sd_text_encoder + pipe.text_encoder = te.to(dtype=devices.dtype) + shared.log.info(f'Load module: type="text_encoder" class="ViT-L" file="{shared.opts.sd_text_encoder}"') + move_model(pipe.text_encoder, devices.device) + if 'vit-g' in shared.opts.sd_text_encoder.lower() and hasattr(shared.sd_model, 'text_encoder_2') and shared.sd_model.text_encoder_2.__class__.__name__ == 'CLIPTextModelWithProjection': + try: + config = transformers.PretrainedConfig.from_json_file('configs/sdxl/text_encoder_2/config.json') + state_dict = load_file(os.path.join(shared.opts.te_dir, f'{shared.opts.sd_text_encoder}.safetensors')) + te = transformers.CLIPTextModelWithProjection.from_pretrained(pretrained_model_name_or_path=None, state_dict=state_dict, config=config) + except Exception as e: + shared.log.error(f'Load module: type module="text_encoder_2" class="ViT-G" file="{shared.opts.sd_text_encoder}" {e}') + if debug: + errors.display(e, 'TE:') + state_dict = None + te = None + if te is not None: + loaded_te = shared.opts.sd_text_encoder + pipe.text_encoder_2 = te.to(dtype=devices.dtype) + shared.log.info(f'Load module: type="text_encoder_2" class="ViT-G" file="{shared.opts.sd_text_encoder}"') + move_model(pipe.text_encoder_2, devices.device) + + +def refresh_te_list(): + te_dict.clear() + for file in files_cache.list_files(shared.opts.te_dir, ext_filter=[".safetensors"]): name = os.path.splitext(os.path.basename(file))[0] - t5_dict[name] = file - shared.log.debug(f'Available T5s: path="{shared.opts.t5_dir}" items={len(t5_dict)}') + te_dict[name] = file + shared.log.info(f'Available TEs: path="{shared.opts.te_dir}" items={len(te_dict)}') diff --git a/modules/modelloader.py b/modules/modelloader.py index 0ba632b11..72205397c 100644 --- a/modules/modelloader.py +++ b/modules/modelloader.py @@ -50,11 +50,11 @@ def download_civit_meta(model_path: str, model_id): if r.status_code == 200: try: shared.writefile(r.json(), filename=fn, mode='w', silent=True) - msg = f'CivitAI download: id={model_id} url={url} file={fn}' + msg = f'CivitAI download: id={model_id} url={url} file="{fn}"' shared.log.info(msg) return msg except Exception as e: - msg = f'CivitAI download error: id={model_id} url={url} file={fn} {e}' + msg = f'CivitAI download error: id={model_id} url={url} file="{fn}" {e}' errors.display(e, 'CivitAI download error') shared.log.error(msg) return msg @@ -66,7 +66,7 @@ def download_civit_preview(model_path: str, preview_url: str): preview_file = os.path.splitext(model_path)[0] + ext if os.path.exists(preview_file): return '' - res = f'CivitAI download: url={preview_url} file={preview_file}' + res = f'CivitAI download: url={preview_url} file="{preview_file}"' r = shared.req(preview_url, stream=True) total_size = int(r.headers.get('content-length', 0)) block_size = 16384 # 16KB blocks @@ -88,7 +88,7 @@ def download_civit_preview(model_path: str, preview_url: str): except Exception as e: os.remove(preview_file) res += f' error={e}' - shared.log.error(f'CivitAI download error: url={preview_url} file={preview_file} written={written} {e}') + shared.log.error(f'CivitAI download error: url={preview_url} file="{preview_file}" written={written} {e}') shared.state.end() if img is None: return res @@ -281,7 +281,7 @@ def load_diffusers_models(clear=True): friendly = os.path.join(place, name) snapshots = os.listdir(os.path.join(folder, "snapshots")) if len(snapshots) == 0: - shared.log.warning(f"Diffusers folder has no snapshots: location={place} folder={folder} name={name}") + shared.log.warning(f'Diffusers folder has no snapshots: location="{place}" folder="{folder}" name="{name}"') continue for snapshot in snapshots: commit = os.path.join(folder, 'snapshots', snapshot) @@ -296,10 +296,10 @@ def load_diffusers_models(clear=True): if os.path.exists(os.path.join(folder, 'hidden')): continue except Exception as e: - debug(f"Error analyzing diffusers model: {folder} {e}") + debug(f'Error analyzing diffusers model: "{folder}" {e}') except Exception as e: shared.log.error(f"Error listing diffusers: {place} {e}") - shared.log.debug(f'Scanning diffusers cache: folder={place} items={len(list(diffuser_repos))} time={time.time()-t0:.2f}') + shared.log.debug(f'Scanning diffusers cache: folder="{place}" items={len(list(diffuser_repos))} time={time.time()-t0:.2f}') return diffuser_repos @@ -444,7 +444,7 @@ def load_file_from_url(url: str, *, model_dir: str, progress: bool = True, file_ file_name = os.path.basename(parts.path) cached_file = os.path.abspath(os.path.join(model_dir, file_name)) if not os.path.exists(cached_file): - shared.log.info(f'Downloading: url="{url}" file={cached_file}') + shared.log.info(f'Downloading: url="{url}" file="{cached_file}"') download_url_to_file(url, cached_file) if os.path.exists(cached_file): return cached_file @@ -577,5 +577,5 @@ def load_upscalers(): names.append(name[8:]) shared.sd_upscalers = sorted(datas, key=lambda x: x.name.lower() if not isinstance(x.scaler, (UpscalerNone, UpscalerLanczos, UpscalerNearest)) else "") # Special case for UpscalerNone keeps it at the beginning of the list. t1 = time.time() - shared.log.debug(f"Load upscalers: total={len(shared.sd_upscalers)} downloaded={len([x for x in shared.sd_upscalers if x.data_path is not None and os.path.isfile(x.data_path)])} user={len([x for x in shared.sd_upscalers if x.custom])} time={t1-t0:.2f} {names}") + shared.log.info(f"Available Upscalers: items={len(shared.sd_upscalers)} downloaded={len([x for x in shared.sd_upscalers if x.data_path is not None and os.path.isfile(x.data_path)])} user={len([x for x in shared.sd_upscalers if x.custom])} time={t1-t0:.2f} types={names}") return [x.name for x in shared.sd_upscalers] diff --git a/modules/paths.py b/modules/paths.py index e71e8e917..e4ee65864 100644 --- a/modules/paths.py +++ b/modules/paths.py @@ -101,7 +101,7 @@ def create_paths(opts): create_path(fix_path('diffusers_dir')) create_path(fix_path('vae_dir')) create_path(fix_path('unet_dir')) - create_path(fix_path('t5_dir')) + create_path(fix_path('te_dir')) create_path(fix_path('lora_dir')) create_path(fix_path('embeddings_dir')) create_path(fix_path('hypernetwork_dir')) diff --git a/modules/prompt_parser_diffusers.py b/modules/prompt_parser_diffusers.py index 31cb7c68f..106b71b49 100644 --- a/modules/prompt_parser_diffusers.py +++ b/modules/prompt_parser_diffusers.py @@ -165,38 +165,58 @@ def encode_prompts(pipe, p, prompts: list, negative_prompts: list, steps: int, c return else: t0 = time.time() - positive_schedule, scheduled = get_prompt_schedule(prompts[0], steps) - negative_schedule, neg_scheduled = get_prompt_schedule(negative_prompts[0], steps) - p.scheduled_prompt = scheduled or neg_scheduled - p.prompt_embeds = [] - p.positive_pooleds = [] - p.negative_embeds = [] - p.negative_pooleds = [] - if shared.opts.diffusers_offload_mode == "balanced": pipe = sd_models.apply_balanced_offload(pipe) elif hasattr(pipe, "maybe_free_model_hooks"): - # if the last job is interrupted, model will stay in the vram and cause oom, send everything back to cpu before continuing pipe.maybe_free_model_hooks() devices.torch_gc() - for i in range(max(len(positive_schedule), len(negative_schedule))): - positive_prompt = positive_schedule[i % len(positive_schedule)] - negative_prompt = negative_schedule[i % len(negative_schedule)] - if shared.opts.prompt_attention == "xhinker parser": - prompt_embed, positive_pooled, negative_embed, negative_pooled = get_xhinker_text_embeddings(pipe, positive_prompt, negative_prompt, clip_skip) - else: - prompt_embed, positive_pooled, negative_embed, negative_pooled = get_weighted_text_embeddings(pipe, positive_prompt, negative_prompt, clip_skip) - if prompt_embed is not None: - p.prompt_embeds.append(torch.cat([prompt_embed] * len(prompts), dim=0)) - if negative_embed is not None: - p.negative_embeds.append(torch.cat([negative_embed] * len(negative_prompts), dim=0)) - if positive_pooled is not None: - p.positive_pooleds.append(torch.cat([positive_pooled] * len(prompts), dim=0)) - if negative_pooled is not None: - p.negative_pooleds.append(torch.cat([negative_pooled] * len(negative_prompts), dim=0)) + prompt_embeds, positive_pooleds, negative_embeds, negative_pooleds = [], [], [], [] + last_prompt, last_negative = None, None + for prompt, negative in zip(prompts, negative_prompts): + prompt_embed, positive_pooled, negative_embed, negative_pooled = None, None, None, None + if last_prompt == prompt and last_negative == negative or False: + prompt_embeds.append(prompt_embed) + positive_pooleds.append(positive_pooled) + negative_embeds.append(negative_embed) + negative_pooleds.append(negative_pooled) + continue + positive_schedule, scheduled = get_prompt_schedule(prompt, steps) + negative_schedule, neg_scheduled = get_prompt_schedule(negative, steps) + p.scheduled_prompt = scheduled or neg_scheduled + p.prompt_embeds = [] + p.positive_pooleds = [] + p.negative_embeds = [] + p.negative_pooleds = [] - if shared.opts.sd_textencoder_cache: + for i in range(max(len(positive_schedule), len(negative_schedule))): + positive_prompt = positive_schedule[i % len(positive_schedule)] + negative_prompt = negative_schedule[i % len(negative_schedule)] + if shared.opts.prompt_attention == "xhinker parser" or 'Flux' in pipe.__class__.__name__: + prompt_embed, positive_pooled, negative_embed, negative_pooled = get_xhinker_text_embeddings(pipe, positive_prompt, negative_prompt, clip_skip) + else: + prompt_embed, positive_pooled, negative_embed, negative_pooled = get_weighted_text_embeddings(pipe, positive_prompt, negative_prompt, clip_skip) + prompt_embeds.append(prompt_embed) + positive_pooleds.append(positive_pooled) + negative_embeds.append(negative_embed) + negative_pooleds.append(negative_pooled) + last_prompt, last_negative = prompt, negative + + def fix_length(embeds): + max_len = max([p.shape[1] for p in embeds]) + for i, p in enumerate(embeds): + if p.shape[1] < max_len: + expanded = torch.zeros((p.shape[0], max_len, p.shape[2]), device=p.device, dtype=p.dtype) + expanded[:, :p.shape[1], :] = p + embeds[i] = expanded + return torch.cat(embeds, dim=0) + + p.prompt_embeds.append(fix_length(prompt_embeds)) + p.negative_embeds.append(fix_length(negative_embeds)) + p.positive_pooleds.append(fix_length(positive_pooleds)) + p.negative_pooleds.append(fix_length(negative_pooleds)) + + if shared.opts.sd_textencoder_cache and p.batch_size == 1: cache.update({ 'prompt_embeds': p.prompt_embeds, 'negative_embeds': p.negative_embeds, diff --git a/modules/sd_models.py b/modules/sd_models.py index 620b9dedc..19d633571 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -161,15 +161,15 @@ def list_models(): if shared.cmd_opts.ckpt is not None: if not os.path.exists(shared.cmd_opts.ckpt) and not shared.native: if shared.cmd_opts.ckpt.lower() != "none": - shared.log.warning(f"Requested checkpoint not found: {shared.cmd_opts.ckpt}") + shared.log.warning(f"Requested model not found: {shared.cmd_opts.ckpt}") else: checkpoint_info = CheckpointInfo(shared.cmd_opts.ckpt) if checkpoint_info.name is not None: checkpoint_info.register() shared.opts.data['sd_model_checkpoint'] = checkpoint_info.title elif shared.cmd_opts.ckpt != shared.default_sd_model_file and shared.cmd_opts.ckpt is not None: - shared.log.warning(f"Checkpoint not found: {shared.cmd_opts.ckpt}") - shared.log.info(f'Available models: path="{shared.opts.ckpt_dir}" items={len(checkpoints_list)} time={time.time()-t0:.2f}') + shared.log.warning(f"Model not found: {shared.cmd_opts.ckpt}") + shared.log.info(f'Available Models: path="{shared.opts.ckpt_dir}" items={len(checkpoints_list)} time={time.time()-t0:.2f}') checkpoints_list = dict(sorted(checkpoints_list.items(), key=lambda cp: cp[1].filename)) @@ -342,7 +342,7 @@ def read_metadata_from_safetensors(filename): metadata_len = int.from_bytes(metadata_len, "little") json_start = file.read(2) if metadata_len <= 2 or json_start not in (b'{"', b"{'"): - shared.log.error(f"Model metadata invalid: fn={filename}") + shared.log.error(f'Model metadata invalid: file="{filename}"') json_data = json_start + file.read(metadata_len-2) json_obj = json.loads(json_data) for k, v in json_obj.get("__metadata__", {}).items(): @@ -370,7 +370,7 @@ def read_metadata_from_safetensors(filename): pass res[k] = v except Exception as e: - shared.log.error(f"Model metadata: fn={filename} {e}") + shared.log.error(f'Model metadata: file="{filename}" {e}') sd_metadata[filename] = res global sd_metadata_pending # pylint: disable=global-statement sd_metadata_pending += 1 @@ -682,28 +682,28 @@ def set_diffuser_options(sd_model, vae = None, op: str = 'model', offload=True): if hasattr(sd_model, "vae"): if vae is not None: sd_model.vae = vae - shared.log.debug(f'Setting {op} VAE: name="{sd_vae.loaded_vae_file}"') + shared.log.debug(f'Setting {op}: component=VAE name="{sd_vae.loaded_vae_file}"') if shared.opts.diffusers_vae_upcast != 'default': sd_model.vae.config.force_upcast = True if shared.opts.diffusers_vae_upcast == 'true' else False - shared.log.debug(f'Setting {op} VAE: upcast={sd_model.vae.config.force_upcast}') + shared.log.debug(f'Setting {op}: component=VAE upcast={sd_model.vae.config.force_upcast}') if shared.opts.no_half_vae: devices.dtype_vae = torch.float32 sd_model.vae.to(devices.dtype_vae) - shared.log.debug(f'Setting {op} VAE: no-half=True') + shared.log.debug(f'Setting {op}: component=VAE no-half=True') if hasattr(sd_model, "enable_vae_slicing"): if shared.opts.diffusers_vae_slicing: - shared.log.debug(f'Setting {op}: slicing=True') + shared.log.debug(f'Setting {op}: component=VAE slicing=True') sd_model.enable_vae_slicing() else: sd_model.disable_vae_slicing() if hasattr(sd_model, "enable_vae_tiling"): if shared.opts.diffusers_vae_tiling: - shared.log.debug(f'Setting {op}: tiling=True') + shared.log.debug(f'Setting {op}: component=VAE tiling=True') sd_model.enable_vae_tiling() else: sd_model.disable_vae_tiling() if hasattr(sd_model, "vqvae"): - shared.log.debug(f'Setting {op} VQVAE: upcast=True') + shared.log.debug(f'Setting {op}: component=VQVAE upcast=True') sd_model.vqvae.to(torch.float32) # vqvae is producing nans in fp16 set_diffusers_attention(sd_model) @@ -1262,8 +1262,8 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No return if shared.opts.diffusers_vae_upcast != 'default' and model_type in ['Stable Diffusion', 'Stable Diffusion XL']: diffusers_load_config['force_upcast'] = True if shared.opts.diffusers_vae_upcast == 'true' else False - if debug_load: - shared.log.debug(f'Model args: {diffusers_load_config}') + # if debug_load: + # shared.log.debug(f'Model args: {diffusers_load_config}') if sd_model is not None: diffusers_load_config.pop('vae', None) diffusers_load_config.pop('safety_checker', None) @@ -1311,6 +1311,12 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No else: model_data.sd_model = sd_model + reload_text_encoder(initial=True) # must be before embeddings + timer.record("te") + + if debug_load: + shared.log.trace(f'Model components: {list(get_signature(sd_model).values())}') + from modules.textual_inversion import textual_inversion sd_model.embedding_db = textual_inversion.EmbeddingDatabase() sd_model.embedding_db.add_embedding_dir(shared.opts.embeddings_dir) @@ -1337,8 +1343,6 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No move_model(sd_model, devices.device) timer.record("move") - reload_text_encoder(initial=True) - if shared.opts.ipex_optimize: sd_model = sd_models_compile.ipex_optimize(sd_model) @@ -1347,14 +1351,14 @@ def load_diffuser(checkpoint_info=None, already_loaded_state_dict=None, timer=No timer.record("compile") except Exception as e: - shared.log.error("Failed to load diffusers model") - errors.display(e, "loading Diffusers model") + shared.log.error("Failed to load model") + errors.display(e, "Model") devices.torch_gc(force=True) if shared.cmd_opts.profile: errors.profile(pr, 'Load') script_callbacks.model_loaded_callback(sd_model) - shared.log.info(f"Load {op}: time={timer.summary()} native={get_native(sd_model)} {memory_stats()}") + shared.log.info(f"Load {op}: time={timer.summary()} native={get_native(sd_model)} memory={memory_stats()}") class DiffusersTaskType(Enum): @@ -1377,6 +1381,11 @@ def get_diffusers_task(pipe: diffusers.DiffusionPipeline) -> DiffusersTaskType: return DiffusersTaskType.TEXT_2_IMAGE +def get_signature(cls): + signature = inspect.signature(cls.__init__, follow_wrapped=True, eval_str=True) + return signature.parameters + + def switch_pipe(cls: diffusers.DiffusionPipeline, pipeline: diffusers.DiffusionPipeline = None, args = {}): """ args: @@ -1393,8 +1402,8 @@ def switch_pipe(cls: diffusers.DiffusionPipeline, pipeline: diffusers.DiffusionP if pipeline is None: pipeline = shared.sd_model new_pipe = None - signature = inspect.signature(cls.__init__, follow_wrapped=True, eval_str=True) - possible = signature.parameters.keys() + signature = get_signature(cls) + possible = signature.keys() if isinstance(pipeline, cls) and args == {}: return pipeline pipe_dict = {} @@ -1412,10 +1421,10 @@ def switch_pipe(cls: diffusers.DiffusionPipeline, pipeline: diffusers.DiffusionP for item in possible: if item in ['self', 'args', 'kwargs']: # skip continue - if signature.parameters[item].default != inspect._empty: # has default value so we dont have to worry about it # pylint: disable=protected-access + if signature[item].default != inspect._empty: # has default value so we dont have to worry about it # pylint: disable=protected-access continue if item not in components_used: - shared.log.warning(f'Pipeling switch: missing component={item} type={signature.parameters[item].annotation}') + shared.log.warning(f'Pipeling switch: missing component={item} type={signature[item].annotation}') pipe_dict[item] = None # try but not likely to work components_missing.append(item) new_pipe = cls(**pipe_dict) @@ -1576,7 +1585,7 @@ def set_diffusers_attention(pipe): if 'ControlNet' in pipe.__class__.__name__: # do not replace attention in ControlNet pipelines return - shared.log.debug(f"Setting model: attention={shared.opts.cross_attention_optimization}") + shared.log.debug(f'Setting model: attention="{shared.opts.cross_attention_optimization}"') if shared.opts.cross_attention_optimization == "Disabled": pass # do nothing elif shared.opts.cross_attention_optimization == "Scaled-Dot-Product": # The default set by Diffusers @@ -1716,16 +1725,19 @@ def load_model(checkpoint_info=None, already_loaded_state_dict=None, timer=None, def reload_text_encoder(initial=False): if initial and (shared.opts.sd_text_encoder is None or shared.opts.sd_text_encoder == 'None'): return # dont unload - signature = inspect.signature(shared.sd_model.__class__.__init__, follow_wrapped=True, eval_str=True).parameters + signature = get_signature(shared.sd_model) t5 = [k for k, v in signature.items() if 'T5EncoderModel' in str(v)] if len(t5) > 0: - from modules.model_t5 import set_t5 + from modules.model_te import set_t5 shared.log.debug(f'Load: t5={shared.opts.sd_text_encoder} module="{t5[0]}"') set_t5(pipe=shared.sd_model, module=t5[0], t5=shared.opts.sd_text_encoder, cache_dir=shared.opts.diffusers_dir) elif hasattr(shared.sd_model, 'text_encoder_3'): - from modules.model_t5 import set_t5 + from modules.model_te import set_t5 shared.log.debug(f'Load: t5={shared.opts.sd_text_encoder} module="text_encoder_3"') set_t5(pipe=shared.sd_model, module='text_encoder_3', t5=shared.opts.sd_text_encoder, cache_dir=shared.opts.diffusers_dir) + elif hasattr(shared.sd_model, 'text_encoder') and 'vit' in shared.opts.sd_text_encoder.lower(): + from modules.model_te import set_te + set_te(pipe=shared.sd_model) def reload_model_weights(sd_model=None, info=None, reuse_dict=False, op='model', force=False): diff --git a/modules/sd_unet.py b/modules/sd_unet.py index a3094d88d..c93342b89 100644 --- a/modules/sd_unet.py +++ b/modules/sd_unet.py @@ -52,7 +52,7 @@ def load_unet(model): if not hasattr(model, 'unet') or model.unet is None: shared.log.error('UNet not found in current model') return - shared.log.info(f'Loading UNet: name="{shared.opts.sd_unet}" file="{unet_dict[shared.opts.sd_unet]}" config="{config_file}"') + shared.log.info(f'Load module: type=UNet name="{shared.opts.sd_unet}" file="{unet_dict[shared.opts.sd_unet]}" config="{config_file}"') from diffusers import UNet2DConditionModel from safetensors.torch import load_file unet = UNet2DConditionModel.from_config(model.unet.config if config is None else config).to(devices.device, devices.dtype) @@ -73,4 +73,4 @@ def refresh_unet_list(): for file in files_cache.list_files(shared.opts.unet_dir, ext_filter=[".safetensors"]): name = os.path.splitext(os.path.basename(file))[0] unet_dict[name] = file - shared.log.debug(f'Available UNets: path="{shared.opts.unet_dir}" items={len(unet_dict)}') + shared.log.info(f'Available UNets: path="{shared.opts.unet_dir}" items={len(unet_dict)}') diff --git a/modules/sd_vae.py b/modules/sd_vae.py index bfe9807ad..99d41fb73 100644 --- a/modules/sd_vae.py +++ b/modules/sd_vae.py @@ -210,7 +210,7 @@ def load_vae_diffusers(model_file, vae_file=None, vae_source="unknown-source"): vae_config = sd_models.get_load_config(model_file, model_type, config_type='json') if vae_config is not None: diffusers_load_config['config'] = os.path.join(vae_config, 'vae') - shared.log.info(f'Load VAE: model="{vae_file}" source={vae_source} config={diffusers_load_config}') + shared.log.info(f'Load module: type=VAE model="{vae_file}" source={vae_source} config={diffusers_load_config}') try: import diffusers if os.path.isfile(vae_file): diff --git a/modules/shared.py b/modules/shared.py index c42e01163..29aa7f31c 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -81,6 +81,14 @@ compatibility_opts = ['clip_skip', 'uni_pc_lower_order_final', 'uni_pc_order'] console = Console(log_time=True, log_time_format='%H:%M:%S-%f') dir_timestamps = {} dir_cache = {} +if os.environ.get("HF_HUB_CACHE", None) is not None: + hfcache_dir = os.environ.get("HF_HUB_CACHE") +elif os.environ.get("HF_HUB", None) is not None: + hfcache_dir = os.path.join(os.environ.get("HF_HUB"), '.cache') +else: + hfcache_dir = os.path.join(os.path.expanduser('~'), '.cache', 'huggingface', 'hub') + os.environ["HF_HUB_CACHE"] = hfcache_dir +log.debug(f'Huggingface cache: folder="{hfcache_dir}"') class Backend(Enum): @@ -406,8 +414,7 @@ options_templates.update(options_section(('sd', "Execution & Models"), { "sd_model_refiner": OptionInfo('None', "Refiner model", gr.Dropdown, lambda: {"choices": ['None'] + list_checkpoint_tiles()}, refresh=refresh_checkpoints), "sd_vae": OptionInfo("Automatic", "VAE model", gr.Dropdown, lambda: {"choices": shared_items.sd_vae_items()}, refresh=shared_items.refresh_vae_list), "sd_unet": OptionInfo("None", "UNET model", gr.Dropdown, lambda: {"choices": shared_items.sd_unet_items()}, refresh=shared_items.refresh_unet_list), - # "sd_text_encoder": OptionInfo('None', "Text encoder model", gr.Dropdown, lambda: {"choices": ['None', 'T5 FP4', 'T5 FP8', 'T5 INT8', 'T5 QINT8', 'T5 FP16']}), - "sd_text_encoder": OptionInfo('None', "Text encoder model", gr.Dropdown, lambda: {"choices": shared_items.sd_t5_items()}, refresh=shared_items.refresh_t5_list), + "sd_text_encoder": OptionInfo('None', "Text encoder model", gr.Dropdown, lambda: {"choices": shared_items.sd_te_items()}, refresh=shared_items.refresh_te_list), "sd_model_dict": OptionInfo('None', "Use separate base dict", gr.Dropdown, lambda: {"choices": ['None'] + list_checkpoint_tiles()}, refresh=refresh_checkpoints), "sd_checkpoint_autoload": OptionInfo(True, "Model autoload on start"), "sd_textencoder_cache": OptionInfo(True, "Cache text encoder results"), @@ -574,10 +581,10 @@ options_templates.update(options_section(('system-paths', "System Paths"), { "models_dir": OptionInfo('models', "Base path where all models are stored", folder=True), "ckpt_dir": OptionInfo(os.path.join(paths.models_path, 'Stable-diffusion'), "Folder with stable diffusion models", folder=True), "diffusers_dir": OptionInfo(os.path.join(paths.models_path, 'Diffusers'), "Folder with Huggingface models", folder=True), - "hfcache_dir": OptionInfo(os.path.join(os.path.expanduser('~'), '.cache', 'huggingface', 'hub'), "Folder for Huggingface cache", folder=True), + "hfcache_dir": OptionInfo(hfcache_dir, "Folder for Huggingface cache", folder=True), "vae_dir": OptionInfo(os.path.join(paths.models_path, 'VAE'), "Folder with VAE files", folder=True), "unet_dir": OptionInfo(os.path.join(paths.models_path, 'UNET'), "Folder with UNET files", folder=True), - "t5_dir": OptionInfo(os.path.join(paths.models_path, 'T5'), "Folder with T5 files", folder=True), + "te_dir": OptionInfo(os.path.join(paths.models_path, 'Text-encoder'), "Folder with Text encoder files", folder=True), "sd_lora": OptionInfo("", "Add LoRA to prompt", gr.Textbox, {"visible": False}), "lora_dir": OptionInfo(os.path.join(paths.models_path, 'Lora'), "Folder with LoRA network(s)", folder=True), "lyco_dir": OptionInfo(os.path.join(paths.models_path, 'LyCORIS'), "Folder with LyCORIS network(s)", gr.Text, {"visible": False}), diff --git a/modules/shared_items.py b/modules/shared_items.py index 9f110f413..503259a73 100644 --- a/modules/shared_items.py +++ b/modules/shared_items.py @@ -23,15 +23,15 @@ def refresh_unet_list(): modules.sd_unet.refresh_unet_list() -def sd_t5_items(): - import modules.model_t5 +def sd_te_items(): + import modules.model_te predefined = ['None', 'T5 FP4', 'T5 FP8', 'T5 INT8', 'T5 QINT8', 'T5 FP16'] - return predefined + list(modules.model_t5.t5_dict) + return predefined + list(modules.model_te.te_dict) -def refresh_t5_list(): - import modules.model_t5 - modules.model_t5.refresh_t5_list() +def refresh_te_list(): + import modules.model_te + modules.model_te.refresh_te_list() def list_crossattention(diffusers=False): diff --git a/modules/styles.py b/modules/styles.py index 4269b3f55..6fc22376d 100644 --- a/modules/styles.py +++ b/modules/styles.py @@ -175,10 +175,10 @@ class StyleDatabase: try: os.makedirs(opts.styles_dir, exist_ok=True) self.save_styles(opts.styles_dir, verbose=True) - shared.log.debug(f'Migrated styles: file={legacy_file} folder={opts.styles_dir}') + shared.log.debug(f'Migrated styles: file="{legacy_file}" folder="{opts.styles_dir}"') self.reload() except Exception as e: - shared.log.error(f'styles failed to migrate: file={legacy_file} error={e}') + shared.log.error(f'styles failed to migrate: file="{legacy_file}" error={e}') if not os.path.isdir(opts.styles_dir): opts.styles_dir = os.path.join(paths.models_path, "styles") self.path = opts.styles_dir @@ -216,7 +216,7 @@ class StyleDatabase: ) self.styles[style["name"]] = new_style except Exception as e: - shared.log.error(f'Failed to load style: file={fn} error={e}') + shared.log.error(f'Failed to load style: file="{fn}" error={e}') return new_style @@ -244,7 +244,7 @@ class StyleDatabase: list_folder(self.path) t1 = time.time() - shared.log.debug(f'Load styles: folder="{self.path}" items={len(self.styles.keys())} time={t1-t0:.2f}') + shared.log.info(f'Available Styles: folder="{self.path}" items={len(self.styles.keys())} time={t1-t0:.2f}') def find_style(self, name): found = [style for style in self.styles.values() if style.name == name] @@ -334,9 +334,9 @@ class StyleDatabase: with open(fn, 'w', encoding='utf-8') as f: json.dump(style, f, indent=2) if verbose: - shared.log.debug(f'Saved style: name={name} file={fn}') + shared.log.debug(f'Saved style: name={name} file="{fn}"') except Exception as e: - shared.log.error(f'Failed to save style: name={name} file={path} error={e}') + shared.log.error(f'Failed to save style: name={name} file="{path}" error={e}') count = len(list(self.styles)) if count > 0: shared.log.debug(f'Saved styles: folder="{path}" items={count}') diff --git a/modules/textual_inversion/textual_inversion.py b/modules/textual_inversion/textual_inversion.py index 7ad0166e7..335860a72 100644 --- a/modules/textual_inversion/textual_inversion.py +++ b/modules/textual_inversion/textual_inversion.py @@ -287,9 +287,7 @@ class EmbeddingDatabase: try: embedding.vector_sizes = [v.shape[-1] for v in embedding.vec] if shared.opts.diffusers_convert_embed and 768 in hiddensizes and 1280 in hiddensizes and 1280 not in embedding.vector_sizes and 768 in embedding.vector_sizes: - embedding.vec.append( - convert_embedding(embedding.vec[embedding.vector_sizes.index(768)], text_encoders[hiddensizes.index(768)], - text_encoders[hiddensizes.index(1280)])) + embedding.vec.append(convert_embedding(embedding.vec[embedding.vector_sizes.index(768)], text_encoders[hiddensizes.index(768)], text_encoders[hiddensizes.index(1280)])) embedding.vector_sizes.append(1280) if (not all(vs in hiddensizes for vs in embedding.vector_sizes) or # Skip SD2.1 in SD1.5/SDXL/SD3 vis versa len(embedding.vector_sizes) > len(hiddensizes) or # Skip SDXL/SD3 in SD1.5 @@ -311,7 +309,7 @@ class EmbeddingDatabase: insert_vectors(embedding, tokenizers, text_encoders, hiddensizes) self.register_embedding(embedding, shared.sd_model) except Exception as e: - shared.log.error(f'Embedding load: name={embedding.name} fn={embedding.filename} {e}') + shared.log.error(f'Embedding load: name="{embedding.name}" file="{embedding.filename}" {e}') return def load_from_file(self, path, filename): @@ -418,7 +416,7 @@ class EmbeddingDatabase: if self.previously_displayed_embeddings != displayed_embeddings: self.previously_displayed_embeddings = displayed_embeddings t1 = time.time() - shared.log.info(f"Load embeddings: loaded={len(self.word_embeddings)} skipped={len(self.skipped_embeddings)} time={t1-t0:.2f}") + shared.log.info(f"Load network: type=embeddings loaded={len(self.word_embeddings)} skipped={len(self.skipped_embeddings)} time={t1-t0:.2f}") def find_embedding_at_position(self, tokens, offset): diff --git a/modules/ui_common.py b/modules/ui_common.py index e168e6981..8faef17d8 100644 --- a/modules/ui_common.py +++ b/modules/ui_common.py @@ -203,10 +203,10 @@ def open_folder(result_gallery, gallery_index = 0): except Exception: folder = shared.opts.outdir_samples if not os.path.exists(folder): - shared.log.warning(f'Folder open: folder={folder} does not exist') + shared.log.warning(f'Folder open: folder="{folder}" does not exist') return elif not os.path.isdir(folder): - shared.log.warning(f"Folder open: folder={folder} not a folder") + shared.log.warning(f'Folder open: folder="{folder}" not a folder') return if not shared.cmd_opts.hide_ui_dir_config: diff --git a/modules/ui_extensions.py b/modules/ui_extensions.py index 4e3fd3588..8a97181ee 100644 --- a/modules/ui_extensions.py +++ b/modules/ui_extensions.py @@ -37,7 +37,7 @@ def list_extensions(): fn = os.path.join(paths.script_path, "html", "extensions.json") extensions_list = shared.readfile(fn, silent=True) or [] if type(extensions_list) != list: - shared.log.warning(f'Invalid extensions list: file={fn}') + shared.log.warning(f'Invalid extensions list: file="{fn}"') extensions_list = [] if len(extensions_list) == 0: shared.log.info('Extension list is empty: refresh required') diff --git a/modules/ui_extra_networks_checkpoints.py b/modules/ui_extra_networks_checkpoints.py index 7a5285d49..e59e51e1a 100644 --- a/modules/ui_extra_networks_checkpoints.py +++ b/modules/ui_extra_networks_checkpoints.py @@ -64,7 +64,7 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage): record["info"] = self.find_info(checkpoint.filename) record["description"] = self.find_description(checkpoint.filename, record["info"]) except Exception as e: - shared.log.debug(f"Networks error: type=model file={name} {e}") + shared.log.debug(f'Networks error: type=model file="{name}" {e}') return record def list_items(self): diff --git a/modules/ui_extra_networks_hypernets.py b/modules/ui_extra_networks_hypernets.py index b6fbbd38f..d187c4af3 100644 --- a/modules/ui_extra_networks_hypernets.py +++ b/modules/ui_extra_networks_hypernets.py @@ -27,7 +27,7 @@ class ExtraNetworksPageHypernetworks(ui_extra_networks.ExtraNetworksPage): "size": os.path.getsize(path), } except Exception as e: - shared.log.debug(f"Networks error: type=hypernetwork file={path} {e}") + shared.log.debug(f'Networks error: type=hypernetwork file="{path}" {e}') def allowed_directories_for_previews(self): return [shared.opts.hypernetwork_dir] diff --git a/modules/ui_extra_networks_styles.py b/modules/ui_extra_networks_styles.py index f03cb22be..29ac541a8 100644 --- a/modules/ui_extra_networks_styles.py +++ b/modules/ui_extra_networks_styles.py @@ -93,7 +93,7 @@ class ExtraNetworksPageStyles(ui_extra_networks.ExtraNetworksPage): "size": os.path.getsize(style.filename), } except Exception as e: - shared.log.debug(f"Networks error: type=style file={k} {e}") + shared.log.debug(f'Networks error: type=style file="{k}" {e}') return item def list_items(self): diff --git a/modules/ui_extra_networks_textual_inversion.py b/modules/ui_extra_networks_textual_inversion.py index 0e086e55d..8a741d248 100644 --- a/modules/ui_extra_networks_textual_inversion.py +++ b/modules/ui_extra_networks_textual_inversion.py @@ -37,7 +37,7 @@ class ExtraNetworksPageTextualInversion(ui_extra_networks.ExtraNetworksPage): record["info"] = self.find_info(embedding.filename) record["description"] = self.find_description(embedding.filename, record["info"]) except Exception as e: - shared.log.debug(f"Networks error: type=embedding file={embedding.filename} {e}") + shared.log.debug(f'Networks error: type=embedding file="{embedding.filename}" {e}') return record def list_items(self): diff --git a/modules/ui_extra_networks_vae.py b/modules/ui_extra_networks_vae.py index 8a161bbbb..de18b5d26 100644 --- a/modules/ui_extra_networks_vae.py +++ b/modules/ui_extra_networks_vae.py @@ -31,7 +31,7 @@ class ExtraNetworksPageVAEs(ui_extra_networks.ExtraNetworksPage): record["description"] = self.find_description(filename, record["info"]) yield record except Exception as e: - shared.log.debug(f"Networks error: type=vae file={filename} {e}") + shared.log.debug(f'Networks error: type=vae file="{filename}" {e}') def allowed_directories_for_previews(self): return [v for v in [shared.opts.vae_dir] if v is not None] diff --git a/requirements.txt b/requirements.txt index a35a6d3f5..383eb1568 100644 --- a/requirements.txt +++ b/requirements.txt @@ -25,6 +25,8 @@ voluptuous yapf fasteners orjson +ruff +pylint invisible-watermark pi-heif safetensors==0.4.5 diff --git a/scripts/resadapter.py b/scripts/resadapter.py index a70967320..cbd0bf671 100644 --- a/scripts/resadapter.py +++ b/scripts/resadapter.py @@ -51,7 +51,7 @@ class Script(scripts.Script): shared.sd_model.unet.load_state_dict(load_file(hf_hub_download(repo_id=repo, subfolder=models[model], filename="diffusion_pytorch_model.safetensors")), strict=False) sd_models.move_model(shared.sd_model, devices.device) # move pipeline to device sd_models.set_diffuser_options(shared.sd_model, vae=None, op='model') - shared.log.debug(f'ResAdapter: pipeline={shared.sd_model.__class__.__name__} model="{model}" weight={weight} fn={models[model]}') + shared.log.debug(f'ResAdapter: pipeline={shared.sd_model.__class__.__name__} model="{model}" weight={weight} file="{models[model]}"') processed = processing.process_images(p) shared.sd_model = old_pipe return processed diff --git a/scripts/xyz_grid_classes.py b/scripts/xyz_grid_classes.py index c822843b4..d52a1da01 100644 --- a/scripts/xyz_grid_classes.py +++ b/scripts/xyz_grid_classes.py @@ -1,5 +1,5 @@ from scripts.xyz_grid_shared import apply_field, apply_task_args, apply_setting, apply_prompt, apply_order, apply_sampler, apply_hr_sampler_name, confirm_samplers, apply_checkpoint, apply_refiner, apply_unet, apply_dict, apply_clip_skip, apply_vae, list_lora, apply_lora, apply_te, apply_styles, apply_upscaler, apply_context, apply_face_restore, apply_override, apply_processing, format_value_add_label, format_value, format_value_join_list, do_nothing, format_nothing, str_permutations # pylint: disable=no-name-in-module -from modules import shared, sd_samplers, ipadapter, sd_models, sd_vae, sd_unet +from modules import shared, shared_items, sd_samplers, ipadapter, sd_models, sd_vae, sd_unet class AxisOption: @@ -86,7 +86,7 @@ axis_options = [ AxisOption("VAE", str, apply_vae, cost=0.7, choices=lambda: ['None'] + list(sd_vae.vae_dict)), AxisOption("LoRA", str, apply_lora, cost=0.5, choices=list_lora), AxisOption("LoRA strength", float, apply_setting('extra_networks_default_multiplier')), - AxisOption("Text encoder", str, apply_te, cost=0.7, choices=lambda: ['None', 'T5 FP4', 'T5 FP8', 'T5 FP16']), + AxisOption("Text encoder", str, apply_te, cost=0.7, choices=shared_items.sd_te_items), AxisOption("Styles", str, apply_styles, choices=lambda: [s.name for s in shared.prompt_styles.styles.values()]), AxisOption("Seed", int, apply_field("seed")), AxisOption("Steps", int, apply_field("steps")), diff --git a/webui.py b/webui.py index 029e22de0..0a5e3664b 100644 --- a/webui.py +++ b/webui.py @@ -24,7 +24,7 @@ import modules.scripts import modules.sd_models import modules.sd_vae import modules.sd_unet -import modules.model_t5 +import modules.model_te import modules.progress import modules.ui import modules.txt2img @@ -91,7 +91,7 @@ def initialize(): modules.sd_unet.refresh_unet_list() timer.startup.record("unet") - modules.model_t5.refresh_t5_list() + modules.model_te.refresh_te_list() timer.startup.record("unet") extensions.list_extensions()