From 2abd89acc66419abf2eee9b03fd093f2737670de Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 28 Jan 2023 20:04:35 +0300 Subject: [PATCH 01/33] index on master: 91c8d0d Merge pull request #7231 from EllangoK/master --- extensions-builtin/Lora/lora.py | 21 +++++++++++++++++-- .../Lora/scripts/lora_script.py | 5 +++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/extensions-builtin/Lora/lora.py b/extensions-builtin/Lora/lora.py index cb8f1d36..568a7675 100644 --- a/extensions-builtin/Lora/lora.py +++ b/extensions-builtin/Lora/lora.py @@ -12,7 +12,7 @@ re_unet_up_blocks = re.compile(r"lora_unet_up_blocks_(\d+)_attentions_(\d+)_(.+) re_text_block = re.compile(r"lora_te_text_model_encoder_layers_(\d+)_(.+)") -def convert_diffusers_name_to_compvis(key): +def convert_diffusers_name_to_compvis(key, is_sd2): def match(match_list, regex): r = re.match(regex, key) if not r: @@ -34,6 +34,14 @@ def convert_diffusers_name_to_compvis(key): return f"diffusion_model_output_blocks_{m[0] * 3 + m[1]}_1_{m[2]}" if match(m, re_text_block): + if is_sd2: + if 'mlp_fc1' in m[1]: + return f"model_transformer_resblocks_{m[0]}_{m[1].replace('mlp_fc1', 'mlp_c_fc')}" + elif 'mlp_fc2' in m[1]: + return f"model_transformer_resblocks_{m[0]}_{m[1].replace('mlp_fc2', 'mlp_c_proj')}" + elif 'self_attn': + return f"model_transformer_resblocks_{m[0]}_{m[1].replace('self_attn', 'attn')}" + return f"transformer_text_model_encoder_layers_{m[0]}_{m[1]}" return key @@ -83,9 +91,10 @@ def load_lora(name, filename): sd = sd_models.read_state_dict(filename) keys_failed_to_match = [] + is_sd2 = 'model_transformer_resblocks' in shared.sd_model.lora_layer_mapping for key_diffusers, weight in sd.items(): - fullkey = convert_diffusers_name_to_compvis(key_diffusers) + fullkey = convert_diffusers_name_to_compvis(key_diffusers, is_sd2) key, lora_key = fullkey.split(".", 1) sd_module = shared.sd_model.lora_layer_mapping.get(key, None) @@ -104,9 +113,13 @@ def load_lora(name, filename): if type(sd_module) == torch.nn.Linear: module = torch.nn.Linear(weight.shape[1], weight.shape[0], bias=False) + elif type(sd_module) == torch.nn.modules.linear.NonDynamicallyQuantizableLinear: + module = torch.nn.modules.linear.NonDynamicallyQuantizableLinear(weight.shape[1], weight.shape[0], bias=False) elif type(sd_module) == torch.nn.Conv2d: module = torch.nn.Conv2d(weight.shape[1], weight.shape[0], (1, 1), bias=False) else: + print(f'Lora layer {key_diffusers} matched a layer with unsupported type: {type(sd_module).__name__}') + continue assert False, f'Lora layer {key_diffusers} matched a layer with unsupported type: {type(sd_module).__name__}' with torch.no_grad(): @@ -182,6 +195,10 @@ def lora_Conv2d_forward(self, input): return lora_forward(self, input, torch.nn.Conv2d_forward_before_lora(self, input)) +def lora_NonDynamicallyQuantizableLinear_forward(self, input): + return lora_forward(self, input, torch.nn.NonDynamicallyQuantizableLinear_forward_before_lora(self, input)) + + def list_available_loras(): available_loras.clear() diff --git a/extensions-builtin/Lora/scripts/lora_script.py b/extensions-builtin/Lora/scripts/lora_script.py index 2e860160..a385ae94 100644 --- a/extensions-builtin/Lora/scripts/lora_script.py +++ b/extensions-builtin/Lora/scripts/lora_script.py @@ -10,6 +10,7 @@ from modules import script_callbacks, ui_extra_networks, extra_networks, shared def unload(): torch.nn.Linear.forward = torch.nn.Linear_forward_before_lora torch.nn.Conv2d.forward = torch.nn.Conv2d_forward_before_lora + torch.nn.modules.linear.NonDynamicallyQuantizableLinear.forward = torch.nn.NonDynamicallyQuantizableLinear_forward_before_lora def before_ui(): @@ -23,8 +24,12 @@ if not hasattr(torch.nn, 'Linear_forward_before_lora'): if not hasattr(torch.nn, 'Conv2d_forward_before_lora'): torch.nn.Conv2d_forward_before_lora = torch.nn.Conv2d.forward +if not hasattr(torch.nn, 'NonDynamicallyQuantizableLinear_forward_before_lora'): + torch.nn.NonDynamicallyQuantizableLinear_forward_before_lora = torch.nn.modules.linear.NonDynamicallyQuantizableLinear.forward + torch.nn.Linear.forward = lora.lora_Linear_forward torch.nn.Conv2d.forward = lora.lora_Conv2d_forward +torch.nn.modules.linear.NonDynamicallyQuantizableLinear.forward = lora.lora_NonDynamicallyQuantizableLinear_forward script_callbacks.on_model_loaded(lora.assign_lora_names_to_compvis_modules) script_callbacks.on_script_unloaded(unload) From 04924241218bb51bee255bebc6c66ef1de449f4a Mon Sep 17 00:00:00 2001 From: bluelovers Date: Sun, 12 Mar 2023 10:18:33 +0800 Subject: [PATCH 02/33] feat: try sort as ignore-case https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/8368 --- extensions-builtin/Lora/lora.py | 2 +- modules/shared.py | 2 +- modules/textual_inversion/textual_inversion.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions-builtin/Lora/lora.py b/extensions-builtin/Lora/lora.py index cb8f1d36..7d3c0f90 100644 --- a/extensions-builtin/Lora/lora.py +++ b/extensions-builtin/Lora/lora.py @@ -192,7 +192,7 @@ def list_available_loras(): glob.glob(os.path.join(shared.cmd_opts.lora_dir, '**/*.safetensors'), recursive=True) + \ glob.glob(os.path.join(shared.cmd_opts.lora_dir, '**/*.ckpt'), recursive=True) - for filename in sorted(candidates): + for filename in sorted(candidates, key=str.lower): if os.path.isdir(filename): continue diff --git a/modules/shared.py b/modules/shared.py index 805f9cc1..1322b96d 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -702,7 +702,7 @@ mem_mon.start() def listfiles(dirname): - filenames = [os.path.join(dirname, x) for x in sorted(os.listdir(dirname)) if not x.startswith(".")] + filenames = [os.path.join(dirname, x) for x in sorted(os.listdir(dirname), key=str.lower) if not x.startswith(".")] return [file for file in filenames if os.path.isfile(file)] diff --git a/modules/textual_inversion/textual_inversion.py b/modules/textual_inversion/textual_inversion.py index c63c7d1d..3d21b9fe 100644 --- a/modules/textual_inversion/textual_inversion.py +++ b/modules/textual_inversion/textual_inversion.py @@ -129,7 +129,7 @@ class EmbeddingDatabase: if first_id not in self.ids_lookup: self.ids_lookup[first_id] = [] - self.ids_lookup[first_id] = sorted(self.ids_lookup[first_id] + [(ids, embedding)], key=lambda x: len(x[0]), reverse=True) + self.ids_lookup[first_id] = sorted(self.ids_lookup[first_id] + [(ids, embedding)], key=lambda x: len(x[0]), reverse=True, cmp=lambda x, y: x.lower() > y.lower()) return embedding @@ -196,7 +196,7 @@ class EmbeddingDatabase: return for root, dirs, fns in os.walk(embdir.path, followlinks=True): - for fn in fns: + for fn in sorted(fns, key=str.lower): try: fullfn = os.path.join(root, fn) From 9e23bacfbcb35f46f28539f71b2bc917276634b8 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Sun, 12 Mar 2023 17:07:03 -0600 Subject: [PATCH 03/33] Make extra networks button togglable --- modules/ui_extra_networks.py | 4 ++-- style.css | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 01df5e90..50e2093e 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -219,10 +219,10 @@ def create_ui(container, button, tabname): def toggle_visibility(is_visible): is_visible = not is_visible - return is_visible, gr.update(visible=is_visible) + return is_visible, gr.update(visible=is_visible), gr.update(variant=("primary" if is_visible else "tool")) state_visible = gr.State(value=False) - button.click(fn=toggle_visibility, inputs=[state_visible], outputs=[state_visible, container]) + button.click(fn=toggle_visibility, inputs=[state_visible], outputs=[state_visible, container, button]) def refresh(): res = [] diff --git a/style.css b/style.css index 2f26ad02..e82aebf3 100644 --- a/style.css +++ b/style.css @@ -968,3 +968,10 @@ footer { [id*='_prompt_container'] > div { margin: 0!important; } + +button[id$='_extra_networks'] { + margin: 0.6em 0em 0.55em 0; + max-width: 2.5em; + min-width: 2.5em !important; + height: 2.4em; +} From 1823526c103ee1d2232dfa65f908636daa22a342 Mon Sep 17 00:00:00 2001 From: Mikhail Gribanov Date: Tue, 14 Mar 2023 13:05:45 +0200 Subject: [PATCH 04/33] Update README.md --- README.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 24f8e799..b67e2296 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ A browser interface based on Gradio library for Stable Diffusion. - Prompt Matrix - Stable Diffusion Upscale - Attention, specify parts of text that the model should pay more attention to - - a man in a ((tuxedo)) - will pay more attention to tuxedo - - a man in a (tuxedo:1.21) - alternative syntax - - select text and press ctrl+up or ctrl+down to automatically adjust attention to selected text (code contributed by anonymous user) + - a man in a `((tuxedo))` - will pay more attention to tuxedo + - a man in a `(tuxedo:1.21)` - alternative syntax + - select text and press `Ctrl+Up` or `Ctrl+Down` to automatically adjust attention to selected text (code contributed by anonymous user) - Loopback, run img2img processing multiple times - X/Y/Z plot, a way to draw a 3 dimensional plot of images with different parameters - Textual Inversion @@ -28,7 +28,7 @@ A browser interface based on Gradio library for Stable Diffusion. - CodeFormer, face restoration tool as an alternative to GFPGAN - RealESRGAN, neural network upscaler - ESRGAN, neural network upscaler with a lot of third party models - - SwinIR and Swin2SR([see here](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/2092)), neural network upscalers + - SwinIR and Swin2SR ([see here](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/2092)), neural network upscalers - LDSR, Latent diffusion super resolution upscaling - Resizing aspect ratio options - Sampling method selection @@ -46,7 +46,7 @@ A browser interface based on Gradio library for Stable Diffusion. - drag and drop an image/text-parameters to promptbox - Read Generation Parameters Button, loads parameters in promptbox to UI - Settings page -- Running arbitrary python code from UI (must run with --allow-code to enable) +- Running arbitrary python code from UI (must run with `--allow-code` to enable) - Mouseover hints for most UI elements - Possible to change defaults/mix/max/step values for UI elements via text config - Tiling support, a checkbox to create images that can be tiled like textures @@ -69,7 +69,7 @@ A browser interface based on Gradio library for Stable Diffusion. - also supports weights for prompts: `a cat :1.2 AND a dog AND a penguin :2.2` - No token limit for prompts (original stable diffusion lets you use up to 75 tokens) - DeepDanbooru integration, creates danbooru style tags for anime prompts -- [xformers](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Xformers), major speed increase for select cards: (add --xformers to commandline args) +- [xformers](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Xformers), major speed increase for select cards: (add `--xformers` to commandline args) - via extension: [History tab](https://github.com/yfszzx/stable-diffusion-webui-images-browser): view, direct and delete images conveniently within the UI - Generate forever option - Training tab @@ -78,11 +78,11 @@ A browser interface based on Gradio library for Stable Diffusion. - Clip skip - Hypernetworks - Loras (same as Hypernetworks but more pretty) -- A sparate UI where you can choose, with preview, which embeddings, hypernetworks or Loras to add to your prompt. +- A sparate UI where you can choose, with preview, which embeddings, hypernetworks or Loras to add to your prompt - Can select to load a different VAE from settings screen - Estimated completion time in progress bar - API -- Support for dedicated [inpainting model](https://github.com/runwayml/stable-diffusion#inpainting-with-stable-diffusion) by RunwayML. +- Support for dedicated [inpainting model](https://github.com/runwayml/stable-diffusion#inpainting-with-stable-diffusion) by RunwayML - via extension: [Aesthetic Gradients](https://github.com/AUTOMATIC1111/stable-diffusion-webui-aesthetic-gradients), a way to generate images with a specific aesthetic by using clip images embeds (implementation of [https://github.com/vicgalle/stable-diffusion-aesthetic-gradients](https://github.com/vicgalle/stable-diffusion-aesthetic-gradients)) - [Stable Diffusion 2.0](https://github.com/Stability-AI/stablediffusion) support - see [wiki](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features#stable-diffusion-20) for instructions - [Alt-Diffusion](https://arxiv.org/abs/2211.06679) support - see [wiki](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features#alt-diffusion) for instructions @@ -91,7 +91,6 @@ A browser interface based on Gradio library for Stable Diffusion. - Eased resolution restriction: generated image's domension must be a multiple of 8 rather than 64 - Now with a license! - Reorder elements in the UI from settings screen -- ## Installation and Running Make sure the required [dependencies](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Dependencies) are met and follow the instructions available for both [NVidia](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Install-and-Run-on-NVidia-GPUs) (recommended) and [AMD](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Install-and-Run-on-AMD-GPUs) GPUs. @@ -101,7 +100,7 @@ Alternatively, use online services (like Google Colab): - [List of Online Services](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Online-Services) ### Automatic Installation on Windows -1. Install [Python 3.10.6](https://www.python.org/downloads/windows/), checking "Add Python to PATH" +1. Install [Python 3.10.6](https://www.python.org/downloads/windows/), checking "Add Python to PATH". 2. Install [git](https://git-scm.com/download/win). 3. Download the stable-diffusion-webui repository, for example by running `git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git`. 4. Run `webui-user.bat` from Windows Explorer as normal, non-administrator, user. @@ -159,4 +158,4 @@ Licenses for borrowed code can be found in `Settings -> Licenses` screen, and al - Security advice - RyotaK - UniPC sampler - Wenliang Zhao - https://github.com/wl-zhao/UniPC - Initial Gradio script - posted on 4chan by an Anonymous user. Thank you Anonymous user. -- (You) +- (You) \ No newline at end of file From fd672a79afc912be46a2a01133269b8c6842a90d Mon Sep 17 00:00:00 2001 From: bluelovers Date: Wed, 15 Mar 2023 13:17:09 +0800 Subject: [PATCH 05/33] fix: remove cmp by ChatGPT --- modules/textual_inversion/textual_inversion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/textual_inversion/textual_inversion.py b/modules/textual_inversion/textual_inversion.py index 3d21b9fe..8b5bb6ce 100644 --- a/modules/textual_inversion/textual_inversion.py +++ b/modules/textual_inversion/textual_inversion.py @@ -129,7 +129,7 @@ class EmbeddingDatabase: if first_id not in self.ids_lookup: self.ids_lookup[first_id] = [] - self.ids_lookup[first_id] = sorted(self.ids_lookup[first_id] + [(ids, embedding)], key=lambda x: len(x[0]), reverse=True, cmp=lambda x, y: x.lower() > y.lower()) + self.ids_lookup[first_id] = sorted(self.ids_lookup[first_id] + [(ids, embedding)], key=lambda x: (len(x[0]), x[0].casefold()), reverse=True) return embedding From 575c17a8f9bc6471a7a0891b665ec42073a18049 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Wed, 15 Mar 2023 16:56:27 -0600 Subject: [PATCH 06/33] Update tooltip per Kilvoctu's suggestion --- javascript/hints.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/javascript/hints.js b/javascript/hints.js index 7f4101b2..83128497 100644 --- a/javascript/hints.js +++ b/javascript/hints.js @@ -21,8 +21,7 @@ titles = { "\u{1f5d1}": "Clear prompt", "\u{1f4cb}": "Apply selected styles to current prompt", "\u{1f4d2}": "Paste available values into the field", - "\u{1f3b4}": "Show extra networks", - + "\u{1f3b4}": "Show/hide extra networks", "Inpaint a part of image": "Draw a mask over an image, and the script will regenerate the masked area with content according to prompt", "SD upscale": "Upscale image normally, split result into tiles, improve each tile using img2img, merge whole image back", From dfa258de5f87ea7f5ff49e29ddf6e7b34ff8ebff Mon Sep 17 00:00:00 2001 From: Vespinian Date: Wed, 15 Mar 2023 22:17:32 -0400 Subject: [PATCH 07/33] Made copies of global scriptrunners, now we clear the copied scriptrunner of alwayson_scripts and only add back the ones that that were requested --- modules/api/api.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index 35e17afc..afbc202a 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -3,6 +3,7 @@ import io import time import datetime import uvicorn +import copy from threading import Lock from io import BytesIO from gradio.processing_utils import decode_base64_to_file @@ -202,6 +203,7 @@ class Api: script_args[0] = 0 # Now check for always on scripts + alwayson_script_to_run = [] # list to replace the one from the global ScriptRunner we copied if request.alwayson_scripts and (len(request.alwayson_scripts) > 0): for alwayson_script_name in request.alwayson_scripts.keys(): alwayson_script = self.get_script(alwayson_script_name, script_runner) @@ -210,13 +212,21 @@ class Api: # Selectable script in always on script param check if alwayson_script.alwayson == False: raise HTTPException(status_code=422, detail=f"Cannot have a selectable script in the always on scripts params") - # always on script with no arg should always run so you don't really need to add them to the requests + # all good, so add to run list and set its args if any + alwayson_script_to_run.append(alwayson_script) if "args" in request.alwayson_scripts[alwayson_script_name]: script_args[alwayson_script.args_from:alwayson_script.args_to] = request.alwayson_scripts[alwayson_script_name]["args"] + + # Remove always on scripts that were not included in the request by resetting the script list in out ScriptRunner + script_runner.alwayson_scripts.clear() + script_runner.alwayson_scripts = alwayson_script_to_run + script_runner.scripts.clear() + script_runner.scripts = alwayson_script_to_run + script_runner.selectable_scripts + return script_args def text2imgapi(self, txt2imgreq: StableDiffusionTxt2ImgProcessingAPI): - script_runner = scripts.scripts_txt2img + script_runner = copy.copy(scripts.scripts_txt2img) # copy so we don't overwrite our globals if not script_runner.scripts: script_runner.initialize_scripts(False) ui.create_ui() @@ -268,7 +278,7 @@ class Api: if mask: mask = decode_base64_to_image(mask) - script_runner = scripts.scripts_img2img + script_runner = copy.copy(scripts.scripts_img2img) # copy so we don't overwrite our globals if not script_runner.scripts: script_runner.initialize_scripts(True) ui.create_ui() From f04bd037a51de3c65072581d9a7dfed1d0d2887e Mon Sep 17 00:00:00 2001 From: Vespinian Date: Wed, 15 Mar 2023 22:27:54 -0400 Subject: [PATCH 08/33] Comment fix --- modules/api/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/api/api.py b/modules/api/api.py index afbc202a..8c06cf20 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -217,7 +217,7 @@ class Api: if "args" in request.alwayson_scripts[alwayson_script_name]: script_args[alwayson_script.args_from:alwayson_script.args_to] = request.alwayson_scripts[alwayson_script_name]["args"] - # Remove always on scripts that were not included in the request by resetting the script list in out ScriptRunner + # Remove always on scripts that were not included in the request by resetting the script list in our ScriptRunner script_runner.alwayson_scripts.clear() script_runner.alwayson_scripts = alwayson_script_to_run script_runner.scripts.clear() From 5c7ab90a4ea0ae907bb915c9ddbe40c529b7bcc9 Mon Sep 17 00:00:00 2001 From: LipeCarmel <44252177+LipeCarmel@users.noreply.github.com> Date: Sat, 25 Mar 2023 14:48:22 -0300 Subject: [PATCH 09/33] loopback.py Colab compatibility and bug fix This code (suggested by @abvgdeabvgde2 ) literally does the same thing and it does not break with Python 3.9, making it helpful for Google Colab users (me included). fixes #8927 Also a partial fix for #8902 but it does not resolve the unresponsive UI problem faced by @Archon332 --- scripts/loopback.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/scripts/loopback.py b/scripts/loopback.py index 9c388aa8..d3065fe6 100644 --- a/scripts/loopback.py +++ b/scripts/loopback.py @@ -54,15 +54,12 @@ class Script(scripts.Script): return strength progress = loop / (loops - 1) - match denoising_curve: - case "Aggressive": - strength = math.sin((progress) * math.pi * 0.5) - - case "Lazy": - strength = 1 - math.cos((progress) * math.pi * 0.5) - - case _: - strength = progress + if denoising_curve == "Aggressive": + strength = math.sin((progress) * math.pi * 0.5) + elif denoising_curve == "Lazy": + strength = 1 - math.cos((progress) * math.pi * 0.5) + else: + strength = progress change = (final_denoising_strength - initial_denoising_strength) * strength return initial_denoising_strength + change From db602b100e271e902e120fa8aa5c859e3ef3962f Mon Sep 17 00:00:00 2001 From: Vespinian Date: Sat, 25 Mar 2023 14:11:38 -0400 Subject: [PATCH 10/33] Revert "Comment fix" This reverts commit f04bd037a51de3c65072581d9a7dfed1d0d2887e. --- modules/api/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/api/api.py b/modules/api/api.py index 8c06cf20..afbc202a 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -217,7 +217,7 @@ class Api: if "args" in request.alwayson_scripts[alwayson_script_name]: script_args[alwayson_script.args_from:alwayson_script.args_to] = request.alwayson_scripts[alwayson_script_name]["args"] - # Remove always on scripts that were not included in the request by resetting the script list in our ScriptRunner + # Remove always on scripts that were not included in the request by resetting the script list in out ScriptRunner script_runner.alwayson_scripts.clear() script_runner.alwayson_scripts = alwayson_script_to_run script_runner.scripts.clear() From f3715795710b3b39029325b88a2c101279c2dd2c Mon Sep 17 00:00:00 2001 From: Vespinian Date: Sat, 25 Mar 2023 14:11:46 -0400 Subject: [PATCH 11/33] Revert "Made copies of global scriptrunners, now we clear the copied scriptrunner of alwayson_scripts and only add back the ones that that were requested" This reverts commit dfa258de5f87ea7f5ff49e29ddf6e7b34ff8ebff. --- modules/api/api.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index afbc202a..35e17afc 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -3,7 +3,6 @@ import io import time import datetime import uvicorn -import copy from threading import Lock from io import BytesIO from gradio.processing_utils import decode_base64_to_file @@ -203,7 +202,6 @@ class Api: script_args[0] = 0 # Now check for always on scripts - alwayson_script_to_run = [] # list to replace the one from the global ScriptRunner we copied if request.alwayson_scripts and (len(request.alwayson_scripts) > 0): for alwayson_script_name in request.alwayson_scripts.keys(): alwayson_script = self.get_script(alwayson_script_name, script_runner) @@ -212,21 +210,13 @@ class Api: # Selectable script in always on script param check if alwayson_script.alwayson == False: raise HTTPException(status_code=422, detail=f"Cannot have a selectable script in the always on scripts params") - # all good, so add to run list and set its args if any - alwayson_script_to_run.append(alwayson_script) + # always on script with no arg should always run so you don't really need to add them to the requests if "args" in request.alwayson_scripts[alwayson_script_name]: script_args[alwayson_script.args_from:alwayson_script.args_to] = request.alwayson_scripts[alwayson_script_name]["args"] - - # Remove always on scripts that were not included in the request by resetting the script list in out ScriptRunner - script_runner.alwayson_scripts.clear() - script_runner.alwayson_scripts = alwayson_script_to_run - script_runner.scripts.clear() - script_runner.scripts = alwayson_script_to_run + script_runner.selectable_scripts - return script_args def text2imgapi(self, txt2imgreq: StableDiffusionTxt2ImgProcessingAPI): - script_runner = copy.copy(scripts.scripts_txt2img) # copy so we don't overwrite our globals + script_runner = scripts.scripts_txt2img if not script_runner.scripts: script_runner.initialize_scripts(False) ui.create_ui() @@ -278,7 +268,7 @@ class Api: if mask: mask = decode_base64_to_image(mask) - script_runner = copy.copy(scripts.scripts_img2img) # copy so we don't overwrite our globals + script_runner = scripts.scripts_img2img if not script_runner.scripts: script_runner.initialize_scripts(True) ui.create_ui() From 23f6dfce4cd3969dab7e707142e7da7c389704fa Mon Sep 17 00:00:00 2001 From: Vespinian Date: Sat, 25 Mar 2023 14:16:35 -0400 Subject: [PATCH 12/33] Reworked this PR, now we have 2 default arg list (one for each tab) that will be initialized on the first api request and then reused afterwards. The init_script_args copies the corresponding default list and applies the modifications asked by the api request. --- modules/api/api.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index 35e17afc..596b20b2 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -3,6 +3,7 @@ import io import time import datetime import uvicorn +import gradio as gr from threading import Lock from io import BytesIO from gradio.processing_utils import decode_base64_to_file @@ -152,6 +153,9 @@ class Api: self.add_api_route("/sdapi/v1/memory", self.get_memory, methods=["GET"], response_model=MemoryResponse) self.add_api_route("/sdapi/v1/scripts", self.get_scripts_list, methods=["GET"], response_model=ScriptsList) + self.default_script_arg_txt2img = [] + self.default_script_arg_img2img = [] + def add_api_route(self, path: str, endpoint, **kwargs): if shared.cmd_opts.api_auth: return self.app.add_api_route(path, endpoint, dependencies=[Depends(self.auth)], **kwargs) @@ -185,7 +189,7 @@ class Api: script_idx = script_name_to_index(script_name, script_runner.scripts) return script_runner.scripts[script_idx] - def init_script_args(self, request, selectable_scripts, selectable_idx, script_runner): + def init_default_script_args(self, script_runner): #find max idx from the scripts in runner and generate a none array to init script_args last_arg_index = 1 for script in script_runner.scripts: @@ -193,13 +197,24 @@ class Api: last_arg_index = script.args_to # None everywhere except position 0 to initialize script args script_args = [None]*last_arg_index + script_args[0] = 0 + + # get default values + with gr.Blocks(): # will throw errors calling ui function without this + for script in script_runner.scripts: + if script.ui(script.is_img2img): + ui_default_values = [] + for elem in script.ui(script.is_img2img): + ui_default_values.append(elem.value) + script_args[script.args_from:script.args_to] = ui_default_values + return script_args + + def init_script_args(self, request, default_script_args, selectable_scripts, selectable_idx, script_runner): + script_args = default_script_args.copy() # position 0 in script_arg is the idx+1 of the selectable script that is going to be run when using scripts.scripts_*2img.run() if selectable_scripts: script_args[selectable_scripts.args_from:selectable_scripts.args_to] = request.script_args script_args[0] = selectable_idx + 1 - else: - # when [0] = 0 no selectable script to run - script_args[0] = 0 # Now check for always on scripts if request.alwayson_scripts and (len(request.alwayson_scripts) > 0): @@ -220,6 +235,8 @@ class Api: if not script_runner.scripts: script_runner.initialize_scripts(False) ui.create_ui() + if not self.default_script_arg_txt2img: + self.default_script_arg_txt2img = self.init_default_script_args(script_runner) selectable_scripts, selectable_script_idx = self.get_selectable_script(txt2imgreq.script_name, script_runner) populate = txt2imgreq.copy(update={ # Override __init__ params @@ -235,7 +252,7 @@ class Api: args.pop('script_args', None) # will refeed them to the pipeline directly after initializing them args.pop('alwayson_scripts', None) - script_args = self.init_script_args(txt2imgreq, selectable_scripts, selectable_script_idx, script_runner) + script_args = self.init_script_args(txt2imgreq, self.default_script_arg_txt2img, selectable_scripts, selectable_script_idx, script_runner) send_images = args.pop('send_images', True) args.pop('save_images', None) @@ -272,6 +289,8 @@ class Api: if not script_runner.scripts: script_runner.initialize_scripts(True) ui.create_ui() + if not self.default_script_arg_img2img: + self.default_script_arg_img2img = self.init_default_script_args(script_runner) selectable_scripts, selectable_script_idx = self.get_selectable_script(img2imgreq.script_name, script_runner) populate = img2imgreq.copy(update={ # Override __init__ params @@ -289,7 +308,7 @@ class Api: args.pop('script_args', None) # will refeed them to the pipeline directly after initializing them args.pop('alwayson_scripts', None) - script_args = self.init_script_args(img2imgreq, selectable_scripts, selectable_script_idx, script_runner) + script_args = self.init_script_args(img2imgreq, self.default_script_arg_img2img, selectable_scripts, selectable_script_idx, script_runner) send_images = args.pop('send_images', True) args.pop('save_images', None) From 9377092a892687a8ae43ba5f7df44f0929ab2997 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Sat, 25 Mar 2023 14:28:20 -0400 Subject: [PATCH 13/33] Fix notifications not triggering --- javascript/notification.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/notification.js b/javascript/notification.js index 5ae6df24..8ddd4c5d 100644 --- a/javascript/notification.js +++ b/javascript/notification.js @@ -15,7 +15,7 @@ onUiUpdate(function(){ } } - const galleryPreviews = gradioApp().querySelectorAll('div[id^="tab_"][style*="display: block"] div[id$="_results"] img.h-full.w-full.overflow-hidden'); + const galleryPreviews = gradioApp().querySelectorAll('div[id^="tab_"][style*="display: block"] div[id$="_results"] .thumbnail-item > img'); if (galleryPreviews == null) return; From 945f6e5e99413ef74c386cdb7f029d1cac93a456 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Sat, 25 Mar 2023 14:44:41 -0400 Subject: [PATCH 14/33] Fix img2img aspect ratio overlay in Gradio 3.23.0 --- javascript/aspectRatioOverlay.js | 47 +++++++++++++++++--------------- style.css | 11 ++++++++ 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/javascript/aspectRatioOverlay.js b/javascript/aspectRatioOverlay.js index 0f164b82..a8278cca 100644 --- a/javascript/aspectRatioOverlay.js +++ b/javascript/aspectRatioOverlay.js @@ -12,7 +12,7 @@ function dimensionChange(e, is_width, is_height){ currentHeight = e.target.value*1.0 } - var inImg2img = Boolean(gradioApp().querySelector("button.rounded-t-lg.border-gray-200")) + var inImg2img = gradioApp().querySelector("#tab_img2img").style.display == "block"; if(!inImg2img){ return; @@ -22,7 +22,7 @@ function dimensionChange(e, is_width, is_height){ var tabIndex = get_tab_index('mode_img2img') if(tabIndex == 0){ // img2img - targetElement = gradioApp().querySelector('div[data-testid=image] img'); + targetElement = gradioApp().querySelector('#img2img_image div[data-testid=image] img'); } else if(tabIndex == 1){ //Sketch targetElement = gradioApp().querySelector('#img2img_sketch div[data-testid=image] img'); } else if(tabIndex == 2){ // Inpaint @@ -30,7 +30,7 @@ function dimensionChange(e, is_width, is_height){ } else if(tabIndex == 3){ // Inpaint sketch targetElement = gradioApp().querySelector('#inpaint_sketch div[data-testid=image] img'); } - + if(targetElement){ @@ -38,7 +38,7 @@ function dimensionChange(e, is_width, is_height){ if(!arPreviewRect){ arPreviewRect = document.createElement('div') arPreviewRect.id = "imageARPreview"; - gradioApp().getRootNode().appendChild(arPreviewRect) + gradioApp().appendChild(arPreviewRect) } @@ -91,23 +91,26 @@ onUiUpdate(function(){ if(arPreviewRect){ arPreviewRect.style.display = 'none'; } - var inImg2img = Boolean(gradioApp().querySelector("button.rounded-t-lg.border-gray-200")) - if(inImg2img){ - let inputs = gradioApp().querySelectorAll('input'); - inputs.forEach(function(e){ - var is_width = e.parentElement.id == "img2img_width" - var is_height = e.parentElement.id == "img2img_height" + var tabImg2img = gradioApp().querySelector("#tab_img2img"); + if (tabImg2img) { + var inImg2img = tabImg2img.style.display == "block"; + if(inImg2img){ + let inputs = gradioApp().querySelectorAll('input'); + inputs.forEach(function(e){ + var is_width = e.parentElement.id == "img2img_width" + var is_height = e.parentElement.id == "img2img_height" - if((is_width || is_height) && !e.classList.contains('scrollwatch')){ - e.addEventListener('input', function(e){dimensionChange(e, is_width, is_height)} ) - e.classList.add('scrollwatch') - } - if(is_width){ - currentWidth = e.value*1.0 - } - if(is_height){ - currentHeight = e.value*1.0 - } - }) - } + if((is_width || is_height) && !e.classList.contains('scrollwatch')){ + e.addEventListener('input', function(e){dimensionChange(e, is_width, is_height)} ) + e.classList.add('scrollwatch') + } + if(is_width){ + currentWidth = e.value*1.0 + } + if(is_height){ + currentHeight = e.value*1.0 + } + }) + } + } }); diff --git a/style.css b/style.css index 0dcc3e25..b252e64d 100644 --- a/style.css +++ b/style.css @@ -507,6 +507,17 @@ div.dimensions-tools{ background-color: rgba(0, 0, 0, 0.8); } +#imageARPreview { + position: absolute; + top: 0px; + left: 0px; + border: 2px solid red; + background: rgba(255, 0, 0, 0.3); + z-index: 900; + pointer-events: none; + display: none; +} + /* context menu (ie for the generate button) */ #context-menu{ From 5eb7ff776878b38d8f15bc9c4a563f259908bee0 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Sat, 25 Mar 2023 14:52:47 -0400 Subject: [PATCH 15/33] Fix Send to img2img buttons --- javascript/imageviewer.js | 24 +++--------------------- javascript/ui.js | 36 +++++++++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/javascript/imageviewer.js b/javascript/imageviewer.js index 7547e771..d6483562 100644 --- a/javascript/imageviewer.js +++ b/javascript/imageviewer.js @@ -32,13 +32,7 @@ function negmod(n, m) { function updateOnBackgroundChange() { const modalImage = gradioApp().getElementById("modalImage") if (modalImage && modalImage.offsetParent) { - let allcurrentButtons = gradioApp().querySelectorAll(".gallery-item.transition-all.\\!ring-2") - let currentButton = null - allcurrentButtons.forEach(function(elem) { - if (elem.parentElement.offsetParent) { - currentButton = elem; - } - }) + let currentButton = selected_gallery_button(); if (currentButton?.children?.length > 0 && modalImage.src != currentButton.children[0].src) { modalImage.src = currentButton.children[0].src; @@ -50,22 +44,10 @@ function updateOnBackgroundChange() { } function modalImageSwitch(offset) { - var allgalleryButtons = gradioApp().querySelectorAll(".gradio-gallery .thumbnail-item") - var galleryButtons = [] - allgalleryButtons.forEach(function(elem) { - if (elem.parentElement.offsetParent) { - galleryButtons.push(elem); - } - }) + var galleryButtons = all_gallery_buttons(); if (galleryButtons.length > 1) { - var allcurrentButtons = gradioApp().querySelectorAll(".gradio-gallery .thumbnail-item.selected") - var currentButton = null - allcurrentButtons.forEach(function(elem) { - if (elem.parentElement.offsetParent) { - currentButton = elem; - } - }) + var currentButton = selected_gallery_button(); var result = -1 galleryButtons.forEach(function(v, i) { diff --git a/javascript/ui.js b/javascript/ui.js index fcaf5608..4a440193 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -7,9 +7,31 @@ function set_theme(theme){ } } +function all_gallery_buttons() { + var allGalleryButtons = gradioApp().querySelectorAll('[style="display: block;"].tabitem div[id$=_gallery].gradio-gallery .thumbnails > .thumbnail-item.thumbnail-small'); + var visibleGalleryButtons = []; + allGalleryButtons.forEach(function(elem) { + if (elem.parentElement.offsetParent) { + visibleGalleryButtons.push(elem); + } + }) + return visibleGalleryButtons; +} + +function selected_gallery_button() { + var allCurrentButtons = gradioApp().querySelectorAll('[style="display: block;"].tabitem div[id$=_gallery].gradio-gallery .thumbnail-item.thumbnail-small.selected'); + var visibleCurrentButton = null; + allCurrentButtons.forEach(function(elem) { + if (elem.parentElement.offsetParent) { + visibleCurrentButton = elem; + } + }) + return visibleCurrentButton; +} + function selected_gallery_index(){ - var buttons = gradioApp().querySelectorAll('[style="display: block;"].tabitem div[id$=_gallery] .gallery-item') - var button = gradioApp().querySelector('[style="display: block;"].tabitem div[id$=_gallery] .gallery-item.\\!ring-2') + var buttons = all_gallery_buttons(); + var button = selected_gallery_button(); var result = -1 buttons.forEach(function(v, i){ if(v==button) { result = i } }) @@ -18,14 +40,18 @@ function selected_gallery_index(){ } function extract_image_from_gallery(gallery){ - if(gallery.length == 1){ - return [gallery[0]] + if (gallery.length == 0){ + return [null]; + } + if (gallery.length == 1){ + return [gallery[0]]; } index = selected_gallery_index() if (index < 0 || index >= gallery.length){ - return [null] + // Use the first image in the gallery as the default + index = 0; } return [gallery[index]]; From d3b188c82d10e8af45357f258c1f65a04cfa4a18 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Sat, 25 Mar 2023 15:52:06 -0400 Subject: [PATCH 16/33] Fix padding on accordion/dropdown list elements --- modules/scripts.py | 12 ++++++++++++ style.css | 15 ++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/modules/scripts.py b/modules/scripts.py index d661be4f..4d0bbd66 100644 --- a/modules/scripts.py +++ b/modules/scripts.py @@ -553,3 +553,15 @@ def IOComponent_init(self, *args, **kwargs): original_IOComponent_init = gr.components.IOComponent.__init__ gr.components.IOComponent.__init__ = IOComponent_init + + +def BlockContext_init(self, *args, **kwargs): + res = original_BlockContext_init(self, *args, **kwargs) + + add_classes_to_gradio_component(self) + + return res + + +original_BlockContext_init = gr.blocks.BlockContext.__init__ +gr.blocks.BlockContext.__init__ = BlockContext_init diff --git a/style.css b/style.css index 0dcc3e25..c7087210 100644 --- a/style.css +++ b/style.css @@ -7,7 +7,7 @@ --block-background-fill: transparent; } -.block.padded{ +.block.padded:not(.gradio-accordion) { padding: 0 !important; } @@ -65,6 +65,19 @@ div.compact{ margin-bottom: 0; } +.gradio-dropdown ul.options { + max-height: 35em; + z-index: 3000; +} + +.gradio-dropdown ul.options li.item { + padding: 0.05em 0; +} + +.gradio-dropdown ul.options li.item.selected { + background-color: var(--secondary-500); +} + .gradio-dropdown div.wrap.wrap.wrap.wrap{ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); } From abc4d3a6934f2024b7e8c217ae8d5c90bf7c20ed Mon Sep 17 00:00:00 2001 From: SirFrags <85357620+SirFrags@users.noreply.github.com> Date: Sat, 25 Mar 2023 15:59:50 -0400 Subject: [PATCH 17/33] preview replace save params --- modules/ui_extra_networks.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index daea03d6..aaa93969 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -2,8 +2,10 @@ import glob import os.path import urllib.parse from pathlib import Path +from PIL import PngImagePlugin from modules import shared +from modules.images import read_info_from_image import gradio as gr import json import html @@ -290,6 +292,7 @@ def setup_ui(ui, gallery): img_info = images[index if index >= 0 else 0] image = image_from_url_text(img_info) + geninfo, items = read_info_from_image(image) is_allowed = False for extra_page in ui.stored_extra_pages: @@ -299,7 +302,12 @@ def setup_ui(ui, gallery): assert is_allowed, f'writing to {filename} is not allowed' - image.save(filename) + if geninfo: + pnginfo_data = PngImagePlugin.PngInfo() + pnginfo_data.add_text('parameters', geninfo) + image.save(filename, pnginfo=pnginfo_data) + else: + image.save(filename) return [page.create_html(ui.tabname) for page in ui.stored_extra_pages] From 80b26d2a69617b75d2d01c1e6b7d11445815ed4d Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 25 Mar 2023 23:06:33 +0300 Subject: [PATCH 18/33] apply Lora by altering layer's weights instead of adding more calculations in forward() --- extensions-builtin/Lora/lora.py | 72 ++++++++++++++----- .../Lora/scripts/lora_script.py | 12 +++- 2 files changed, 66 insertions(+), 18 deletions(-) diff --git a/extensions-builtin/Lora/lora.py b/extensions-builtin/Lora/lora.py index 7c371deb..a737fec3 100644 --- a/extensions-builtin/Lora/lora.py +++ b/extensions-builtin/Lora/lora.py @@ -131,7 +131,7 @@ def load_lora(name, filename): with torch.no_grad(): module.weight.copy_(weight) - module.to(device=devices.device, dtype=devices.dtype) + module.to(device=devices.cpu, dtype=devices.dtype) if lora_key == "lora_up.weight": lora_module.up = module @@ -177,29 +177,69 @@ def load_loras(names, multipliers=None): loaded_loras.append(lora) -def lora_forward(module, input, res): - input = devices.cond_cast_unet(input) - if len(loaded_loras) == 0: - return res +def lora_apply_weights(self: torch.nn.Conv2d | torch.nn.Linear): + """ + Applies the currently selected set of Loras to the weight of torch layer self. + If weights already have this particular set of loras applied, does nothing. + If not, restores orginal weights from backup and alters weights according to loras. + """ - lora_layer_name = getattr(module, 'lora_layer_name', None) - for lora in loaded_loras: - module = lora.modules.get(lora_layer_name, None) - if module is not None: - if shared.opts.lora_apply_to_outputs and res.shape == input.shape: - res = res + module.up(module.down(res)) * lora.multiplier * (module.alpha / module.up.weight.shape[1] if module.alpha else 1.0) - else: - res = res + module.up(module.down(input)) * lora.multiplier * (module.alpha / module.up.weight.shape[1] if module.alpha else 1.0) + current_names = getattr(self, "lora_current_names", ()) + wanted_names = tuple((x.name, x.multiplier) for x in loaded_loras) - return res + weights_backup = getattr(self, "lora_weights_backup", None) + if weights_backup is None: + weights_backup = self.weight.to(devices.cpu, copy=True) + self.lora_weights_backup = weights_backup + + if current_names != wanted_names: + if weights_backup is not None: + self.weight.copy_(weights_backup) + + lora_layer_name = getattr(self, 'lora_layer_name', None) + for lora in loaded_loras: + module = lora.modules.get(lora_layer_name, None) + if module is None: + continue + + with torch.no_grad(): + up = module.up.weight.to(self.weight.device, dtype=self.weight.dtype) + down = module.down.weight.to(self.weight.device, dtype=self.weight.dtype) + + if up.shape[2:] == (1, 1) and down.shape[2:] == (1, 1): + updown = (up.squeeze(2).squeeze(2) @ down.squeeze(2).squeeze(2)).unsqueeze(2).unsqueeze(3) + else: + updown = up @ down + + self.weight += updown * lora.multiplier * (module.alpha / module.up.weight.shape[1] if module.alpha else 1.0) + + setattr(self, "lora_current_names", wanted_names) def lora_Linear_forward(self, input): - return lora_forward(self, input, torch.nn.Linear_forward_before_lora(self, input)) + lora_apply_weights(self) + + return torch.nn.Linear_forward_before_lora(self, input) + + +def lora_Linear_load_state_dict(self: torch.nn.Linear, *args, **kwargs): + setattr(self, "lora_current_names", ()) + setattr(self, "lora_weights_backup", None) + + return torch.nn.Linear_load_state_dict_before_lora(self, *args, **kwargs) def lora_Conv2d_forward(self, input): - return lora_forward(self, input, torch.nn.Conv2d_forward_before_lora(self, input)) + lora_apply_weights(self) + + return torch.nn.Conv2d_forward_before_lora(self, input) + + +def lora_Conv2d_load_state_dict(self: torch.nn.Conv2d, *args, **kwargs): + setattr(self, "lora_current_names", ()) + setattr(self, "lora_weights_backup", None) + + return torch.nn.Conv2d_load_state_dict_before_lora(self, *args, **kwargs) def list_available_loras(): diff --git a/extensions-builtin/Lora/scripts/lora_script.py b/extensions-builtin/Lora/scripts/lora_script.py index 2e860160..dc329e81 100644 --- a/extensions-builtin/Lora/scripts/lora_script.py +++ b/extensions-builtin/Lora/scripts/lora_script.py @@ -9,7 +9,9 @@ from modules import script_callbacks, ui_extra_networks, extra_networks, shared def unload(): torch.nn.Linear.forward = torch.nn.Linear_forward_before_lora + torch.nn.Linear._load_from_state_dict = torch.nn.Linear_load_state_dict_before_lora torch.nn.Conv2d.forward = torch.nn.Conv2d_forward_before_lora + torch.nn.Conv2d._load_from_state_dict = torch.nn.Conv2d_load_state_dict_before_lora def before_ui(): @@ -20,11 +22,19 @@ def before_ui(): if not hasattr(torch.nn, 'Linear_forward_before_lora'): torch.nn.Linear_forward_before_lora = torch.nn.Linear.forward +if not hasattr(torch.nn, 'Linear_load_state_dict_before_lora'): + torch.nn.Linear_load_state_dict_before_lora = torch.nn.Linear._load_from_state_dict + if not hasattr(torch.nn, 'Conv2d_forward_before_lora'): torch.nn.Conv2d_forward_before_lora = torch.nn.Conv2d.forward +if not hasattr(torch.nn, 'Conv2d_load_state_dict_before_lora'): + torch.nn.Conv2d_load_state_dict_before_lora = torch.nn.Conv2d._load_from_state_dict + torch.nn.Linear.forward = lora.lora_Linear_forward +torch.nn.Linear._load_from_state_dict = lora.lora_Linear_load_state_dict torch.nn.Conv2d.forward = lora.lora_Conv2d_forward +torch.nn.Conv2d._load_from_state_dict = lora.lora_Conv2d_load_state_dict script_callbacks.on_model_loaded(lora.assign_lora_names_to_compvis_modules) script_callbacks.on_script_unloaded(unload) @@ -33,6 +43,4 @@ script_callbacks.on_before_ui(before_ui) shared.options_templates.update(shared.options_section(('extra_networks', "Extra Networks"), { "sd_lora": shared.OptionInfo("None", "Add Lora to prompt", gr.Dropdown, lambda: {"choices": [""] + [x for x in lora.available_loras]}, refresh=lora.list_available_loras), - "lora_apply_to_outputs": shared.OptionInfo(False, "Apply Lora to outputs rather than inputs when possible (experimental)"), - })) From 254ad09ef3b356cbe945ad7ca476c5dcf8d842f8 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Sat, 25 Mar 2023 15:01:10 -0600 Subject: [PATCH 19/33] Update style.css --- style.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/style.css b/style.css index bef15875..7b93811b 100644 --- a/style.css +++ b/style.css @@ -786,8 +786,8 @@ footer { } button[id$='_extra_networks'] { - margin: 0.6em 0em 0.55em 0; - max-width: 2.5em; - min-width: 2.5em !important; + max-width: 2.2em; + min-width: 2.2em !important; height: 2.4em; + line-height: 1em !important; } From d64ff4248bab7fd4f5908f482e4ea2fa22f671ea Mon Sep 17 00:00:00 2001 From: bluelovers Date: Sun, 26 Mar 2023 06:15:09 +0800 Subject: [PATCH 20/33] remove changes in textual_inversion.py --- modules/textual_inversion/textual_inversion.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/textual_inversion/textual_inversion.py b/modules/textual_inversion/textual_inversion.py index 8b5bb6ce..c63c7d1d 100644 --- a/modules/textual_inversion/textual_inversion.py +++ b/modules/textual_inversion/textual_inversion.py @@ -129,7 +129,7 @@ class EmbeddingDatabase: if first_id not in self.ids_lookup: self.ids_lookup[first_id] = [] - self.ids_lookup[first_id] = sorted(self.ids_lookup[first_id] + [(ids, embedding)], key=lambda x: (len(x[0]), x[0].casefold()), reverse=True) + self.ids_lookup[first_id] = sorted(self.ids_lookup[first_id] + [(ids, embedding)], key=lambda x: len(x[0]), reverse=True) return embedding @@ -196,7 +196,7 @@ class EmbeddingDatabase: return for root, dirs, fns in os.walk(embdir.path, followlinks=True): - for fn in sorted(fns, key=str.lower): + for fn in fns: try: fullfn = os.path.join(root, fn) From d286df0a7145ac578b40a5c5c8cc47e4268284a6 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Sat, 25 Mar 2023 21:00:02 -0600 Subject: [PATCH 21/33] Fix dropdown width --- style.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/style.css b/style.css index 0dcc3e25..48973d5a 100644 --- a/style.css +++ b/style.css @@ -56,6 +56,9 @@ div.compact{ .gradio-dropdown ul.options{ z-index: 3000; + min-width: fit-content; + max-width: inherit; + white-space: nowrap; } .gradio-dropdown label span:not(.has-info), From 650ddc9dd3c1d126221682be8270f7fba1b5b6ce Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sun, 26 Mar 2023 10:44:20 +0300 Subject: [PATCH 22/33] Lora support for SD2 --- extensions-builtin/Lora/lora.py | 157 +++++++++++++----- .../Lora/scripts/lora_script.py | 10 ++ 2 files changed, 127 insertions(+), 40 deletions(-) diff --git a/extensions-builtin/Lora/lora.py b/extensions-builtin/Lora/lora.py index d4345ada..edd95f78 100644 --- a/extensions-builtin/Lora/lora.py +++ b/extensions-builtin/Lora/lora.py @@ -8,14 +8,27 @@ from modules import shared, devices, sd_models, errors metadata_tags_order = {"ss_sd_model_name": 1, "ss_resolution": 2, "ss_clip_skip": 3, "ss_num_train_images": 10, "ss_tag_frequency": 20} re_digits = re.compile(r"\d+") -re_unet_down_blocks = re.compile(r"lora_unet_down_blocks_(\d+)_attentions_(\d+)_(.+)") -re_unet_mid_blocks = re.compile(r"lora_unet_mid_block_attentions_(\d+)_(.+)") -re_unet_up_blocks = re.compile(r"lora_unet_up_blocks_(\d+)_attentions_(\d+)_(.+)") -re_text_block = re.compile(r"lora_te_text_model_encoder_layers_(\d+)_(.+)") +re_x_proj = re.compile(r"(.*)_([qkv]_proj)$") +re_compiled = {} + +suffix_conversion = { + "attentions": {}, + "resnets": { + "conv1": "in_layers_2", + "conv2": "out_layers_3", + "time_emb_proj": "emb_layers_1", + "conv_shortcut": "skip_connection", + } +} def convert_diffusers_name_to_compvis(key, is_sd2): - def match(match_list, regex): + def match(match_list, regex_text): + regex = re_compiled.get(regex_text) + if regex is None: + regex = re.compile(regex_text) + re_compiled[regex_text] = regex + r = re.match(regex, key) if not r: return False @@ -26,16 +39,25 @@ def convert_diffusers_name_to_compvis(key, is_sd2): m = [] - if match(m, re_unet_down_blocks): - return f"diffusion_model_input_blocks_{1 + m[0] * 3 + m[1]}_1_{m[2]}" + if match(m, r"lora_unet_down_blocks_(\d+)_(attentions|resnets)_(\d+)_(.+)"): + suffix = suffix_conversion.get(m[1], {}).get(m[3], m[3]) + return f"diffusion_model_input_blocks_{1 + m[0] * 3 + m[2]}_{1 if m[1] == 'attentions' else 0}_{suffix}" - if match(m, re_unet_mid_blocks): - return f"diffusion_model_middle_block_1_{m[1]}" + if match(m, r"lora_unet_mid_block_(attentions|resnets)_(\d+)_(.+)"): + suffix = suffix_conversion.get(m[0], {}).get(m[2], m[2]) + return f"diffusion_model_middle_block_{1 if m[0] == 'attentions' else m[1] * 2}_{suffix}" - if match(m, re_unet_up_blocks): - return f"diffusion_model_output_blocks_{m[0] * 3 + m[1]}_1_{m[2]}" + if match(m, r"lora_unet_up_blocks_(\d+)_(attentions|resnets)_(\d+)_(.+)"): + suffix = suffix_conversion.get(m[1], {}).get(m[3], m[3]) + return f"diffusion_model_output_blocks_{m[0] * 3 + m[2]}_{1 if m[1] == 'attentions' else 0}_{suffix}" - if match(m, re_text_block): + if match(m, r"lora_unet_down_blocks_(\d+)_downsamplers_0_conv"): + return f"diffusion_model_input_blocks_{3 + m[0] * 3}_0_op" + + if match(m, r"lora_unet_up_blocks_(\d+)_upsamplers_0_conv"): + return f"diffusion_model_output_blocks_{2 + m[0] * 3}_{2 if m[0]>0 else 1}_conv" + + if match(m, r"lora_te_text_model_encoder_layers_(\d+)_(.+)"): if is_sd2: if 'mlp_fc1' in m[1]: return f"model_transformer_resblocks_{m[0]}_{m[1].replace('mlp_fc1', 'mlp_c_fc')}" @@ -109,16 +131,22 @@ def load_lora(name, filename): sd = sd_models.read_state_dict(filename) - keys_failed_to_match = [] + keys_failed_to_match = {} is_sd2 = 'model_transformer_resblocks' in shared.sd_model.lora_layer_mapping for key_diffusers, weight in sd.items(): - fullkey = convert_diffusers_name_to_compvis(key_diffusers, is_sd2) - key, lora_key = fullkey.split(".", 1) + key_diffusers_without_lora_parts, lora_key = key_diffusers.split(".", 1) + key = convert_diffusers_name_to_compvis(key_diffusers_without_lora_parts, is_sd2) sd_module = shared.sd_model.lora_layer_mapping.get(key, None) + if sd_module is None: - keys_failed_to_match.append(key_diffusers) + m = re_x_proj.match(key) + if m: + sd_module = shared.sd_model.lora_layer_mapping.get(m.group(1), None) + + if sd_module is None: + keys_failed_to_match[key_diffusers] = key continue lora_module = lora.modules.get(key, None) @@ -133,7 +161,9 @@ def load_lora(name, filename): if type(sd_module) == torch.nn.Linear: module = torch.nn.Linear(weight.shape[1], weight.shape[0], bias=False) elif type(sd_module) == torch.nn.modules.linear.NonDynamicallyQuantizableLinear: - module = torch.nn.modules.linear.NonDynamicallyQuantizableLinear(weight.shape[1], weight.shape[0], bias=False) + module = torch.nn.Linear(weight.shape[1], weight.shape[0], bias=False) + elif type(sd_module) == torch.nn.MultiheadAttention: + module = torch.nn.Linear(weight.shape[1], weight.shape[0], bias=False) elif type(sd_module) == torch.nn.Conv2d: module = torch.nn.Conv2d(weight.shape[1], weight.shape[0], (1, 1), bias=False) else: @@ -190,54 +220,94 @@ def load_loras(names, multipliers=None): loaded_loras.append(lora) -def lora_apply_weights(self: torch.nn.Conv2d | torch.nn.Linear): +def lora_calc_updown(lora, module, target): + with torch.no_grad(): + up = module.up.weight.to(target.device, dtype=target.dtype) + down = module.down.weight.to(target.device, dtype=target.dtype) + + if up.shape[2:] == (1, 1) and down.shape[2:] == (1, 1): + updown = (up.squeeze(2).squeeze(2) @ down.squeeze(2).squeeze(2)).unsqueeze(2).unsqueeze(3) + else: + updown = up @ down + + updown = updown * lora.multiplier * (module.alpha / module.up.weight.shape[1] if module.alpha else 1.0) + + return updown + + +def lora_apply_weights(self: torch.nn.Conv2d | torch.nn.Linear | torch.nn.MultiheadAttention): """ - Applies the currently selected set of Loras to the weight of torch layer self. + Applies the currently selected set of Loras to the weights of torch layer self. If weights already have this particular set of loras applied, does nothing. If not, restores orginal weights from backup and alters weights according to loras. """ + lora_layer_name = getattr(self, 'lora_layer_name', None) + if lora_layer_name is None: + return + current_names = getattr(self, "lora_current_names", ()) wanted_names = tuple((x.name, x.multiplier) for x in loaded_loras) weights_backup = getattr(self, "lora_weights_backup", None) if weights_backup is None: - weights_backup = self.weight.to(devices.cpu, copy=True) + if isinstance(self, torch.nn.MultiheadAttention): + weights_backup = (self.in_proj_weight.to(devices.cpu, copy=True), self.out_proj.weight.to(devices.cpu, copy=True)) + else: + weights_backup = self.weight.to(devices.cpu, copy=True) + self.lora_weights_backup = weights_backup if current_names != wanted_names: if weights_backup is not None: - self.weight.copy_(weights_backup) + if isinstance(self, torch.nn.MultiheadAttention): + self.in_proj_weight.copy_(weights_backup[0]) + self.out_proj.weight.copy_(weights_backup[1]) + else: + self.weight.copy_(weights_backup) - lora_layer_name = getattr(self, 'lora_layer_name', None) for lora in loaded_loras: module = lora.modules.get(lora_layer_name, None) + if module is not None and hasattr(self, 'weight'): + self.weight += lora_calc_updown(lora, module, self.weight) + continue + + module_q = lora.modules.get(lora_layer_name + "_q_proj", None) + module_k = lora.modules.get(lora_layer_name + "_k_proj", None) + module_v = lora.modules.get(lora_layer_name + "_v_proj", None) + module_out = lora.modules.get(lora_layer_name + "_out_proj", None) + + if isinstance(self, torch.nn.MultiheadAttention) and module_q and module_k and module_v and module_out: + updown_q = lora_calc_updown(lora, module_q, self.in_proj_weight) + updown_k = lora_calc_updown(lora, module_k, self.in_proj_weight) + updown_v = lora_calc_updown(lora, module_v, self.in_proj_weight) + updown_qkv = torch.vstack([updown_q, updown_k, updown_v]) + + self.in_proj_weight += updown_qkv + self.out_proj.weight += lora_calc_updown(lora, module_out, self.out_proj.weight) + continue + if module is None: continue - with torch.no_grad(): - up = module.up.weight.to(self.weight.device, dtype=self.weight.dtype) - down = module.down.weight.to(self.weight.device, dtype=self.weight.dtype) - - if up.shape[2:] == (1, 1) and down.shape[2:] == (1, 1): - updown = (up.squeeze(2).squeeze(2) @ down.squeeze(2).squeeze(2)).unsqueeze(2).unsqueeze(3) - else: - updown = up @ down - - self.weight += updown * lora.multiplier * (module.alpha / module.up.weight.shape[1] if module.alpha else 1.0) + print(f'failed to calculate lora weights for layer {lora_layer_name}') setattr(self, "lora_current_names", wanted_names) +def lora_reset_cached_weight(self: torch.nn.Conv2d | torch.nn.Linear): + setattr(self, "lora_current_names", ()) + setattr(self, "lora_weights_backup", None) + + def lora_Linear_forward(self, input): lora_apply_weights(self) return torch.nn.Linear_forward_before_lora(self, input) -def lora_Linear_load_state_dict(self: torch.nn.Linear, *args, **kwargs): - setattr(self, "lora_current_names", ()) - setattr(self, "lora_weights_backup", None) +def lora_Linear_load_state_dict(self, *args, **kwargs): + lora_reset_cached_weight(self) return torch.nn.Linear_load_state_dict_before_lora(self, *args, **kwargs) @@ -248,15 +318,22 @@ def lora_Conv2d_forward(self, input): return torch.nn.Conv2d_forward_before_lora(self, input) -def lora_Conv2d_load_state_dict(self: torch.nn.Conv2d, *args, **kwargs): - setattr(self, "lora_current_names", ()) - setattr(self, "lora_weights_backup", None) +def lora_Conv2d_load_state_dict(self, *args, **kwargs): + lora_reset_cached_weight(self) return torch.nn.Conv2d_load_state_dict_before_lora(self, *args, **kwargs) -def lora_NonDynamicallyQuantizableLinear_forward(self, input): - return lora_forward(self, input, torch.nn.NonDynamicallyQuantizableLinear_forward_before_lora(self, input)) +def lora_MultiheadAttention_forward(self, *args, **kwargs): + lora_apply_weights(self) + + return torch.nn.MultiheadAttention_forward_before_lora(self, *args, **kwargs) + + +def lora_MultiheadAttention_load_state_dict(self, *args, **kwargs): + lora_reset_cached_weight(self) + + return torch.nn.MultiheadAttention_load_state_dict_before_lora(self, *args, **kwargs) def list_available_loras(): diff --git a/extensions-builtin/Lora/scripts/lora_script.py b/extensions-builtin/Lora/scripts/lora_script.py index dc329e81..0adab225 100644 --- a/extensions-builtin/Lora/scripts/lora_script.py +++ b/extensions-builtin/Lora/scripts/lora_script.py @@ -12,6 +12,8 @@ def unload(): torch.nn.Linear._load_from_state_dict = torch.nn.Linear_load_state_dict_before_lora torch.nn.Conv2d.forward = torch.nn.Conv2d_forward_before_lora torch.nn.Conv2d._load_from_state_dict = torch.nn.Conv2d_load_state_dict_before_lora + torch.nn.MultiheadAttention.forward = torch.nn.MultiheadAttention_forward_before_lora + torch.nn.MultiheadAttention._load_from_state_dict = torch.nn.MultiheadAttention_load_state_dict_before_lora def before_ui(): @@ -31,10 +33,18 @@ if not hasattr(torch.nn, 'Conv2d_forward_before_lora'): if not hasattr(torch.nn, 'Conv2d_load_state_dict_before_lora'): torch.nn.Conv2d_load_state_dict_before_lora = torch.nn.Conv2d._load_from_state_dict +if not hasattr(torch.nn, 'MultiheadAttention_forward_before_lora'): + torch.nn.MultiheadAttention_forward_before_lora = torch.nn.MultiheadAttention.forward + +if not hasattr(torch.nn, 'MultiheadAttention_load_state_dict_before_lora'): + torch.nn.MultiheadAttention_load_state_dict_before_lora = torch.nn.MultiheadAttention._load_from_state_dict + torch.nn.Linear.forward = lora.lora_Linear_forward torch.nn.Linear._load_from_state_dict = lora.lora_Linear_load_state_dict torch.nn.Conv2d.forward = lora.lora_Conv2d_forward torch.nn.Conv2d._load_from_state_dict = lora.lora_Conv2d_load_state_dict +torch.nn.MultiheadAttention.forward = lora.lora_MultiheadAttention_forward +torch.nn.MultiheadAttention._load_from_state_dict = lora.lora_MultiheadAttention_load_state_dict script_callbacks.on_model_loaded(lora.assign_lora_names_to_compvis_modules) script_callbacks.on_script_unloaded(unload) From 4c1ad743e3baf1246db0711aa0107debf036a12b Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sun, 26 Mar 2023 11:01:32 +0300 Subject: [PATCH 23/33] for img2img, use None as upscaler instead of erroring out if the desired upscaler is not found --- modules/images.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/images.py b/modules/images.py index 7030aaaa..b3535070 100644 --- a/modules/images.py +++ b/modules/images.py @@ -261,9 +261,12 @@ def resize_image(resize_mode, im, width, height, upscaler_name=None): if scale > 1.0: upscalers = [x for x in shared.sd_upscalers if x.name == upscaler_name] - assert len(upscalers) > 0, f"could not find upscaler named {upscaler_name}" + if len(upscalers) == 0: + upscaler = shared.sd_upscalers[0] + print(f"could not find upscaler named {upscaler_name or ''}, using {upscaler.name} as a fallback") + else: + upscaler = upscalers[0] - upscaler = upscalers[0] im = upscaler.scaler.upscale(im, scale, upscaler.data_path) if im.width != w or im.height != h: From 9d7390d2d19a8baf04ee4ebe598b96ac6ba7f97e Mon Sep 17 00:00:00 2001 From: camenduru <54370274+camenduru@users.noreply.github.com> Date: Mon, 27 Mar 2023 04:28:40 +0300 Subject: [PATCH 24/33] convert to python v3.9 --- extensions-builtin/Lora/lora.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions-builtin/Lora/lora.py b/extensions-builtin/Lora/lora.py index edd95f78..79d11e0e 100644 --- a/extensions-builtin/Lora/lora.py +++ b/extensions-builtin/Lora/lora.py @@ -2,6 +2,7 @@ import glob import os import re import torch +from typing import Union from modules import shared, devices, sd_models, errors @@ -235,7 +236,7 @@ def lora_calc_updown(lora, module, target): return updown -def lora_apply_weights(self: torch.nn.Conv2d | torch.nn.Linear | torch.nn.MultiheadAttention): +def lora_apply_weights(self: Union[torch.nn.Conv2d, torch.nn.Linear, torch.nn.MultiheadAttention]): """ Applies the currently selected set of Loras to the weights of torch layer self. If weights already have this particular set of loras applied, does nothing. From 6a147db1287fe660e1bfb2ebf5b3fadc14835c69 Mon Sep 17 00:00:00 2001 From: camenduru <54370274+camenduru@users.noreply.github.com> Date: Mon, 27 Mar 2023 04:40:31 +0300 Subject: [PATCH 25/33] convert to python v3.9 --- extensions-builtin/Lora/lora.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions-builtin/Lora/lora.py b/extensions-builtin/Lora/lora.py index 79d11e0e..696be8ea 100644 --- a/extensions-builtin/Lora/lora.py +++ b/extensions-builtin/Lora/lora.py @@ -296,7 +296,7 @@ def lora_apply_weights(self: Union[torch.nn.Conv2d, torch.nn.Linear, torch.nn.Mu setattr(self, "lora_current_names", wanted_names) -def lora_reset_cached_weight(self: torch.nn.Conv2d | torch.nn.Linear): +def lora_reset_cached_weight(self: Union[torch.nn.Conv2d, torch.nn.Linear]): setattr(self, "lora_current_names", ()) setattr(self, "lora_weights_backup", None) From 774c691df8b99f5e24d3f41e451fe65f5b447952 Mon Sep 17 00:00:00 2001 From: pieresimakp <69743585+pieresimakp@users.noreply.github.com> Date: Mon, 27 Mar 2023 11:31:56 +0800 Subject: [PATCH 26/33] fixed style box wrapping --- style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/style.css b/style.css index 0dcc3e25..2218739a 100644 --- a/style.css +++ b/style.css @@ -69,7 +69,7 @@ div.compact{ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); } -.gradio-dropdown .wrap-inner.wrap-inner.wrap-inner{ +.gradio-dropdown:not(.multiselect) .wrap-inner.wrap-inner.wrap-inner{ flex-wrap: unset; } From ff0d97c1e3dafe9b15d37d157859a8b0fa934bff Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Mon, 27 Mar 2023 07:11:39 +0300 Subject: [PATCH 27/33] bring back -y --- modules/cmd_args.py | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/cmd_args.py b/modules/cmd_args.py index 0af87251..4614e82d 100644 --- a/modules/cmd_args.py +++ b/modules/cmd_args.py @@ -4,6 +4,7 @@ from modules.paths_internal import models_path, script_path, data_path, extensio parser = argparse.ArgumentParser() +parser.add_argument("-y", action='store_true', help=argparse.SUPPRESS) # allows running as root; implemented outside of webui parser.add_argument("--update-all-extensions", action='store_true', help="launch.py argument: download updates for all extensions when starting the program") parser.add_argument("--skip-python-version-check", action='store_true', help="launch.py argument: do not check python version") parser.add_argument("--skip-torch-cuda-test", action='store_true', help="launch.py argument: do not check if CUDA is able to work properly") From 3d09b4e99fc77acddcb2a4562efe8d6444a87e3c Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Mon, 27 Mar 2023 07:12:40 +0300 Subject: [PATCH 28/33] remove -y, bring back -f --- modules/cmd_args.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cmd_args.py b/modules/cmd_args.py index 4614e82d..81c0b82a 100644 --- a/modules/cmd_args.py +++ b/modules/cmd_args.py @@ -4,7 +4,7 @@ from modules.paths_internal import models_path, script_path, data_path, extensio parser = argparse.ArgumentParser() -parser.add_argument("-y", action='store_true', help=argparse.SUPPRESS) # allows running as root; implemented outside of webui +parser.add_argument("-f", action='store_true', help=argparse.SUPPRESS) # allows running as root; implemented outside of webui parser.add_argument("--update-all-extensions", action='store_true', help="launch.py argument: download updates for all extensions when starting the program") parser.add_argument("--skip-python-version-check", action='store_true', help="launch.py argument: do not check python version") parser.add_argument("--skip-torch-cuda-test", action='store_true', help="launch.py argument: do not check if CUDA is able to work properly") From b40538a7fe2ee6914b3121f252f0d5dc9f7e8fab Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Mon, 27 Mar 2023 07:30:38 +0300 Subject: [PATCH 29/33] reformat css from latest commits, change color for dropdown selection to more neutral one --- style.css | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/style.css b/style.css index 84fee3d3..5ecfce98 100644 --- a/style.css +++ b/style.css @@ -54,13 +54,6 @@ div.compact{ gap: 1em; } -.gradio-dropdown ul.options{ - z-index: 3000; - min-width: fit-content; - max-width: inherit; - white-space: nowrap; -} - .gradio-dropdown label span:not(.has-info), .gradio-textbox label span:not(.has-info), .gradio-number label span:not(.has-info) @@ -68,17 +61,23 @@ div.compact{ margin-bottom: 0; } -.gradio-dropdown ul.options { - max-height: 35em; - z-index: 3000; +.gradio-dropdown ul.options{ + z-index: 3000; + min-width: fit-content; + max-width: inherit; + white-space: nowrap; } .gradio-dropdown ul.options li.item { - padding: 0.05em 0; + padding: 0.05em 0; } .gradio-dropdown ul.options li.item.selected { - background-color: var(--secondary-500); + background-color: var(--neutral-100); +} + +.dark .gradio-dropdown ul.options li.item.selected { + background-color: var(--neutral-900); } .gradio-dropdown div.wrap.wrap.wrap.wrap{ From a70ae917ea86270f365f1347674980e4e7b0bfb2 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Mon, 27 Mar 2023 08:05:55 +0300 Subject: [PATCH 30/33] update button pressed down style for #8569 --- modules/ui_extra_networks.py | 2 +- style.css | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 2a3b8553..7f73131e 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -252,7 +252,7 @@ def create_ui(container, button, tabname): def toggle_visibility(is_visible): is_visible = not is_visible - return is_visible, gr.update(visible=is_visible), gr.update(variant=("primary" if is_visible else "tool")) + return is_visible, gr.update(visible=is_visible), gr.update(variant=("secondary-down" if is_visible else "secondary")) state_visible = gr.State(value=False) button.click(fn=toggle_visibility, inputs=[state_visible], outputs=[state_visible, container, button]) diff --git a/style.css b/style.css index 6a6f73d7..7a979f22 100644 --- a/style.css +++ b/style.css @@ -138,6 +138,18 @@ div.gradio-html.min{ border-radius: 0.5em; } +.gradio-button.secondary-down{ + background: var(--button-secondary-background-fill); + color: var(--button-secondary-text-color); +} +.gradio-button.secondary-down, .gradio-button.secondary-down:hover{ + box-shadow: 1px 1px 1px rgba(0,0,0,0.25) inset, 0px 0px 3px rgba(0,0,0,0.15) inset; +} +.gradio-button.secondary-down:hover{ + background: var(--button-secondary-background-fill-hover); + color: var(--button-secondary-text-color-hover); +} + .checkboxes-row{ margin-bottom: 0.5em; margin-left: 0em; @@ -810,10 +822,3 @@ footer { .extra-network-cards .card ul a:hover{ color: red; } - -button[id$='_extra_networks'] { - max-width: 2.2em; - min-width: 2.2em !important; - height: 2.4em; - line-height: 1em !important; -} From 5cf3822e463393e6ba1eab0b5647174f754886b9 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Mon, 27 Mar 2023 08:18:28 +0300 Subject: [PATCH 31/33] Revert "Merge pull request #8651 from vladmandic/flicker" This reverts commit 84026821188ccf81b91c9c854a18f9852b90b13f, reversing changes made to e8bbc344c3309c70f7f5abeb34b90f14bd0cfa51. --- webui.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/webui.py b/webui.py index 30f3e4a1..b570895f 100644 --- a/webui.py +++ b/webui.py @@ -265,9 +265,6 @@ def webui(): inbrowser=cmd_opts.autolaunch, prevent_thread_lock=True ) - for dep in shared.demo.dependencies: - dep['show_progress'] = False # disable gradio css animation on component update - # after initial launch, disable --autolaunch for subsequent restarts cmd_opts.autolaunch = False From 5fcd4bfa3d86c3ebf91ff993b6472a68a3a4f6ad Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Mon, 27 Mar 2023 10:02:30 +0300 Subject: [PATCH 32/33] do not read extensions' git stuff at startup --- modules/extensions.py | 29 +++++++++++++++++++---------- modules/ui_extensions.py | 5 +++++ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/modules/extensions.py b/modules/extensions.py index a14ffbf0..0d34b89a 100644 --- a/modules/extensions.py +++ b/modules/extensions.py @@ -5,13 +5,14 @@ import traceback import time import git -from modules import paths, shared +from modules import shared from modules.paths_internal import extensions_dir, extensions_builtin_dir extensions = [] -if not os.path.exists(paths.extensions_dir): - os.makedirs(paths.extensions_dir) +if not os.path.exists(extensions_dir): + os.makedirs(extensions_dir) + def active(): return [x for x in extensions if x.enabled] @@ -26,21 +27,29 @@ class Extension: self.can_update = False self.is_builtin = is_builtin self.version = '' + self.remote = None + self.have_info_from_repo = False + + def read_info_from_repo(self): + if self.have_info_from_repo: + return + + self.have_info_from_repo = True repo = None try: - if os.path.exists(os.path.join(path, ".git")): - repo = git.Repo(path) + if os.path.exists(os.path.join(self.path, ".git")): + repo = git.Repo(self.path) except Exception: - print(f"Error reading github repository info from {path}:", file=sys.stderr) + print(f"Error reading github repository info from {self.path}:", file=sys.stderr) print(traceback.format_exc(), file=sys.stderr) if repo is None or repo.bare: self.remote = None else: try: - self.remote = next(repo.remote().urls, None) self.status = 'unknown' + self.remote = next(repo.remote().urls, None) head = repo.head.commit ts = time.asctime(time.gmtime(repo.head.commit.committed_date)) self.version = f'{head.hexsha[:8]} ({ts})' @@ -85,11 +94,11 @@ class Extension: def list_extensions(): extensions.clear() - if not os.path.isdir(paths.extensions_dir): + if not os.path.isdir(extensions_dir): return extension_paths = [] - for dirname in [paths.extensions_dir, paths.extensions_builtin_dir]: + for dirname in [extensions_dir, extensions_builtin_dir]: if not os.path.isdir(dirname): return @@ -98,7 +107,7 @@ def list_extensions(): if not os.path.isdir(path): continue - extension_paths.append((extension_dirname, path, dirname == paths.extensions_builtin_dir)) + extension_paths.append((extension_dirname, path, dirname == extensions_builtin_dir)) for dirname, path, is_builtin in extension_paths: extension = Extension(name=dirname, path=path, enabled=dirname not in shared.opts.disabled_extensions, is_builtin=is_builtin) diff --git a/modules/ui_extensions.py b/modules/ui_extensions.py index da7e79f0..b4a0d6ec 100644 --- a/modules/ui_extensions.py +++ b/modules/ui_extensions.py @@ -63,6 +63,9 @@ def check_updates(id_task, disable_list): try: ext.check_updates() + except FileNotFoundError as e: + if 'FETCH_HEAD' not in str(e): + raise except Exception: print(f"Error checking updates for {ext.name}:", file=sys.stderr) print(traceback.format_exc(), file=sys.stderr) @@ -87,6 +90,8 @@ def extension_table(): """ for ext in extensions.extensions: + ext.read_info_from_repo() + remote = f"""{html.escape("built-in" if ext.is_builtin else ext.remote or '')}""" if ext.can_update: From 9e82896d5f0365b25aacf755799e9fb08078b0ba Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Mon, 27 Mar 2023 10:20:01 +0300 Subject: [PATCH 33/33] remove an extra unneeded row in outputs --- modules/ui_common.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/ui_common.py b/modules/ui_common.py index 0f3427c8..3b11dcc8 100644 --- a/modules/ui_common.py +++ b/modules/ui_common.py @@ -145,8 +145,7 @@ Requested path was: {f} ) if tabname != "extras": - with gr.Row(): - download_files = gr.File(None, file_count="multiple", interactive=False, show_label=False, visible=False, elem_id=f'download_files_{tabname}') + download_files = gr.File(None, file_count="multiple", interactive=False, show_label=False, visible=False, elem_id=f'download_files_{tabname}') with gr.Group(): html_info = gr.HTML(elem_id=f'html_info_{tabname}', elem_classes="infotext")