diff --git a/.github/workflows/run_tests.yaml b/.github/workflows/run_tests.yaml index e9370cc07..3dafaf8dc 100644 --- a/.github/workflows/run_tests.yaml +++ b/.github/workflows/run_tests.yaml @@ -41,6 +41,7 @@ jobs: --skip-prepare-environment --skip-torch-cuda-test --test-server + --do-not-download-clip --no-half --disable-opt-split-attention --use-cpu all diff --git a/CHANGELOG.md b/CHANGELOG.md index 30783d9db..792529ece 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * SD XL support * user metadata system for custom networks * extended Lora metadata editor: set activation text, default weight, view tags, training info + * Lora extension rework to include other types of networks (all that were previously handled by LyCORIS extension) * show github stars for extenstions * img2img batch mode can read extra stuff from png info * img2img batch works with subdirectories @@ -11,6 +12,9 @@ * restyle time taken/VRAM display * add textual inversion hashes to infotext * optimization: cache git extension repo information + * move generate button next to the generated picture for mobile clients + * hide cards for networks of incompatible Stable Diffusion version in Lora extra networks interface + * skip installing packages with pip if they all are already installed - startup speedup of about 2 seconds ### Minor: * checkbox to check/uncheck all extensions in the Installed tab @@ -25,6 +29,7 @@ * speedup extra networks listing * added `[none]` filename token. * removed thumbs extra networks view mode (use settings tab to change width/height/scale to get thumbs) + * add always_discard_next_to_last_sigma option to XYZ plot ### Extensions and API: * api endpoints: /sdapi/v1/server-kill, /sdapi/v1/server-restart, /sdapi/v1/server-stop @@ -53,9 +58,7 @@ * fix: check fill size none zero when resize (fixes #11425) * use submit and blur for quick settings textbox * save img2img batch with images.save_image() - * - - + * prevent running preload.py for disabled extensions ## 1.4.1 diff --git a/extensions-builtin/Lora/extra_networks_lora.py b/extensions-builtin/Lora/extra_networks_lora.py index 084c41d08..ba2945c6f 100644 --- a/extensions-builtin/Lora/extra_networks_lora.py +++ b/extensions-builtin/Lora/extra_networks_lora.py @@ -25,7 +25,7 @@ class ExtraNetworkLora(extra_networks.ExtraNetwork): te_multiplier = float(params.positional[1]) if len(params.positional) > 1 else 1.0 te_multiplier = float(params.named.get("te", te_multiplier)) - unet_multiplier = float(params.positional[2]) if len(params.positional) > 2 else 1.0 + unet_multiplier = float(params.positional[2]) if len(params.positional) > 2 else te_multiplier unet_multiplier = float(params.named.get("unet", unet_multiplier)) dyn_dim = int(params.positional[3]) if len(params.positional) > 3 else None diff --git a/extensions-builtin/Lora/networks.py b/extensions-builtin/Lora/networks.py index 7b4c03123..af8188e36 100644 --- a/extensions-builtin/Lora/networks.py +++ b/extensions-builtin/Lora/networks.py @@ -11,7 +11,7 @@ import network_full import torch from typing import Union -from modules import shared, devices, sd_models, errors, scripts, sd_hijack, paths +from modules import shared, devices, sd_models, errors, scripts, sd_hijack module_types = [ network_lora.ModuleTypeLora(), @@ -399,7 +399,7 @@ def list_available_networks(): os.makedirs(shared.cmd_opts.lora_dir, exist_ok=True) candidates = list(shared.walk_files(shared.cmd_opts.lora_dir, allowed_extensions=[".pt", ".ckpt", ".safetensors"])) - candidates += list(shared.walk_files(os.path.join(paths.models_path, "LyCORIS"), allowed_extensions=[".pt", ".ckpt", ".safetensors"])) + candidates += list(shared.walk_files(shared.cmd_opts.lyco_dir_backcompat, allowed_extensions=[".pt", ".ckpt", ".safetensors"])) for filename in candidates: if os.path.isdir(filename): continue diff --git a/extensions-builtin/Lora/preload.py b/extensions-builtin/Lora/preload.py index 863dc5c0b..50961be33 100644 --- a/extensions-builtin/Lora/preload.py +++ b/extensions-builtin/Lora/preload.py @@ -4,3 +4,4 @@ from modules import paths def preload(parser): parser.add_argument("--lora-dir", type=str, help="Path to directory with Lora networks.", default=os.path.join(paths.models_path, 'Lora')) + parser.add_argument("--lyco-dir-backcompat", type=str, help="Path to directory with LyCORIS networks (for backawards compatibility; can also use --lyco-dir).", default=os.path.join(paths.models_path, 'LyCORIS')) diff --git a/extensions-builtin/Lora/ui_edit_user_metadata.py b/extensions-builtin/Lora/ui_edit_user_metadata.py index c8730443c..2ca997f7c 100644 --- a/extensions-builtin/Lora/ui_edit_user_metadata.py +++ b/extensions-builtin/Lora/ui_edit_user_metadata.py @@ -1,3 +1,4 @@ +import datetime import html import random @@ -71,6 +72,7 @@ class LoraUserMetadataEditor(ui_extra_networks_user_metadata.UserMetadataEditor) keys = { 'ss_sd_model_name': "Model:", 'ss_clip_skip': "Clip skip:", + 'ss_network_module': "Kohya module:", } for key, label in keys.items(): @@ -78,6 +80,10 @@ class LoraUserMetadataEditor(ui_extra_networks_user_metadata.UserMetadataEditor) if value is not None and str(value) != "None": table.append((label, html.escape(value))) + ss_training_started_at = metadata.get('ss_training_started_at') + if ss_training_started_at: + table.append(("Date trained:", datetime.datetime.utcfromtimestamp(float(ss_training_started_at)).strftime('%Y-%m-%d %H:%M'))) + ss_bucket_info = metadata.get("ss_bucket_info") if ss_bucket_info and "buckets" in ss_bucket_info: resolutions = {} diff --git a/extensions-builtin/Lora/ui_extra_networks_lora.py b/extensions-builtin/Lora/ui_extra_networks_lora.py index 4b32098b8..3629e5c0c 100644 --- a/extensions-builtin/Lora/ui_extra_networks_lora.py +++ b/extensions-builtin/Lora/ui_extra_networks_lora.py @@ -3,7 +3,7 @@ import os import network import networks -from modules import shared, ui_extra_networks, paths +from modules import shared, ui_extra_networks from modules.ui_extra_networks import quote_js from ui_edit_user_metadata import LoraUserMetadataEditor @@ -72,7 +72,7 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage): yield item def allowed_directories_for_previews(self): - return [shared.cmd_opts.lora_dir, os.path.join(paths.models_path, "LyCORIS")] + return [shared.cmd_opts.lora_dir, shared.cmd_opts.lyco_dir_backcompat] def create_user_metadata_editor(self, ui, tabname): return LoraUserMetadataEditor(ui, tabname, self) diff --git a/launch.py b/launch.py index e9667c88e..370f2bf3c 100644 --- a/launch.py +++ b/launch.py @@ -18,6 +18,7 @@ run_pip = launch_utils.run_pip check_run_python = launch_utils.check_run_python git_clone = launch_utils.git_clone git_pull_recursive = launch_utils.git_pull_recursive +list_extensions = launch_utils.list_extensions run_extension_installer = launch_utils.run_extension_installer prepare_environment = launch_utils.prepare_environment configure_for_tests = launch_utils.configure_for_tests diff --git a/modules/api/models.py b/modules/api/models.py index 96cfe920e..bf97b1a37 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -1,5 +1,4 @@ import inspect -import types from pydantic import BaseModel, Field, create_model from typing import Any, Optional @@ -209,11 +208,9 @@ class PreprocessResponse(BaseModel): fields = {} for key, metadata in opts.data_labels.items(): value = opts.data.get(key) - if key == 'sd_model_checkpoint': - value = None - optType = opts.typemap.get(type(metadata.default), type(value)) + optType = opts.typemap.get(type(metadata.default), type(metadata.default)) - if isinstance(optType, types.NoneType): + if metadata.default is None: pass elif metadata is not None: fields.update({key: (Optional[optType], Field(default=metadata.default, description=metadata.label))}) diff --git a/modules/cmd_args.py b/modules/cmd_args.py index ae78f469e..e401f6413 100644 --- a/modules/cmd_args.py +++ b/modules/cmd_args.py @@ -15,6 +15,7 @@ parser.add_argument("--update-check", action='store_true', help="launch.py argum parser.add_argument("--test-server", action='store_true', help="launch.py argument: configure server for testing") parser.add_argument("--skip-prepare-environment", action='store_true', help="launch.py argument: skip all environment preparation") parser.add_argument("--skip-install", action='store_true', help="launch.py argument: skip installation of packages") +parser.add_argument("--do-not-download-clip", action='store_true', help="do not download CLIP model even if it's not included in the checkpoint") parser.add_argument("--data-dir", type=str, default=os.path.dirname(os.path.dirname(os.path.realpath(__file__))), help="base path where all user data is stored") parser.add_argument("--config", type=str, default=sd_default_config, help="path to config which constructs model",) parser.add_argument("--ckpt", type=str, default=sd_model_file, help="path to checkpoint of stable diffusion model; if specified, this checkpoint will be added to the list of checkpoints and loaded",) diff --git a/modules/script_loading.py b/modules/script_loading.py index 306a1f35f..0d55f1932 100644 --- a/modules/script_loading.py +++ b/modules/script_loading.py @@ -12,11 +12,12 @@ def load_module(path): return module -def preload_extensions(extensions_dir, parser): +def preload_extensions(extensions_dir, parser, extension_list=None): if not os.path.isdir(extensions_dir): return - for dirname in sorted(os.listdir(extensions_dir)): + extensions = extension_list if extension_list is not None else os.listdir(extensions_dir) + for dirname in sorted(extensions): preload_script = os.path.join(extensions_dir, dirname, "preload.py") if not os.path.isfile(preload_script): continue diff --git a/modules/sd_models.py b/modules/sd_models.py index 5813b5502..fb31a7937 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -494,7 +494,7 @@ def load_model(checkpoint_info=None, already_loaded_state_dict=None): sd_model = None try: - with sd_disable_initialization.DisableInitialization(disable_clip=clip_is_included_into_sd): + with sd_disable_initialization.DisableInitialization(disable_clip=clip_is_included_into_sd or shared.cmd_opts.do_not_download_clip): sd_model = instantiate_from_config(sd_config.model) except Exception: pass diff --git a/modules/shared.py b/modules/shared.py index a256d0907..1ce7b49e5 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -11,6 +11,7 @@ import gradio as gr import torch import tqdm +import launch import modules.interrogate import modules.memmon import modules.styles @@ -26,7 +27,7 @@ demo = None parser = cmd_args.parser -script_loading.preload_extensions(extensions_dir, parser) +script_loading.preload_extensions(extensions_dir, parser, extension_list=launch.list_extensions(launch.args.ui_settings_file)) script_loading.preload_extensions(extensions_builtin_dir, parser) if os.environ.get('IGNORE_CMD_ARGS_ERRORS', None) is None: @@ -409,7 +410,7 @@ options_templates.update(options_section(('training', "Training"), { })) options_templates.update(options_section(('sd', "Stable Diffusion"), { - "sd_model_checkpoint": OptionInfo("", "Stable Diffusion checkpoint", gr.Dropdown, lambda: {"choices": list_checkpoint_tiles()}, refresh=refresh_checkpoints), + "sd_model_checkpoint": OptionInfo(None, "Stable Diffusion checkpoint", gr.Dropdown, lambda: {"choices": list_checkpoint_tiles()}, refresh=refresh_checkpoints), "sd_checkpoint_cache": OptionInfo(0, "Checkpoints to cache in RAM", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}), "sd_vae_checkpoint_cache": OptionInfo(0, "VAE Checkpoints to cache in RAM", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}), "sd_vae": OptionInfo("Automatic", "SD VAE", gr.Dropdown, lambda: {"choices": shared_items.sd_vae_items()}, refresh=shared_items.refresh_vae_list).info("choose VAE model: Automatic = use one with same filename as checkpoint; None = use VAE from checkpoint"),