From 4130e5db3d5c2fa7cbfe9e09e5eadba1ed958ab0 Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Fri, 25 Aug 2023 10:12:19 +0900 Subject: [PATCH 001/186] img2img batch PNG info model hash --- modules/img2img.py | 12 +++++++++++- modules/ui.py | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/modules/img2img.py b/modules/img2img.py index 1519e132..c81c7ab9 100644 --- a/modules/img2img.py +++ b/modules/img2img.py @@ -10,6 +10,7 @@ from modules import images as imgutil from modules.generation_parameters_copypaste import create_override_settings_dict, parse_generation_parameters from modules.processing import Processed, StableDiffusionProcessingImg2Img, process_images from modules.shared import opts, state +from modules.sd_models import get_closet_checkpoint_match import modules.shared as shared import modules.processing as processing from modules.ui import plaintext_to_html @@ -41,7 +42,8 @@ def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args, to_scale=Fal cfg_scale = p.cfg_scale sampler_name = p.sampler_name steps = p.steps - + override_settings = p.override_settings + sd_model_checkpoint_override = get_closet_checkpoint_match(override_settings.get("sd_model_checkpoint", None)) for i, image in enumerate(images): state.job = f"{i+1} out of {len(images)}" if state.skipped: @@ -104,6 +106,14 @@ def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args, to_scale=Fal p.sampler_name = parsed_parameters.get("Sampler", sampler_name) p.steps = int(parsed_parameters.get("Steps", steps)) + model_info = get_closet_checkpoint_match(parsed_parameters.get("Model hash", None)) + if model_info is not None: + p.override_settings['sd_model_checkpoint'] = model_info.name + elif sd_model_checkpoint_override: + p.override_settings['sd_model_checkpoint'] = sd_model_checkpoint_override + else: + p.override_settings.pop("sd_model_checkpoint", None) + proc = modules.scripts.scripts_img2img.run(p, *args) if proc is None: if output_dir: diff --git a/modules/ui.py b/modules/ui.py index 2b6a13cb..9c5082c3 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -614,7 +614,7 @@ def create_ui(): with gr.Accordion("PNG info", open=False): img2img_batch_use_png_info = gr.Checkbox(label="Append png info to prompts", **shared.hide_dirs, elem_id="img2img_batch_use_png_info") img2img_batch_png_info_dir = gr.Textbox(label="PNG info directory", **shared.hide_dirs, placeholder="Leave empty to use input directory", elem_id="img2img_batch_png_info_dir") - img2img_batch_png_info_props = gr.CheckboxGroup(["Prompt", "Negative prompt", "Seed", "CFG scale", "Sampler", "Steps"], label="Parameters to take from png info", info="Prompts from png info will be appended to prompts set in ui.") + img2img_batch_png_info_props = gr.CheckboxGroup(["Prompt", "Negative prompt", "Seed", "CFG scale", "Sampler", "Steps", "Model hash"], label="Parameters to take from png info", info="Prompts from png info will be appended to prompts set in ui.") img2img_tabs = [tab_img2img, tab_sketch, tab_inpaint, tab_inpaint_color, tab_inpaint_upload, tab_batch] From 3369fb27df6c1badd39bcb59b3f71c61a47d3d91 Mon Sep 17 00:00:00 2001 From: SpenserCai Date: Fri, 25 Aug 2023 22:15:35 +0800 Subject: [PATCH 002/186] support installed extensions list api --- modules/api/api.py | 20 ++++++++++++++++++++ modules/api/models.py | 9 +++++++++ 2 files changed, 29 insertions(+) diff --git a/modules/api/api.py b/modules/api/api.py index e6edffe7..0bcf5497 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -243,6 +243,7 @@ class Api: self.add_api_route("/sdapi/v1/reload-checkpoint", self.reloadapi, methods=["POST"]) self.add_api_route("/sdapi/v1/scripts", self.get_scripts_list, methods=["GET"], response_model=models.ScriptsList) self.add_api_route("/sdapi/v1/script-info", self.get_script_info, methods=["GET"], response_model=List[models.ScriptInfo]) + self.add_api_route("/sdapi/v1/extensions", self.get_extensions_list, methods=["GET"], response_model=List[models.ExtensionItem]) if shared.cmd_opts.api_server_stop: self.add_api_route("/sdapi/v1/server-kill", self.kill_webui, methods=["POST"]) @@ -769,6 +770,25 @@ class Api: except Exception as err: cuda = {'error': f'{err}'} return models.MemoryResponse(ram=ram, cuda=cuda) + + def get_extensions_list(self): + from modules import extensions + extensions.list_extensions() + ext_list = [] + for ext in extensions.extensions: + ext: extensions.Extension + ext.read_info_from_repo() + if ext.remote is not None: + ext_list.append({ + "name": ext.name, + "remote": ext.remote, + "branch": ext.branch, + "commit_hash":ext.commit_hash, + "commit_date":ext.commit_date, + "version":ext.version, + "enabled":ext.enabled + }) + return ext_list def launch(self, server_name, port, root_path): self.app.include_router(self.router) diff --git a/modules/api/models.py b/modules/api/models.py index 6a574771..731ab03d 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -311,3 +311,12 @@ class ScriptInfo(BaseModel): is_alwayson: bool = Field(default=None, title="IsAlwayson", description="Flag specifying whether this script is an alwayson script") is_img2img: bool = Field(default=None, title="IsImg2img", description="Flag specifying whether this script is an img2img script") args: List[ScriptArg] = Field(title="Arguments", description="List of script's arguments") + +class ExtensionItem(BaseModel): + name: str = Field(title="Name", description="Extension name") + remote: str = Field(title="Remote", description="Extension Repository URL") + branch: str = Field(title="Branch", description="Extension Repository Branch") + commit_hash: str = Field(title="Commit Hash", description="Extension Repository Commit Hash") + version: str = Field(title="Version", description="Extension Version") + commit_date: str = Field(title="Commit Date", description="Extension Repository Commit Date") + enabled: bool = Field(title="Enabled", description="Flag specifying whether this extension is enabled") From dd07b5193efa547929629b310ef5c9ff0fc83a19 Mon Sep 17 00:00:00 2001 From: SpenserCai Date: Fri, 25 Aug 2023 22:23:17 +0800 Subject: [PATCH 003/186] fix format error --- 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 0bcf5497..785ee828 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -770,7 +770,7 @@ class Api: except Exception as err: cuda = {'error': f'{err}'} return models.MemoryResponse(ram=ram, cuda=cuda) - + def get_extensions_list(self): from modules import extensions extensions.list_extensions() From db56bdce33264aab5e6b565a41df503d785bbbff Mon Sep 17 00:00:00 2001 From: catboxanon <122327233+catboxanon@users.noreply.github.com> Date: Fri, 25 Aug 2023 16:04:06 -0400 Subject: [PATCH 004/186] Don't show hidden samplers in dropdown for XYZ script --- scripts/xyz_grid.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/xyz_grid.py b/scripts/xyz_grid.py index daaf761f..517d6332 100644 --- a/scripts/xyz_grid.py +++ b/scripts/xyz_grid.py @@ -238,9 +238,9 @@ axis_options = [ AxisOptionImg2Img("Image CFG Scale", float, apply_field("image_cfg_scale")), AxisOption("Prompt S/R", str, apply_prompt, format_value=format_value), AxisOption("Prompt order", str_permutations, apply_order, format_value=format_value_join_list), - AxisOptionTxt2Img("Sampler", str, apply_field("sampler_name"), format_value=format_value, confirm=confirm_samplers, choices=lambda: [x.name for x in sd_samplers.samplers]), - AxisOptionTxt2Img("Hires sampler", str, apply_field("hr_sampler_name"), confirm=confirm_samplers, choices=lambda: [x.name for x in sd_samplers.samplers_for_img2img]), - AxisOptionImg2Img("Sampler", str, apply_field("sampler_name"), format_value=format_value, confirm=confirm_samplers, choices=lambda: [x.name for x in sd_samplers.samplers_for_img2img]), + AxisOptionTxt2Img("Sampler", str, apply_field("sampler_name"), format_value=format_value, confirm=confirm_samplers, choices=lambda: [x.name for x in sd_samplers.samplers if x.name not in opts.hide_samplers]), + AxisOptionTxt2Img("Hires sampler", str, apply_field("hr_sampler_name"), confirm=confirm_samplers, choices=lambda: [x.name for x in sd_samplers.samplers_for_img2img if x.name not in opts.hide_samplers]), + AxisOptionImg2Img("Sampler", str, apply_field("sampler_name"), format_value=format_value, confirm=confirm_samplers, choices=lambda: [x.name for x in sd_samplers.samplers_for_img2img if x.name not in opts.hide_samplers]), AxisOption("Checkpoint name", str, apply_checkpoint, format_value=format_remove_path, confirm=confirm_checkpoints, cost=1.0, choices=lambda: sorted(sd_models.checkpoints_list, key=str.casefold)), AxisOption("Negative Guidance minimum sigma", float, apply_field("s_min_uncond")), AxisOption("Sigma Churn", float, apply_field("s_churn")), From bb90b0ff42ea55cbc73df15ea1ef8fd79af2e026 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 26 Aug 2023 06:33:48 +0300 Subject: [PATCH 005/186] fix defaults settings page breaking when any of main UI tabs are hidden --- modules/ui.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/ui.py b/modules/ui.py index 9c5082c3..f4028475 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -1279,11 +1279,8 @@ def create_ui(): with gr.TabItem(label, id=ifid, elem_id=f"tab_{ifid}"): interface.render() - for interface, _label, ifid in interfaces: - if ifid in ["extensions", "settings"]: - continue - - loadsave.add_block(interface, ifid) + if ifid not in ["extensions", "settings"]: + loadsave.add_block(interface, ifid) loadsave.add_component(f"webui/Tabs@{tabs.elem_id}", tabs) From 72ee347eabf04d1a238a738a03e7973cc2a46ca3 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 26 Aug 2023 06:52:18 +0300 Subject: [PATCH 006/186] update pnginfo checkpoint to return dict with parsed values --- modules/api/api.py | 10 ++++------ modules/api/models.py | 3 ++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index 785ee828..844e31ee 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -17,7 +17,7 @@ from fastapi.encoders import jsonable_encoder from secrets import compare_digest import modules.shared as shared -from modules import sd_samplers, deepbooru, sd_hijack, images, scripts, ui, postprocessing, errors, restart, shared_items +from modules import sd_samplers, deepbooru, sd_hijack, images, scripts, ui, postprocessing, errors, restart, shared_items, script_callbacks, generation_parameters_copypaste from modules.api import models from modules.shared import opts from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img, process_images @@ -474,9 +474,6 @@ class Api: return models.ExtrasBatchImagesResponse(images=list(map(encode_pil_to_base64, result[0])), html_info=result[1]) def pnginfoapi(self, req: models.PNGInfoRequest): - if(not req.image.strip()): - return models.PNGInfoResponse(info="") - image = decode_base64_to_image(req.image.strip()) if image is None: return models.PNGInfoResponse(info="") @@ -485,9 +482,10 @@ class Api: if geninfo is None: geninfo = "" - items = {**{'parameters': geninfo}, **items} + params = generation_parameters_copypaste.parse_generation_parameters(geninfo) + script_callbacks.infotext_pasted_callback(geninfo, params) - return models.PNGInfoResponse(info=geninfo, items=items) + return models.PNGInfoResponse(info=geninfo, items=items, parameters=params) def progressapi(self, req: models.ProgressRequest = Depends()): # copy from check_progress_call of ui.py diff --git a/modules/api/models.py b/modules/api/models.py index 731ab03d..94eca97d 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -178,7 +178,8 @@ class PNGInfoRequest(BaseModel): class PNGInfoResponse(BaseModel): info: str = Field(title="Image info", description="A string with the parameters used to generate the image") - items: dict = Field(title="Items", description="An object containing all the info the image had") + items: dict = Field(title="Items", description="A dictionary containing all the other fields the image had") + parameters: dict = Field(title="Parameters", description="A dictionary with parsed generation info fields") class ProgressRequest(BaseModel): skip_current_image: bool = Field(default=False, title="Skip current image", description="Skip current image serialization") From ec54257cb21bacd6281a5f9c6f74c2529fe446c5 Mon Sep 17 00:00:00 2001 From: catboxanon <122327233+catboxanon@users.noreply.github.com> Date: Sat, 26 Aug 2023 07:00:09 -0400 Subject: [PATCH 007/186] Hide broken image crop tool for now --- style.css | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/style.css b/style.css index d67b6336..5090f289 100644 --- a/style.css +++ b/style.css @@ -2,6 +2,14 @@ @import url('https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;600&display=swap'); +/* temporary fix to hide gradio crop tool until it's fixed https://github.com/gradio-app/gradio/issues/3810 */ + + +div.gradio-image button[aria-label="Edit"] { + display: none; +} + + /* general gradio fixes */ :root, .dark{ From 73f69a74534be17c020fd1a5e64dfce71981fc31 Mon Sep 17 00:00:00 2001 From: catboxanon <122327233+catboxanon@users.noreply.github.com> Date: Sat, 26 Aug 2023 07:04:11 -0400 Subject: [PATCH 008/186] Fix CSS whitespace --- style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/style.css b/style.css index 5090f289..e336e79d 100644 --- a/style.css +++ b/style.css @@ -2,8 +2,8 @@ @import url('https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;600&display=swap'); -/* temporary fix to hide gradio crop tool until it's fixed https://github.com/gradio-app/gradio/issues/3810 */ +/* temporary fix to hide gradio crop tool until it's fixed https://github.com/gradio-app/gradio/issues/3810 */ div.gradio-image button[aria-label="Edit"] { display: none; From 168eac319d0f45c778d5b9d35dd5ce280f8d5094 Mon Sep 17 00:00:00 2001 From: Daniel Dengler Date: Sat, 26 Aug 2023 23:22:57 +0200 Subject: [PATCH 009/186] is_automatic is missing () for call --- modules/sd_vae.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/sd_vae.py b/modules/sd_vae.py index 669097da..31306d8b 100644 --- a/modules/sd_vae.py +++ b/modules/sd_vae.py @@ -159,7 +159,7 @@ def resolve_vae_from_user_metadata(checkpoint_file) -> VaeResolution: def resolve_vae_near_checkpoint(checkpoint_file) -> VaeResolution: vae_near_checkpoint = find_vae_near_checkpoint(checkpoint_file) - if vae_near_checkpoint is not None and (not shared.opts.sd_vae_overrides_per_model_preferences or is_automatic): + if vae_near_checkpoint is not None and (not shared.opts.sd_vae_overrides_per_model_preferences or is_automatic()): return VaeResolution(vae_near_checkpoint, 'found near the checkpoint') return VaeResolution(resolved=False) From 9d8d279d0d4d103e1b7d0bad21a3eb835dbab9aa Mon Sep 17 00:00:00 2001 From: catboxanon <122327233+catboxanon@users.noreply.github.com> Date: Sat, 26 Aug 2023 15:56:17 -0400 Subject: [PATCH 010/186] Prevent duplicate resize handler --- javascript/resizeHandle.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/javascript/resizeHandle.js b/javascript/resizeHandle.js index 2fd3c4d2..8c5c5169 100644 --- a/javascript/resizeHandle.js +++ b/javascript/resizeHandle.js @@ -134,6 +134,8 @@ onUiLoaded(function() { for (var elem of gradioApp().querySelectorAll('.resize-handle-row')) { - setupResizeHandle(elem); + if (!elem.querySelector('.resize-handle')) { + setupResizeHandle(elem); + } } }); From b7f0e815624dab182aff406c8f227b39ec17452f Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 27 Aug 2023 08:41:26 +0300 Subject: [PATCH 011/186] fix error that causes some extra networks to be disabled if both and are present in the prompt --- modules/extra_networks.py | 60 ++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/modules/extra_networks.py b/modules/extra_networks.py index fa28ac75..b9533677 100644 --- a/modules/extra_networks.py +++ b/modules/extra_networks.py @@ -1,6 +1,7 @@ import json import os import re +import logging from collections import defaultdict from modules import errors @@ -86,27 +87,55 @@ class ExtraNetwork: raise NotImplementedError +def lookup_extra_networks(extra_network_data): + """returns a dict mapping ExtraNetwork objects to lists of arguments for those extra networks. + + Example input: + { + 'lora': [], + 'lyco': [], + 'hypernet': [] + } + + Example output: + + { + : [, ], + : [] + } + """ + + res = {} + + for extra_network_name, extra_network_args in list(extra_network_data.items()): + extra_network = extra_network_registry.get(extra_network_name, None) + alias = extra_network_aliases.get(extra_network_name, None) + + if alias is not None and extra_network is None: + extra_network = alias + + if extra_network is None: + logging.info(f"Skipping unknown extra network: {extra_network_name}") + continue + + res.setdefault(extra_network, []).extend(extra_network_args) + + return res + + def activate(p, extra_network_data): """call activate for extra networks in extra_network_data in specified order, then call activate for all remaining registered networks with an empty argument list""" activated = [] - for extra_network_name, extra_network_args in extra_network_data.items(): - extra_network = extra_network_registry.get(extra_network_name, None) - - if extra_network is None: - extra_network = extra_network_aliases.get(extra_network_name, None) - - if extra_network is None: - print(f"Skipping unknown extra network: {extra_network_name}") - continue + for extra_network, extra_network_args in lookup_extra_networks(extra_network_data).items(): try: extra_network.activate(p, extra_network_args) activated.append(extra_network) except Exception as e: - errors.display(e, f"activating extra network {extra_network_name} with arguments {extra_network_args}") + errors.display(e, f"activating extra network {extra_network.name} with arguments {extra_network_args}") for extra_network_name, extra_network in extra_network_registry.items(): if extra_network in activated: @@ -125,19 +154,16 @@ def deactivate(p, extra_network_data): """call deactivate for extra networks in extra_network_data in specified order, then call deactivate for all remaining registered networks""" - for extra_network_name in extra_network_data: - extra_network = extra_network_registry.get(extra_network_name, None) - if extra_network is None: - continue + data = lookup_extra_networks(extra_network_data) + for extra_network in data: try: extra_network.deactivate(p) except Exception as e: - errors.display(e, f"deactivating extra network {extra_network_name}") + errors.display(e, f"deactivating extra network {extra_network.name}") for extra_network_name, extra_network in extra_network_registry.items(): - args = extra_network_data.get(extra_network_name, None) - if args is not None: + if extra_network in data: continue try: From cb5f0823c6f7fadb5eb81b93e5a587a11856b478 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 27 Aug 2023 08:45:16 +0300 Subject: [PATCH 012/186] update gradio to 3.41.2 --- modules/errors.py | 2 +- requirements.txt | 2 +- requirements_versions.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/errors.py b/modules/errors.py index a56fd30c..8c339464 100644 --- a/modules/errors.py +++ b/modules/errors.py @@ -95,7 +95,7 @@ def check_versions(): expected_torch_version = "2.0.0" expected_xformers_version = "0.0.20" - expected_gradio_version = "3.41.0" + expected_gradio_version = "3.41.2" if version.parse(torch.__version__) < version.parse(expected_torch_version): print_error_explanation(f""" diff --git a/requirements.txt b/requirements.txt index 960fa0bd..80b43845 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ clean-fid einops fastapi>=0.90.1 gfpgan -gradio==3.41.0 +gradio==3.41.2 inflection jsonmerge kornia diff --git a/requirements_versions.txt b/requirements_versions.txt index 6c679e24..f8ae1f38 100644 --- a/requirements_versions.txt +++ b/requirements_versions.txt @@ -7,7 +7,7 @@ clean-fid==0.1.35 einops==0.4.1 fastapi==0.94.0 gfpgan==1.3.8 -gradio==3.41.0 +gradio==3.41.2 httpcore==0.15 inflection==0.5.1 jsonmerge==1.8.0 From f2c55523c0cde3dca5cc154c45046316396b14a6 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 27 Aug 2023 08:45:25 +0300 Subject: [PATCH 013/186] update changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c5e0f11..5e78b3d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,7 +67,7 @@ * make it possible to localize tooltips and placeholders ### Extensions and API: - * gradio 3.41.0 + * gradio 3.41.2 * also bump versions for packages: transformers, GitPython, accelerate, scikit-image, timm, tomesd * support tooltip kwarg for gradio elements: gr.Textbox(label='hello', tooltip='world') * properly clear the total console progressbar when using txt2img and img2img from API @@ -127,6 +127,9 @@ * set devices.dtype_unet correctly * run RealESRGAN on GPU for non-CUDA devices ([#12737](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12737)) * prevent extra network buttons being obscured by description for very small card sizes ([#12745](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12745)) + * fix error that causes some extra networks to be disabled if both and are present in the prompt + * fix defaults settings page breaking when any of main UI tabs are hidden + * fix incorrect save/display of new values in Defaults page in settings ## 1.5.2 From bd5c16e8da5837b2b08fe6e329694553dd688a5f Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 27 Aug 2023 09:19:02 +0300 Subject: [PATCH 014/186] fix for Reload UI function: if you reload UI on one tab, other opened tabs will no longer stop working --- CHANGELOG.md | 1 + modules/generation_parameters_copypaste.py | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e78b3d2..1bbde234 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -130,6 +130,7 @@ * fix error that causes some extra networks to be disabled if both and are present in the prompt * fix defaults settings page breaking when any of main UI tabs are hidden * fix incorrect save/display of new values in Defaults page in settings + * fix for Reload UI function: if you reload UI on one tab, other opened tabs will no longer stop working ## 1.5.2 diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py index 386517ac..2ca16055 100644 --- a/modules/generation_parameters_copypaste.py +++ b/modules/generation_parameters_copypaste.py @@ -32,6 +32,7 @@ class ParamBinding: def reset(): paste_fields.clear() + registered_param_bindings.clear() def quote(text): From 23c6b5f1242f89c37a094d7a9237491c1ca1da34 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 27 Aug 2023 09:39:37 +0300 Subject: [PATCH 015/186] fix style editing dialog breaking if it's opened in both img2img and txt2img tabs --- javascript/extraNetworks.js | 9 +++++++++ modules/ui_common.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 3bc723d3..ad1a4e00 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -249,6 +249,15 @@ function popup(contents) { globalPopup.style.display = "flex"; } +var storedPopupIds = {}; +function popupId(id) { + if(! storedPopupIds[id]){ + storedPopupIds[id] = gradioApp().getElementById(id); + } + + popup(storedPopupIds[id]); +} + function extraNetworksShowMetadata(text) { var elem = document.createElement('pre'); elem.classList.add('popup-metadata'); diff --git a/modules/ui_common.py b/modules/ui_common.py index eddc4bc8..84a7d7f2 100644 --- a/modules/ui_common.py +++ b/modules/ui_common.py @@ -261,7 +261,7 @@ def setup_dialog(button_show, dialog, *, button_close=None): fn=lambda: gr.update(visible=True), inputs=[], outputs=[dialog], - ).then(fn=None, _js="function(){ popup(gradioApp().getElementById('" + dialog.elem_id + "')); }") + ).then(fn=None, _js="function(){ popupId('" + dialog.elem_id + "'); }") if button_close: button_close.click(fn=None, _js="closePopup") From 897312de46352c39d03b6811844c128426fbae80 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 27 Aug 2023 09:44:13 +0300 Subject: [PATCH 016/186] update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bbde234..07798b5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -131,6 +131,10 @@ * fix defaults settings page breaking when any of main UI tabs are hidden * fix incorrect save/display of new values in Defaults page in settings * fix for Reload UI function: if you reload UI on one tab, other opened tabs will no longer stop working + * fix an error that prevents VAE being reloaded after an option change if a VAE near the checkpoint exists ([#12797](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12737)) + * hide broken image crop tool ([#12792](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12737)) + * don't show hidden samplers in dropdown for XYZ script ([#12780](https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12737)) + * fix style editing dialog breaking if it's opened in both img2img and txt2img tabs ## 1.5.2 From 63d3150dc4f5c4452a4a385329eb8954f53d6451 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 27 Aug 2023 10:11:14 +0300 Subject: [PATCH 017/186] lint --- javascript/extraNetworks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index ad1a4e00..493f31af 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -251,7 +251,7 @@ function popup(contents) { var storedPopupIds = {}; function popupId(id) { - if(! storedPopupIds[id]){ + if (!storedPopupIds[id]) { storedPopupIds[id] = gradioApp().getElementById(id); } From 896fde789ee69bd5dd2a829def0878793aa28079 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 27 Aug 2023 20:16:50 +0300 Subject: [PATCH 018/186] hide --gradio-auth and --api-auth values from /internal/sysinfo report --- modules/sysinfo.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/modules/sysinfo.py b/modules/sysinfo.py index 058e66ce..2db7551d 100644 --- a/modules/sysinfo.py +++ b/modules/sysinfo.py @@ -82,7 +82,7 @@ def get_dict(): "Data path": paths_internal.data_path, "Extensions dir": paths_internal.extensions_dir, "Checksum": checksum_token, - "Commandline": sys.argv, + "Commandline": get_argv(), "Torch env info": get_torch_sysinfo(), "Exceptions": get_exceptions(), "CPU": { @@ -123,6 +123,22 @@ def get_environment(): return {k: os.environ[k] for k in sorted(os.environ) if k in environment_whitelist} +def get_argv(): + res = [] + + for v in sys.argv: + if shared.cmd_opts.gradio_auth and shared.cmd_opts.gradio_auth == v: + res.append("") + continue + + if shared.cmd_opts.api_auth and shared.cmd_opts.api_auth == v: + res.append("") + continue + + res.append(v) + + return res + re_newline = re.compile(r"\r*\n") From e422f19ee9f78066ec023d6b3b0a307a677c6ad9 Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Mon, 28 Aug 2023 03:27:07 +0900 Subject: [PATCH 019/186] non-local condition --- modules/shared_cmd_options.py | 2 +- webui.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/shared_cmd_options.py b/modules/shared_cmd_options.py index af24938b..dd93f520 100644 --- a/modules/shared_cmd_options.py +++ b/modules/shared_cmd_options.py @@ -15,4 +15,4 @@ else: cmd_opts, _ = parser.parse_known_args() -cmd_opts.disable_extension_access = (cmd_opts.share or cmd_opts.listen or cmd_opts.server_name) and not cmd_opts.enable_insecure_extension_access +cmd_opts.disable_extension_access = any([cmd_opts.share, cmd_opts.listen, cmd_opts.ngrok, cmd_opts.server_name]) and not cmd_opts.enable_insecure_extension_access diff --git a/webui.py b/webui.py index 5c827dae..12328423 100644 --- a/webui.py +++ b/webui.py @@ -74,7 +74,7 @@ def webui(): if shared.opts.auto_launch_browser == "Remote" or cmd_opts.autolaunch: auto_launch_browser = True elif shared.opts.auto_launch_browser == "Local": - auto_launch_browser = not any([cmd_opts.listen, cmd_opts.share, cmd_opts.ngrok]) + auto_launch_browser = not any([cmd_opts.listen, cmd_opts.share, cmd_opts.ngrok, cmd_opts.server_name]) app, local_url, share_url = shared.demo.launch( share=cmd_opts.share, From 18e3e6d6abfc084324cc8ae13f70ba4af5ddc35f Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Mon, 28 Aug 2023 03:42:02 +0900 Subject: [PATCH 020/186] consolidate local check --- modules/shared_cmd_options.py | 4 ++-- webui.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/shared_cmd_options.py b/modules/shared_cmd_options.py index dd93f520..c9626667 100644 --- a/modules/shared_cmd_options.py +++ b/modules/shared_cmd_options.py @@ -14,5 +14,5 @@ if os.environ.get('IGNORE_CMD_ARGS_ERRORS', None) is None: else: cmd_opts, _ = parser.parse_known_args() - -cmd_opts.disable_extension_access = any([cmd_opts.share, cmd_opts.listen, cmd_opts.ngrok, cmd_opts.server_name]) and not cmd_opts.enable_insecure_extension_access +cmd_opts.webui_is_non_local = any([cmd_opts.share, cmd_opts.listen, cmd_opts.ngrok, cmd_opts.server_name]) +cmd_opts.disable_extension_access = cmd_opts.webui_is_non_local and not cmd_opts.enable_insecure_extension_access diff --git a/webui.py b/webui.py index 12328423..9ed20b30 100644 --- a/webui.py +++ b/webui.py @@ -74,7 +74,7 @@ def webui(): if shared.opts.auto_launch_browser == "Remote" or cmd_opts.autolaunch: auto_launch_browser = True elif shared.opts.auto_launch_browser == "Local": - auto_launch_browser = not any([cmd_opts.listen, cmd_opts.share, cmd_opts.ngrok, cmd_opts.server_name]) + auto_launch_browser = not cmd_opts.webui_is_non_local app, local_url, share_url = shared.demo.launch( share=cmd_opts.share, From 2b8484a29d7d1dbfd69d97616e4617cb02006192 Mon Sep 17 00:00:00 2001 From: catboxanon <122327233+catboxanon@users.noreply.github.com> Date: Sun, 27 Aug 2023 16:25:26 -0400 Subject: [PATCH 021/186] Add missing infotext for RNG --- modules/shared_options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/shared_options.py b/modules/shared_options.py index 83f56314..0f054f47 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -144,7 +144,7 @@ options_templates.update(options_section(('sd', "Stable Diffusion"), { "comma_padding_backtrack": OptionInfo(20, "Prompt word wrap length limit", gr.Slider, {"minimum": 0, "maximum": 74, "step": 1}).info("in tokens - for texts shorter than specified, if they don't fit into 75 token limit, move them to the next 75 token chunk"), "CLIP_stop_at_last_layers": OptionInfo(1, "Clip skip", gr.Slider, {"minimum": 1, "maximum": 12, "step": 1}, infotext="Clip skip").link("wiki", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features#clip-skip").info("ignore last layers of CLIP network; 1 ignores none, 2 ignores one layer"), "upcast_attn": OptionInfo(False, "Upcast cross attention layer to float32"), - "randn_source": OptionInfo("GPU", "Random number generator source.", gr.Radio, {"choices": ["GPU", "CPU", "NV"]}).info("changes seeds drastically; use CPU to produce the same picture across different videocard vendors; use NV to produce same picture as on NVidia videocards"), + "randn_source": OptionInfo("GPU", "Random number generator source.", gr.Radio, {"choices": ["GPU", "CPU", "NV"]}, infotext="RNG").info("changes seeds drastically; use CPU to produce the same picture across different videocard vendors; use NV to produce same picture as on NVidia videocards"), "tiling": OptionInfo(False, "Tiling", infotext='Tiling').info("produce a tileable picture"), })) From f3d1631aab82d559294126a9230c979ef4c4e1d6 Mon Sep 17 00:00:00 2001 From: JaredTherriault Date: Sun, 27 Aug 2023 21:54:05 -0700 Subject: [PATCH 022/186] Offloading custom work -custom_statics works to do mass replace strings, intended for copy-pasting gen info from internet generations and replacing unsavory prompts with safer prompts for my own sanity -tried to implement this into generation_parameters_copypaste but it didn't work out this iteration, presumably because we return a string and the calling method is looking for an object type -updated webui-user.bat to set a custom temp directory (for disk space concerns) and to apply xformers (for generation speed) I probably won't be merging any of this work into the main repo since I don't want to mess with anyone else's prompts, this is just intended to keep my workspace safe from anything I don't want to see. Eventually this should be done in an extension which I could then publish, but I need to learn a lot more about the extension and callback systems in the main repo first. just uploading this to my fork for now so i don't lose the current progress. --- modules/custom_statics.py | 29 ++++++++++++++++++++++ modules/generation_parameters_copypaste.py | 8 ++++++ webui-user.bat | 4 +-- 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 modules/custom_statics.py diff --git a/modules/custom_statics.py b/modules/custom_statics.py new file mode 100644 index 00000000..207bd5fb --- /dev/null +++ b/modules/custom_statics.py @@ -0,0 +1,29 @@ +import os +import gc +import re + +import modules.paths as paths + +class CustomStatics: + + @staticmethod + # loads a file with strings structured as below, on each line with a : between the search and replace strings, into a list + # search0:replace0 + # search string:replace string + # + # Then replaces all occurrences of the list's search strings with the list's replace strings in one go + def mass_replace_strings(input_string): + with open(os.path.join(paths.data_path, "custom_statics/Replacements.txt"), "r", encoding="utf8") as file: + replacements = file.readlines() + + replacement_dict = {} + for line in replacements: + search, replace = line.strip().split(":") + replacement_dict[search] = replace + + def replace(match_text): + return replacement_dict[match_text.group(0)] + + return re.sub('|'.join(r'\b%s\b' % re.escape(s) for s in replacement_dict.keys()), replace, str(input_string)) + + return str(geninfo) \ No newline at end of file diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py index a3448be9..bd7b0018 100644 --- a/modules/generation_parameters_copypaste.py +++ b/modules/generation_parameters_copypaste.py @@ -370,6 +370,14 @@ def connect_paste(button, paste_fields, input_comp, override_settings_component, prompt = file.read() params = parse_generation_parameters(prompt) + + # This sanitizes unsavory prompt words when copying from another image + # for my own sanity. This is not intended to be contributed to the main repo, + # it's just so I don't have to see anything I'm not interested in when batch + # reproducing images from civit.ai or elsewhere when working on loras + # todo: make this work with the callback instead of forcing it here, this can be an extension when I feel like putting it together :D + from modules import custom_statics + params = custom_statics.CustomStatics.mass_replace_strings(params) script_callbacks.infotext_pasted_callback(prompt, params) res = [] diff --git a/webui-user.bat b/webui-user.bat index e5a257be..1ba2116d 100644 --- a/webui-user.bat +++ b/webui-user.bat @@ -1,8 +1,8 @@ @echo off - +set TEMP=G:\SD-temp set PYTHON= set GIT= set VENV_DIR= -set COMMANDLINE_ARGS= +set COMMANDLINE_ARGS= --xformers call webui.bat From f898833ea38718e87b39ab090b2a2325638559cb Mon Sep 17 00:00:00 2001 From: omahs <73983677+omahs@users.noreply.github.com> Date: Mon, 28 Aug 2023 10:43:13 +0200 Subject: [PATCH 023/186] fix typos --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b796d150..007d6fde 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ A browser interface based on Gradio library for Stable Diffusion. - [Alt-Diffusion](https://arxiv.org/abs/2211.06679) support - see [wiki](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features#alt-diffusion) for instructions - Now without any bad letters! - Load checkpoints in safetensors format -- Eased resolution restriction: generated image's domension must be a multiple of 8 rather than 64 +- Eased resolution restriction: generated image's dimensions must be a multiple of 8 rather than 64 - Now with a license! - Reorder elements in the UI from settings screen @@ -100,7 +100,7 @@ Alternatively, use online services (like Google Colab): - [List of Online Services](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Online-Services) ### Installation on Windows 10/11 with NVidia-GPUs using release package -1. Download `sd.webui.zip` from [v1.0.0-pre](https://github.com/AUTOMATIC1111/stable-diffusion-webui/releases/tag/v1.0.0-pre) and extract it's contents. +1. Download `sd.webui.zip` from [v1.0.0-pre](https://github.com/AUTOMATIC1111/stable-diffusion-webui/releases/tag/v1.0.0-pre) and extract its contents. 2. Run `update.bat`. 3. Run `run.bat`. > For more details see [Install-and-Run-on-NVidia-GPUs](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Install-and-Run-on-NVidia-GPUs) From 99acbd5ebe3e43e7d07905c7fc274b321cc905be Mon Sep 17 00:00:00 2001 From: catboxanon <122327233+catboxanon@users.noreply.github.com> Date: Mon, 28 Aug 2023 11:17:47 -0400 Subject: [PATCH 024/186] Don't print blank stdout in extension installers --- modules/launch_utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/launch_utils.py b/modules/launch_utils.py index 7e4d5a61..43b986c6 100644 --- a/modules/launch_utils.py +++ b/modules/launch_utils.py @@ -228,7 +228,9 @@ def run_extension_installer(extension_dir): env = os.environ.copy() env['PYTHONPATH'] = f"{os.path.abspath('.')}{os.pathsep}{env.get('PYTHONPATH', '')}" - print(run(f'"{python}" "{path_installer}"', errdesc=f"Error running install.py for extension {extension_dir}", custom_env=env)) + stdout = run(f'"{python}" "{path_installer}"', errdesc=f"Error running install.py for extension {extension_dir}", custom_env=env) + if stdout: + print(stdout) except Exception as e: errors.report(str(e)) From 20df81b0cc146c117c8a8c002997941063b32f13 Mon Sep 17 00:00:00 2001 From: catboxanon <122327233+catboxanon@users.noreply.github.com> Date: Mon, 28 Aug 2023 11:26:50 -0400 Subject: [PATCH 025/186] Honor `--skip-install` for extension installers --- modules/launch_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/launch_utils.py b/modules/launch_utils.py index 7e4d5a61..27d7d617 100644 --- a/modules/launch_utils.py +++ b/modules/launch_utils.py @@ -404,7 +404,8 @@ def prepare_environment(): run_pip(f"install -r \"{requirements_file}\"", "requirements") startup_timer.record("install requirements") - run_extensions_installers(settings_file=args.ui_settings_file) + if not args.skip_install: + run_extensions_installers(settings_file=args.ui_settings_file) if args.update_check: version_check(commit) From 592b0dcfa705c42654eff48a40a51d8d6924f987 Mon Sep 17 00:00:00 2001 From: catboxanon <122327233+catboxanon@users.noreply.github.com> Date: Mon, 28 Aug 2023 12:09:37 -0400 Subject: [PATCH 026/186] Fix notification not playing when built-in webui tab is inactive --- javascript/notification.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/notification.js b/javascript/notification.js index 76c5715d..6d799561 100644 --- a/javascript/notification.js +++ b/javascript/notification.js @@ -15,7 +15,7 @@ onAfterUiUpdate(function() { } } - const galleryPreviews = gradioApp().querySelectorAll('div[id^="tab_"][style*="display: block"] div[id$="_results"] .thumbnail-item > img'); + const galleryPreviews = gradioApp().querySelectorAll('div[id^="tab_"] div[id$="_results"] .thumbnail-item > img'); if (galleryPreviews == null) return; From cd48308a2a37b1e838b1b0cc5e8e507a174b14fb Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Mon, 28 Aug 2023 22:22:35 +0300 Subject: [PATCH 027/186] always show NV as RNG source in infotext --- modules/processing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/processing.py b/modules/processing.py index 7dc931ba..0138e5ac 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -689,7 +689,7 @@ def create_infotext(p, all_prompts, all_seeds, all_subseeds, comments=None, iter "Token merging ratio": None if token_merging_ratio == 0 else token_merging_ratio, "Token merging ratio hr": None if not enable_hr or token_merging_ratio_hr == 0 else token_merging_ratio_hr, "Init image hash": getattr(p, 'init_img_hash', None), - "RNG": opts.randn_source if opts.randn_source != "GPU" and opts.randn_source != "NV" else None, + "RNG": opts.randn_source if opts.randn_source != "GPU" else None, "NGMS": None if p.s_min_uncond == 0 else p.s_min_uncond, "Tiling": "True" if p.tiling else None, **p.extra_generation_params, From 739686b1c521a77b0d3ce43c1f3b123df0317504 Mon Sep 17 00:00:00 2001 From: bluelovers Date: Tue, 29 Aug 2023 06:19:22 +0800 Subject: [PATCH 028/186] style: file-metadata word-break --- style.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/style.css b/style.css index e336e79d..bbfb7d39 100644 --- a/style.css +++ b/style.css @@ -1009,6 +1009,8 @@ div.block.gradio-box.edit-user-metadata { .edit-user-metadata .file-metadata th, .edit-user-metadata .file-metadata td{ padding: 0.3em 1em; + overflow-wrap: anywhere; + word-break: break-word; } .edit-user-metadata .wrap.translucent{ From 1bb21f35102326da28e1360750a879386c015716 Mon Sep 17 00:00:00 2001 From: bluelovers Date: Tue, 29 Aug 2023 06:25:16 +0800 Subject: [PATCH 029/186] feat: display file metadata path https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/12289 --- modules/ui_extra_networks_user_metadata.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/modules/ui_extra_networks_user_metadata.py b/modules/ui_extra_networks_user_metadata.py index b11622a1..877d0285 100644 --- a/modules/ui_extra_networks_user_metadata.py +++ b/modules/ui_extra_networks_user_metadata.py @@ -2,10 +2,29 @@ import datetime import html import json import os.path +from pathlib import Path import gradio as gr from modules import generation_parameters_copypaste, images, sysinfo, errors +from modules.paths_internal import models_path + + +def windows_to_unix_style(path): + return Path(path).as_posix() + + +def exclude_root_path(root_path, path_to_exclude): + try: + relative_path = os.path.relpath(path_to_exclude, root_path) + # 如果路径已经在 root_path 之外,relpath 会返回绝对路径 + # 所以需要检查路径是否在 root_path 之内 + if not relative_path.startswith('..'): + return windows_to_unix_style(relative_path) + except ValueError: + pass + # 如果路径无法相对化,或者位于 root_path 之外,则返回原始路径 + return windows_to_unix_style(path_to_exclude) class UserMetadataEditor: @@ -98,6 +117,7 @@ class UserMetadataEditor: stats = os.stat(filename) params = [ ('Filename: ', os.path.basename(filename)), + ('Path: ', exclude_root_path(models_path, filename)), ('File size: ', sysinfo.pretty_bytes(stats.st_size)), ('Hash: ', shorthash), ('Modified: ', datetime.datetime.fromtimestamp(stats.st_mtime).strftime('%Y-%m-%d %H:%M')), From d83a1ba65be1b0fbdba8f10212193c52dc8f5e90 Mon Sep 17 00:00:00 2001 From: bluelovers Date: Tue, 29 Aug 2023 06:33:00 +0800 Subject: [PATCH 030/186] feat: display file metadata ss_output_name https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/12289 --- extensions-builtin/Lora/ui_edit_user_metadata.py | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions-builtin/Lora/ui_edit_user_metadata.py b/extensions-builtin/Lora/ui_edit_user_metadata.py index 390d9dde..c7011909 100644 --- a/extensions-builtin/Lora/ui_edit_user_metadata.py +++ b/extensions-builtin/Lora/ui_edit_user_metadata.py @@ -70,6 +70,7 @@ class LoraUserMetadataEditor(ui_extra_networks_user_metadata.UserMetadataEditor) metadata = item.get("metadata") or {} keys = { + 'ss_output_name': "Output name:", 'ss_sd_model_name': "Model:", 'ss_clip_skip': "Clip skip:", 'ss_network_module': "Kohya module:", From 02e7824e6a9c3d74af7b383dd66a3a1c231ef082 Mon Sep 17 00:00:00 2001 From: ibrainventures Date: Tue, 29 Aug 2023 02:04:07 +0200 Subject: [PATCH 031/186] [RC 1.6.1 - zoom is partly hidden] Update style.css If a image / batch result image is higher or wider than the current viewport, and is zoomed (left corner zoom icon) it is cutted off on the top and also to the left. This new rule seems to be the culprit. --- style.css | 7 ------- 1 file changed, 7 deletions(-) diff --git a/style.css b/style.css index e336e79d..56e2cb4c 100644 --- a/style.css +++ b/style.css @@ -660,13 +660,6 @@ table.popup-table .link{ min-height: 0; } -#modalImage{ - position: absolute; - top: 50%; - left: 50%; - transform: translateX(-50%) translateY(-50%); -} - .modalPrev, .modalNext { cursor: pointer; From 5070ab80042e0cafb1eb97f5da9d1d871cb85de6 Mon Sep 17 00:00:00 2001 From: dhwz Date: Tue, 29 Aug 2023 07:16:32 +0200 Subject: [PATCH 032/186] remove xformers Python version check --- modules/launch_utils.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/modules/launch_utils.py b/modules/launch_utils.py index 7e4d5a61..14252c3a 100644 --- a/modules/launch_utils.py +++ b/modules/launch_utils.py @@ -366,17 +366,7 @@ def prepare_environment(): startup_timer.record("install open_clip") if (not is_installed("xformers") or args.reinstall_xformers) and args.xformers: - if platform.system() == "Windows": - if platform.python_version().startswith("3.10"): - run_pip(f"install -U -I --no-deps {xformers_package}", "xformers", live=True) - else: - print("Installation of xformers is not supported in this version of Python.") - print("You can also check this and build manually: https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Xformers#building-xformers-on-windows-by-duckness") - if not is_installed("xformers"): - exit(0) - elif platform.system() == "Linux": - run_pip(f"install -U -I --no-deps {xformers_package}", "xformers") - + run_pip(f"install -U -I --no-deps {xformers_package}", "xformers") startup_timer.record("install xformers") if not is_installed("ngrok") and args.ngrok: From 7ab16e99eedf3b5da7e596218a585f6966aee4d8 Mon Sep 17 00:00:00 2001 From: catboxanon <122327233+catboxanon@users.noreply.github.com> Date: Tue, 29 Aug 2023 01:51:13 -0400 Subject: [PATCH 033/186] Add option to align with sgm repo sampling implementation --- modules/sd_samplers_kdiffusion.py | 14 ++++++++++++-- modules/shared_options.py | 1 + scripts/xyz_grid.py | 1 + 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/modules/sd_samplers_kdiffusion.py b/modules/sd_samplers_kdiffusion.py index b9e0d577..a8a2735f 100644 --- a/modules/sd_samplers_kdiffusion.py +++ b/modules/sd_samplers_kdiffusion.py @@ -144,7 +144,13 @@ class KDiffusionSampler(sd_samplers_common.Sampler): sigmas = self.get_sigmas(p, steps) sigma_sched = sigmas[steps - t_enc - 1:] - xi = x + noise * sigma_sched[0] + if opts.sgm_noise_multiplier: + p.extra_generation_params["SGM noise multiplier"] = True + noise_multiplier = torch.sqrt(1.0 + sigma_sched[0] ** 2.0) + else: + noise_multiplier = sigma_sched[0] + + xi = x + noise * noise_multiplier if opts.img2img_extra_noise > 0: p.extra_generation_params["Extra noise"] = opts.img2img_extra_noise @@ -197,7 +203,11 @@ class KDiffusionSampler(sd_samplers_common.Sampler): sigmas = self.get_sigmas(p, steps) - x = x * sigmas[0] + if opts.sgm_noise_multiplier: + p.extra_generation_params["SGM noise multiplier"] = True + x = x * torch.sqrt(1.0 + sigmas[0] ** 2.0) + else: + x = x * sigmas[0] extra_params_kwargs = self.initialize(p) parameters = inspect.signature(self.func).parameters diff --git a/modules/shared_options.py b/modules/shared_options.py index 83f56314..67f7a8df 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -309,6 +309,7 @@ options_templates.update(options_section(('sampler-params', "Sampler parameters" 'rho': OptionInfo(0.0, "rho", gr.Number, infotext='Schedule rho').info("0 = default (7 for karras, 1 for polyexponential); higher values result in a steeper noise schedule (decreases faster)"), 'eta_noise_seed_delta': OptionInfo(0, "Eta noise seed delta", gr.Number, {"precision": 0}, infotext='ENSD').info("ENSD; does not improve anything, just produces different results for ancestral samplers - only useful for reproducing images"), 'always_discard_next_to_last_sigma': OptionInfo(False, "Always discard next-to-last sigma", infotext='Discard penultimate sigma').link("PR", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/6044"), + 'sgm_noise_multiplier': OptionInfo(False, "SGM noise multiplier", infotext='SGM noise multplier').link("PR", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/12818").info("Match initial noise to official SDXL implementation - only useful for reproducing images"), 'uni_pc_variant': OptionInfo("bh1", "UniPC variant", gr.Radio, {"choices": ["bh1", "bh2", "vary_coeff"]}, infotext='UniPC variant'), 'uni_pc_skip_type': OptionInfo("time_uniform", "UniPC skip type", gr.Radio, {"choices": ["time_uniform", "time_quadratic", "logSNR"]}, infotext='UniPC skip type'), 'uni_pc_order': OptionInfo(3, "UniPC order", gr.Slider, {"minimum": 1, "maximum": 50, "step": 1}, infotext='UniPC order').info("must be < sampling steps"), diff --git a/scripts/xyz_grid.py b/scripts/xyz_grid.py index 517d6332..939d8605 100644 --- a/scripts/xyz_grid.py +++ b/scripts/xyz_grid.py @@ -265,6 +265,7 @@ axis_options = [ AxisOption("Token merging ratio", float, apply_override('token_merging_ratio')), AxisOption("Token merging ratio high-res", float, apply_override('token_merging_ratio_hr')), AxisOption("Always discard next-to-last sigma", str, apply_override('always_discard_next_to_last_sigma', boolean=True), choices=boolean_choice(reverse=True)), + AxisOption("SGM noise multiplier", str, apply_override('sgm_noise_multiplier', boolean=True), choices=boolean_choice(reverse=True)), AxisOption("Refiner checkpoint", str, apply_field('refiner_checkpoint'), format_value=format_remove_path, confirm=confirm_checkpoints_or_none, cost=1.0, choices=lambda: ['None'] + sorted(sd_models.checkpoints_list, key=str.casefold)), AxisOption("Refiner switch at", float, apply_field('refiner_switch_at')), AxisOption("RNG source", str, apply_override("randn_source"), choices=lambda: ["GPU", "CPU", "NV"]), From b6c1a1bbbf29a3041025aa336f6f843ffd7c7d46 Mon Sep 17 00:00:00 2001 From: a666 <19142162+a666@users.noreply.github.com> Date: Fri, 25 Aug 2023 01:58:19 -0600 Subject: [PATCH 034/186] Fix some deprecated types --- modules/api/api.py | 26 +++++++++++++------------- modules/api/models.py | 24 +++++++++++------------- modules/gitpython_hack.py | 2 +- modules/prompt_parser.py | 7 +++---- modules/script_callbacks.py | 6 +++--- modules/sub_quadratic_attention.py | 4 ++-- modules/ui.py | 3 +-- 7 files changed, 34 insertions(+), 38 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index 844e31ee..905ef9c9 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -29,7 +29,7 @@ from modules.sd_models import unload_model_weights, reload_model_weights, checkp from modules.sd_models_config import find_checkpoint_config_near_filename from modules.realesrgan_model import get_realesrgan_models from modules import devices -from typing import Dict, List, Any +from typing import Any import piexif import piexif.helper from contextlib import closing @@ -221,15 +221,15 @@ class Api: self.add_api_route("/sdapi/v1/options", self.get_config, methods=["GET"], response_model=models.OptionsModel) self.add_api_route("/sdapi/v1/options", self.set_config, methods=["POST"]) self.add_api_route("/sdapi/v1/cmd-flags", self.get_cmd_flags, methods=["GET"], response_model=models.FlagsModel) - self.add_api_route("/sdapi/v1/samplers", self.get_samplers, methods=["GET"], response_model=List[models.SamplerItem]) - self.add_api_route("/sdapi/v1/upscalers", self.get_upscalers, methods=["GET"], response_model=List[models.UpscalerItem]) - self.add_api_route("/sdapi/v1/latent-upscale-modes", self.get_latent_upscale_modes, methods=["GET"], response_model=List[models.LatentUpscalerModeItem]) - self.add_api_route("/sdapi/v1/sd-models", self.get_sd_models, methods=["GET"], response_model=List[models.SDModelItem]) - self.add_api_route("/sdapi/v1/sd-vae", self.get_sd_vaes, methods=["GET"], response_model=List[models.SDVaeItem]) - self.add_api_route("/sdapi/v1/hypernetworks", self.get_hypernetworks, methods=["GET"], response_model=List[models.HypernetworkItem]) - self.add_api_route("/sdapi/v1/face-restorers", self.get_face_restorers, methods=["GET"], response_model=List[models.FaceRestorerItem]) - self.add_api_route("/sdapi/v1/realesrgan-models", self.get_realesrgan_models, methods=["GET"], response_model=List[models.RealesrganItem]) - self.add_api_route("/sdapi/v1/prompt-styles", self.get_prompt_styles, methods=["GET"], response_model=List[models.PromptStyleItem]) + self.add_api_route("/sdapi/v1/samplers", self.get_samplers, methods=["GET"], response_model=list[models.SamplerItem]) + self.add_api_route("/sdapi/v1/upscalers", self.get_upscalers, methods=["GET"], response_model=list[models.UpscalerItem]) + self.add_api_route("/sdapi/v1/latent-upscale-modes", self.get_latent_upscale_modes, methods=["GET"], response_model=list[models.LatentUpscalerModeItem]) + self.add_api_route("/sdapi/v1/sd-models", self.get_sd_models, methods=["GET"], response_model=list[models.SDModelItem]) + self.add_api_route("/sdapi/v1/sd-vae", self.get_sd_vaes, methods=["GET"], response_model=list[models.SDVaeItem]) + self.add_api_route("/sdapi/v1/hypernetworks", self.get_hypernetworks, methods=["GET"], response_model=list[models.HypernetworkItem]) + self.add_api_route("/sdapi/v1/face-restorers", self.get_face_restorers, methods=["GET"], response_model=list[models.FaceRestorerItem]) + self.add_api_route("/sdapi/v1/realesrgan-models", self.get_realesrgan_models, methods=["GET"], response_model=list[models.RealesrganItem]) + self.add_api_route("/sdapi/v1/prompt-styles", self.get_prompt_styles, methods=["GET"], response_model=list[models.PromptStyleItem]) self.add_api_route("/sdapi/v1/embeddings", self.get_embeddings, methods=["GET"], response_model=models.EmbeddingsResponse) self.add_api_route("/sdapi/v1/refresh-checkpoints", self.refresh_checkpoints, methods=["POST"]) self.add_api_route("/sdapi/v1/refresh-vae", self.refresh_vae, methods=["POST"]) @@ -242,8 +242,8 @@ class Api: self.add_api_route("/sdapi/v1/unload-checkpoint", self.unloadapi, methods=["POST"]) self.add_api_route("/sdapi/v1/reload-checkpoint", self.reloadapi, methods=["POST"]) self.add_api_route("/sdapi/v1/scripts", self.get_scripts_list, methods=["GET"], response_model=models.ScriptsList) - self.add_api_route("/sdapi/v1/script-info", self.get_script_info, methods=["GET"], response_model=List[models.ScriptInfo]) - self.add_api_route("/sdapi/v1/extensions", self.get_extensions_list, methods=["GET"], response_model=List[models.ExtensionItem]) + self.add_api_route("/sdapi/v1/script-info", self.get_script_info, methods=["GET"], response_model=list[models.ScriptInfo]) + self.add_api_route("/sdapi/v1/extensions", self.get_extensions_list, methods=["GET"], response_model=list[models.ExtensionItem]) if shared.cmd_opts.api_server_stop: self.add_api_route("/sdapi/v1/server-kill", self.kill_webui, methods=["POST"]) @@ -563,7 +563,7 @@ class Api: return options - def set_config(self, req: Dict[str, Any]): + def set_config(self, req: dict[str, Any]): checkpoint_name = req.get("sd_model_checkpoint", None) if checkpoint_name is not None and checkpoint_name not in checkpoint_aliases: raise RuntimeError(f"model {checkpoint_name!r} not found") diff --git a/modules/api/models.py b/modules/api/models.py index 94eca97d..a0d80af8 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -1,12 +1,10 @@ import inspect from pydantic import BaseModel, Field, create_model -from typing import Any, Optional -from typing_extensions import Literal +from typing import Any, Optional, Literal from inflection import underscore from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img from modules.shared import sd_upscalers, opts, parser -from typing import Dict, List API_NOT_ALLOWED = [ "self", @@ -130,12 +128,12 @@ StableDiffusionImg2ImgProcessingAPI = PydanticModelGenerator( ).generate_model() class TextToImageResponse(BaseModel): - images: List[str] = Field(default=None, title="Image", description="The generated image in base64 format.") + images: list[str] = Field(default=None, title="Image", description="The generated image in base64 format.") parameters: dict info: str class ImageToImageResponse(BaseModel): - images: List[str] = Field(default=None, title="Image", description="The generated image in base64 format.") + images: list[str] = Field(default=None, title="Image", description="The generated image in base64 format.") parameters: dict info: str @@ -168,10 +166,10 @@ class FileData(BaseModel): name: str = Field(title="File name") class ExtrasBatchImagesRequest(ExtrasBaseRequest): - imageList: List[FileData] = Field(title="Images", description="List of images to work on. Must be Base64 strings") + imageList: list[FileData] = Field(title="Images", description="List of images to work on. Must be Base64 strings") class ExtrasBatchImagesResponse(ExtraBaseResponse): - images: List[str] = Field(title="Images", description="The generated images in base64 format.") + images: list[str] = Field(title="Images", description="The generated images in base64 format.") class PNGInfoRequest(BaseModel): image: str = Field(title="Image", description="The base64 encoded PNG image") @@ -233,8 +231,8 @@ FlagsModel = create_model("Flags", **flags) class SamplerItem(BaseModel): name: str = Field(title="Name") - aliases: List[str] = Field(title="Aliases") - options: Dict[str, str] = Field(title="Options") + aliases: list[str] = Field(title="Aliases") + options: dict[str, str] = Field(title="Options") class UpscalerItem(BaseModel): name: str = Field(title="Name") @@ -285,8 +283,8 @@ class EmbeddingItem(BaseModel): vectors: int = Field(title="Vectors", description="The number of vectors in the embedding") class EmbeddingsResponse(BaseModel): - loaded: Dict[str, EmbeddingItem] = Field(title="Loaded", description="Embeddings loaded for the current model") - skipped: Dict[str, EmbeddingItem] = Field(title="Skipped", description="Embeddings skipped for the current model (likely due to architecture incompatibility)") + loaded: dict[str, EmbeddingItem] = Field(title="Loaded", description="Embeddings loaded for the current model") + skipped: dict[str, EmbeddingItem] = Field(title="Skipped", description="Embeddings skipped for the current model (likely due to architecture incompatibility)") class MemoryResponse(BaseModel): ram: dict = Field(title="RAM", description="System memory stats") @@ -304,14 +302,14 @@ class ScriptArg(BaseModel): minimum: Optional[Any] = Field(default=None, title="Minimum", description="Minimum allowed value for the argumentin UI") maximum: Optional[Any] = Field(default=None, title="Minimum", description="Maximum allowed value for the argumentin UI") step: Optional[Any] = Field(default=None, title="Minimum", description="Step for changing value of the argumentin UI") - choices: Optional[List[str]] = Field(default=None, title="Choices", description="Possible values for the argument") + choices: Optional[list[str]] = Field(default=None, title="Choices", description="Possible values for the argument") class ScriptInfo(BaseModel): name: str = Field(default=None, title="Name", description="Script name") is_alwayson: bool = Field(default=None, title="IsAlwayson", description="Flag specifying whether this script is an alwayson script") is_img2img: bool = Field(default=None, title="IsImg2img", description="Flag specifying whether this script is an img2img script") - args: List[ScriptArg] = Field(title="Arguments", description="List of script's arguments") + args: list[ScriptArg] = Field(title="Arguments", description="List of script's arguments") class ExtensionItem(BaseModel): name: str = Field(title="Name", description="Extension name") diff --git a/modules/gitpython_hack.py b/modules/gitpython_hack.py index e537c1df..b55f0640 100644 --- a/modules/gitpython_hack.py +++ b/modules/gitpython_hack.py @@ -23,7 +23,7 @@ class Git(git.Git): ) return self._parse_object_header(ret) - def stream_object_data(self, ref: str) -> tuple[str, str, int, "Git.CatFileContentStream"]: + def stream_object_data(self, ref: str) -> tuple[str, str, int, Git.CatFileContentStream]: # Not really streaming, per se; this buffers the entire object in memory. # Shouldn't be a problem for our use case, since we're only using this for # object headers (commit objects). diff --git a/modules/prompt_parser.py b/modules/prompt_parser.py index 334efeef..ddf4d2dd 100644 --- a/modules/prompt_parser.py +++ b/modules/prompt_parser.py @@ -2,7 +2,6 @@ from __future__ import annotations import re from collections import namedtuple -from typing import List import lark # a prompt like this: "fantasy landscape with a [mountain:lake:0.25] and [an oak:a christmas tree:0.75][ in foreground::0.6][ in background:0.25] [shoddy:masterful:0.5]" @@ -240,14 +239,14 @@ def get_multicond_prompt_list(prompts: SdConditioning | list[str]): class ComposableScheduledPromptConditioning: def __init__(self, schedules, weight=1.0): - self.schedules: List[ScheduledPromptConditioning] = schedules + self.schedules: list[ScheduledPromptConditioning] = schedules self.weight: float = weight class MulticondLearnedConditioning: def __init__(self, shape, batch): self.shape: tuple = shape # the shape field is needed to send this object to DDIM/PLMS - self.batch: List[List[ComposableScheduledPromptConditioning]] = batch + self.batch: list[list[ComposableScheduledPromptConditioning]] = batch def get_multicond_learned_conditioning(model, prompts, steps, hires_steps=None, use_old_scheduling=False) -> MulticondLearnedConditioning: @@ -278,7 +277,7 @@ class DictWithShape(dict): return self["crossattn"].shape -def reconstruct_cond_batch(c: List[List[ScheduledPromptConditioning]], current_step): +def reconstruct_cond_batch(c: list[list[ScheduledPromptConditioning]], current_step): param = c[0][0].cond is_dict = isinstance(param, dict) diff --git a/modules/script_callbacks.py b/modules/script_callbacks.py index fab23551..9c2a6541 100644 --- a/modules/script_callbacks.py +++ b/modules/script_callbacks.py @@ -1,7 +1,7 @@ import inspect import os from collections import namedtuple -from typing import Optional, Dict, Any +from typing import Optional, Any from fastapi import FastAPI from gradio import Blocks @@ -255,7 +255,7 @@ def image_grid_callback(params: ImageGridLoopParams): report_exception(c, 'image_grid') -def infotext_pasted_callback(infotext: str, params: Dict[str, Any]): +def infotext_pasted_callback(infotext: str, params: dict[str, Any]): for c in callback_map['callbacks_infotext_pasted']: try: c.callback(infotext, params) @@ -446,7 +446,7 @@ def on_infotext_pasted(callback): """register a function to be called before applying an infotext. The callback is called with two arguments: - infotext: str - raw infotext. - - result: Dict[str, any] - parsed infotext parameters. + - result: dict[str, any] - parsed infotext parameters. """ add_callback(callback_map['callbacks_infotext_pasted'], callback) diff --git a/modules/sub_quadratic_attention.py b/modules/sub_quadratic_attention.py index ae4ee4bb..4cb561ef 100644 --- a/modules/sub_quadratic_attention.py +++ b/modules/sub_quadratic_attention.py @@ -15,7 +15,7 @@ import torch from torch import Tensor from torch.utils.checkpoint import checkpoint import math -from typing import Optional, NamedTuple, List +from typing import Optional, NamedTuple def narrow_trunc( @@ -97,7 +97,7 @@ def _query_chunk_attention( ) return summarize_chunk(query, key_chunk, value_chunk) - chunks: List[AttnChunk] = [ + chunks: list[AttnChunk] = [ chunk_scanner(chunk) for chunk in torch.arange(0, k_tokens, kv_chunk_size) ] acc_chunk = AttnChunk(*map(torch.stack, zip(*chunks))) diff --git a/modules/ui.py b/modules/ui.py index f4028475..9a569182 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -1338,7 +1338,6 @@ checkpoint: N/A def setup_ui_api(app): from pydantic import BaseModel, Field - from typing import List class QuicksettingsHint(BaseModel): name: str = Field(title="Name of the quicksettings field") @@ -1347,7 +1346,7 @@ def setup_ui_api(app): def quicksettings_hint(): return [QuicksettingsHint(name=k, label=v.label) for k, v in opts.data_labels.items()] - app.add_api_route("/internal/quicksettings-hint", quicksettings_hint, methods=["GET"], response_model=List[QuicksettingsHint]) + app.add_api_route("/internal/quicksettings-hint", quicksettings_hint, methods=["GET"], response_model=list[QuicksettingsHint]) app.add_api_route("/internal/ping", lambda: {}, methods=["GET"]) From 04b90328c0aa86670cfe5d31612d341e29b5a258 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Tue, 29 Aug 2023 15:38:05 +0300 Subject: [PATCH 035/186] revert SGM noise multiplier change for img2img because it breaks hires fix --- modules/sd_samplers_kdiffusion.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/modules/sd_samplers_kdiffusion.py b/modules/sd_samplers_kdiffusion.py index a8a2735f..72c352a6 100644 --- a/modules/sd_samplers_kdiffusion.py +++ b/modules/sd_samplers_kdiffusion.py @@ -144,13 +144,7 @@ class KDiffusionSampler(sd_samplers_common.Sampler): sigmas = self.get_sigmas(p, steps) sigma_sched = sigmas[steps - t_enc - 1:] - if opts.sgm_noise_multiplier: - p.extra_generation_params["SGM noise multiplier"] = True - noise_multiplier = torch.sqrt(1.0 + sigma_sched[0] ** 2.0) - else: - noise_multiplier = sigma_sched[0] - - xi = x + noise * noise_multiplier + xi = x + noise * sigma_sched[0] if opts.img2img_extra_noise > 0: p.extra_generation_params["Extra noise"] = opts.img2img_extra_noise From ba7d0d225a97a7d79a150d1f649011f54552b3bf Mon Sep 17 00:00:00 2001 From: ibrainventures Date: Tue, 29 Aug 2023 15:31:01 +0200 Subject: [PATCH 036/186] Update style.css --- style.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/style.css b/style.css index 56e2cb4c..34869b89 100644 --- a/style.css +++ b/style.css @@ -621,6 +621,9 @@ table.popup-table .link{ .modalControls { display: flex; + position: absolute; + right: 0px; + left: 0px; gap: 1em; padding: 1em; background-color:rgba(0,0,0,0); From f564d8ed2c5c5644101c5670d2cec15b03ccb51b Mon Sep 17 00:00:00 2001 From: bluelovers Date: Tue, 29 Aug 2023 22:11:18 +0800 Subject: [PATCH 037/186] refactor: refactor function --- modules/ui_extra_networks_user_metadata.py | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/modules/ui_extra_networks_user_metadata.py b/modules/ui_extra_networks_user_metadata.py index 877d0285..588f84c7 100644 --- a/modules/ui_extra_networks_user_metadata.py +++ b/modules/ui_extra_networks_user_metadata.py @@ -10,21 +10,12 @@ from modules import generation_parameters_copypaste, images, sysinfo, errors from modules.paths_internal import models_path -def windows_to_unix_style(path): - return Path(path).as_posix() +def exclude_root_path(root_path, path): + path_object = Path(path) + if path_object.is_relative_to(root_path): + path_object = path_object.relative_to(root_path) - -def exclude_root_path(root_path, path_to_exclude): - try: - relative_path = os.path.relpath(path_to_exclude, root_path) - # 如果路径已经在 root_path 之外,relpath 会返回绝对路径 - # 所以需要检查路径是否在 root_path 之内 - if not relative_path.startswith('..'): - return windows_to_unix_style(relative_path) - except ValueError: - pass - # 如果路径无法相对化,或者位于 root_path 之外,则返回原始路径 - return windows_to_unix_style(path_to_exclude) + return path_object.as_posix() class UserMetadataEditor: From cb2a4f24247c6159740813935a973a6fe1ccc30e Mon Sep 17 00:00:00 2001 From: bluelovers Date: Tue, 29 Aug 2023 22:47:10 +0800 Subject: [PATCH 038/186] chore: change extension time format --- modules/ui_extensions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui_extensions.py b/modules/ui_extensions.py index e0138267..83dcd303 100644 --- a/modules/ui_extensions.py +++ b/modules/ui_extensions.py @@ -177,7 +177,7 @@ def extension_table(): {remote} {ext.branch} {version_link} - {time.asctime(time.gmtime(ext.commit_date))} + {time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(ext.commit_date))} {ext_status} """ From e3939f33394de31594f7c459a7bd352d206f7669 Mon Sep 17 00:00:00 2001 From: catboxanon <122327233+catboxanon@users.noreply.github.com> Date: Tue, 29 Aug 2023 12:19:10 -0400 Subject: [PATCH 039/186] Do not change quicksettings value when value returned is `None` --- modules/ui_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui_settings.py b/modules/ui_settings.py index 6dde4b6a..8ff9c074 100644 --- a/modules/ui_settings.py +++ b/modules/ui_settings.py @@ -87,7 +87,7 @@ class UiSettings: if not opts.same_type(value, opts.data_labels[key].default): return gr.update(visible=True), opts.dumpjson() - if not opts.set(key, value): + if value is None or not opts.set(key, value): return gr.update(value=getattr(opts, key)), opts.dumpjson() opts.save(shared.config_filename) From 7e5fcdaf694343bd66d58af6644e47d5b8f8b879 Mon Sep 17 00:00:00 2001 From: dhwz Date: Tue, 29 Aug 2023 18:49:42 +0200 Subject: [PATCH 040/186] don't print empty lines --- modules/launch_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/launch_utils.py b/modules/launch_utils.py index 9aa0f071..05488fe6 100644 --- a/modules/launch_utils.py +++ b/modules/launch_utils.py @@ -228,7 +228,7 @@ def run_extension_installer(extension_dir): env = os.environ.copy() env['PYTHONPATH'] = f"{os.path.abspath('.')}{os.pathsep}{env.get('PYTHONPATH', '')}" - stdout = run(f'"{python}" "{path_installer}"', errdesc=f"Error running install.py for extension {extension_dir}", custom_env=env) + stdout = run(f'"{python}" "{path_installer}"', errdesc=f"Error running install.py for extension {extension_dir}", custom_env=env).strip() if stdout: print(stdout) except Exception as e: From 549b475be9b5cf1dca0a7bad2b6a6381e50e2b37 Mon Sep 17 00:00:00 2001 From: catboxanon <122327233+catboxanon@users.noreply.github.com> Date: Tue, 29 Aug 2023 14:22:04 -0400 Subject: [PATCH 041/186] Add noisy latent to ExtraNoiseParams for callback --- modules/script_callbacks.py | 7 +++++-- modules/sd_samplers_kdiffusion.py | 2 +- modules/sd_samplers_timesteps.py | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/script_callbacks.py b/modules/script_callbacks.py index fab23551..c99695eb 100644 --- a/modules/script_callbacks.py +++ b/modules/script_callbacks.py @@ -29,12 +29,15 @@ class ImageSaveParams: class ExtraNoiseParams: - def __init__(self, noise, x): + def __init__(self, noise, x, xi): self.noise = noise """Random noise generated by the seed""" self.x = x - """Latent image representation of the image""" + """Latent representation of the image""" + + self.xi = xi + """Noisy latent representation of the image""" class CFGDenoiserParams: diff --git a/modules/sd_samplers_kdiffusion.py b/modules/sd_samplers_kdiffusion.py index 72c352a6..8a8c87e0 100644 --- a/modules/sd_samplers_kdiffusion.py +++ b/modules/sd_samplers_kdiffusion.py @@ -148,7 +148,7 @@ class KDiffusionSampler(sd_samplers_common.Sampler): if opts.img2img_extra_noise > 0: p.extra_generation_params["Extra noise"] = opts.img2img_extra_noise - extra_noise_params = ExtraNoiseParams(noise, x) + extra_noise_params = ExtraNoiseParams(noise, x, xi) extra_noise_callback(extra_noise_params) noise = extra_noise_params.noise xi += noise * opts.img2img_extra_noise diff --git a/modules/sd_samplers_timesteps.py b/modules/sd_samplers_timesteps.py index 7a6cbd46..b17a8f93 100644 --- a/modules/sd_samplers_timesteps.py +++ b/modules/sd_samplers_timesteps.py @@ -107,7 +107,7 @@ class CompVisSampler(sd_samplers_common.Sampler): if opts.img2img_extra_noise > 0: p.extra_generation_params["Extra noise"] = opts.img2img_extra_noise - extra_noise_params = ExtraNoiseParams(noise, x) + extra_noise_params = ExtraNoiseParams(noise, x, xi) extra_noise_callback(extra_noise_params) noise = extra_noise_params.noise xi += noise * opts.img2img_extra_noise * sqrt_alpha_cumprod From 9a4a1aac81df8c29d6dcce1abbf1b58fb7e4fc75 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Wed, 30 Aug 2023 08:05:18 +0300 Subject: [PATCH 042/186] get progressbar to display correctly in extensions tab --- javascript/extensions.js | 2 +- modules/ui_extensions.py | 8 ++++++-- style.css | 5 +++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/javascript/extensions.js b/javascript/extensions.js index 1f7254c5..312131b7 100644 --- a/javascript/extensions.js +++ b/javascript/extensions.js @@ -33,7 +33,7 @@ function extensions_check() { var id = randomId(); - requestProgress(id, gradioApp().getElementById('extensions_installed_top'), null, function() { + requestProgress(id, gradioApp().getElementById('extensions_installed_html'), null, function() { }); diff --git a/modules/ui_extensions.py b/modules/ui_extensions.py index e0138267..67a243c3 100644 --- a/modules/ui_extensions.py +++ b/modules/ui_extensions.py @@ -557,8 +557,12 @@ def create_ui(): msg = '"--disable-extra-extensions" was used, remove it to load all extensions again' html = f'{msg}' - info = gr.HTML(html) - extensions_table = gr.HTML('Loading...') + with gr.Row(): + info = gr.HTML(html) + + with gr.Row(elem_classes="progress-container"): + extensions_table = gr.HTML('Loading...', elem_id="extensions_installed_html") + ui.load(fn=extension_table, inputs=[], outputs=[extensions_table]) apply.click( diff --git a/style.css b/style.css index 92d3030e..fb4e2f1f 100644 --- a/style.css +++ b/style.css @@ -517,6 +517,11 @@ table.popup-table .link{ background: #b4c0cc; border-radius: 3px !important; top: -20px; + width: 100%; +} + +.progress-container{ + position: relative; } [id$=_results].mobile{ From edf3ad5aed9435e2ff3cc0f98895be6056f1f950 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Wed, 30 Aug 2023 08:22:06 +0300 Subject: [PATCH 043/186] go back to single path for filenames in extra networks metadata dialog --- modules/ui_extra_networks_user_metadata.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/modules/ui_extra_networks_user_metadata.py b/modules/ui_extra_networks_user_metadata.py index 588f84c7..ae972fbb 100644 --- a/modules/ui_extra_networks_user_metadata.py +++ b/modules/ui_extra_networks_user_metadata.py @@ -2,22 +2,13 @@ import datetime import html import json import os.path -from pathlib import Path import gradio as gr -from modules import generation_parameters_copypaste, images, sysinfo, errors +from modules import generation_parameters_copypaste, images, sysinfo, errors, ui_extra_networks from modules.paths_internal import models_path -def exclude_root_path(root_path, path): - path_object = Path(path) - if path_object.is_relative_to(root_path): - path_object = path_object.relative_to(root_path) - - return path_object.as_posix() - - class UserMetadataEditor: def __init__(self, ui, tabname, page): @@ -99,6 +90,13 @@ class UserMetadataEditor: return preview + def relative_path(self, path): + for parent_path in self.page.allowed_directories_for_previews(): + if ui_extra_networks.path_is_parent(parent_path, path): + return os.path.relpath(path, parent_path) + + return os.path.basename(path) + def get_metadata_table(self, name): item = self.page.items.get(name, {}) try: @@ -107,8 +105,7 @@ class UserMetadataEditor: stats = os.stat(filename) params = [ - ('Filename: ', os.path.basename(filename)), - ('Path: ', exclude_root_path(models_path, filename)), + ('Filename: ', self.relative_path(filename)), ('File size: ', sysinfo.pretty_bytes(stats.st_size)), ('Hash: ', shorthash), ('Modified: ', datetime.datetime.fromtimestamp(stats.st_mtime).strftime('%Y-%m-%d %H:%M')), From f874b1bcad05d7ea4c3cc28df82904ac7c12e64f Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Wed, 30 Aug 2023 08:54:31 +0300 Subject: [PATCH 044/186] keep order in list of checkpoints when loading model that doesn't have a checksum --- modules/sd_models.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/modules/sd_models.py b/modules/sd_models.py index 547e93c4..930d0bee 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -27,6 +27,24 @@ checkpoint_alisases = checkpoint_aliases # for compatibility with old name checkpoints_loaded = collections.OrderedDict() +def replace_key(d, key, new_key, value): + keys = list(d.keys()) + + d[new_key] = value + + if key not in keys: + return d + + index = keys.index(key) + keys[index] = new_key + + new_d = {k: d[k] for k in keys} + + d.clear() + d.update(new_d) + return d + + class CheckpointInfo: def __init__(self, filename): self.filename = filename @@ -91,9 +109,11 @@ class CheckpointInfo: if self.shorthash not in self.ids: self.ids += [self.shorthash, self.sha256, f'{self.name} [{self.shorthash}]', f'{self.name_for_extra} [{self.shorthash}]'] - checkpoints_list.pop(self.title, None) + old_title = self.title self.title = f'{self.name} [{self.shorthash}]' self.short_title = f'{self.name_for_extra} [{self.shorthash}]' + + replace_key(checkpoints_list, old_title, self.title, self) self.register() return self.shorthash From 28b084ca25387340ba07a5ffed8403d8d289cb70 Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Wed, 30 Aug 2023 15:28:46 +0900 Subject: [PATCH 045/186] extension time format in system time zone --- modules/ui_extensions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui_extensions.py b/modules/ui_extensions.py index 83557d7a..fa831f57 100644 --- a/modules/ui_extensions.py +++ b/modules/ui_extensions.py @@ -177,7 +177,7 @@ def extension_table(): {remote} {ext.branch} {version_link} - {time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(ext.commit_date))} + {datetime.fromtimestamp(ext.commit_date) if ext.commit_date else ""} {ext_status} """ From 67cd4ec0aabb69d1133dfb18543ab080be855323 Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Wed, 30 Aug 2023 15:37:13 +0900 Subject: [PATCH 046/186] lint --- modules/ui_extra_networks_user_metadata.py | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/ui_extra_networks_user_metadata.py b/modules/ui_extra_networks_user_metadata.py index ae972fbb..bfec140c 100644 --- a/modules/ui_extra_networks_user_metadata.py +++ b/modules/ui_extra_networks_user_metadata.py @@ -6,7 +6,6 @@ import os.path import gradio as gr from modules import generation_parameters_copypaste, images, sysinfo, errors, ui_extra_networks -from modules.paths_internal import models_path class UserMetadataEditor: From c985d23c52b541e5a5d0dcf2c3f3a0629cee23f9 Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Wed, 30 Aug 2023 16:18:31 +0900 Subject: [PATCH 047/186] extension update time, convert to system time zone --- modules/ui_extensions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ui_extensions.py b/modules/ui_extensions.py index fa831f57..2e8c1d6d 100644 --- a/modules/ui_extensions.py +++ b/modules/ui_extensions.py @@ -2,7 +2,7 @@ import json import os import threading import time -from datetime import datetime +from datetime import datetime, timezone import git @@ -442,7 +442,7 @@ sort_ordering = [ def get_date(info: dict, key): try: - return datetime.strptime(info.get(key), "%Y-%m-%dT%H:%M:%SZ").strftime("%Y-%m-%d") + return datetime.strptime(info.get(key), "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=timezone.utc).astimezone().strftime("%Y-%m-%d") except (ValueError, TypeError): return '' From ae0b2cc1964486ba847290ad752d9a284b6d63ba Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Wed, 30 Aug 2023 18:22:50 +0300 Subject: [PATCH 048/186] add an option to choose how to combine hires fix and refiner --- modules/processing.py | 16 +++++----------- modules/sd_samplers_common.py | 13 +++++++++++-- modules/shared_options.py | 1 + 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/modules/processing.py b/modules/processing.py index 0138e5ac..f696e925 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -1148,18 +1148,12 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): else: decoded_samples = None - current = shared.sd_model.sd_checkpoint_info - try: - if self.hr_checkpoint_info is not None: - self.sampler = None - sd_models.reload_model_weights(info=self.hr_checkpoint_info) - devices.torch_gc() + with sd_models.SkipWritingToConfig(): + sd_models.reload_model_weights(info=self.hr_checkpoint_info) - return self.sample_hr_pass(samples, decoded_samples, seeds, subseeds, subseed_strength, prompts) - finally: - self.sampler = None - sd_models.reload_model_weights(info=current) - devices.torch_gc() + devices.torch_gc() + + return self.sample_hr_pass(samples, decoded_samples, seeds, subseeds, subseed_strength, prompts) def sample_hr_pass(self, samples, decoded_samples, seeds, subseeds, subseed_strength, prompts): if shared.state.interrupted: diff --git a/modules/sd_samplers_common.py b/modules/sd_samplers_common.py index 60fa161c..6c935a38 100644 --- a/modules/sd_samplers_common.py +++ b/modules/sd_samplers_common.py @@ -164,8 +164,17 @@ def apply_refiner(cfg_denoiser): if refiner_checkpoint_info is None or shared.sd_model.sd_checkpoint_info == refiner_checkpoint_info: return False - if getattr(cfg_denoiser.p, "enable_hr", False) and not cfg_denoiser.p.is_hr_pass: - return False + if getattr(cfg_denoiser.p, "enable_hr", False): + is_second_pass = cfg_denoiser.p.is_hr_pass + + if opts.hires_fix_refiner_pass == "first pass" and is_second_pass: + return False + + if opts.hires_fix_refiner_pass == "second pass" and not is_second_pass: + return False + + if opts.hires_fix_refiner_pass != "second pass": + cfg_denoiser.p.extra_generation_params['Hires refiner'] = opts.hires_fix_refiner_pass cfg_denoiser.p.extra_generation_params['Refiner'] = refiner_checkpoint_info.short_title cfg_denoiser.p.extra_generation_params['Refiner switch at'] = refiner_switch_at diff --git a/modules/shared_options.py b/modules/shared_options.py index 78652ea2..00b273fa 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -146,6 +146,7 @@ options_templates.update(options_section(('sd', "Stable Diffusion"), { "upcast_attn": OptionInfo(False, "Upcast cross attention layer to float32"), "randn_source": OptionInfo("GPU", "Random number generator source.", gr.Radio, {"choices": ["GPU", "CPU", "NV"]}, infotext="RNG").info("changes seeds drastically; use CPU to produce the same picture across different videocard vendors; use NV to produce same picture as on NVidia videocards"), "tiling": OptionInfo(False, "Tiling", infotext='Tiling').info("produce a tileable picture"), + "hires_fix_refiner_pass": OptionInfo("second pass", "Hires fix: which pass to enable refiner for", gr.Radio, {"choices": ["first pass", "second pass", "both passes"]}, infotext="Hires refiner"), })) options_templates.update(options_section(('sdxl', "Stable Diffusion XL"), { From 6adf2b71c2c89f84d4aee1e230276dcd1a3fab62 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Wed, 30 Aug 2023 19:08:04 +0300 Subject: [PATCH 049/186] fix inpainting models in txt2img creating black pictures --- modules/processing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/processing.py b/modules/processing.py index f696e925..e08b6305 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -91,8 +91,8 @@ def create_binary_mask(image): def txt2img_image_conditioning(sd_model, x, width, height): if sd_model.model.conditioning_key in {'hybrid', 'concat'}: # Inpainting models - # The "masked-image" in this case will just be all zeros since the entire image is masked. - image_conditioning = torch.zeros(x.shape[0], 3, height, width, device=x.device) + # The "masked-image" in this case will just be all 0.5 since the entire image is masked. + image_conditioning = torch.ones(x.shape[0], 3, height, width, device=x.device) * 0.5 image_conditioning = images_tensor_to_samples(image_conditioning, approximation_indexes.get(opts.sd_vae_encode_method)) # Add the fake full 1s mask to the first dimension. From 541a3db05ba7241b466f7370533e2bef24dbe9de Mon Sep 17 00:00:00 2001 From: ljleb Date: Wed, 30 Aug 2023 21:38:21 -0400 Subject: [PATCH 050/186] fix generation params regex --- modules/generation_parameters_copypaste.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py index 2ca16055..d39f2eba 100644 --- a/modules/generation_parameters_copypaste.py +++ b/modules/generation_parameters_copypaste.py @@ -9,7 +9,7 @@ from modules.paths import data_path from modules import shared, ui_tempdir, script_callbacks, processing from PIL import Image -re_param_code = r'\s*([\w ]+):\s*("(?:\\"[^,]|\\"|\\|[^\"])+"|[^,]*)(?:,|$)' +re_param_code = r'\s*([\w ]+):\s*("(?:\\.|[^\\"])+"|[^,]*)(?:,|$)' re_param = re.compile(re_param_code) re_imagesize = re.compile(r"^(\d+)x(\d+)$") re_hypernet_hash = re.compile("\(([0-9a-f]+)\)$") From 41196ccbf7552274cf111de24a43ebfa836175a6 Mon Sep 17 00:00:00 2001 From: zixaphir Date: Wed, 30 Aug 2023 20:20:19 -0700 Subject: [PATCH 051/186] account for customizable extra network separators in remove code previous behavior only searched for leading spaces --- javascript/extraNetworks.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 493f31af..eb2b9ebd 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -141,9 +141,12 @@ function setupExtraNetworks() { onUiLoaded(setupExtraNetworks); var re_extranet = /<([^:]+:[^:]+):[\d.]+>(.*)/; -var re_extranet_g = /\s+<([^:]+:[^:]+):[\d.]+>/g; +var re_extranet_str = '<([^:]+:[^:]+):[\\d.]+>'; function tryToRemoveExtraNetworkFromPrompt(textarea, text) { + function reEscape(s) { + return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + } var m = text.match(re_extranet); var replaced = false; var newTextareaText; @@ -151,7 +154,9 @@ function tryToRemoveExtraNetworkFromPrompt(textarea, text) { var extraTextAfterNet = m[2]; var partToSearch = m[1]; var foundAtPosition = -1; - newTextareaText = textarea.value.replaceAll(re_extranet_g, function(found, net, pos) { + var escapedSeparator = reEscape(opts.extra_networks_add_text_separator); + var re = new RegExp(escapedSeparator + re_extranet_str, 'g'); + newTextareaText = textarea.value.replaceAll(re, function(found, net, pos) { m = found.match(re_extranet); if (m[1] == partToSearch) { replaced = true; From 76b1ad7daf35f8667e07ff9cff9ef42b828b1b83 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Wed, 30 Aug 2023 23:07:18 -0600 Subject: [PATCH 052/186] Use default dropdown padding on mobile --- style.css | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/style.css b/style.css index fb4e2f1f..58eb29c1 100644 --- a/style.css +++ b/style.css @@ -83,8 +83,10 @@ div.compact{ white-space: nowrap; } -.gradio-dropdown ul.options li.item { - padding: 0.05em 0; +@media (pointer:fine) { + .gradio-dropdown ul.options li.item { + padding: 0.05em 0; + } } .gradio-dropdown ul.options li.item.selected { From 348c6022f330c6e64a6a0fb40fd2b3e65bf0ce6a Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Fri, 1 Sep 2023 00:55:56 +0900 Subject: [PATCH 053/186] Action to calculate all SD checkpoint hashes --- modules/ui_settings.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/modules/ui_settings.py b/modules/ui_settings.py index 8ff9c074..c6fe3604 100644 --- a/modules/ui_settings.py +++ b/modules/ui_settings.py @@ -5,6 +5,7 @@ from modules.call_queue import wrap_gradio_call from modules.shared import opts from modules.ui_components import FormRow from modules.ui_gradio_extensions import reload_javascript +from concurrent.futures import ThreadPoolExecutor, as_completed def get_value_for_setting(key): @@ -175,6 +176,9 @@ class UiSettings: with gr.Row(): unload_sd_model = gr.Button(value='Unload SD checkpoint to free VRAM', elem_id="sett_unload_sd_model") reload_sd_model = gr.Button(value='Reload the last SD checkpoint back into VRAM', elem_id="sett_reload_sd_model") + with gr.Row(): + calculate_all_checkpoint_hash = gr.Button(value='Calculate hash for all checkpoint', elem_id="calculate_all_checkpoint_hash") + calculate_all_checkpoint_hash_threads = gr.Number(value=1, label="Number of parallel calculations", elem_id="calculate_all_checkpoint_hash_threads", precision=0, minimum=1) with gr.TabItem("Licenses", id="licenses", elem_id="settings_tab_licenses"): gr.HTML(shared.html("licenses.html"), elem_id="licenses") @@ -241,6 +245,21 @@ class UiSettings: outputs=[sysinfo_check_output], ) + def calculate_all_checkpoint_hash_fn(max_thread): + checkpoints_list = sd_models.checkpoints_list.values() + with ThreadPoolExecutor(max_workers=max_thread) as executor: + futures = [executor.submit(checkpoint.calculate_shorthash) for checkpoint in checkpoints_list] + completed = 0 + for _ in as_completed(futures): + completed += 1 + print(f"{completed} / {len(checkpoints_list)} ") + print("Finish calculating hash for all checkpoints") + + calculate_all_checkpoint_hash.click( + fn=calculate_all_checkpoint_hash_fn, + inputs=[calculate_all_checkpoint_hash_threads], + ) + self.interface = settings_interface def add_quicksettings(self): From 5681bf801664aa09fa02ab8b4e73f780d9563440 Mon Sep 17 00:00:00 2001 From: catboxanon <122327233+catboxanon@users.noreply.github.com> Date: Thu, 31 Aug 2023 14:57:16 -0400 Subject: [PATCH 054/186] More accurate check for enabling cuDNN benchmark on 16XX cards --- modules/devices.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/devices.py b/modules/devices.py index c01f0602..63c38eff 100644 --- a/modules/devices.py +++ b/modules/devices.py @@ -60,7 +60,8 @@ def enable_tf32(): # enabling benchmark option seems to enable a range of cards to do fp16 when they otherwise can't # see https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/4407 - if any(torch.cuda.get_device_capability(devid) == (7, 5) for devid in range(0, torch.cuda.device_count())): + device_id = (int(shared.cmd_opts.device_id) if shared.cmd_opts.device_id.isdigit() else 0) or torch.cuda.current_device() + if torch.cuda.get_device_capability(device_id) == (7, 5) and torch.cuda.get_device_name(device_id).startswith("NVIDIA GeForce GTX 16"): torch.backends.cudnn.benchmark = True torch.backends.cuda.matmul.allow_tf32 = True From bd9b3d15e8f631f9475d14a5fd07560c177dc2f3 Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Fri, 1 Sep 2023 04:05:58 +0900 Subject: [PATCH 055/186] fix batch img2img output dir with script --- modules/img2img.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/img2img.py b/modules/img2img.py index c81c7ab9..0c6d1af5 100644 --- a/modules/img2img.py +++ b/modules/img2img.py @@ -114,11 +114,14 @@ def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args, to_scale=Fal else: p.override_settings.pop("sd_model_checkpoint", None) + if output_dir: + p.outpath_samples = output_dir + p.override_settings['save_to_dirs'] = False + proc = modules.scripts.scripts_img2img.run(p, *args) + if proc is None: if output_dir: - p.outpath_samples = output_dir - p.override_settings['save_to_dirs'] = False if p.n_iter > 1 or p.batch_size > 1: p.override_settings['samples_filename_pattern'] = f'{image_path.stem}-[generation_number]' else: From 78c1a74660a2e25b3960beb42e3a6f8419c8b3c3 Mon Sep 17 00:00:00 2001 From: zixaphir Date: Thu, 31 Aug 2023 14:18:35 -0700 Subject: [PATCH 056/186] Account for edge case where user deleted leading separator. --- javascript/extraNetworks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index eb2b9ebd..ca87bead 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -154,7 +154,7 @@ function tryToRemoveExtraNetworkFromPrompt(textarea, text) { var extraTextAfterNet = m[2]; var partToSearch = m[1]; var foundAtPosition = -1; - var escapedSeparator = reEscape(opts.extra_networks_add_text_separator); + var escapedSeparator = `(?:${reEscape(opts.extra_networks_add_text_separator)})?`; var re = new RegExp(escapedSeparator + re_extranet_str, 'g'); newTextareaText = textarea.value.replaceAll(re, function(found, net, pos) { m = found.match(re_extranet); From 737a013377dd698e620f39e405594f7688656af0 Mon Sep 17 00:00:00 2001 From: Beinsezii Date: Thu, 31 Aug 2023 15:03:08 -0700 Subject: [PATCH 057/186] WEBUI.SH Navi 3 torch 2.1.0 rc instead of nightly With the release candidates being out for both torch and vision, webui should default to these over nightly for a more stable experience. Stable release isn't excpected until October 4th: https://dev-discuss.pytorch.org/c/release-announcements/27 --- webui.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/webui.sh b/webui.sh index 3d0f87ee..29a6d311 100755 --- a/webui.sh +++ b/webui.sh @@ -141,9 +141,8 @@ case "$gpu_info" in *"Navi 2"*) export HSA_OVERRIDE_GFX_VERSION=10.3.0 ;; *"Navi 3"*) [[ -z "${TORCH_COMMAND}" ]] && \ - export TORCH_COMMAND="pip install --pre torch torchvision --index-url https://download.pytorch.org/whl/nightly/rocm5.6" - # Navi 3 needs at least 5.5 which is only on the nightly chain, previous versions are no longer online (torch==2.1.0.dev-20230614+rocm5.5 torchvision==0.16.0.dev-20230614+rocm5.5 torchaudio==2.1.0.dev-20230614+rocm5.5) - # so switch to nightly rocm5.6 without explicit versions this time + export TORCH_COMMAND="pip install torch torchvision --index-url https://download.pytorch.org/whl/test/rocm5.6" + # Navi 3 needs at least 5.5 which is only on the torch 2.1.0 release candidates right now ;; *"Renoir"*) export HSA_OVERRIDE_GFX_VERSION=9.0.0 printf "\n%s\n" "${delimiter}" From 317d00b2a6f81eb58e33487abf05a8b84ef01dd0 Mon Sep 17 00:00:00 2001 From: AnyISalIn Date: Fri, 1 Sep 2023 21:45:11 +0800 Subject: [PATCH 058/186] fix: update shared.opts.data when add_option Signed-off-by: AnyISalIn --- modules/options.py | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/options.py b/modules/options.py index 758b1ce5..e75916d2 100644 --- a/modules/options.py +++ b/modules/options.py @@ -210,6 +210,7 @@ class Options: def add_option(self, key, info): self.data_labels[key] = info + self.data[key] = info.default def reorder(self): """reorder settings so that all items related to section always go together""" From bf0b08321688f65905168b6444d6d13b1a1d9d91 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Fri, 1 Sep 2023 16:14:33 -0600 Subject: [PATCH 059/186] Add button to copy prompt to style editor --- modules/ui_prompt_styles.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/ui_prompt_styles.py b/modules/ui_prompt_styles.py index 85eb3a64..46a26573 100644 --- a/modules/ui_prompt_styles.py +++ b/modules/ui_prompt_styles.py @@ -4,6 +4,7 @@ from modules import shared, ui_common, ui_components, styles styles_edit_symbol = '\U0001f58c\uFE0F' # 🖌️ styles_materialize_symbol = '\U0001f4cb' # 📋 +styles_copy_symbol = '\U0001f4dd' # 📝 def select_style(name): @@ -62,6 +63,7 @@ class UiPromptStyles: self.selection = gr.Dropdown(label="Styles", elem_id=f"{tabname}_styles_edit_select", choices=list(shared.prompt_styles.styles), value=[], allow_custom_value=True, info="Styles allow you to add custom text to prompt. Use the {prompt} token in style text, and it will be replaced with user's prompt when applying style. Otherwise, style's text will be added to the end of the prompt.") ui_common.create_refresh_button([self.dropdown, self.selection], shared.prompt_styles.reload, lambda: {"choices": list(shared.prompt_styles.styles)}, f"refresh_{tabname}_styles") self.materialize = ui_components.ToolButton(value=styles_materialize_symbol, elem_id=f"{tabname}_style_apply", tooltip="Apply all selected styles from the style selction dropdown in main UI to the prompt.") + self.copy = ui_components.ToolButton(value=styles_copy_symbol, elem_id=f"{tabname}_style_copy", tooltip="Copy main UI prompt to style.") with gr.Row(): self.prompt = gr.Textbox(label="Prompt", show_label=True, elem_id=f"{tabname}_edit_style_prompt", lines=3) @@ -102,6 +104,13 @@ class UiPromptStyles: outputs=[main_ui_prompt, main_ui_negative_prompt, self.dropdown], show_progress=False, ).then(fn=None, _js="function(){update_"+tabname+"_tokens(); closePopup();}", show_progress=False) + + self.copy.click( + fn=lambda p, n: (p, n), + inputs=[main_ui_prompt, main_ui_negative_prompt], + outputs=[self.prompt, self.neg_prompt], + show_progress=False, + ) ui_common.setup_dialog(button_show=edit_button, dialog=styles_dialog, button_close=self.close) From d7e3ea68b3604aaec6607aad3272e999657e6331 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Fri, 1 Sep 2023 16:24:35 -0600 Subject: [PATCH 060/186] Remove whitespace --- modules/ui_prompt_styles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui_prompt_styles.py b/modules/ui_prompt_styles.py index 46a26573..64d379ef 100644 --- a/modules/ui_prompt_styles.py +++ b/modules/ui_prompt_styles.py @@ -104,7 +104,7 @@ class UiPromptStyles: outputs=[main_ui_prompt, main_ui_negative_prompt, self.dropdown], show_progress=False, ).then(fn=None, _js="function(){update_"+tabname+"_tokens(); closePopup();}", show_progress=False) - + self.copy.click( fn=lambda p, n: (p, n), inputs=[main_ui_prompt, main_ui_negative_prompt], From 3e67017dfb767f18f599f13e62fff9355ea14160 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Fri, 1 Sep 2023 17:01:08 -0600 Subject: [PATCH 061/186] Restore missing tooltips --- modules/processing_scripts/seed.py | 4 ++-- modules/ui.py | 12 ++++++------ modules/ui_extra_networks.py | 2 +- scripts/postprocessing_upscale.py | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/processing_scripts/seed.py b/modules/processing_scripts/seed.py index 6b6ff987..dc9c2da5 100644 --- a/modules/processing_scripts/seed.py +++ b/modules/processing_scripts/seed.py @@ -29,8 +29,8 @@ class ScriptSeed(scripts.ScriptBuiltinUI): else: self.seed = gr.Number(label='Seed', value=-1, elem_id=self.elem_id("seed"), min_width=100, precision=0) - random_seed = ToolButton(ui.random_symbol, elem_id=self.elem_id("random_seed"), label='Random seed') - reuse_seed = ToolButton(ui.reuse_symbol, elem_id=self.elem_id("reuse_seed"), label='Reuse seed') + random_seed = ToolButton(ui.random_symbol, elem_id=self.elem_id("random_seed"), tooltip="Set seed to -1, which will cause a new random number to be used every time") + reuse_seed = ToolButton(ui.reuse_symbol, elem_id=self.elem_id("reuse_seed"), tooltip="Reuse seed from last generation, mostly useful if it was randomized") seed_checkbox = gr.Checkbox(label='Extra', elem_id=self.elem_id("subseed_show"), value=False) diff --git a/modules/ui.py b/modules/ui.py index 579bab98..89173053 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -215,9 +215,9 @@ class Toprow: ) with gr.Row(elem_id=f"{id_part}_tools"): - self.paste = ToolButton(value=paste_symbol, elem_id="paste") - self.clear_prompt_button = ToolButton(value=clear_prompt_symbol, elem_id=f"{id_part}_clear_prompt") - self.restore_progress_button = ToolButton(value=restore_progress_symbol, elem_id=f"{id_part}_restore_progress", visible=False) + self.paste = ToolButton(value=paste_symbol, elem_id="paste", tooltip="Read generation parameters from prompt or last generation if prompt is empty into user interface.") + self.clear_prompt_button = ToolButton(value=clear_prompt_symbol, elem_id=f"{id_part}_clear_prompt", tooltip="Clear prompt") + self.restore_progress_button = ToolButton(value=restore_progress_symbol, elem_id=f"{id_part}_restore_progress", visible=False, tooltip="Restore progress") self.token_counter = gr.HTML(value="0/75", elem_id=f"{id_part}_token_counter", elem_classes=["token-counter"]) self.token_button = gr.Button(visible=False, elem_id=f"{id_part}_token_button") @@ -348,7 +348,7 @@ def create_ui(): height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="txt2img_height") with gr.Column(elem_id="txt2img_dimensions_row", scale=1, elem_classes="dimensions-tools"): - res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="txt2img_res_switch_btn", label="Switch dims") + res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="txt2img_res_switch_btn", tooltip="Switch width/height") if opts.dimensions_and_batch_together: with gr.Column(elem_id="txt2img_column_batch"): @@ -661,8 +661,8 @@ def create_ui(): width = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="img2img_width") height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="img2img_height") with gr.Column(elem_id="img2img_dimensions_row", scale=1, elem_classes="dimensions-tools"): - res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="img2img_res_switch_btn") - detect_image_size_btn = ToolButton(value=detect_image_size_symbol, elem_id="img2img_detect_image_size_btn") + res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="img2img_res_switch_btn", tooltip="Switch width/height") + detect_image_size_btn = ToolButton(value=detect_image_size_symbol, elem_id="img2img_detect_image_size_btn", tooltip="Auto detect size from img2img") with gr.Tab(label="Resize by", elem_id="img2img_tab_resize_by") as tab_scale_by: scale_by = gr.Slider(minimum=0.05, maximum=4.0, step=0.05, label="Scale", value=1.0, elem_id="img2img_scale") diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 063bd7b8..21eed6a1 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -374,7 +374,7 @@ def create_ui(interface: gr.Blocks, unrelated_tabs, tabname): edit_search = gr.Textbox('', show_label=False, elem_id=tabname+"_extra_search", elem_classes="search", placeholder="Search...", visible=False, interactive=True) dropdown_sort = gr.Dropdown(choices=['Default Sort', 'Date Created', 'Date Modified', 'Name'], value='Default Sort', elem_id=tabname+"_extra_sort", elem_classes="sort", multiselect=False, visible=False, show_label=False, interactive=True, label=tabname+"_extra_sort_order") - button_sortorder = ToolButton(switch_values_symbol, elem_id=tabname+"_extra_sortorder", elem_classes="sortorder", visible=False) + button_sortorder = ToolButton(switch_values_symbol, elem_id=tabname+"_extra_sortorder", elem_classes="sortorder", visible=False, tooltip="Invert sort order") button_refresh = gr.Button('Refresh', elem_id=tabname+"_extra_refresh", visible=False) checkbox_show_dirs = gr.Checkbox(True, label='Show dirs', elem_id=tabname+"_extra_show_dirs", elem_classes="show-dirs", visible=False) diff --git a/scripts/postprocessing_upscale.py b/scripts/postprocessing_upscale.py index edb70ac0..eb42a29e 100644 --- a/scripts/postprocessing_upscale.py +++ b/scripts/postprocessing_upscale.py @@ -29,7 +29,7 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): upscaling_resize_w = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="extras_upscaling_resize_w") upscaling_resize_h = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="extras_upscaling_resize_h") with gr.Column(elem_id="upscaling_dimensions_row", scale=1, elem_classes="dimensions-tools"): - upscaling_res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="upscaling_res_switch_btn") + upscaling_res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="upscaling_res_switch_btn", tooltip="Switch width/height") upscaling_crop = gr.Checkbox(label='Crop to fit', value=True, elem_id="extras_upscaling_crop") with FormRow(): From ba05e327896898eb73caec3ed710fe45d1e38732 Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Sat, 2 Sep 2023 14:12:59 +0900 Subject: [PATCH 062/186] update cmd arg description --- modules/cmd_args.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/cmd_args.py b/modules/cmd_args.py index aab62286..a77c7e77 100644 --- a/modules/cmd_args.py +++ b/modules/cmd_args.py @@ -112,8 +112,8 @@ parser.add_argument("--skip-version-check", action='store_true', help="Do not ch parser.add_argument("--no-hashing", action='store_true', help="disable sha256 hashing of checkpoints to help loading performance", default=False) parser.add_argument("--no-download-sd-model", action='store_true', help="don't download SD1.5 model even if no model is found in --ckpt-dir", default=False) parser.add_argument('--subpath', type=str, help='customize the subpath for gradio, use with reverse proxy') -parser.add_argument('--add-stop-route', action='store_true', help='add /_stop route to stop server') +parser.add_argument('--add-stop-route', action='store_true', help='does not do anything') parser.add_argument('--api-server-stop', action='store_true', help='enable server stop/restart/kill via api') parser.add_argument('--timeout-keep-alive', type=int, default=30, help='set timeout_keep_alive for uvicorn') parser.add_argument("--disable-all-extensions", action='store_true', help="prevent all extensions from running regardless of any other settings", default=False) -parser.add_argument("--disable-extra-extensions", action='store_true', help=" prevent all extensions except built-in from running regardless of any other settings", default=False) +parser.add_argument("--disable-extra-extensions", action='store_true', help="prevent all extensions except built-in from running regardless of any other settings", default=False) From 061a4a295dd53f089ac460bc2c1585491ef26f24 Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Sat, 2 Sep 2023 18:11:08 +0900 Subject: [PATCH 063/186] Update bug_report.yml --- .github/ISSUE_TEMPLATE/bug_report.yml | 96 +++++++++++++++++++++------ 1 file changed, 77 insertions(+), 19 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index cf6a2be8..70c2e160 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -4,22 +4,45 @@ title: "[Bug]: " labels: ["bug-report"] body: - - type: checkboxes - attributes: - label: Is there an existing issue for this? - description: Please search to see if an issue already exists for the bug you encountered, and that it hasn't been fixed in a recent build/commit. - options: - - label: I have searched the existing issues and checked the recent builds/commits - required: true - type: markdown attributes: value: | - *Please fill this form with as much information as possible, don't forget to fill "What OS..." and "What browsers" and *provide screenshots if possible** + > The title the bug report should be short and descriptive + > Use relevant keywords for searchability + > Don't leave it blank but also don't put the entire error log in it + - type: checkboxes + attributes: + label: Checklist + description: | + Please perform basic debugging to see if extensions or configuration is the cause of the issue. + Basic debug procedure +  1. Disable all third-party extensions - check if extension is the cause +  2. Update extensions and webui - sometimes thing just need to be updated +  3. Backup and remove your config.json and ui-config.json - check if the issue is caused bed configuration +  4. delete venv with third-party extensions disable - sometimes extensions might cause wrong libraries to be installed +  5. try a fresh installation webui in a different directory - see if a clean installation solves the issue + Before making a issue report please check that the issue hasn't been reported recently + options: + - label: The issue exist after disabling all extensions + - label: The issue exist on a clean installation of webui + - label: The issue is caused by an extension but it is caused by a bug in webui + - label: The issue exist in current version of webui + - label: The issue haven't been reported before recently + - label: The issue has been reported before but hasn't been fixed yet + - type: markdown + attributes: + value: | + > Please fill this form with as much information as possible, don't forget to "Upload Sysinfo" and "What browsers" and provide screenshots if possible - type: textarea id: what-did attributes: label: What happened? description: Tell us what happened in a very clear and simple way + placeholder: | + I tried to use txt2img with XYZ grid with Sampler DPM++ SDE,DPM++ 2M SDE + it should generate a grid of 2 images but I only got 1 + + add screenshot or screen recording if necessary validations: required: true - type: textarea @@ -27,10 +50,10 @@ body: attributes: label: Steps to reproduce the problem description: Please provide us with precise step by step instructions on how to reproduce the bug - value: | - 1. Go to .... - 2. Press .... - 3. ... + placeholder: | + 1. Go to txt2img tab Select XYZ grid + 2. Set axis type Sampler and select DPM++ 2M SDE, DPM++ 3M SDE + 3. Set Sampling steps to 1 and click Generate button validations: required: true - type: textarea @@ -38,13 +61,9 @@ body: attributes: label: What should have happened? description: Tell us what you think the normal behavior should be - validations: - required: true - - type: textarea - id: sysinfo - attributes: - label: Sysinfo - description: System info file, generated by WebUI. You can generate it in settings, on the Sysinfo page. Drag the file into the field to upload it. If you submit your report without including the sysinfo file, the report will be closed. If needed, review the report to make sure it includes no personal information you don't want to share. If you can't start WebUI, you can use --dump-sysinfo commandline argument to generate the file. + placeholder: | + It should generate a grid of 2 images + this was working in webui version 1.x.x validations: required: true - type: dropdown @@ -58,13 +77,47 @@ body: - Brave - Apple Safari - Microsoft Edge + - Android + - iOS - Other + - type: textarea + id: sysinfo + attributes: + label: Sysinfo + description: System info file, generated by WebUI. You can generate it in settings, on the Sysinfo page. Drag the file into the field to upload it. If you submit your report without including the sysinfo file, the report will be closed. If needed, review the report to make sure it includes no personal information you don't want to share. If you can't start WebUI, you can use --dump-sysinfo commandline argument to generate the file. + placeholder: | + Upload the Sysinfo as a attached file + Don't paste it in as text + validations: + required: true - type: textarea id: logs attributes: label: Console logs description: Please provide **full** cmd/terminal logs from the moment you started UI to the end of it, after your bug happened. If it's very long, provide a link to pastebin or similar service. render: Shell + placeholder: | + generating image for xyz plot: UnboundLocalError + Traceback (most recent call last): + File "B:\GitHub\stable-diffusion-webui\scripts\xyz_grid.py", line 698, in cell + res = process_images(pc) + File "B:\GitHub\stable-diffusion-webui\modules\processing.py", line 732, in process_images + res = process_images_inner(p) + File "B:\GitHub\stable-diffusion-webui\modules\processing.py", line 867, in process_images_inner + samples_ddim = p.sample(conditioning=p.c, unconditional_conditioning=p.uc, seeds=p.seeds, subseeds=p.subseeds, subseed_strength=p.subseed_strength, prompts=p.prompts) + File "B:\GitHub\stable-diffusion-webui\modules\processing.py", line 1140, in sample + samples = self.sampler.sample(self, x, conditioning, unconditional_conditioning, image_conditioning=self.txt2img_image_conditioning(x)) + File "B:\GitHub\stable-diffusion-webui\modules\sd_samplers_kdiffusion.py", line 235, in sample + samples = self.launch_sampling(steps, lambda: self.func(self.model_wrap_cfg, x, extra_args=self.sampler_extra_args, disable=False, callback=self.callback_state, **extra_params_kwargs)) + File "B:\GitHub\stable-diffusion-webui\modules\sd_samplers_common.py", line 261, in launch_sampling + return func() + File "B:\GitHub\stable-diffusion-webui\modules\sd_samplers_kdiffusion.py", line 235, in + samples = self.launch_sampling(steps, lambda: self.func(self.model_wrap_cfg, x, extra_args=self.sampler_extra_args, disable=False, callback=self.callback_state, **extra_params_kwargs)) + File "B:\GitHub\stable-diffusion-webui\venv\lib\site-packages\torch\utils\_contextlib.py", line 115, in decorate_context + return func(*args, **kwargs) + File "B:\GitHub\stable-diffusion-webui\repositories\k-diffusion\k_diffusion\sampling.py", line 651, in sample_dpmpp_2m_sde + h_last = h + UnboundLocalError: local variable 'h' referenced before assignment validations: required: true - type: textarea @@ -72,3 +125,8 @@ body: attributes: label: Additional information description: Please provide us with any relevant additional info or context. + placeholder: | + Examples + I have updated the GPU driver recently + I suspect the issue is caused by XXXXX + I am using a VPN From a51721cb09aa8dc68beb08cf8f0a2602b41d052c Mon Sep 17 00:00:00 2001 From: uservar <63248296+uservar@users.noreply.github.com> Date: Sat, 2 Sep 2023 11:35:30 +0000 Subject: [PATCH 064/186] Fix bug with sigma min/max overrides. --- modules/shared_options.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/shared_options.py b/modules/shared_options.py index 00b273fa..73588a22 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -305,8 +305,8 @@ options_templates.update(options_section(('sampler-params', "Sampler parameters" 's_tmax': OptionInfo(0.0, "sigma tmax", gr.Slider, {"minimum": 0.0, "maximum": 999.0, "step": 0.01}, infotext='Sigma tmax').info("0 = inf; end value of the sigma range; only applies to Euler, Heun, and DPM2"), 's_noise': OptionInfo(1.0, "sigma noise", gr.Slider, {"minimum": 0.0, "maximum": 1.1, "step": 0.001}, infotext='Sigma noise').info('amount of additional noise to counteract loss of detail during sampling'), 'k_sched_type': OptionInfo("Automatic", "Scheduler type", gr.Dropdown, {"choices": ["Automatic", "karras", "exponential", "polyexponential"]}, infotext='Schedule type').info("lets you override the noise schedule for k-diffusion samplers; choosing Automatic disables the three parameters below"), - 'sigma_min': OptionInfo(0.0, "sigma min", gr.Number, infotext='Schedule max sigma').info("0 = default (~0.03); minimum noise strength for k-diffusion noise scheduler"), - 'sigma_max': OptionInfo(0.0, "sigma max", gr.Number, infotext='Schedule min sigma').info("0 = default (~14.6); maximum noise strength for k-diffusion noise scheduler"), + 'sigma_min': OptionInfo(0.0, "sigma min", gr.Number, infotext='Schedule min sigma').info("0 = default (~0.03); minimum noise strength for k-diffusion noise scheduler"), + 'sigma_max': OptionInfo(0.0, "sigma max", gr.Number, infotext='Schedule max sigma').info("0 = default (~14.6); maximum noise strength for k-diffusion noise scheduler"), 'rho': OptionInfo(0.0, "rho", gr.Number, infotext='Schedule rho').info("0 = default (7 for karras, 1 for polyexponential); higher values result in a steeper noise schedule (decreases faster)"), 'eta_noise_seed_delta': OptionInfo(0, "Eta noise seed delta", gr.Number, {"precision": 0}, infotext='ENSD').info("ENSD; does not improve anything, just produces different results for ancestral samplers - only useful for reproducing images"), 'always_discard_next_to_last_sigma': OptionInfo(False, "Always discard next-to-last sigma", infotext='Discard penultimate sigma').link("PR", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/6044"), From f593cbfec417a3ea40589fe64e7f8806c9a81e5a Mon Sep 17 00:00:00 2001 From: AngelBottomless Date: Sun, 3 Sep 2023 21:07:36 +0900 Subject: [PATCH 065/186] fallback if exif data was invalid --- modules/images.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/images.py b/modules/images.py index eb644733..8512a46e 100644 --- a/modules/images.py +++ b/modules/images.py @@ -718,7 +718,12 @@ def read_info_from_image(image: Image.Image) -> tuple[str | None, dict]: geninfo = items.pop('parameters', None) if "exif" in items: - exif = piexif.load(items["exif"]) + exif_data = items["exif"] + try: + exif = piexif.load(exif_data) + except OSError: + # memory / exif was not valid so piexif tried to read from a file + exif = None exif_comment = (exif or {}).get("Exif", {}).get(piexif.ExifIFD.UserComment, b'') try: exif_comment = piexif.helper.UserComment.load(exif_comment) From 8f3b02f09535f55d3673aa9ea589396b8614f799 Mon Sep 17 00:00:00 2001 From: JaredTherriault Date: Sun, 3 Sep 2023 13:31:42 -0700 Subject: [PATCH 066/186] Revert "Offloading custom work" This reverts commit f3d1631aab82d559294126a9230c979ef4c4e1d6. This work has been offloaded now into an extension called Prompt Control. --- modules/custom_statics.py | 29 ---------------------- modules/generation_parameters_copypaste.py | 8 ------ webui-user.bat | 4 +-- 3 files changed, 2 insertions(+), 39 deletions(-) delete mode 100644 modules/custom_statics.py diff --git a/modules/custom_statics.py b/modules/custom_statics.py deleted file mode 100644 index 207bd5fb..00000000 --- a/modules/custom_statics.py +++ /dev/null @@ -1,29 +0,0 @@ -import os -import gc -import re - -import modules.paths as paths - -class CustomStatics: - - @staticmethod - # loads a file with strings structured as below, on each line with a : between the search and replace strings, into a list - # search0:replace0 - # search string:replace string - # - # Then replaces all occurrences of the list's search strings with the list's replace strings in one go - def mass_replace_strings(input_string): - with open(os.path.join(paths.data_path, "custom_statics/Replacements.txt"), "r", encoding="utf8") as file: - replacements = file.readlines() - - replacement_dict = {} - for line in replacements: - search, replace = line.strip().split(":") - replacement_dict[search] = replace - - def replace(match_text): - return replacement_dict[match_text.group(0)] - - return re.sub('|'.join(r'\b%s\b' % re.escape(s) for s in replacement_dict.keys()), replace, str(input_string)) - - return str(geninfo) \ No newline at end of file diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py index bd7b0018..a3448be9 100644 --- a/modules/generation_parameters_copypaste.py +++ b/modules/generation_parameters_copypaste.py @@ -370,14 +370,6 @@ def connect_paste(button, paste_fields, input_comp, override_settings_component, prompt = file.read() params = parse_generation_parameters(prompt) - - # This sanitizes unsavory prompt words when copying from another image - # for my own sanity. This is not intended to be contributed to the main repo, - # it's just so I don't have to see anything I'm not interested in when batch - # reproducing images from civit.ai or elsewhere when working on loras - # todo: make this work with the callback instead of forcing it here, this can be an extension when I feel like putting it together :D - from modules import custom_statics - params = custom_statics.CustomStatics.mass_replace_strings(params) script_callbacks.infotext_pasted_callback(prompt, params) res = [] diff --git a/webui-user.bat b/webui-user.bat index 1ba2116d..e5a257be 100644 --- a/webui-user.bat +++ b/webui-user.bat @@ -1,8 +1,8 @@ @echo off -set TEMP=G:\SD-temp + set PYTHON= set GIT= set VENV_DIR= -set COMMANDLINE_ARGS= --xformers +set COMMANDLINE_ARGS= call webui.bat From 022639a145751d61db1c144e5e657aa6481e2bc0 Mon Sep 17 00:00:00 2001 From: JaredTherriault Date: Mon, 4 Sep 2023 17:37:48 -0700 Subject: [PATCH 067/186] Load comments from gif images to gather geninfo from gif outputs --- modules/images.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/images.py b/modules/images.py index eb644733..8c6e862f 100644 --- a/modules/images.py +++ b/modules/images.py @@ -728,6 +728,8 @@ def read_info_from_image(image: Image.Image) -> tuple[str | None, dict]: if exif_comment: items['exif comment'] = exif_comment geninfo = exif_comment + elif "comment" in items: # for gif + geninfo = items["comment"].decode('utf8', errors="ignore") for field in IGNORED_INFO_KEYS: items.pop(field, None) From 0c1c9e74cda6637ab1305b4c294b7719eb141927 Mon Sep 17 00:00:00 2001 From: liubo0902 <38622806+liubo0902@users.noreply.github.com> Date: Tue, 5 Sep 2023 15:06:47 +0800 Subject: [PATCH 068/186] Update localization.py --- modules/localization.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/modules/localization.py b/modules/localization.py index c1320288..3392b055 100644 --- a/modules/localization.py +++ b/modules/localization.py @@ -1,7 +1,7 @@ import json import os -from modules import errors, scripts +from modules import errors localizations = {} @@ -14,21 +14,27 @@ def list_localizations(dirname): if ext.lower() != ".json": continue - localizations[fn] = os.path.join(dirname, file) + fn = fn.replace(" ", "").replace("(", "_").replace(")","") + localizations[fn] = [os.path.join(dirname, file)] + from modules import scripts for file in scripts.list_scripts("localizations", ".json"): fn, ext = os.path.splitext(file.filename) - localizations[fn] = file.path + fn = fn.replace(" ", "").replace("(", "_").replace(")","") + if fn not in localizations: + localizations[fn] = [] + localizations[fn].append(file.path) def localization_js(current_localization_name: str) -> str: - fn = localizations.get(current_localization_name, None) + fns = localizations.get(current_localization_name, None) data = {} - if fn is not None: - try: - with open(fn, "r", encoding="utf8") as file: - data = json.load(file) - except Exception: - errors.report(f"Error loading localization from {fn}", exc_info=True) + if fns is not None: + for fn in fns: + try: + with open(fn, "r", encoding="utf8") as file: + data.update(json.load(file)) + except Exception: + errors.report(f"Error loading localization from {fn}", exc_info=True) return f"window.localization = {json.dumps(data)}" From ff7027ffc075ae44ddaa56014f900d392cf53ca8 Mon Sep 17 00:00:00 2001 From: liubo0902 <38622806+liubo0902@users.noreply.github.com> Date: Tue, 5 Sep 2023 15:08:59 +0800 Subject: [PATCH 069/186] Update localization.py --- modules/localization.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/localization.py b/modules/localization.py index 3392b055..262d49ee 100644 --- a/modules/localization.py +++ b/modules/localization.py @@ -1,7 +1,7 @@ import json import os -from modules import errors +from modules import errors, scripts localizations = {} @@ -17,7 +17,6 @@ def list_localizations(dirname): fn = fn.replace(" ", "").replace("(", "_").replace(")","") localizations[fn] = [os.path.join(dirname, file)] - from modules import scripts for file in scripts.list_scripts("localizations", ".json"): fn, ext = os.path.splitext(file.filename) fn = fn.replace(" ", "").replace("(", "_").replace(")","") From de5bb4ca88df44362c9263de7334b30156540e21 Mon Sep 17 00:00:00 2001 From: AngelBottomless Date: Tue, 5 Sep 2023 22:35:17 +0900 Subject: [PATCH 070/186] Fix #13080 - Hypernetwork/TI preview generation Fixes sampler name reference Same patch will be done for TI. --- modules/hypernetworks/hypernetwork.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/hypernetworks/hypernetwork.py b/modules/hypernetworks/hypernetwork.py index 70f1cbd2..65b63f2f 100644 --- a/modules/hypernetworks/hypernetwork.py +++ b/modules/hypernetworks/hypernetwork.py @@ -468,7 +468,7 @@ def create_hypernetwork(name, enable_sizes, overwrite_old, layer_structure=None, shared.reload_hypernetworks() -def train_hypernetwork(id_task, hypernetwork_name, learn_rate, batch_size, gradient_step, data_root, log_directory, training_width, training_height, varsize, steps, clip_grad_mode, clip_grad_value, shuffle_tags, tag_drop_out, latent_sampling_method, use_weight, create_image_every, save_hypernetwork_every, template_filename, preview_from_txt2img, preview_prompt, preview_negative_prompt, preview_steps, preview_sampler_index, preview_cfg_scale, preview_seed, preview_width, preview_height): +def train_hypernetwork(id_task, hypernetwork_name:str, learn_rate:float, batch_size:int, gradient_step:int, data_root:str, log_directory:str, training_width:int, training_height:int, varsize:bool, steps:int, clip_grad_mode:str, clip_grad_value:float, shuffle_tags:bool, tag_drop_out:bool, latent_sampling_method:str, use_weight:bool, create_image_every:int, save_hypernetwork_every:int, template_filename:str, preview_from_txt2img:bool, preview_prompt:str, preview_negative_prompt:str, preview_steps:int, preview_sampler_name:str, preview_cfg_scale:float, preview_seed:int, preview_width:int, preview_height:int): from modules import images, processing save_hypernetwork_every = save_hypernetwork_every or 0 @@ -698,7 +698,7 @@ def train_hypernetwork(id_task, hypernetwork_name, learn_rate, batch_size, gradi p.prompt = preview_prompt p.negative_prompt = preview_negative_prompt p.steps = preview_steps - p.sampler_name = sd_samplers.samplers[preview_sampler_index].name + p.sampler_name = sd_samplers.samplers_map[preview_sampler_name.lower()] p.cfg_scale = preview_cfg_scale p.seed = preview_seed p.width = preview_width From 47033afa5c08e72b622348b0bcfd71fd1a66e2cb Mon Sep 17 00:00:00 2001 From: AngelBottomless Date: Tue, 5 Sep 2023 22:38:02 +0900 Subject: [PATCH 071/186] Fix preview for textual inversion training --- 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 aa79dc09..401a0a2a 100644 --- a/modules/textual_inversion/textual_inversion.py +++ b/modules/textual_inversion/textual_inversion.py @@ -386,7 +386,7 @@ def validate_train_inputs(model_name, learn_rate, batch_size, gradient_step, dat assert log_directory, "Log directory is empty" -def train_embedding(id_task, embedding_name, learn_rate, batch_size, gradient_step, data_root, log_directory, training_width, training_height, varsize, steps, clip_grad_mode, clip_grad_value, shuffle_tags, tag_drop_out, latent_sampling_method, use_weight, create_image_every, save_embedding_every, template_filename, save_image_with_stored_embedding, preview_from_txt2img, preview_prompt, preview_negative_prompt, preview_steps, preview_sampler_index, preview_cfg_scale, preview_seed, preview_width, preview_height): +def train_embedding(id_task, embedding_name, learn_rate, batch_size, gradient_step, data_root, log_directory, training_width, training_height, varsize, steps, clip_grad_mode, clip_grad_value, shuffle_tags, tag_drop_out, latent_sampling_method, use_weight, create_image_every, save_embedding_every, template_filename, save_image_with_stored_embedding, preview_from_txt2img, preview_prompt, preview_negative_prompt, preview_steps, preview_sampler_name, preview_cfg_scale, preview_seed, preview_width, preview_height): from modules import processing save_embedding_every = save_embedding_every or 0 @@ -590,7 +590,7 @@ def train_embedding(id_task, embedding_name, learn_rate, batch_size, gradient_st p.prompt = preview_prompt p.negative_prompt = preview_negative_prompt p.steps = preview_steps - p.sampler_name = sd_samplers.samplers[preview_sampler_index].name + p.sampler_name = sd_samplers.samplers_map[preview_sampler_name.lower()] p.cfg_scale = preview_cfg_scale p.seed = preview_seed p.width = preview_width From 25189b29afb04ff0c203e7f666c8acaead09dcde Mon Sep 17 00:00:00 2001 From: catboxanon <122327233+catboxanon@users.noreply.github.com> Date: Tue, 5 Sep 2023 22:13:36 -0400 Subject: [PATCH 072/186] Grammar fixes --- .github/ISSUE_TEMPLATE/bug_report.yml | 62 +++++++++++++-------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 70c2e160..a53db9f0 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,5 +1,5 @@ name: Bug Report -description: You think somethings is broken in the UI +description: You think something is broken in the UI title: "[Bug]: " labels: ["bug-report"] @@ -7,9 +7,9 @@ body: - type: markdown attributes: value: | - > The title the bug report should be short and descriptive - > Use relevant keywords for searchability - > Don't leave it blank but also don't put the entire error log in it + > The title of the bug report should be short and descriptive. + > Use relevant keywords for searchability. + > Do not leave it blank, but also do not put an entire error log in it. - type: checkboxes attributes: label: Checklist @@ -17,32 +17,32 @@ body: Please perform basic debugging to see if extensions or configuration is the cause of the issue. Basic debug procedure  1. Disable all third-party extensions - check if extension is the cause -  2. Update extensions and webui - sometimes thing just need to be updated -  3. Backup and remove your config.json and ui-config.json - check if the issue is caused bed configuration -  4. delete venv with third-party extensions disable - sometimes extensions might cause wrong libraries to be installed -  5. try a fresh installation webui in a different directory - see if a clean installation solves the issue - Before making a issue report please check that the issue hasn't been reported recently +  2. Update extensions and webui - sometimes things just need to be updated +  3. Backup and remove your config.json and ui-config.json - check if the issue is caused by bad configuration +  4. Delete venv with third-party extensions disabled - sometimes extensions might cause wrong libraries to be installed +  5. Try a fresh installation webui in a different directory - see if a clean installation solves the issue + Before making a issue report please, check that the issue hasn't been reported recently. options: - - label: The issue exist after disabling all extensions - - label: The issue exist on a clean installation of webui - - label: The issue is caused by an extension but it is caused by a bug in webui - - label: The issue exist in current version of webui - - label: The issue haven't been reported before recently - - label: The issue has been reported before but hasn't been fixed yet + - label: The issue exists after disabling all extensions + - label: The issue exists on a clean installation of webui + - label: The issue is caused by an extension, but I believe it is caused by a bug in the webui + - label: The issue exists in the current version of the webui + - label: The issue has not been reported before recently + - label: The issue has been reported before but has not been fixed yet - type: markdown attributes: value: | - > Please fill this form with as much information as possible, don't forget to "Upload Sysinfo" and "What browsers" and provide screenshots if possible + > Please fill this form with as much information as possible. Don't forget to "Upload Sysinfo" and "What browsers" and provide screenshots if possible - type: textarea id: what-did attributes: label: What happened? description: Tell us what happened in a very clear and simple way placeholder: | - I tried to use txt2img with XYZ grid with Sampler DPM++ SDE,DPM++ 2M SDE - it should generate a grid of 2 images but I only got 1 + I tried to use txt2img with the XYZ grid script, with DPM++ SDE, DPM++ 2M SDE samplers. + It should generate a grid of 2 images but I only got 1. - add screenshot or screen recording if necessary + (add screenshot or screen recording if necessary) validations: required: true - type: textarea @@ -51,9 +51,9 @@ body: label: Steps to reproduce the problem description: Please provide us with precise step by step instructions on how to reproduce the bug placeholder: | - 1. Go to txt2img tab Select XYZ grid - 2. Set axis type Sampler and select DPM++ 2M SDE, DPM++ 3M SDE - 3. Set Sampling steps to 1 and click Generate button + 1. Go to txt2img tab, select XYZ grid script + 2. Set axis type to `Sampler`, and select DPM++ 2M SDE, DPM++ 3M SDE + 3. Set `Sampling steps` to 1, click generate button validations: required: true - type: textarea @@ -62,8 +62,8 @@ body: label: What should have happened? description: Tell us what you think the normal behavior should be placeholder: | - It should generate a grid of 2 images - this was working in webui version 1.x.x + It should generate a grid of 2 images. + This was working in webui version 1.x.x validations: required: true - type: dropdown @@ -86,15 +86,15 @@ body: label: Sysinfo description: System info file, generated by WebUI. You can generate it in settings, on the Sysinfo page. Drag the file into the field to upload it. If you submit your report without including the sysinfo file, the report will be closed. If needed, review the report to make sure it includes no personal information you don't want to share. If you can't start WebUI, you can use --dump-sysinfo commandline argument to generate the file. placeholder: | - Upload the Sysinfo as a attached file - Don't paste it in as text + Upload the Sysinfo as a attached file. + Do not paste it in as text. validations: required: true - type: textarea id: logs attributes: label: Console logs - description: Please provide **full** cmd/terminal logs from the moment you started UI to the end of it, after your bug happened. If it's very long, provide a link to pastebin or similar service. + description: Please provide **full** cmd/terminal logs from the moment you started UI to the end of it, after the bug occured. If it's very long, provide a link to pastebin or similar service. render: Shell placeholder: | generating image for xyz plot: UnboundLocalError @@ -126,7 +126,7 @@ body: label: Additional information description: Please provide us with any relevant additional info or context. placeholder: | - Examples - I have updated the GPU driver recently - I suspect the issue is caused by XXXXX - I am using a VPN + Examples: + I have updated the GPU driver recently. + I suspect the issue is caused by XXXXX. + I am using a VPN. From 35d1c94549cf75e7e312372d90fee0acc2806426 Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Wed, 6 Sep 2023 20:24:26 +0900 Subject: [PATCH 073/186] save_images_add_number_suffix --- modules/images.py | 10 +++++++++- modules/shared_options.py | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/modules/images.py b/modules/images.py index eb644733..10dcd9ab 100644 --- a/modules/images.py +++ b/modules/images.py @@ -661,7 +661,15 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='png', i save_image_with_geninfo(image_to_save, info, temp_file_path, extension, existing_pnginfo=params.pnginfo, pnginfo_section_name=pnginfo_section_name) - os.replace(temp_file_path, filename_without_extension + extension) + full_file_name = filename_without_extension + extension + if shared.opts.save_images_add_number_suffix and os.path.exists(full_file_name): + count = 1 + while True: + full_file_name = f"{filename_without_extension}_{count}{extension}" + if not os.path.exists(full_file_name): + break + count += 1 + os.replace(temp_file_path, full_file_name) fullfn_without_extension, extension = os.path.splitext(params.filename) if hasattr(os, 'statvfs'): diff --git a/modules/shared_options.py b/modules/shared_options.py index 00b273fa..2f4caa9d 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -26,7 +26,7 @@ options_templates.update(options_section(('saving-images', "Saving images/grids" "samples_format": OptionInfo('png', 'File format for images'), "samples_filename_pattern": OptionInfo("", "Images filename pattern", component_args=hide_dirs).link("wiki", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Custom-Images-Filename-Name-and-Subdirectory"), "save_images_add_number": OptionInfo(True, "Add number to filename when saving", component_args=hide_dirs), - + "save_images_add_number_suffix": OptionInfo(True, "Add number suffix when necessary", component_args=hide_dirs).info("prevent existing image from being override"), "grid_save": OptionInfo(True, "Always save all generated image grids"), "grid_format": OptionInfo('png', 'File format for grids'), "grid_extended_filename": OptionInfo(False, "Add extended info (seed, prompt) to filename when saving grid"), From 657404b75b2f214a97281afbec1adcb4313d24eb Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Wed, 6 Sep 2023 20:33:43 +0900 Subject: [PATCH 074/186] use original filename batch img2img with scripts --- modules/img2img.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/modules/img2img.py b/modules/img2img.py index 0c6d1af5..c1cae22f 100644 --- a/modules/img2img.py +++ b/modules/img2img.py @@ -117,15 +117,14 @@ def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args, to_scale=Fal if output_dir: p.outpath_samples = output_dir p.override_settings['save_to_dirs'] = False + if p.n_iter > 1 or p.batch_size > 1: + p.override_settings['samples_filename_pattern'] = f'{image_path.stem}-[generation_number]' + else: + p.override_settings['samples_filename_pattern'] = f'{image_path.stem}' proc = modules.scripts.scripts_img2img.run(p, *args) if proc is None: - if output_dir: - if p.n_iter > 1 or p.batch_size > 1: - p.override_settings['samples_filename_pattern'] = f'{image_path.stem}-[generation_number]' - else: - p.override_settings['samples_filename_pattern'] = f'{image_path.stem}' process_images(p) From 340fce2113b6d68f06f5bb8c897be998f03b4c8c Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Thu, 7 Sep 2023 10:01:16 +0900 Subject: [PATCH 075/186] enable console prompts in settings --- modules/img2img.py | 2 +- modules/shared_options.py | 1 + modules/txt2img.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/img2img.py b/modules/img2img.py index c81c7ab9..cbd80bac 100644 --- a/modules/img2img.py +++ b/modules/img2img.py @@ -199,7 +199,7 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s p.user = request.username - if shared.cmd_opts.enable_console_prompts: + if shared.opts.enable_console_prompts or shared.cmd_opts.enable_console_prompts: print(f"\nimg2img: {prompt}", file=shared.progress_print_out) if mask: diff --git a/modules/shared_options.py b/modules/shared_options.py index 00b273fa..44fb1670 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -100,6 +100,7 @@ options_templates.update(options_section(('face-restoration', "Face restoration" options_templates.update(options_section(('system', "System"), { "auto_launch_browser": OptionInfo("Local", "Automatically open webui in browser on startup", gr.Radio, lambda: {"choices": ["Disable", "Local", "Remote"]}), + "enable_console_prompts": OptionInfo(False, "Print prompts to console when generating with txt2img and img2img."), "show_warnings": OptionInfo(False, "Show warnings in console.").needs_reload_ui(), "show_gradio_deprecation_warnings": OptionInfo(True, "Show gradio deprecation warnings in console.").needs_reload_ui(), "memmon_poll_rate": OptionInfo(8, "VRAM usage polls per second during generation.", gr.Slider, {"minimum": 0, "maximum": 40, "step": 1}).info("0 = disable"), diff --git a/modules/txt2img.py b/modules/txt2img.py index 1ee592ad..379ef859 100644 --- a/modules/txt2img.py +++ b/modules/txt2img.py @@ -45,7 +45,7 @@ def txt2img(id_task: str, prompt: str, negative_prompt: str, prompt_styles, step p.user = request.username - if cmd_opts.enable_console_prompts: + if shared.opts.enable_console_prompts or cmd_opts.enable_console_prompts: print(f"\ntxt2img: {prompt}", file=shared.progress_print_out) with closing(p): From 45881703c5b1c0499406a76fa49ec7bd408a4898 Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Thu, 7 Sep 2023 12:11:36 +0900 Subject: [PATCH 076/186] consolidated allowed preview formats --- modules/ui_extra_networks.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 063bd7b8..2e816254 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -15,6 +15,11 @@ from modules.ui_components import ToolButton extra_pages = [] allowed_dirs = set() +allowed_preview_extensions = ["png", "jpg", "jpeg", "webp", "gif"] +if shared.opts.samples_format not in allowed_preview_extensions: + allowed_preview_extensions.append(shared.opts.samples_format) +allowed_preview_extensions_dot = ['.' + extension for extension in allowed_preview_extensions] + def register_page(page): """registers extra networks page for the UI; recommend doing it in on_before_ui() callback for extensions""" @@ -34,7 +39,7 @@ def fetch_file(filename: str = ""): raise ValueError(f"File cannot be fetched: {filename}. Must be in one of directories registered by extra pages.") ext = os.path.splitext(filename)[1].lower() - if ext not in (".png", ".jpg", ".jpeg", ".webp", ".gif"): + if ext not in allowed_preview_extensions_dot: raise ValueError(f"File cannot be fetched: {filename}. Only png, jpg, webp, and gif.") # would profit from returning 304 @@ -273,11 +278,7 @@ class ExtraNetworksPage: Find a preview PNG for a given path (without extension) and call link_preview on it. """ - preview_extensions = ["png", "jpg", "jpeg", "webp"] - if shared.opts.samples_format not in preview_extensions: - preview_extensions.append(shared.opts.samples_format) - - potential_files = sum([[path + "." + ext, path + ".preview." + ext] for ext in preview_extensions], []) + potential_files = sum([[path + "." + ext, path + ".preview." + ext] for ext in allowed_preview_extensions], []) for file in potential_files: if os.path.isfile(file): From c3d51fc696bbe5f9ea2de63933234c90c55afbbd Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Thu, 7 Sep 2023 19:35:55 +0900 Subject: [PATCH 077/186] Update bug_report.yml --- .github/ISSUE_TEMPLATE/bug_report.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index a53db9f0..a423f052 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -86,8 +86,9 @@ body: label: Sysinfo description: System info file, generated by WebUI. You can generate it in settings, on the Sysinfo page. Drag the file into the field to upload it. If you submit your report without including the sysinfo file, the report will be closed. If needed, review the report to make sure it includes no personal information you don't want to share. If you can't start WebUI, you can use --dump-sysinfo commandline argument to generate the file. placeholder: | - Upload the Sysinfo as a attached file. - Do not paste it in as text. + 1. Go to WebUI Settings -> Sysinfo -> Download system info. + If WebUI fails to launch, use --dump-sysinfo commandline argument to generate the file + 2. Upload the Sysinfo as a attached file, Do NOT paste it in as plain text. validations: required: true - type: textarea From f11eec81e31bfc9195bbacda13b2a3ce7b98fd92 Mon Sep 17 00:00:00 2001 From: ibrainventures Date: Thu, 7 Sep 2023 23:19:52 +0200 Subject: [PATCH 078/186] (feat) Include Program Version in info response. Update processing.py This would help to organize / memorize the program version for the creation process. (as it is also unformated included inside the infotext). --- modules/processing.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/processing.py b/modules/processing.py index e124e7f0..0c191428 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -533,6 +533,7 @@ class Processed: self.all_seeds = all_seeds or p.all_seeds or [self.seed] self.all_subseeds = all_subseeds or p.all_subseeds or [self.subseed] self.infotexts = infotexts or [info] + self.version = program_version() def js(self): obj = { @@ -567,6 +568,7 @@ class Processed: "job_timestamp": self.job_timestamp, "clip_skip": self.clip_skip, "is_using_inpainting_conditioning": self.is_using_inpainting_conditioning, + "version": self.version, } return json.dumps(obj) From e4726cccf960257e1b456db84a59f28cea019c8f Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Fri, 8 Sep 2023 09:46:34 +0900 Subject: [PATCH 079/186] parsing string to path --- modules/sd_models.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/sd_models.py b/modules/sd_models.py index 930d0bee..9b0923de 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -49,11 +49,12 @@ class CheckpointInfo: def __init__(self, filename): self.filename = filename abspath = os.path.abspath(filename) + abs_ckpt_dir = os.path.abspath(shared.cmd_opts.ckpt_dir) if shared.cmd_opts.ckpt_dir is not None else None self.is_safetensors = os.path.splitext(filename)[1].lower() == ".safetensors" - if shared.cmd_opts.ckpt_dir is not None and abspath.startswith(shared.cmd_opts.ckpt_dir): - name = abspath.replace(shared.cmd_opts.ckpt_dir, '') + if abs_ckpt_dir and abspath.startswith(abs_ckpt_dir): + name = abspath.replace(abs_ckpt_dir, '') elif abspath.startswith(model_path): name = abspath.replace(model_path, '') else: From 63485b2c55d2e5d1d5fc64d3964120a7305a9aee Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Fri, 8 Sep 2023 10:00:27 +0900 Subject: [PATCH 080/186] option use short name for checkpoint dropdown --- modules/shared_items.py | 4 ++-- modules/shared_options.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/shared_items.py b/modules/shared_items.py index 84d69c8d..b1459f8c 100644 --- a/modules/shared_items.py +++ b/modules/shared_items.py @@ -44,9 +44,9 @@ def refresh_unet_list(): modules.sd_unet.list_unets() -def list_checkpoint_tiles(): +def list_checkpoint_tiles(use_short=False): import modules.sd_models - return modules.sd_models.checkpoint_tiles() + return modules.sd_models.checkpoint_tiles(use_short) def refresh_checkpoints(): diff --git a/modules/shared_options.py b/modules/shared_options.py index 00b273fa..7f71c517 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -133,7 +133,7 @@ options_templates.update(options_section(('training', "Training"), { })) options_templates.update(options_section(('sd', "Stable Diffusion"), { - "sd_model_checkpoint": OptionInfo(None, "Stable Diffusion checkpoint", gr.Dropdown, lambda: {"choices": shared_items.list_checkpoint_tiles()}, refresh=shared_items.refresh_checkpoints, infotext='Model hash'), + "sd_model_checkpoint": OptionInfo(None, "Stable Diffusion checkpoint", gr.Dropdown, lambda: {"choices": shared_items.list_checkpoint_tiles(shared.opts.sd_checkpoint_dropdown_use_short)}, refresh=shared_items.refresh_checkpoints, infotext='Model hash'), "sd_checkpoints_limit": OptionInfo(1, "Maximum number of checkpoints loaded at the same time", gr.Slider, {"minimum": 1, "maximum": 10, "step": 1}), "sd_checkpoints_keep_in_cpu": OptionInfo(True, "Only keep one model on device").info("will keep models other than the currently used one in RAM rather than VRAM"), "sd_checkpoint_cache": OptionInfo(0, "Checkpoints to cache in RAM", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}).info("obsolete; set to 0 and use the two settings above instead"), @@ -261,6 +261,7 @@ options_templates.update(options_section(('ui', "User interface"), { "ui_tab_order": OptionInfo([], "UI tab order", ui_components.DropdownMulti, lambda: {"choices": list(shared.tab_names)}).needs_reload_ui(), "hidden_tabs": OptionInfo([], "Hidden UI tabs", ui_components.DropdownMulti, lambda: {"choices": list(shared.tab_names)}).needs_reload_ui(), "ui_reorder_list": OptionInfo([], "txt2img/img2img UI item order", ui_components.DropdownMulti, lambda: {"choices": list(shared_items.ui_reorder_categories())}).info("selected items appear first").needs_reload_ui(), + "sd_checkpoint_dropdown_use_short": OptionInfo(False, "Use short name for Stable Diffusion checkpoint dropdown"), "hires_fix_show_sampler": OptionInfo(False, "Hires fix: show hires checkpoint and sampler selection").needs_reload_ui(), "hires_fix_show_prompts": OptionInfo(False, "Hires fix: show hires prompt and negative prompt").needs_reload_ui(), "disable_token_counters": OptionInfo(False, "Disable prompt token counters").needs_reload_ui(), From 259768f27fc4da61000610bc81a16f0152b36550 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 9 Sep 2023 08:38:42 +0300 Subject: [PATCH 081/186] fix the bug in script-info API --- modules/scripts.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/scripts.py b/modules/scripts.py index e8518ad0..f1f17a5f 100644 --- a/modules/scripts.py +++ b/modules/scripts.py @@ -491,11 +491,15 @@ class ScriptRunner: arg_info = api_models.ScriptArg(label=control.label or "") - for field in ("value", "minimum", "maximum", "step", "choices"): + for field in ("value", "minimum", "maximum", "step"): v = getattr(control, field, None) if v is not None: setattr(arg_info, field, v) + choices = getattr(control, 'choices', None) # as of gradio 3.41, some items in choices are strings, and some are tuples where the first elem is the string + if choices is not None: + setattr(arg_info, 'choices', [x[0] if isinstance(x, tuple) else x for x in choices]) + api_args.append(arg_info) script.api_info = api_models.ScriptInfo( From 7b44b85730d392733a285fe7e5c9e077f7bbccd3 Mon Sep 17 00:00:00 2001 From: ljleb Date: Sat, 9 Sep 2023 02:01:12 -0400 Subject: [PATCH 082/186] refact --- modules/ui.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/ui.py b/modules/ui.py index f4028475..0e78b6e1 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -1,4 +1,5 @@ import datetime +import functools import mimetypes import os import sys @@ -151,11 +152,14 @@ def connect_clear_prompt(button): ) -def update_token_counter(text, steps): +def update_token_counter(text, steps, is_positive=True): try: text, _ = extra_networks.parse_prompt(text) - _, prompt_flat_list, _ = prompt_parser.get_multicond_prompt_list([text]) + if is_positive: + _, prompt_flat_list, _ = prompt_parser.get_multicond_prompt_list([text]) + else: + prompt_flat_list = [text] prompt_schedules = prompt_parser.get_learned_conditioning_prompt_schedules(prompt_flat_list, steps) except Exception: @@ -533,7 +537,7 @@ def create_ui(): ] toprow.token_button.click(fn=wrap_queued_call(update_token_counter), inputs=[toprow.prompt, steps], outputs=[toprow.token_counter]) - toprow.negative_token_button.click(fn=wrap_queued_call(update_token_counter), inputs=[toprow.negative_prompt, steps], outputs=[toprow.negative_token_counter]) + toprow.negative_token_button.click(fn=wrap_queued_call(functools.partial(update_token_counter, is_positive=False)), inputs=[toprow.negative_prompt, steps], outputs=[toprow.negative_token_counter]) extra_networks_ui = ui_extra_networks.create_ui(txt2img_interface, [txt2img_generation_tab], 'txt2img') ui_extra_networks.setup_ui(extra_networks_ui, txt2img_gallery) From 3ca4655a18eb80cca5f806412f2cb2d56cc536e5 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 9 Sep 2023 09:08:31 +0300 Subject: [PATCH 083/186] update for #12926 --- modules/images.py | 16 +++++++--------- modules/shared_options.py | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/modules/images.py b/modules/images.py index 10dcd9ab..5cf3c825 100644 --- a/modules/images.py +++ b/modules/images.py @@ -661,15 +661,13 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='png', i save_image_with_geninfo(image_to_save, info, temp_file_path, extension, existing_pnginfo=params.pnginfo, pnginfo_section_name=pnginfo_section_name) - full_file_name = filename_without_extension + extension - if shared.opts.save_images_add_number_suffix and os.path.exists(full_file_name): - count = 1 - while True: - full_file_name = f"{filename_without_extension}_{count}{extension}" - if not os.path.exists(full_file_name): - break - count += 1 - os.replace(temp_file_path, full_file_name) + filename = filename_without_extension + extension + if shared.opts.save_images_replace_action != "Replace": + n = 0 + while os.path.exists(filename): + n += 1 + filename = f"{filename_without_extension}-{n}{extension}" + os.replace(temp_file_path, filename) fullfn_without_extension, extension = os.path.splitext(params.filename) if hasattr(os, 'statvfs'): diff --git a/modules/shared_options.py b/modules/shared_options.py index 2f4caa9d..1befb6ea 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -26,7 +26,7 @@ options_templates.update(options_section(('saving-images', "Saving images/grids" "samples_format": OptionInfo('png', 'File format for images'), "samples_filename_pattern": OptionInfo("", "Images filename pattern", component_args=hide_dirs).link("wiki", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Custom-Images-Filename-Name-and-Subdirectory"), "save_images_add_number": OptionInfo(True, "Add number to filename when saving", component_args=hide_dirs), - "save_images_add_number_suffix": OptionInfo(True, "Add number suffix when necessary", component_args=hide_dirs).info("prevent existing image from being override"), + "save_images_replace_action": OptionInfo("Replace", "Saving the image to an existing file", gr.Radio, {"choices": ["Replace", "Add number suffix"], **hide_dirs}), "grid_save": OptionInfo(True, "Always save all generated image grids"), "grid_format": OptionInfo('png', 'File format for grids'), "grid_extended_filename": OptionInfo(False, "Add extended info (seed, prompt) to filename when saving grid"), From 4c4d7dd01f77f021381a09cb18b4ca8a8b7734b1 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 9 Sep 2023 09:15:09 +0300 Subject: [PATCH 084/186] fix whitespace for #13084 --- modules/hypernetworks/hypernetwork.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hypernetworks/hypernetwork.py b/modules/hypernetworks/hypernetwork.py index 65b63f2f..be3e4648 100644 --- a/modules/hypernetworks/hypernetwork.py +++ b/modules/hypernetworks/hypernetwork.py @@ -468,7 +468,7 @@ def create_hypernetwork(name, enable_sizes, overwrite_old, layer_structure=None, shared.reload_hypernetworks() -def train_hypernetwork(id_task, hypernetwork_name:str, learn_rate:float, batch_size:int, gradient_step:int, data_root:str, log_directory:str, training_width:int, training_height:int, varsize:bool, steps:int, clip_grad_mode:str, clip_grad_value:float, shuffle_tags:bool, tag_drop_out:bool, latent_sampling_method:str, use_weight:bool, create_image_every:int, save_hypernetwork_every:int, template_filename:str, preview_from_txt2img:bool, preview_prompt:str, preview_negative_prompt:str, preview_steps:int, preview_sampler_name:str, preview_cfg_scale:float, preview_seed:int, preview_width:int, preview_height:int): +def train_hypernetwork(id_task, hypernetwork_name: str, learn_rate: float, batch_size: int, gradient_step: int, data_root: str, log_directory: str, training_width: int, training_height: int, varsize: bool, steps: int, clip_grad_mode: str, clip_grad_value: float, shuffle_tags: bool, tag_drop_out: bool, latent_sampling_method: str, use_weight: bool, create_image_every: int, save_hypernetwork_every: int, template_filename: str, preview_from_txt2img: bool, preview_prompt: str, preview_negative_prompt: str, preview_steps: int, preview_sampler_name: str, preview_cfg_scale: float, preview_seed: int, preview_width: int, preview_height: int): from modules import images, processing save_hypernetwork_every = save_hypernetwork_every or 0 From 46375f059276cb2d4d1e47bf65f984c6466dc2a0 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 9 Sep 2023 09:39:37 +0300 Subject: [PATCH 085/186] fix for crash when running #12924 without --device-id --- modules/devices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/devices.py b/modules/devices.py index 63c38eff..1d4eb563 100644 --- a/modules/devices.py +++ b/modules/devices.py @@ -60,7 +60,7 @@ def enable_tf32(): # enabling benchmark option seems to enable a range of cards to do fp16 when they otherwise can't # see https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/4407 - device_id = (int(shared.cmd_opts.device_id) if shared.cmd_opts.device_id.isdigit() else 0) or torch.cuda.current_device() + device_id = (int(shared.cmd_opts.device_id) if shared.cmd_opts.device_id is not None and shared.cmd_opts.device_id.isdigit() else 0) or torch.cuda.current_device() if torch.cuda.get_device_capability(device_id) == (7, 5) and torch.cuda.get_device_name(device_id).startswith("NVIDIA GeForce GTX 16"): torch.backends.cudnn.benchmark = True From 46ef1857098df7610c36e73903731e486feca927 Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Sat, 9 Sep 2023 15:53:10 +0900 Subject: [PATCH 086/186] deprecate --enable-console-prompts use --enable-console-prompts as the default value for shared.opts.enable_console_prompts --- modules/cmd_args.py | 2 +- modules/img2img.py | 2 +- modules/shared_options.py | 2 +- modules/txt2img.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/cmd_args.py b/modules/cmd_args.py index aab62286..fe4d4ecc 100644 --- a/modules/cmd_args.py +++ b/modules/cmd_args.py @@ -90,7 +90,7 @@ parser.add_argument("--autolaunch", action='store_true', help="open the webui UR parser.add_argument("--theme", type=str, help="launches the UI with light or dark theme", default=None) parser.add_argument("--use-textbox-seed", action='store_true', help="use textbox for seeds in UI (no up/down, but possible to input long seeds)", default=False) parser.add_argument("--disable-console-progressbars", action='store_true', help="do not output progressbars to console", default=False) -parser.add_argument("--enable-console-prompts", action='store_true', help="print prompts to console when generating with txt2img and img2img", default=False) +parser.add_argument("--enable-console-prompts", action='store_true', help="does not do anything", default=False) # Legacy compatibility, use as default value shared.opts.enable_console_prompts parser.add_argument('--vae-path', type=str, help='Checkpoint to use as VAE; setting this argument disables all settings related to VAE', default=None) parser.add_argument("--disable-safe-unpickle", action='store_true', help="disable checking pytorch models for malicious code", default=False) parser.add_argument("--api", action='store_true', help="use api=True to launch the API together with the webui (use --nowebui instead for only the API)") diff --git a/modules/img2img.py b/modules/img2img.py index cbd80bac..72ee7bc2 100644 --- a/modules/img2img.py +++ b/modules/img2img.py @@ -199,7 +199,7 @@ def img2img(id_task: str, mode: int, prompt: str, negative_prompt: str, prompt_s p.user = request.username - if shared.opts.enable_console_prompts or shared.cmd_opts.enable_console_prompts: + if shared.opts.enable_console_prompts: print(f"\nimg2img: {prompt}", file=shared.progress_print_out) if mask: diff --git a/modules/shared_options.py b/modules/shared_options.py index 44fb1670..1ea5c8f8 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -100,7 +100,7 @@ options_templates.update(options_section(('face-restoration', "Face restoration" options_templates.update(options_section(('system', "System"), { "auto_launch_browser": OptionInfo("Local", "Automatically open webui in browser on startup", gr.Radio, lambda: {"choices": ["Disable", "Local", "Remote"]}), - "enable_console_prompts": OptionInfo(False, "Print prompts to console when generating with txt2img and img2img."), + "enable_console_prompts": OptionInfo(shared.cmd_opts.enable_console_prompts, "Print prompts to console when generating with txt2img and img2img."), "show_warnings": OptionInfo(False, "Show warnings in console.").needs_reload_ui(), "show_gradio_deprecation_warnings": OptionInfo(True, "Show gradio deprecation warnings in console.").needs_reload_ui(), "memmon_poll_rate": OptionInfo(8, "VRAM usage polls per second during generation.", gr.Slider, {"minimum": 0, "maximum": 40, "step": 1}).info("0 = disable"), diff --git a/modules/txt2img.py b/modules/txt2img.py index 379ef859..721206dd 100644 --- a/modules/txt2img.py +++ b/modules/txt2img.py @@ -45,7 +45,7 @@ def txt2img(id_task: str, prompt: str, negative_prompt: str, prompt_styles, step p.user = request.username - if shared.opts.enable_console_prompts or cmd_opts.enable_console_prompts: + if shared.opts.enable_console_prompts: print(f"\ntxt2img: {prompt}", file=shared.progress_print_out) with closing(p): From c68aabc852151633016d3d5c84b433041f09d96e Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Sat, 9 Sep 2023 15:59:22 +0900 Subject: [PATCH 087/186] lint --- modules/txt2img.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/txt2img.py b/modules/txt2img.py index 721206dd..e4e18ceb 100644 --- a/modules/txt2img.py +++ b/modules/txt2img.py @@ -3,7 +3,7 @@ from contextlib import closing import modules.scripts from modules import processing from modules.generation_parameters_copypaste import create_override_settings_dict -from modules.shared import opts, cmd_opts +from modules.shared import opts import modules.shared as shared from modules.ui import plaintext_to_html import gradio as gr From 9cebe308e9f30f2a9555cf9fc43bf20652c4a619 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 9 Sep 2023 10:20:06 +0300 Subject: [PATCH 088/186] return apply styles to main UI --- modules/ui.py | 2 ++ modules/ui_prompt_styles.py | 21 +++++++++++---------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/modules/ui.py b/modules/ui.py index b2aed7db..06aa509b 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -217,6 +217,7 @@ class Toprow: with gr.Row(elem_id=f"{id_part}_tools"): self.paste = ToolButton(value=paste_symbol, elem_id="paste", tooltip="Read generation parameters from prompt or last generation if prompt is empty into user interface.") self.clear_prompt_button = ToolButton(value=clear_prompt_symbol, elem_id=f"{id_part}_clear_prompt", tooltip="Clear prompt") + self.apply_styles = ToolButton(value=ui_prompt_styles.styles_materialize_symbol, elem_id=f"{id_part}_style_apply", tooltip="Apply all selected styles to prompts.") self.restore_progress_button = ToolButton(value=restore_progress_symbol, elem_id=f"{id_part}_restore_progress", visible=False, tooltip="Restore progress") self.token_counter = gr.HTML(value="0/75", elem_id=f"{id_part}_token_counter", elem_classes=["token-counter"]) @@ -232,6 +233,7 @@ class Toprow: ) self.ui_styles = ui_prompt_styles.UiPromptStyles(id_part, self.prompt, self.negative_prompt) + self.ui_styles.setup_apply_button(self.apply_styles) self.prompt_img.change( fn=modules.images.image_data, diff --git a/modules/ui_prompt_styles.py b/modules/ui_prompt_styles.py index 64d379ef..3bcf092f 100644 --- a/modules/ui_prompt_styles.py +++ b/modules/ui_prompt_styles.py @@ -53,6 +53,8 @@ def refresh_styles(): class UiPromptStyles: def __init__(self, tabname, main_ui_prompt, main_ui_negative_prompt): self.tabname = tabname + self.main_ui_prompt = main_ui_prompt + self.main_ui_negative_prompt = main_ui_negative_prompt with gr.Row(elem_id=f"{tabname}_styles_row"): self.dropdown = gr.Dropdown(label="Styles", show_label=False, elem_id=f"{tabname}_styles", choices=list(shared.prompt_styles.styles), value=[], multiselect=True, tooltip="Styles") @@ -62,7 +64,7 @@ class UiPromptStyles: with gr.Row(): self.selection = gr.Dropdown(label="Styles", elem_id=f"{tabname}_styles_edit_select", choices=list(shared.prompt_styles.styles), value=[], allow_custom_value=True, info="Styles allow you to add custom text to prompt. Use the {prompt} token in style text, and it will be replaced with user's prompt when applying style. Otherwise, style's text will be added to the end of the prompt.") ui_common.create_refresh_button([self.dropdown, self.selection], shared.prompt_styles.reload, lambda: {"choices": list(shared.prompt_styles.styles)}, f"refresh_{tabname}_styles") - self.materialize = ui_components.ToolButton(value=styles_materialize_symbol, elem_id=f"{tabname}_style_apply", tooltip="Apply all selected styles from the style selction dropdown in main UI to the prompt.") + self.materialize = ui_components.ToolButton(value=styles_materialize_symbol, elem_id=f"{tabname}_style_apply_dialog", tooltip="Apply all selected styles from the style selction dropdown in main UI to the prompt.") self.copy = ui_components.ToolButton(value=styles_copy_symbol, elem_id=f"{tabname}_style_copy", tooltip="Copy main UI prompt to style.") with gr.Row(): @@ -98,12 +100,7 @@ class UiPromptStyles: show_progress=False, ).then(refresh_styles, outputs=[self.dropdown, self.selection], show_progress=False) - self.materialize.click( - fn=materialize_styles, - inputs=[main_ui_prompt, main_ui_negative_prompt, self.dropdown], - outputs=[main_ui_prompt, main_ui_negative_prompt, self.dropdown], - show_progress=False, - ).then(fn=None, _js="function(){update_"+tabname+"_tokens(); closePopup();}", show_progress=False) + self.setup_apply_button(self.materialize) self.copy.click( fn=lambda p, n: (p, n), @@ -114,6 +111,10 @@ class UiPromptStyles: ui_common.setup_dialog(button_show=edit_button, dialog=styles_dialog, button_close=self.close) - - - + def setup_apply_button(self, button): + button.click( + fn=materialize_styles, + inputs=[self.main_ui_prompt, self.main_ui_negative_prompt, self.dropdown], + outputs=[self.main_ui_prompt, self.main_ui_negative_prompt, self.dropdown], + show_progress=False, + ).then(fn=None, _js="function(){update_"+self.tabname+"_tokens(); closePopup();}", show_progress=False) From 06af73bd1d17b417f234746c9a2f8e8e92cf6149 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 9 Sep 2023 10:23:53 +0300 Subject: [PATCH 089/186] linter --- modules/scripts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/scripts.py b/modules/scripts.py index f1f17a5f..5c6e0226 100644 --- a/modules/scripts.py +++ b/modules/scripts.py @@ -498,7 +498,7 @@ class ScriptRunner: choices = getattr(control, 'choices', None) # as of gradio 3.41, some items in choices are strings, and some are tuples where the first elem is the string if choices is not None: - setattr(arg_info, 'choices', [x[0] if isinstance(x, tuple) else x for x in choices]) + arg_info.choices = [x[0] if isinstance(x, tuple) else x for x in choices] api_args.append(arg_info) From c9c457eda8dec414dc38e874691d1e2736d6dcbb Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 9 Sep 2023 10:27:16 +0300 Subject: [PATCH 090/186] stylistic changes for #13118 --- modules/ui.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/ui.py b/modules/ui.py index 5f0f1cd1..569dc807 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -1,5 +1,4 @@ import datetime -import functools import mimetypes import os import sys @@ -152,7 +151,7 @@ def connect_clear_prompt(button): ) -def update_token_counter(text, steps, is_positive=True): +def update_token_counter(text, steps, *, is_positive=True): try: text, _ = extra_networks.parse_prompt(text) @@ -160,6 +159,7 @@ def update_token_counter(text, steps, is_positive=True): _, prompt_flat_list, _ = prompt_parser.get_multicond_prompt_list([text]) else: prompt_flat_list = [text] + prompt_schedules = prompt_parser.get_learned_conditioning_prompt_schedules(prompt_flat_list, steps) except Exception: @@ -173,6 +173,10 @@ def update_token_counter(text, steps, is_positive=True): return f"{token_count}/{max_length}" +def update_negative_prompt_token_counter(text, steps): + return update_token_counter(text, steps, is_positive=False) + + class Toprow: """Creates a top row UI with prompts, generate button, styles, extra little buttons for things, and enables some functionality related to their operation""" @@ -539,7 +543,7 @@ def create_ui(): ] toprow.token_button.click(fn=wrap_queued_call(update_token_counter), inputs=[toprow.prompt, steps], outputs=[toprow.token_counter]) - toprow.negative_token_button.click(fn=wrap_queued_call(functools.partial(update_token_counter, is_positive=False)), inputs=[toprow.negative_prompt, steps], outputs=[toprow.negative_token_counter]) + toprow.negative_token_button.click(fn=wrap_queued_call(update_negative_prompt_token_counter), inputs=[toprow.negative_prompt, steps], outputs=[toprow.negative_token_counter]) extra_networks_ui = ui_extra_networks.create_ui(txt2img_interface, [txt2img_generation_tab], 'txt2img') ui_extra_networks.setup_ui(extra_networks_ui, txt2img_gallery) From f8042cb323f0b581e3a49880dd0023e39d7dcc2c Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Sat, 9 Sep 2023 22:35:07 +0900 Subject: [PATCH 091/186] Ensure not override images with script enabled --- modules/img2img.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/img2img.py b/modules/img2img.py index 7ca10cf0..52cb577a 100644 --- a/modules/img2img.py +++ b/modules/img2img.py @@ -117,6 +117,7 @@ def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args, to_scale=Fal if output_dir: p.outpath_samples = output_dir p.override_settings['save_to_dirs'] = False + p.override_settings['save_images_replace_action'] = "Add number suffix" if p.n_iter > 1 or p.batch_size > 1: p.override_settings['samples_filename_pattern'] = f'{image_path.stem}-[generation_number]' else: @@ -125,6 +126,7 @@ def process_batch(p, input_dir, output_dir, inpaint_mask_dir, args, to_scale=Fal proc = modules.scripts.scripts_img2img.run(p, *args) if proc is None: + p.override_settings.pop('save_images_replace_action', None) process_images(p) From ab5741717546758c57cf6c2a040645ec2b44690a Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Sat, 9 Sep 2023 22:35:50 +0900 Subject: [PATCH 092/186] prevent accessing non-existing keys --- modules/processing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/processing.py b/modules/processing.py index 0c191428..618f8abe 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -711,7 +711,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed: if p.scripts is not None: p.scripts.before_process(p) - stored_opts = {k: opts.data[k] for k in p.override_settings.keys()} + stored_opts = {k: opts.data[k] for k in p.override_settings.keys() if k in opts.data} try: # if no checkpoint override or the override checkpoint can't be found, remove override entry and load opts checkpoint From d6478a60aa7c6f96a959ca6e3b9e8d51ad22d895 Mon Sep 17 00:00:00 2001 From: zixaphir Date: Sat, 9 Sep 2023 17:22:10 -0700 Subject: [PATCH 093/186] Remove extra network separator without regex --- javascript/extraNetworks.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index ca87bead..ff58d3dc 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -140,23 +140,19 @@ function setupExtraNetworks() { onUiLoaded(setupExtraNetworks); -var re_extranet = /<([^:]+:[^:]+):[\d.]+>(.*)/; -var re_extranet_str = '<([^:]+:[^:]+):[\\d.]+>'; +var re_extranet = /<([^:^>]+:[^:]+):[\d.]+>(.*)/; +var re_extranet_g = /<([^:^>]+:[^:]+):[\d.]+>/g; function tryToRemoveExtraNetworkFromPrompt(textarea, text) { - function reEscape(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); - } var m = text.match(re_extranet); var replaced = false; var newTextareaText; if (m) { + var extraTextBeforeNet = opts.extra_networks_add_text_separator; var extraTextAfterNet = m[2]; var partToSearch = m[1]; var foundAtPosition = -1; - var escapedSeparator = `(?:${reEscape(opts.extra_networks_add_text_separator)})?`; - var re = new RegExp(escapedSeparator + re_extranet_str, 'g'); - newTextareaText = textarea.value.replaceAll(re, function(found, net, pos) { + newTextareaText = textarea.value.replaceAll(re_extranet_g, function(found, net, pos) { m = found.match(re_extranet); if (m[1] == partToSearch) { replaced = true; @@ -166,8 +162,13 @@ function tryToRemoveExtraNetworkFromPrompt(textarea, text) { return found; }); - if (foundAtPosition >= 0 && newTextareaText.substr(foundAtPosition, extraTextAfterNet.length) == extraTextAfterNet) { - newTextareaText = newTextareaText.substr(0, foundAtPosition) + newTextareaText.substr(foundAtPosition + extraTextAfterNet.length); + if (foundAtPosition >= 0) { + if (newTextareaText.substr(foundAtPosition, extraTextAfterNet.length) == extraTextAfterNet) { + newTextareaText = newTextareaText.substr(0, foundAtPosition) + newTextareaText.substr(foundAtPosition + extraTextAfterNet.length); + } + if (newTextareaText.substr(foundAtPosition - extraTextBeforeNet.length, extraTextBeforeNet.length) == extraTextBeforeNet) { + newTextareaText = newTextareaText.substr(0, foundAtPosition - extraTextBeforeNet.length) + newTextareaText.substr(foundAtPosition); + } } } else { newTextareaText = textarea.value.replaceAll(new RegExp(text, "g"), function(found) { From 26d0d87f5b05c345abfec8eb0f8bd703cacd9619 Mon Sep 17 00:00:00 2001 From: zixaphir Date: Sat, 9 Sep 2023 17:26:46 -0700 Subject: [PATCH 094/186] Remove extra spaces --- javascript/extraNetworks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index ff58d3dc..158b5b64 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -140,7 +140,7 @@ function setupExtraNetworks() { onUiLoaded(setupExtraNetworks); -var re_extranet = /<([^:^>]+:[^:]+):[\d.]+>(.*)/; +var re_extranet = /<([^:^>]+:[^:]+):[\d.]+>(.*)/; var re_extranet_g = /<([^:^>]+:[^:]+):[\d.]+>/g; function tryToRemoveExtraNetworkFromPrompt(textarea, text) { From 7d4d871d4679b5b78ff67b501da5367413542984 Mon Sep 17 00:00:00 2001 From: dongwenpu Date: Sun, 10 Sep 2023 17:53:42 +0800 Subject: [PATCH 095/186] fix: lora-bias-backup don't reset cache --- extensions-builtin/Lora/networks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions-builtin/Lora/networks.py b/extensions-builtin/Lora/networks.py index 96f935b2..315682b3 100644 --- a/extensions-builtin/Lora/networks.py +++ b/extensions-builtin/Lora/networks.py @@ -418,6 +418,7 @@ def network_forward(module, input, original_forward): def network_reset_cached_weight(self: Union[torch.nn.Conv2d, torch.nn.Linear]): self.network_current_names = () self.network_weights_backup = None + self.network_bias_backup = None def network_Linear_forward(self, input): From 413123f08a745e9417fd384d2c1bee1e0e5e5730 Mon Sep 17 00:00:00 2001 From: liubo0902 <38622806+liubo0902@users.noreply.github.com> Date: Mon, 11 Sep 2023 09:22:27 +0800 Subject: [PATCH 096/186] Update localization.py --- modules/localization.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/localization.py b/modules/localization.py index 262d49ee..108f792e 100644 --- a/modules/localization.py +++ b/modules/localization.py @@ -14,12 +14,10 @@ def list_localizations(dirname): if ext.lower() != ".json": continue - fn = fn.replace(" ", "").replace("(", "_").replace(")","") localizations[fn] = [os.path.join(dirname, file)] for file in scripts.list_scripts("localizations", ".json"): fn, ext = os.path.splitext(file.filename) - fn = fn.replace(" ", "").replace("(", "_").replace(")","") if fn not in localizations: localizations[fn] = [] localizations[fn].append(file.path) From c485a7d12e26df1497309023d09cb3c106d7ae2b Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Mon, 11 Sep 2023 13:47:44 +0900 Subject: [PATCH 097/186] make InputAccordion work with ui-config --- modules/ui_loadsave.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/modules/ui_loadsave.py b/modules/ui_loadsave.py index ec8fa8e8..eb20ff25 100644 --- a/modules/ui_loadsave.py +++ b/modules/ui_loadsave.py @@ -4,7 +4,7 @@ import os import gradio as gr from modules import errors -from modules.ui_components import ToolButton +from modules.ui_components import ToolButton, InputAccordion def radio_choices(comp): # gradio 3.41 changes choices from list of values to list of pairs @@ -32,8 +32,6 @@ class UiLoadsave: self.error_loading = True errors.display(e, "loading settings") - - def add_component(self, path, x): """adds component to the registry of tracked components""" @@ -43,20 +41,24 @@ class UiLoadsave: key = f"{path}/{field}" if getattr(obj, 'custom_script_source', None) is not None: - key = f"customscript/{obj.custom_script_source}/{key}" + key = f"customscript/{obj.custom_script_source}/{key}" if getattr(obj, 'do_not_save_to_config', False): return saved_value = self.ui_settings.get(key, None) + + if isinstance(obj, gr.Accordion) and isinstance(x, InputAccordion) and field == 'value': + field = 'open' + if saved_value is None: self.ui_settings[key] = getattr(obj, field) elif condition and not condition(saved_value): pass else: - if isinstance(x, gr.Textbox) and field == 'value': # due to an undesirable behavior of gr.Textbox, if you give it an int value instead of str, everything dies + if isinstance(obj, gr.Textbox) and field == 'value': # due to an undesirable behavior of gr.Textbox, if you give it an int value instead of str, everything dies saved_value = str(saved_value) - elif isinstance(x, gr.Number) and field == 'value': + elif isinstance(obj, gr.Number) and field == 'value': try: saved_value = float(saved_value) except ValueError: @@ -67,7 +69,7 @@ class UiLoadsave: init_field(saved_value) if field == 'value' and key not in self.component_mapping: - self.component_mapping[key] = x + self.component_mapping[key] = obj if type(x) in [gr.Slider, gr.Radio, gr.Checkbox, gr.Textbox, gr.Number, gr.Dropdown, ToolButton, gr.Button] and x.visible: apply_field(x, 'visible') @@ -100,6 +102,12 @@ class UiLoadsave: apply_field(x, 'value', check_dropdown, getattr(x, 'init_field', None)) + if type(x) == InputAccordion: + if x.accordion.visible: + apply_field(x.accordion, 'visible') + apply_field(x, 'value') + apply_field(x.accordion, 'value') + def check_tab_id(tab_id): tab_items = list(filter(lambda e: isinstance(e, gr.TabItem), x.children)) if type(tab_id) == str: From 59544321aa019d71d220b1da1eec703aa44fa8eb Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Mon, 11 Sep 2023 21:17:28 +0300 Subject: [PATCH 098/186] initial work on sd_unet for SDXL --- modules/sd_hijack.py | 17 ++++++++++++----- modules/sd_unet.py | 4 ++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/modules/sd_hijack.py b/modules/sd_hijack.py index 592f0055..22a1eb5c 100644 --- a/modules/sd_hijack.py +++ b/modules/sd_hijack.py @@ -2,7 +2,7 @@ import torch from torch.nn.functional import silu from types import MethodType -from modules import devices, sd_hijack_optimizations, shared, script_callbacks, errors, sd_unet +from modules import devices, sd_hijack_optimizations, shared, script_callbacks, errors, sd_unet, patches from modules.hypernetworks import hypernetwork from modules.shared import cmd_opts from modules import sd_hijack_clip, sd_hijack_open_clip, sd_hijack_unet, sd_hijack_xlmr, xlmr @@ -10,6 +10,7 @@ from modules import sd_hijack_clip, sd_hijack_open_clip, sd_hijack_unet, sd_hija import ldm.modules.attention import ldm.modules.diffusionmodules.model import ldm.modules.diffusionmodules.openaimodel +import ldm.models.diffusion.ddpm import ldm.models.diffusion.ddim import ldm.models.diffusion.plms import ldm.modules.encoders.modules @@ -37,6 +38,8 @@ ldm.models.diffusion.ddpm.print = shared.ldm_print optimizers = [] current_optimizer: sd_hijack_optimizations.SdOptimization = None +ldm_original_forward = patches.patch(__file__, ldm.modules.diffusionmodules.openaimodel.UNetModel, "forward", sd_unet.UNetModel_forward) +sgm_original_forward = patches.patch(__file__, sgm.modules.diffusionmodules.openaimodel.UNetModel, "forward", sd_unet.UNetModel_forward) def list_optimizers(): new_optimizers = script_callbacks.list_optimizers_callback() @@ -239,10 +242,13 @@ class StableDiffusionModelHijack: self.layers = flatten(m) - if not hasattr(ldm.modules.diffusionmodules.openaimodel, 'copy_of_UNetModel_forward_for_webui'): - ldm.modules.diffusionmodules.openaimodel.copy_of_UNetModel_forward_for_webui = ldm.modules.diffusionmodules.openaimodel.UNetModel.forward + if isinstance(m, ldm.models.diffusion.ddpm.LatentDiffusion): + sd_unet.original_forward = ldm_original_forward + elif isinstance(m, sgm.models.diffusion.DiffusionEngine): + sd_unet.original_forward = sgm_original_forward + else: + sd_unet.original_forward = None - ldm.modules.diffusionmodules.openaimodel.UNetModel.forward = sd_unet.UNetModel_forward def undo_hijack(self, m): conditioner = getattr(m, 'conditioner', None) @@ -279,7 +285,8 @@ class StableDiffusionModelHijack: self.layers = None self.clip = None - ldm.modules.diffusionmodules.openaimodel.UNetModel.forward = ldm.modules.diffusionmodules.openaimodel.copy_of_UNetModel_forward_for_webui + sd_unet.original_forward = None + def apply_circular(self, enable): if self.circular_enabled == enable: diff --git a/modules/sd_unet.py b/modules/sd_unet.py index 5525cfbc..6a7bc9e2 100644 --- a/modules/sd_unet.py +++ b/modules/sd_unet.py @@ -1,11 +1,11 @@ import torch.nn -import ldm.modules.diffusionmodules.openaimodel from modules import script_callbacks, shared, devices unet_options = [] current_unet_option = None current_unet = None +original_forward = None def list_unets(): @@ -88,5 +88,5 @@ def UNetModel_forward(self, x, timesteps=None, context=None, *args, **kwargs): if current_unet is not None: return current_unet.forward(x, timesteps, context, *args, **kwargs) - return ldm.modules.diffusionmodules.openaimodel.copy_of_UNetModel_forward_for_webui(self, x, timesteps, context, *args, **kwargs) + return original_forward(self, x, timesteps, context, *args, **kwargs) From 6fb2194d9cc2c9b52bc2006117d592283e00b7d6 Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Tue, 12 Sep 2023 16:50:56 +0900 Subject: [PATCH 099/186] fetch version info when webui_dir is not work_dir --- modules/launch_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/launch_utils.py b/modules/launch_utils.py index 6e54d063..8cdbafa5 100644 --- a/modules/launch_utils.py +++ b/modules/launch_utils.py @@ -64,7 +64,7 @@ Use --skip-python-version-check to suppress this warning. @lru_cache() def commit_hash(): try: - return subprocess.check_output([git, "rev-parse", "HEAD"], shell=False, encoding='utf8').strip() + return subprocess.check_output([git, "-C", script_path, "rev-parse", "HEAD"], shell=False, encoding='utf8').strip() except Exception: return "" @@ -72,7 +72,7 @@ def commit_hash(): @lru_cache() def git_tag(): try: - return subprocess.check_output([git, "describe", "--tags"], shell=False, encoding='utf8').strip() + return subprocess.check_output([git, "-C", script_path, "describe", "--tags"], shell=False, encoding='utf8').strip() except Exception: try: From 93015964c7c920fbb834bf99977ab8e16296efac Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Tue, 12 Sep 2023 22:43:35 +0900 Subject: [PATCH 100/186] fix add_option overriding config with default --- modules/options.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/options.py b/modules/options.py index e75916d2..ab40aff7 100644 --- a/modules/options.py +++ b/modules/options.py @@ -210,7 +210,8 @@ class Options: def add_option(self, key, info): self.data_labels[key] = info - self.data[key] = info.default + if key not in self.data: + self.data[key] = info.default def reorder(self): """reorder settings so that all items related to section always go together""" From 5b761b49ade392eb8ff4c54ab1841b63475b5dd0 Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Wed, 13 Sep 2023 16:05:55 +0900 Subject: [PATCH 101/186] correct webpath when webui_dir is not work_dir --- modules/paths.py | 2 +- modules/paths_internal.py | 1 + modules/ui_gradio_extensions.py | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/paths.py b/modules/paths.py index 25052339..187b9496 100644 --- a/modules/paths.py +++ b/modules/paths.py @@ -1,6 +1,6 @@ import os import sys -from modules.paths_internal import models_path, script_path, data_path, extensions_dir, extensions_builtin_dir # noqa: F401 +from modules.paths_internal import models_path, script_path, data_path, extensions_dir, extensions_builtin_dir, cwd # noqa: F401 import modules.safe # noqa: F401 diff --git a/modules/paths_internal.py b/modules/paths_internal.py index 005a9b0a..89131a54 100644 --- a/modules/paths_internal.py +++ b/modules/paths_internal.py @@ -8,6 +8,7 @@ import shlex commandline_args = os.environ.get('COMMANDLINE_ARGS', "") sys.argv += shlex.split(commandline_args) +cwd = os.getcwd() modules_path = os.path.dirname(os.path.realpath(__file__)) script_path = os.path.dirname(modules_path) diff --git a/modules/ui_gradio_extensions.py b/modules/ui_gradio_extensions.py index b824b113..0d368f8b 100644 --- a/modules/ui_gradio_extensions.py +++ b/modules/ui_gradio_extensions.py @@ -2,12 +2,12 @@ import os import gradio as gr from modules import localization, shared, scripts -from modules.paths import script_path, data_path +from modules.paths import script_path, data_path, cwd def webpath(fn): - if fn.startswith(script_path): - web_path = os.path.relpath(fn, script_path).replace('\\', '/') + if fn.startswith(cwd): + web_path = os.path.relpath(fn, cwd) else: web_path = os.path.abspath(fn) From cf1edc2b54d7ae8acec45ddba098957a6caa7867 Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Wed, 13 Sep 2023 16:27:02 +0900 Subject: [PATCH 102/186] initialize state.time_start befroe state.job_count --- modules/shared_state.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/shared_state.py b/modules/shared_state.py index d272ee5b..a68789cc 100644 --- a/modules/shared_state.py +++ b/modules/shared_state.py @@ -103,6 +103,7 @@ class State: def begin(self, job: str = "(unknown)"): self.sampling_step = 0 + self.time_start = time.time() self.job_count = -1 self.processing_has_refined_job_count = False self.job_no = 0 @@ -114,7 +115,6 @@ class State: self.skipped = False self.interrupted = False self.textinfo = None - self.time_start = time.time() self.job = job devices.torch_gc() log.info("Starting job %s", job) From 0ad38a9b87b7781315ea6324a8aa6c924d1275de Mon Sep 17 00:00:00 2001 From: Der Chien Date: Wed, 13 Sep 2023 20:20:01 +0800 Subject: [PATCH 103/186] 20230913 setup GIT_PYTHON_GIT_EXECUTABLE for GitPython --- webui.bat | 1 + webui.sh | 2 ++ 2 files changed, 3 insertions(+) diff --git a/webui.bat b/webui.bat index 42e7d517..a630ea4d 100644 --- a/webui.bat +++ b/webui.bat @@ -1,6 +1,7 @@ @echo off if not defined PYTHON (set PYTHON=python) +if defined GIT (set "GIT_PYTHON_GIT_EXECUTABLE=%GIT%") if not defined VENV_DIR (set "VENV_DIR=%~dp0%venv") set SD_WEBUI_RESTART=tmp/restart diff --git a/webui.sh b/webui.sh index 3d0f87ee..bdab3f05 100755 --- a/webui.sh +++ b/webui.sh @@ -51,6 +51,8 @@ fi if [[ -z "${GIT}" ]] then export GIT="git" +else + export GIT_PYTHON_GIT_EXECUTABLE="${GIT}" fi # python3 venv without trailing slash (defaults to ${install_dir}/${clone_dir}/venv) From ab3d3528a18ea1a81f1af22ea71bfc0d8c710dde Mon Sep 17 00:00:00 2001 From: Leon Date: Thu, 14 Sep 2023 18:42:56 +0800 Subject: [PATCH 104/186] add --skip-load-model-at-start --- modules/cmd_args.py | 1 + modules/initialize.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/cmd_args.py b/modules/cmd_args.py index 5be879dd..4e602a84 100644 --- a/modules/cmd_args.py +++ b/modules/cmd_args.py @@ -117,3 +117,4 @@ parser.add_argument('--api-server-stop', action='store_true', help='enable serve parser.add_argument('--timeout-keep-alive', type=int, default=30, help='set timeout_keep_alive for uvicorn') parser.add_argument("--disable-all-extensions", action='store_true', help="prevent all extensions from running regardless of any other settings", default=False) parser.add_argument("--disable-extra-extensions", action='store_true', help="prevent all extensions except built-in from running regardless of any other settings", default=False) +parser.add_argument("--skip-load-model-at-start", action='store_true', help="if load a model at web start, only take effect when --nowebui", ) diff --git a/modules/initialize.py b/modules/initialize.py index f24f7637..ac95fc6f 100644 --- a/modules/initialize.py +++ b/modules/initialize.py @@ -151,8 +151,8 @@ def initialize_rest(*, reload_script_modules=False): from modules import devices devices.first_time_calculation() - - Thread(target=load_model).start() + if not shared.cmd_opts.skip_load_model_at_start: + Thread(target=load_model).start() from modules import shared_items shared_items.reload_hypernetworks() From afd06245876004710007fa1abd0a1b4b2564c181 Mon Sep 17 00:00:00 2001 From: Won-Kyu Park Date: Fri, 15 Sep 2023 17:10:01 +0900 Subject: [PATCH 105/186] xyz_grid: add prepare option to AxisOption --- scripts/xyz_grid.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/xyz_grid.py b/scripts/xyz_grid.py index 939d8605..ce5a1a19 100644 --- a/scripts/xyz_grid.py +++ b/scripts/xyz_grid.py @@ -205,13 +205,14 @@ def csv_string_to_list_strip(data_str): class AxisOption: - def __init__(self, label, type, apply, format_value=format_value_add_label, confirm=None, cost=0.0, choices=None): + def __init__(self, label, type, apply, format_value=format_value_add_label, confirm=None, cost=0.0, choices=None, prepare=None): self.label = label self.type = type self.apply = apply self.format_value = format_value self.confirm = confirm self.cost = cost + self.prepare = prepare self.choices = choices @@ -536,6 +537,8 @@ class Script(scripts.Script): if opt.choices is not None and not csv_mode: valslist = vals_dropdown + elif opt.prepare is not None: + valslist = opt.prepare(vals) else: valslist = csv_string_to_list_strip(vals) From 813535d38bbcdd8ccc51d0618a7d9fd353677bb9 Mon Sep 17 00:00:00 2001 From: "qiuwen.wang" Date: Fri, 15 Sep 2023 18:23:23 +0800 Subject: [PATCH 106/186] use dict[key]=model; did not update orderdict order, should use move to end --- modules/sd_models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/sd_models.py b/modules/sd_models.py index 930d0bee..6d17dd3c 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -309,6 +309,7 @@ def get_checkpoint_state_dict(checkpoint_info: CheckpointInfo, timer): if checkpoint_info in checkpoints_loaded: # use checkpoint cache print(f"Loading weights [{sd_model_hash}] from cache") + checkpoints_loaded.move_to_end(checkpoint_info) return checkpoints_loaded[checkpoint_info] print(f"Loading weights [{sd_model_hash}] from {checkpoint_info.filename}") From d9d94141dcfc1a84e98370bc137ffd888509b65e Mon Sep 17 00:00:00 2001 From: woweenie <145132974+woweenie@users.noreply.github.com> Date: Fri, 15 Sep 2023 18:59:44 +0200 Subject: [PATCH 107/186] patch DDPM.register_betas so that users can put given_betas in model yaml --- modules/sd_models.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/modules/sd_models.py b/modules/sd_models.py index 930d0bee..8e4983a4 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -7,7 +7,7 @@ import threading import torch import re import safetensors.torch -from omegaconf import OmegaConf +from omegaconf import OmegaConf, ListConfig from os import mkdir from urllib import request import ldm.modules.midas as midas @@ -17,6 +17,7 @@ from ldm.util import instantiate_from_config from modules import paths, shared, modelloader, devices, script_callbacks, sd_vae, sd_disable_initialization, errors, hashes, sd_models_config, sd_unet, sd_models_xl, cache, extra_networks, processing, lowvram, sd_hijack from modules.timer import Timer import tomesd +import numpy as np model_dir = "Stable-diffusion" model_path = os.path.abspath(os.path.join(paths.models_path, model_dir)) @@ -132,6 +133,7 @@ def setup_model(): os.makedirs(model_path, exist_ok=True) enable_midas_autodownload() + patch_given_betas() def checkpoint_tiles(use_short=False): @@ -453,6 +455,17 @@ def enable_midas_autodownload(): midas.api.load_model = load_model_wrapper +def patch_given_betas(): + original_register_schedule = ldm.models.diffusion.ddpm.DDPM.register_schedule + def patched_register_schedule(*args, **kwargs): + if args[1] is not None and isinstance(args[1], ListConfig): + modified_args = list(args) # Convert args tuple to a list + modified_args[1] = np.array(args[1]) # Modify the desired element + args = tuple(modified_args) # Convert the list back to a tuple + original_register_schedule(*args, **kwargs) + ldm.models.diffusion.ddpm.DDPM.register_schedule = patched_register_schedule + + def repair_config(sd_config): if not hasattr(sd_config.model.params, "use_ema"): From 663fb8797612b863b0f0b94496039ec2ac18701c Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Sat, 16 Sep 2023 09:05:42 +0900 Subject: [PATCH 108/186] Config states time ISO in system time zone --- modules/config_states.py | 3 +-- modules/ui_extensions.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/config_states.py b/modules/config_states.py index b766aef1..651793c7 100644 --- a/modules/config_states.py +++ b/modules/config_states.py @@ -4,7 +4,6 @@ Supports saving and restoring webui and extensions from a known working set of c import os import json -import time import tqdm from datetime import datetime @@ -38,7 +37,7 @@ def list_config_states(): config_states = sorted(config_states, key=lambda cs: cs["created_at"], reverse=True) for cs in config_states: - timestamp = time.asctime(time.gmtime(cs["created_at"])) + timestamp = datetime.fromtimestamp(cs["created_at"]).strftime('%Y-%m-%d %H:%M:%S') name = cs.get("name", "Config") full_name = f"{name}: {timestamp}" all_config_states[full_name] = cs diff --git a/modules/ui_extensions.py b/modules/ui_extensions.py index 2e8c1d6d..c0a73b57 100644 --- a/modules/ui_extensions.py +++ b/modules/ui_extensions.py @@ -197,7 +197,7 @@ def update_config_states_table(state_name): config_state = config_states.all_config_states[state_name] config_name = config_state.get("name", "Config") - created_date = time.asctime(time.gmtime(config_state["created_at"])) + created_date = datetime.fromtimestamp(config_state["created_at"]).strftime('%Y-%m-%d %H:%M:%S') filepath = config_state.get("filepath", "") try: From d2878a8b0b952c08d832e0308e575e352a1bc3f1 Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Sat, 16 Sep 2023 09:49:53 +0900 Subject: [PATCH 109/186] XYZ if not Include Sub Grids do not save Sub Grid --- scripts/xyz_grid.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/xyz_grid.py b/scripts/xyz_grid.py index 939d8605..99ad96be 100644 --- a/scripts/xyz_grid.py +++ b/scripts/xyz_grid.py @@ -773,6 +773,8 @@ class Script(scripts.Script): # TODO: See previous comment about intentional data misalignment. adj_g = g-1 if g > 0 else g images.save_image(processed.images[g], p.outpath_grids, "xyz_grid", info=processed.infotexts[g], extension=opts.grid_format, prompt=processed.all_prompts[adj_g], seed=processed.all_seeds[adj_g], grid=True, p=processed) + if not include_sub_grids: # if not include_sub_grids then skip saving after the first grid + break if not include_sub_grids: # Done with sub-grids, drop all related information: From 701feabf496b7ce0327ccdb1ef1dc942deab25ea Mon Sep 17 00:00:00 2001 From: Zolxys Date: Sun, 17 Sep 2023 11:37:15 -0500 Subject: [PATCH 110/186] Fix: --sd_model in "Promts from file or textbox" script is not working Fix for bug report #8079 --- scripts/prompts_from_file.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/scripts/prompts_from_file.py b/scripts/prompts_from_file.py index 50320d55..ca73b2a5 100644 --- a/scripts/prompts_from_file.py +++ b/scripts/prompts_from_file.py @@ -5,11 +5,17 @@ import shlex import modules.scripts as scripts import gradio as gr -from modules import sd_samplers, errors +from modules import sd_samplers, errors, sd_models from modules.processing import Processed, process_images from modules.shared import state +def process_model_tag(tag): + info = sd_models.get_closet_checkpoint_match(tag) + assert info is not None, f'Unknown checkpoint: {tag}' + return info.name + + def process_string_tag(tag): return tag @@ -27,7 +33,7 @@ def process_boolean_tag(tag): prompt_tags = { - "sd_model": None, + "sd_model": process_model_tag, "outpath_samples": process_string_tag, "outpath_grids": process_string_tag, "prompt_for_display": process_string_tag, @@ -156,7 +162,10 @@ class Script(scripts.Script): copy_p = copy.copy(p) for k, v in args.items(): - setattr(copy_p, k, v) + if k == "sd_model": + copy_p.override_settings['sd_model_checkpoint'] = v + else: + setattr(copy_p, k, v) proc = process_images(copy_p) images += proc.images From 8e355fbd7552f1a7f5124c4685d6fa36f3d0ede1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=A7=8B=E6=96=87/qwwang?= Date: Mon, 18 Sep 2023 16:45:42 +0800 Subject: [PATCH 111/186] fix --- modules/sd_models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/sd_models.py b/modules/sd_models.py index 6d17dd3c..eedb38c6 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -309,6 +309,7 @@ def get_checkpoint_state_dict(checkpoint_info: CheckpointInfo, timer): if checkpoint_info in checkpoints_loaded: # use checkpoint cache print(f"Loading weights [{sd_model_hash}] from cache") + # move to end as latest checkpoints_loaded.move_to_end(checkpoint_info) return checkpoints_loaded[checkpoint_info] From 702a1e1cc70240f2adbcfb707a644a5a98b5443c Mon Sep 17 00:00:00 2001 From: superhero-7 <537093830@qq.com> Date: Sat, 23 Sep 2023 17:51:41 +0800 Subject: [PATCH 112/186] support m18 --- configs/alt-diffusion-m18-inference.yaml | 73 ++++++++++ modules/sd_hijack.py | 6 +- modules/sd_models_config.py | 6 +- modules/xlmr_m18.py | 164 +++++++++++++++++++++++ 4 files changed, 244 insertions(+), 5 deletions(-) create mode 100644 configs/alt-diffusion-m18-inference.yaml create mode 100644 modules/xlmr_m18.py diff --git a/configs/alt-diffusion-m18-inference.yaml b/configs/alt-diffusion-m18-inference.yaml new file mode 100644 index 00000000..41a031d5 --- /dev/null +++ b/configs/alt-diffusion-m18-inference.yaml @@ -0,0 +1,73 @@ +model: + base_learning_rate: 1.0e-04 + target: ldm.models.diffusion.ddpm.LatentDiffusion + params: + linear_start: 0.00085 + linear_end: 0.0120 + num_timesteps_cond: 1 + log_every_t: 200 + timesteps: 1000 + first_stage_key: "jpg" + cond_stage_key: "txt" + image_size: 64 + channels: 4 + cond_stage_trainable: false # Note: different from the one we trained before + conditioning_key: crossattn + monitor: val/loss_simple_ema + scale_factor: 0.18215 + use_ema: False + + scheduler_config: # 10000 warmup steps + target: ldm.lr_scheduler.LambdaLinearScheduler + params: + warm_up_steps: [ 10000 ] + cycle_lengths: [ 10000000000000 ] # incredibly large number to prevent corner cases + f_start: [ 1.e-6 ] + f_max: [ 1. ] + f_min: [ 1. ] + + unet_config: + target: ldm.modules.diffusionmodules.openaimodel.UNetModel + params: + image_size: 32 # unused + in_channels: 4 + out_channels: 4 + model_channels: 320 + attention_resolutions: [ 4, 2, 1 ] + num_res_blocks: 2 + channel_mult: [ 1, 2, 4, 4 ] + num_head_channels: 64 + use_spatial_transformer: True + use_linear_in_transformer: True + transformer_depth: 1 + context_dim: 1024 + use_checkpoint: True + legacy: False + + first_stage_config: + target: ldm.models.autoencoder.AutoencoderKL + params: + embed_dim: 4 + monitor: val/rec_loss + ddconfig: + double_z: true + z_channels: 4 + resolution: 256 + in_channels: 3 + out_ch: 3 + ch: 128 + ch_mult: + - 1 + - 2 + - 4 + - 4 + num_res_blocks: 2 + attn_resolutions: [] + dropout: 0.0 + lossconfig: + target: torch.nn.Identity + + cond_stage_config: + target: modules.xlmr_m18.BertSeriesModelWithTransformation + params: + name: "XLMR-Large" diff --git a/modules/sd_hijack.py b/modules/sd_hijack.py index 592f0055..ae9b2a65 100644 --- a/modules/sd_hijack.py +++ b/modules/sd_hijack.py @@ -5,7 +5,7 @@ from types import MethodType from modules import devices, sd_hijack_optimizations, shared, script_callbacks, errors, sd_unet from modules.hypernetworks import hypernetwork from modules.shared import cmd_opts -from modules import sd_hijack_clip, sd_hijack_open_clip, sd_hijack_unet, sd_hijack_xlmr, xlmr +from modules import sd_hijack_clip, sd_hijack_open_clip, sd_hijack_unet, sd_hijack_xlmr, xlmr, xlmr_m18 import ldm.modules.attention import ldm.modules.diffusionmodules.model @@ -208,11 +208,10 @@ class StableDiffusionModelHijack: else: m.cond_stage_model = conditioner - if type(m.cond_stage_model) == xlmr.BertSeriesModelWithTransformation: + if type(m.cond_stage_model) == xlmr.BertSeriesModelWithTransformation or type(m.cond_stage_model) == xlmr_m18.BertSeriesModelWithTransformation: model_embeddings = m.cond_stage_model.roberta.embeddings model_embeddings.token_embedding = EmbeddingsWithFixes(model_embeddings.word_embeddings, self) m.cond_stage_model = sd_hijack_xlmr.FrozenXLMREmbedderWithCustomWords(m.cond_stage_model, self) - elif type(m.cond_stage_model) == ldm.modules.encoders.modules.FrozenCLIPEmbedder: model_embeddings = m.cond_stage_model.transformer.text_model.embeddings model_embeddings.token_embedding = EmbeddingsWithFixes(model_embeddings.token_embedding, self) @@ -258,7 +257,6 @@ class StableDiffusionModelHijack: if hasattr(m, 'cond_stage_model'): delattr(m, 'cond_stage_model') - elif type(m.cond_stage_model) == sd_hijack_xlmr.FrozenXLMREmbedderWithCustomWords: m.cond_stage_model = m.cond_stage_model.wrapped diff --git a/modules/sd_models_config.py b/modules/sd_models_config.py index 08dd03f1..9ba89dfc 100644 --- a/modules/sd_models_config.py +++ b/modules/sd_models_config.py @@ -21,7 +21,7 @@ config_unopenclip = os.path.join(sd_repo_configs_path, "v2-1-stable-unclip-h-inf config_inpainting = os.path.join(sd_configs_path, "v1-inpainting-inference.yaml") config_instruct_pix2pix = os.path.join(sd_configs_path, "instruct-pix2pix.yaml") config_alt_diffusion = os.path.join(sd_configs_path, "alt-diffusion-inference.yaml") - +config_alt_diffusion_m18 = os.path.join(sd_configs_path, "alt-diffusion-m18-inference.yaml") def is_using_v_parameterization_for_sd2(state_dict): """ @@ -95,7 +95,11 @@ def guess_model_config_from_state_dict(sd, filename): if diffusion_model_input.shape[1] == 8: return config_instruct_pix2pix + + # import pdb; pdb.set_trace() if sd.get('cond_stage_model.roberta.embeddings.word_embeddings.weight', None) is not None: + if sd.get('cond_stage_model.transformation.weight').size()[0] == 1024: + return config_alt_diffusion_m18 return config_alt_diffusion return config_default diff --git a/modules/xlmr_m18.py b/modules/xlmr_m18.py new file mode 100644 index 00000000..18785692 --- /dev/null +++ b/modules/xlmr_m18.py @@ -0,0 +1,164 @@ +from transformers import BertPreTrainedModel,BertModel,BertConfig +import torch.nn as nn +import torch +from transformers.models.xlm_roberta.configuration_xlm_roberta import XLMRobertaConfig +from transformers import XLMRobertaModel,XLMRobertaTokenizer +from typing import Optional + +class BertSeriesConfig(BertConfig): + def __init__(self, vocab_size=30522, hidden_size=768, num_hidden_layers=12, num_attention_heads=12, intermediate_size=3072, hidden_act="gelu", hidden_dropout_prob=0.1, attention_probs_dropout_prob=0.1, max_position_embeddings=512, type_vocab_size=2, initializer_range=0.02, layer_norm_eps=1e-12, pad_token_id=0, position_embedding_type="absolute", use_cache=True, classifier_dropout=None,project_dim=512, pooler_fn="average",learn_encoder=False,model_type='bert',**kwargs): + + super().__init__(vocab_size, hidden_size, num_hidden_layers, num_attention_heads, intermediate_size, hidden_act, hidden_dropout_prob, attention_probs_dropout_prob, max_position_embeddings, type_vocab_size, initializer_range, layer_norm_eps, pad_token_id, position_embedding_type, use_cache, classifier_dropout, **kwargs) + self.project_dim = project_dim + self.pooler_fn = pooler_fn + self.learn_encoder = learn_encoder + +class RobertaSeriesConfig(XLMRobertaConfig): + def __init__(self, pad_token_id=1, bos_token_id=0, eos_token_id=2,project_dim=512,pooler_fn='cls',learn_encoder=False, **kwargs): + super().__init__(pad_token_id=pad_token_id, bos_token_id=bos_token_id, eos_token_id=eos_token_id, **kwargs) + self.project_dim = project_dim + self.pooler_fn = pooler_fn + self.learn_encoder = learn_encoder + + +class BertSeriesModelWithTransformation(BertPreTrainedModel): + + _keys_to_ignore_on_load_unexpected = [r"pooler"] + _keys_to_ignore_on_load_missing = [r"position_ids", r"predictions.decoder.bias"] + config_class = BertSeriesConfig + + def __init__(self, config=None, **kargs): + # modify initialization for autoloading + if config is None: + config = XLMRobertaConfig() + config.attention_probs_dropout_prob= 0.1 + config.bos_token_id=0 + config.eos_token_id=2 + config.hidden_act='gelu' + config.hidden_dropout_prob=0.1 + config.hidden_size=1024 + config.initializer_range=0.02 + config.intermediate_size=4096 + config.layer_norm_eps=1e-05 + config.max_position_embeddings=514 + + config.num_attention_heads=16 + config.num_hidden_layers=24 + config.output_past=True + config.pad_token_id=1 + config.position_embedding_type= "absolute" + + config.type_vocab_size= 1 + config.use_cache=True + config.vocab_size= 250002 + config.project_dim = 1024 + config.learn_encoder = False + super().__init__(config) + self.roberta = XLMRobertaModel(config) + self.transformation = nn.Linear(config.hidden_size,config.project_dim) + # self.pre_LN=nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.tokenizer = XLMRobertaTokenizer.from_pretrained('xlm-roberta-large') + # self.pooler = lambda x: x[:,0] + # self.post_init() + + self.has_pre_transformation = True + if self.has_pre_transformation: + self.transformation_pre = nn.Linear(config.hidden_size, config.project_dim) + self.pre_LN = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.post_init() + + def encode(self,c): + device = next(self.parameters()).device + text = self.tokenizer(c, + truncation=True, + max_length=77, + return_length=False, + return_overflowing_tokens=False, + padding="max_length", + return_tensors="pt") + text["input_ids"] = torch.tensor(text["input_ids"]).to(device) + text["attention_mask"] = torch.tensor( + text['attention_mask']).to(device) + features = self(**text) + return features['projection_state'] + + def forward( + self, + input_ids: Optional[torch.Tensor] = None, + attention_mask: Optional[torch.Tensor] = None, + token_type_ids: Optional[torch.Tensor] = None, + position_ids: Optional[torch.Tensor] = None, + head_mask: Optional[torch.Tensor] = None, + inputs_embeds: Optional[torch.Tensor] = None, + encoder_hidden_states: Optional[torch.Tensor] = None, + encoder_attention_mask: Optional[torch.Tensor] = None, + output_attentions: Optional[bool] = None, + return_dict: Optional[bool] = None, + output_hidden_states: Optional[bool] = None, + ) : + r""" + """ + + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + + + outputs = self.roberta( + input_ids=input_ids, + attention_mask=attention_mask, + token_type_ids=token_type_ids, + position_ids=position_ids, + head_mask=head_mask, + inputs_embeds=inputs_embeds, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_attention_mask, + output_attentions=output_attentions, + output_hidden_states=True, + return_dict=return_dict, + ) + + # # last module outputs + # sequence_output = outputs[0] + + + # # project every module + # sequence_output_ln = self.pre_LN(sequence_output) + + # # pooler + # pooler_output = self.pooler(sequence_output_ln) + # pooler_output = self.transformation(pooler_output) + # projection_state = self.transformation(outputs.last_hidden_state) + + if self.has_pre_transformation: + sequence_output2 = outputs["hidden_states"][-2] + sequence_output2 = self.pre_LN(sequence_output2) + projection_state2 = self.transformation_pre(sequence_output2) + + return { + "projection_state": projection_state2, + "last_hidden_state": outputs.last_hidden_state, + "hidden_states": outputs.hidden_states, + "attentions": outputs.attentions, + } + else: + projection_state = self.transformation(outputs.last_hidden_state) + return { + "projection_state": projection_state, + "last_hidden_state": outputs.last_hidden_state, + "hidden_states": outputs.hidden_states, + "attentions": outputs.attentions, + } + + + # return { + # 'pooler_output':pooler_output, + # 'last_hidden_state':outputs.last_hidden_state, + # 'hidden_states':outputs.hidden_states, + # 'attentions':outputs.attentions, + # 'projection_state':projection_state, + # 'sequence_out': sequence_output + # } + + +class RobertaSeriesModelWithTransformation(BertSeriesModelWithTransformation): + base_model_prefix = 'roberta' + config_class= RobertaSeriesConfig \ No newline at end of file From f8f4ff2bb8f56877dede466934dd8ddf25c21063 Mon Sep 17 00:00:00 2001 From: superhero-7 <537093830@qq.com> Date: Sat, 23 Sep 2023 17:55:19 +0800 Subject: [PATCH 113/186] support altdiffusion-m18 --- modules/sd_hijack.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/sd_hijack.py b/modules/sd_hijack.py index ae9b2a65..4b36c0e9 100644 --- a/modules/sd_hijack.py +++ b/modules/sd_hijack.py @@ -212,6 +212,7 @@ class StableDiffusionModelHijack: model_embeddings = m.cond_stage_model.roberta.embeddings model_embeddings.token_embedding = EmbeddingsWithFixes(model_embeddings.word_embeddings, self) m.cond_stage_model = sd_hijack_xlmr.FrozenXLMREmbedderWithCustomWords(m.cond_stage_model, self) + elif type(m.cond_stage_model) == ldm.modules.encoders.modules.FrozenCLIPEmbedder: model_embeddings = m.cond_stage_model.transformer.text_model.embeddings model_embeddings.token_embedding = EmbeddingsWithFixes(model_embeddings.token_embedding, self) @@ -257,6 +258,7 @@ class StableDiffusionModelHijack: if hasattr(m, 'cond_stage_model'): delattr(m, 'cond_stage_model') + elif type(m.cond_stage_model) == sd_hijack_xlmr.FrozenXLMREmbedderWithCustomWords: m.cond_stage_model = m.cond_stage_model.wrapped From fdecf813b63db4a4e49b92ebfdf705a0d4047287 Mon Sep 17 00:00:00 2001 From: ezt19 <136929737+ezt19@users.noreply.github.com> Date: Sat, 23 Sep 2023 20:41:28 +0000 Subject: [PATCH 114/186] Update dragdrop.js Fixing a problem when u cannot put two images and they are going into two different places for images. --- javascript/dragdrop.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/dragdrop.js b/javascript/dragdrop.js index 5803daea..d680daf5 100644 --- a/javascript/dragdrop.js +++ b/javascript/dragdrop.js @@ -119,7 +119,7 @@ window.addEventListener('paste', e => { } const firstFreeImageField = visibleImageFields - .filter(el => el.querySelector('input[type=file]'))?.[0]; + .filter(el => !el.querySelector('img'))?.[0]; dropReplaceImage( firstFreeImageField ? From d00f6dca2825c2a73bbc1a5c707be276d62acc6b Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Mon, 25 Sep 2023 22:08:24 -0600 Subject: [PATCH 115/186] Escape item names --- modules/ui_extra_networks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 063bd7b8..60b95f21 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -213,9 +213,9 @@ class ExtraNetworksPage: metadata_button = "" metadata = item.get("metadata") if metadata: - metadata_button = f"" + metadata_button = f"" - edit_button = f"
" + edit_button = f"
" local_path = "" filename = item.get("filename", "") From 99aa702015b2a7c6707081cc975cfd12c40d55c4 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Tue, 26 Sep 2023 21:08:55 -0600 Subject: [PATCH 116/186] Update card on correct tab --- javascript/extraNetworks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 493f31af..e927346c 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -335,7 +335,7 @@ function extraNetworksEditUserMetadata(event, tabname, extraPage, cardName) { function extraNetworksRefreshSingleCard(page, tabname, name) { requestGet("./sd_extra_networks/get-single-card", {page: page, tabname: tabname, name: name}, function(data) { if (data && data.html) { - var card = gradioApp().querySelector('.card[data-name=' + JSON.stringify(name) + ']'); // likely using the wrong stringify function + var card = gradioApp().querySelector(`#${tabname}_${page.replace(" ", "_")}_cards > .card[data-name="${name}"]`); var newDiv = document.createElement('DIV'); newDiv.innerHTML = data.html; From a69daae012458bbd3d2cc472dc757fd78090ae05 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Tue, 26 Sep 2023 22:02:52 -0600 Subject: [PATCH 117/186] Fix data-sort-name containing spaces --- modules/ui_extra_networks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 063bd7b8..799bd174 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -235,7 +235,7 @@ class ExtraNetworksPage: if search_only and shared.opts.extra_networks_hidden_models == "Never": return "" - sort_keys = " ".join([html.escape(f'data-sort-{k}={v}') for k, v in item.get("sort_keys", {}).items()]).strip() + sort_keys = " ".join([f'data-sort-{k}="{html.escape(str(v))}"' for k, v in item.get("sort_keys", {}).items()]).strip() args = { "background_image": background_image, From 30f4f25b2ed77464104f31c35b40f5994ffce67b Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 27 Sep 2023 10:21:14 +0300 Subject: [PATCH 118/186] Bump to torchsde==0.2.6 --- requirements_versions.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_versions.txt b/requirements_versions.txt index f8ae1f38..7d27f2be 100644 --- a/requirements_versions.txt +++ b/requirements_versions.txt @@ -27,5 +27,5 @@ timm==0.9.2 tomesd==0.1.3 torch torchdiffeq==0.2.3 -torchsde==0.2.5 +torchsde==0.2.6 transformers==4.30.2 From ad3b8a1c41b8ab98b8b98c6364d13cb8b6d5fa88 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 30 Sep 2023 08:23:12 +0300 Subject: [PATCH 119/186] alternative solution to #13434 --- modules/restart.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/restart.py b/modules/restart.py index 18eacaf3..2dd6493b 100644 --- a/modules/restart.py +++ b/modules/restart.py @@ -14,7 +14,9 @@ def is_restartable() -> bool: def restart_program() -> None: """creates file tmp/restart and immediately stops the process, which webui.bat/webui.sh interpret as a command to start webui again""" - (Path(script_path) / "tmp" / "restart").touch() + tmpdir = Path(script_path) / "tmp" + tmpdir.mkdir(parents=True, exist_ok=True) + (tmpdir / "restart").touch() stop_program() From 87b50397a6da273fe0160016a209e4eb0cbf4a89 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 30 Sep 2023 09:11:31 +0300 Subject: [PATCH 120/186] add missing import, simplify code, use patches module for #13276 --- modules/sd_models.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/modules/sd_models.py b/modules/sd_models.py index e3755253..5ef7aa13 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -14,7 +14,7 @@ import ldm.modules.midas as midas from ldm.util import instantiate_from_config -from modules import paths, shared, modelloader, devices, script_callbacks, sd_vae, sd_disable_initialization, errors, hashes, sd_models_config, sd_unet, sd_models_xl, cache, extra_networks, processing, lowvram, sd_hijack +from modules import paths, shared, modelloader, devices, script_callbacks, sd_vae, sd_disable_initialization, errors, hashes, sd_models_config, sd_unet, sd_models_xl, cache, extra_networks, processing, lowvram, sd_hijack, patches from modules.timer import Timer import tomesd import numpy as np @@ -130,6 +130,8 @@ except Exception: def setup_model(): + """called once at startup to do various one-time tasks related to SD models""" + os.makedirs(model_path, exist_ok=True) enable_midas_autodownload() @@ -458,14 +460,17 @@ def enable_midas_autodownload(): def patch_given_betas(): - original_register_schedule = ldm.models.diffusion.ddpm.DDPM.register_schedule + import ldm.models.diffusion.ddpm + def patched_register_schedule(*args, **kwargs): - if args[1] is not None and isinstance(args[1], ListConfig): - modified_args = list(args) # Convert args tuple to a list - modified_args[1] = np.array(args[1]) # Modify the desired element - args = tuple(modified_args) # Convert the list back to a tuple + """a modified version of register_schedule function that converts plain list from Omegaconf into numpy""" + + if isinstance(args[1], ListConfig): + args = (args[0], np.array(args[1]), *args[2:]) + original_register_schedule(*args, **kwargs) - ldm.models.diffusion.ddpm.DDPM.register_schedule = patched_register_schedule + + original_register_schedule = patches.patch(__name__, ldm.models.diffusion.ddpm.DDPM, 'register_schedule', patched_register_schedule) def repair_config(sd_config): From ab63054f95a7b7dfb2f0ffa88e805c8d2f950605 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 30 Sep 2023 09:34:50 +0300 Subject: [PATCH 121/186] write infotext to gif image as comment --- modules/images.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/images.py b/modules/images.py index 1e63118e..daf4eebe 100644 --- a/modules/images.py +++ b/modules/images.py @@ -561,6 +561,8 @@ def save_image_with_geninfo(image, geninfo, filename, extension=None, existing_p }) piexif.insert(exif_bytes, filename) + elif extension.lower() == ".gif": + image.save(filename, format=image_format, comment=geninfo) else: image.save(filename, format=image_format, quality=opts.jpeg_quality) From 1cc7c4bfb31b80b6667154145f1455541951db18 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Sat, 30 Sep 2023 01:09:09 -0600 Subject: [PATCH 122/186] Allow editing whitespace delimiters --- javascript/edit-attention.js | 3 ++- modules/shared_options.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index 8906c892..bc4ebed4 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -46,7 +46,8 @@ function keyupEditAttention(event) { function selectCurrentWord() { if (selectionStart !== selectionEnd) return false; - const delimiters = opts.keyedit_delimiters + " \r\n\t"; + let delimiters = opts.keyedit_delimiters.replace(/(^|[^\\])(\\\\)*\\$/, "$&\\").replace(/(^|[^\\])((\\\\)*")/g, "$1\\$2"); + delimiters = JSON.parse(`"${delimiters}"`); // seek backward until to find beggining while (!delimiters.includes(text[selectionStart - 1]) && selectionStart > 0) { diff --git a/modules/shared_options.py b/modules/shared_options.py index 00b273fa..a1f157c6 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -255,7 +255,7 @@ options_templates.update(options_section(('ui', "User interface"), { "dimensions_and_batch_together": OptionInfo(True, "Show Width/Height and Batch sliders in same row").needs_reload_ui(), "keyedit_precision_attention": OptionInfo(0.1, "Ctrl+up/down precision when editing (attention:1.1)", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}), "keyedit_precision_extra": OptionInfo(0.05, "Ctrl+up/down precision when editing ", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}), - "keyedit_delimiters": OptionInfo(".,\\/!?%^*;:{}=`~()", "Ctrl+up/down word delimiters"), + "keyedit_delimiters": OptionInfo(r".,\\/!?%^*;:{}=`~() \r\n\t", "Ctrl+up/down word delimiters"), "keyedit_move": OptionInfo(True, "Alt+left/right moves prompt elements"), "quicksettings_list": OptionInfo(["sd_model_checkpoint"], "Quicksettings list", ui_components.DropdownMulti, lambda: {"choices": list(shared.opts.data_labels.keys())}).js("info", "settingsHintsShowQuicksettings").info("setting entries that appear at the top of page rather than in settings tab").needs_reload_ui(), "ui_tab_order": OptionInfo([], "UI tab order", ui_components.DropdownMulti, lambda: {"choices": list(shared.tab_names)}).needs_reload_ui(), From 5cc7bf387661354e5c268c0e8198cc44328b3282 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 30 Sep 2023 10:10:57 +0300 Subject: [PATCH 123/186] reword sd_checkpoint_dropdown_use_short setting and add explanation --- modules/shared_options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/shared_options.py b/modules/shared_options.py index 8e8d402d..61b93d47 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -262,7 +262,7 @@ options_templates.update(options_section(('ui', "User interface"), { "ui_tab_order": OptionInfo([], "UI tab order", ui_components.DropdownMulti, lambda: {"choices": list(shared.tab_names)}).needs_reload_ui(), "hidden_tabs": OptionInfo([], "Hidden UI tabs", ui_components.DropdownMulti, lambda: {"choices": list(shared.tab_names)}).needs_reload_ui(), "ui_reorder_list": OptionInfo([], "txt2img/img2img UI item order", ui_components.DropdownMulti, lambda: {"choices": list(shared_items.ui_reorder_categories())}).info("selected items appear first").needs_reload_ui(), - "sd_checkpoint_dropdown_use_short": OptionInfo(False, "Use short name for Stable Diffusion checkpoint dropdown"), + "sd_checkpoint_dropdown_use_short": OptionInfo(False, "Checkpoint dropdown: use filenames without paths").info("models in subdirectories like photo/sd15.ckpt will be listed as just sd15.ckpt"), "hires_fix_show_sampler": OptionInfo(False, "Hires fix: show hires checkpoint and sampler selection").needs_reload_ui(), "hires_fix_show_prompts": OptionInfo(False, "Hires fix: show hires prompt and negative prompt").needs_reload_ui(), "disable_token_counters": OptionInfo(False, "Disable prompt token counters").needs_reload_ui(), From b2f9709538ee40c6bbf11e3f17f7e3ea4b9cb78a Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 30 Sep 2023 10:29:10 +0300 Subject: [PATCH 124/186] get #13121 to work without restart --- modules/ui_extra_networks.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index d8c31142..59d6ecc6 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -1,3 +1,4 @@ +import functools import os.path import urllib.parse from pathlib import Path @@ -15,10 +16,16 @@ from modules.ui_components import ToolButton extra_pages = [] allowed_dirs = set() -allowed_preview_extensions = ["png", "jpg", "jpeg", "webp", "gif"] -if shared.opts.samples_format not in allowed_preview_extensions: - allowed_preview_extensions.append(shared.opts.samples_format) -allowed_preview_extensions_dot = ['.' + extension for extension in allowed_preview_extensions] +default_allowed_preview_extensions = ["png", "jpg", "jpeg", "webp", "gif"] + + +@functools.cache +def allowed_preview_extensions_with_extra(extra_extensions=None): + return set(default_allowed_preview_extensions) | set(extra_extensions or []) + + +def allowed_preview_extensions(): + return allowed_preview_extensions_with_extra((shared.opts.samples_format, )) def register_page(page): @@ -38,9 +45,9 @@ def fetch_file(filename: str = ""): if not any(Path(x).absolute() in Path(filename).absolute().parents for x in allowed_dirs): raise ValueError(f"File cannot be fetched: {filename}. Must be in one of directories registered by extra pages.") - ext = os.path.splitext(filename)[1].lower() - if ext not in allowed_preview_extensions_dot: - raise ValueError(f"File cannot be fetched: {filename}. Only png, jpg, webp, and gif.") + ext = os.path.splitext(filename)[1].lower()[1:] + if ext not in allowed_preview_extensions(): + raise ValueError(f"File cannot be fetched: {filename}. Extensions allowed: {allowed_preview_extensions()}.") # would profit from returning 304 return FileResponse(filename, headers={"Accept-Ranges": "bytes"}) @@ -278,7 +285,7 @@ class ExtraNetworksPage: Find a preview PNG for a given path (without extension) and call link_preview on it. """ - potential_files = sum([[path + "." + ext, path + ".preview." + ext] for ext in allowed_preview_extensions], []) + potential_files = sum([[path + "." + ext, path + ".preview." + ext] for ext in allowed_preview_extensions()], []) for file in potential_files: if os.path.isfile(file): From 0935d2c3047210b799cbc6f8ce15d3dffca95af7 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Sat, 30 Sep 2023 18:37:44 -0600 Subject: [PATCH 125/186] Use checkboxes for whitespace delimiters --- javascript/edit-attention.js | 8 ++++++-- modules/shared_options.py | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index bc4ebed4..943d81b0 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -46,8 +46,12 @@ function keyupEditAttention(event) { function selectCurrentWord() { if (selectionStart !== selectionEnd) return false; - let delimiters = opts.keyedit_delimiters.replace(/(^|[^\\])(\\\\)*\\$/, "$&\\").replace(/(^|[^\\])((\\\\)*")/g, "$1\\$2"); - delimiters = JSON.parse(`"${delimiters}"`); + const whitespace_delimiters = {"Tab": "\t", "Carriage Return": "\r", "Line Feed": "\n"}; + let delimiters = opts.keyedit_delimiters; + + for (let i of opts.keyedit_delimiters_whitespace) { + delimiters += whitespace_delimiters[i]; + } // seek backward until to find beggining while (!delimiters.includes(text[selectionStart - 1]) && selectionStart > 0) { diff --git a/modules/shared_options.py b/modules/shared_options.py index a1f157c6..717c948b 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -255,7 +255,8 @@ options_templates.update(options_section(('ui', "User interface"), { "dimensions_and_batch_together": OptionInfo(True, "Show Width/Height and Batch sliders in same row").needs_reload_ui(), "keyedit_precision_attention": OptionInfo(0.1, "Ctrl+up/down precision when editing (attention:1.1)", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}), "keyedit_precision_extra": OptionInfo(0.05, "Ctrl+up/down precision when editing ", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}), - "keyedit_delimiters": OptionInfo(r".,\\/!?%^*;:{}=`~() \r\n\t", "Ctrl+up/down word delimiters"), + "keyedit_delimiters": OptionInfo(r".,\/!?%^*;:{}=`~() ", "Ctrl+up/down word delimiters"), + "keyedit_delimiters_whitespace": OptionInfo(["Tab", "Carriage Return", "Line Feed"], "Ctrl+up/down whitespace delimiters", gr.CheckboxGroup, lambda: {"choices": ["Tab", "Carriage Return", "Line Feed"]}), "keyedit_move": OptionInfo(True, "Alt+left/right moves prompt elements"), "quicksettings_list": OptionInfo(["sd_model_checkpoint"], "Quicksettings list", ui_components.DropdownMulti, lambda: {"choices": list(shared.opts.data_labels.keys())}).js("info", "settingsHintsShowQuicksettings").info("setting entries that appear at the top of page rather than in settings tab").needs_reload_ui(), "ui_tab_order": OptionInfo([], "UI tab order", ui_components.DropdownMulti, lambda: {"choices": list(shared.tab_names)}).needs_reload_ui(), From 0eb5fde2fd42184f431ccba5f25d714272e3e6b0 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Sat, 30 Sep 2023 21:20:58 -0600 Subject: [PATCH 126/186] Remove unneeded code --- javascript/edit-attention.js | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index 943d81b0..df218c1e 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -18,22 +18,11 @@ function keyupEditAttention(event) { const before = text.substring(0, selectionStart); let beforeParen = before.lastIndexOf(OPEN); if (beforeParen == -1) return false; - let beforeParenClose = before.lastIndexOf(CLOSE); - while (beforeParenClose !== -1 && beforeParenClose > beforeParen) { - beforeParen = before.lastIndexOf(OPEN, beforeParen - 1); - beforeParenClose = before.lastIndexOf(CLOSE, beforeParenClose - 1); - } // Find closing parenthesis around current cursor const after = text.substring(selectionStart); let afterParen = after.indexOf(CLOSE); if (afterParen == -1) return false; - let afterParenOpen = after.indexOf(OPEN); - while (afterParenOpen !== -1 && afterParen > afterParenOpen) { - afterParen = after.indexOf(CLOSE, afterParen + 1); - afterParenOpen = after.indexOf(OPEN, afterParenOpen + 1); - } - if (beforeParen === -1 || afterParen === -1) return false; // Set the selection to the text between the parenthesis const parenContent = text.substring(beforeParen + 1, selectionStart + afterParen); @@ -53,7 +42,7 @@ function keyupEditAttention(event) { delimiters += whitespace_delimiters[i]; } - // seek backward until to find beggining + // seek backward to find beginning while (!delimiters.includes(text[selectionStart - 1]) && selectionStart > 0) { selectionStart--; } From 56ef5e9d48750fd43f9faba31ff67e64368153b7 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Sat, 30 Sep 2023 21:44:05 -0600 Subject: [PATCH 127/186] Remove end parenthesis from weight --- javascript/edit-attention.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index df218c1e..794453bf 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -86,7 +86,7 @@ function keyupEditAttention(event) { } var end = text.slice(selectionEnd + 1).indexOf(closeCharacter) + 1; - var weight = parseFloat(text.slice(selectionEnd + 1, selectionEnd + 1 + end)); + var weight = parseFloat(text.slice(selectionEnd + 1, selectionEnd + end)); if (isNaN(weight)) return; weight += isPlus ? delta : -delta; From 2d947175b902d6838c803036d9757e7d3226b41d Mon Sep 17 00:00:00 2001 From: superhero-7 <537093830@qq.com> Date: Sun, 1 Oct 2023 12:25:19 +0800 Subject: [PATCH 128/186] fix linter issues --- modules/sd_hijack.py | 4 ++-- modules/sd_models_config.py | 3 +-- modules/xlmr_m18.py | 12 ++++++------ 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/modules/sd_hijack.py b/modules/sd_hijack.py index 4b36c0e9..0689699c 100644 --- a/modules/sd_hijack.py +++ b/modules/sd_hijack.py @@ -212,7 +212,7 @@ class StableDiffusionModelHijack: model_embeddings = m.cond_stage_model.roberta.embeddings model_embeddings.token_embedding = EmbeddingsWithFixes(model_embeddings.word_embeddings, self) m.cond_stage_model = sd_hijack_xlmr.FrozenXLMREmbedderWithCustomWords(m.cond_stage_model, self) - + elif type(m.cond_stage_model) == ldm.modules.encoders.modules.FrozenCLIPEmbedder: model_embeddings = m.cond_stage_model.transformer.text_model.embeddings model_embeddings.token_embedding = EmbeddingsWithFixes(model_embeddings.token_embedding, self) @@ -258,7 +258,7 @@ class StableDiffusionModelHijack: if hasattr(m, 'cond_stage_model'): delattr(m, 'cond_stage_model') - + elif type(m.cond_stage_model) == sd_hijack_xlmr.FrozenXLMREmbedderWithCustomWords: m.cond_stage_model = m.cond_stage_model.wrapped diff --git a/modules/sd_models_config.py b/modules/sd_models_config.py index 9ba89dfc..deab2f6e 100644 --- a/modules/sd_models_config.py +++ b/modules/sd_models_config.py @@ -95,8 +95,7 @@ def guess_model_config_from_state_dict(sd, filename): if diffusion_model_input.shape[1] == 8: return config_instruct_pix2pix - - # import pdb; pdb.set_trace() + if sd.get('cond_stage_model.roberta.embeddings.word_embeddings.weight', None) is not None: if sd.get('cond_stage_model.transformation.weight').size()[0] == 1024: return config_alt_diffusion_m18 diff --git a/modules/xlmr_m18.py b/modules/xlmr_m18.py index 18785692..a727e865 100644 --- a/modules/xlmr_m18.py +++ b/modules/xlmr_m18.py @@ -1,4 +1,4 @@ -from transformers import BertPreTrainedModel,BertModel,BertConfig +from transformers import BertPreTrainedModel,BertConfig import torch.nn as nn import torch from transformers.models.xlm_roberta.configuration_xlm_roberta import XLMRobertaConfig @@ -28,7 +28,7 @@ class BertSeriesModelWithTransformation(BertPreTrainedModel): config_class = BertSeriesConfig def __init__(self, config=None, **kargs): - # modify initialization for autoloading + # modify initialization for autoloading if config is None: config = XLMRobertaConfig() config.attention_probs_dropout_prob= 0.1 @@ -80,7 +80,7 @@ class BertSeriesModelWithTransformation(BertPreTrainedModel): text["attention_mask"] = torch.tensor( text['attention_mask']).to(device) features = self(**text) - return features['projection_state'] + return features['projection_state'] def forward( self, @@ -147,8 +147,8 @@ class BertSeriesModelWithTransformation(BertPreTrainedModel): "hidden_states": outputs.hidden_states, "attentions": outputs.attentions, } - - + + # return { # 'pooler_output':pooler_output, # 'last_hidden_state':outputs.last_hidden_state, @@ -161,4 +161,4 @@ class BertSeriesModelWithTransformation(BertPreTrainedModel): class RobertaSeriesModelWithTransformation(BertSeriesModelWithTransformation): base_model_prefix = 'roberta' - config_class= RobertaSeriesConfig \ No newline at end of file + config_class= RobertaSeriesConfig From c7e810a9856641fbaf520976fde24c5536a4fd56 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 1 Oct 2023 10:15:23 +0300 Subject: [PATCH 129/186] add onEdit function for js and rework token-counter.js to use it --- .eslintrc.js | 1 + javascript/token-counters.js | 26 +++++++++----------------- javascript/ui.js | 17 +++++++++++++++++ 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 4777c276..cf839769 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -74,6 +74,7 @@ module.exports = { create_submit_args: "readonly", restart_reload: "readonly", updateInput: "readonly", + onEdit: "readonly", //extraNetworks.js requestGet: "readonly", popup: "readonly", diff --git a/javascript/token-counters.js b/javascript/token-counters.js index 9d81a723..710345eb 100644 --- a/javascript/token-counters.js +++ b/javascript/token-counters.js @@ -1,10 +1,9 @@ -let promptTokenCountDebounceTime = 800; -let promptTokenCountTimeouts = {}; -var promptTokenCountUpdateFunctions = {}; +let promptTokenCountUpdateFunctions = {}; function update_txt2img_tokens(...args) { // Called from Gradio update_token_counter("txt2img_token_button"); + update_token_counter("txt2img_negative_token_button"); if (args.length == 2) { return args[0]; } @@ -14,6 +13,7 @@ function update_txt2img_tokens(...args) { function update_img2img_tokens(...args) { // Called from Gradio update_token_counter("img2img_token_button"); + update_token_counter("img2img_negative_token_button"); if (args.length == 2) { return args[0]; } @@ -21,16 +21,7 @@ function update_img2img_tokens(...args) { } function update_token_counter(button_id) { - if (opts.disable_token_counters) { - return; - } - if (promptTokenCountTimeouts[button_id]) { - clearTimeout(promptTokenCountTimeouts[button_id]); - } - promptTokenCountTimeouts[button_id] = setTimeout( - () => gradioApp().getElementById(button_id)?.click(), - promptTokenCountDebounceTime, - ); + promptTokenCountUpdateFunctions[button_id]?.(); } @@ -69,10 +60,11 @@ function setupTokenCounting(id, id_counter, id_button) { prompt.parentElement.insertBefore(counter, prompt); prompt.parentElement.style.position = "relative"; - promptTokenCountUpdateFunctions[id] = function() { - update_token_counter(id_button); - }; - textarea.addEventListener("input", promptTokenCountUpdateFunctions[id]); + func = onEdit(id, textarea, 800, function() { + gradioApp().getElementById(id_button)?.click(); + }); + promptTokenCountUpdateFunctions[id] = func; + promptTokenCountUpdateFunctions[id_button] = func; } function setupTokenCounters() { diff --git a/javascript/ui.js b/javascript/ui.js index bedcbf3e..aee0d1da 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -366,3 +366,20 @@ function switchWidthHeight(tabname) { updateInput(height); return []; } + + +var onEditTimers = {}; + +// calls func after afterMs milliseconds has passed since the input elem has beed enited by user +function onEdit(editId, elem, afterMs, func) { + var edited = function() { + var existingTimer = onEditTimers[editId]; + if (existingTimer) clearTimeout(existingTimer); + + onEditTimers[editId] = setTimeout(func, afterMs); + }; + + elem.addEventListener("input", edited); + + return edited; +} From deeec0b34359b0cc4c93ee01d3c7bca9c2f609b8 Mon Sep 17 00:00:00 2001 From: Won-Kyu Park Date: Sun, 1 Oct 2023 00:25:53 +0900 Subject: [PATCH 130/186] fix fieldname regex to accept additional [-/] chars --- modules/generation_parameters_copypaste.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py index d39f2eba..f1b0225e 100644 --- a/modules/generation_parameters_copypaste.py +++ b/modules/generation_parameters_copypaste.py @@ -9,7 +9,7 @@ from modules.paths import data_path from modules import shared, ui_tempdir, script_callbacks, processing from PIL import Image -re_param_code = r'\s*([\w ]+):\s*("(?:\\.|[^\\"])+"|[^,]*)(?:,|$)' +re_param_code = r'\s*(\w[\w -/]+):\s*("(?:\\.|[^\\"])+"|[^,]*)(?:,|$)' re_param = re.compile(re_param_code) re_imagesize = re.compile(r"^(\d+)x(\d+)$") re_hypernet_hash = re.compile("\(([0-9a-f]+)\)$") From c0113872c5f814cf8cf96deca541bffaf1af2568 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 1 Oct 2023 11:48:41 +0300 Subject: [PATCH 131/186] add search field to settings --- javascript/settings.js | 46 ++++++++++++++++++++++++++++++++++++ javascript/token-counters.js | 2 +- javascript/ui.js | 15 ------------ modules/ui_settings.py | 16 +++++++++++-- style.css | 2 ++ 5 files changed, 63 insertions(+), 18 deletions(-) create mode 100644 javascript/settings.js diff --git a/javascript/settings.js b/javascript/settings.js new file mode 100644 index 00000000..7889cf1c --- /dev/null +++ b/javascript/settings.js @@ -0,0 +1,46 @@ +let settingsExcludeTabsFromShowAll = { + settings_tab_defaults: 1, + settings_tab_sysinfo: 1, + settings_tab_actions: 1, + settings_tab_licenses: 1, +}; + +function settingsShowAllTabs() { + gradioApp().querySelectorAll('#settings > div').forEach(function(elem) { + if (settingsExcludeTabsFromShowAll[elem.id]) return; + + elem.style.display = "block"; + }); +} + +function settingsShowOneTab() { + gradioApp().querySelector('#settings_show_one_page').click(); +} + +onUiLoaded(function() { + var edit = gradioApp().querySelector('#settings_search'); + var editTextarea = gradioApp().querySelector('#settings_search > label > input'); + var buttonShowAllPages = gradioApp().getElementById('settings_show_all_pages'); + var settings_tabs = gradioApp().querySelector('#settings div'); + + onEdit('settingsSearch', editTextarea, 250, function() { + var searchText = (editTextarea.value || "").trim(); + + gradioApp().querySelectorAll('#settings > div[id^=settings_] div[id^=column_settings_] > *').forEach(function(elem) { + var visible = elem.textContent.trim().indexOf(searchText) != -1; + elem.style.display = visible ? "" : "none"; + }); + + if (searchText != "") { + settingsShowAllTabs(); + } else { + settingsShowOneTab(); + } + }); + + settings_tabs.insertBefore(edit, settings_tabs.firstChild); + settings_tabs.appendChild(buttonShowAllPages); + + + buttonShowAllPages.addEventListener("click", settingsShowAllTabs); +}); diff --git a/javascript/token-counters.js b/javascript/token-counters.js index 710345eb..2ecc7d91 100644 --- a/javascript/token-counters.js +++ b/javascript/token-counters.js @@ -60,7 +60,7 @@ function setupTokenCounting(id, id_counter, id_button) { prompt.parentElement.insertBefore(counter, prompt); prompt.parentElement.style.position = "relative"; - func = onEdit(id, textarea, 800, function() { + var func = onEdit(id, textarea, 800, function() { gradioApp().getElementById(id_button)?.click(); }); promptTokenCountUpdateFunctions[id] = func; diff --git a/javascript/ui.js b/javascript/ui.js index aee0d1da..2e262602 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -263,21 +263,6 @@ onAfterUiUpdate(function() { json_elem.parentElement.style.display = "none"; setupTokenCounters(); - - var show_all_pages = gradioApp().getElementById('settings_show_all_pages'); - var settings_tabs = gradioApp().querySelector('#settings div'); - if (show_all_pages && settings_tabs) { - settings_tabs.appendChild(show_all_pages); - show_all_pages.onclick = function() { - gradioApp().querySelectorAll('#settings > div').forEach(function(elem) { - if (elem.id == "settings_tab_licenses") { - return; - } - - elem.style.display = "block"; - }); - }; - } }); onOptionsChanged(function() { diff --git a/modules/ui_settings.py b/modules/ui_settings.py index c6fe3604..74a3aef3 100644 --- a/modules/ui_settings.py +++ b/modules/ui_settings.py @@ -64,6 +64,9 @@ class UiSettings: quicksettings_list = None quicksettings_names = None text_settings = None + show_all_pages = None + show_one_page = None + search_input = None def run_settings(self, *args): changed = [] @@ -136,7 +139,7 @@ class UiSettings: gr.Group() current_tab = gr.TabItem(elem_id=f"settings_{elem_id}", label=text) current_tab.__enter__() - current_row = gr.Column(variant='compact') + current_row = gr.Column(elem_id=f"column_settings_{elem_id}", variant='compact') current_row.__enter__() previous_section = item.section @@ -183,7 +186,11 @@ class UiSettings: with gr.TabItem("Licenses", id="licenses", elem_id="settings_tab_licenses"): gr.HTML(shared.html("licenses.html"), elem_id="licenses") - gr.Button(value="Show all pages", elem_id="settings_show_all_pages") + self.show_all_pages = gr.Button(value="Show all pages", elem_id="settings_show_all_pages") + self.show_one_page = gr.Button(value="Show only one page", elem_id="settings_show_one_page", visible=False) + self.show_one_page.click(lambda: None) + + self.search_input = gr.Textbox(value="", elem_id="settings_search", max_lines=1, placeholder="Search...", show_label=False) self.text_settings = gr.Textbox(elem_id="settings_json", value=lambda: opts.dumpjson(), visible=False) @@ -313,3 +320,8 @@ class UiSettings: outputs=[self.component_dict[k] for k in component_keys], queue=False, ) + + def search(self, text): + print(text) + + return [gr.update(visible=text in (comp.label or "")) for comp in self.components] diff --git a/style.css b/style.css index 58eb29c1..eee50552 100644 --- a/style.css +++ b/style.css @@ -423,6 +423,7 @@ div#extras_scale_to_tab div.form{ #settings > div{ border: none; margin-left: 10em; + padding: 0 var(--spacing-xl); } #settings > div.tab-nav{ @@ -437,6 +438,7 @@ div#extras_scale_to_tab div.form{ border: none; text-align: left; white-space: initial; + padding: 4px; } #settings_result{ From f71e919ecb001c4d191b76a87477d6118de7be12 Mon Sep 17 00:00:00 2001 From: FluttyProger Date: Sun, 1 Oct 2023 18:06:48 +0300 Subject: [PATCH 132/186] Ability for extensions to return custom data via api in response.images --- modules/api/api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/api/api.py b/modules/api/api.py index 905ef9c9..efedafa4 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -103,7 +103,8 @@ def decode_base64_to_image(encoding): def encode_pil_to_base64(image): with io.BytesIO() as output_bytes: - + if isinstance(image, str): + return image if opts.samples_format.lower() == 'png': use_metadata = False metadata = PngImagePlugin.PngInfo() From 3f763d41e8ff7f09f89adb00eec440f18566d260 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Sun, 1 Oct 2023 22:38:27 -0600 Subject: [PATCH 133/186] Change denoising_strength default to None. --- modules/processing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/processing.py b/modules/processing.py index e124e7f0..061d9955 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -142,7 +142,7 @@ class StableDiffusionProcessing: overlay_images: list = None eta: float = None do_not_reload_embeddings: bool = False - denoising_strength: float = 0 + denoising_strength: float = None ddim_discretize: str = None s_min_uncond: float = None s_churn: float = None From 6ab0b65ed15730f590a3e530f00a2047f582b488 Mon Sep 17 00:00:00 2001 From: PermissionDenied7335 Date: Mon, 2 Oct 2023 15:43:59 +0800 Subject: [PATCH 134/186] Added an option not to enable venv --- webui.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/webui.sh b/webui.sh index 3d0f87ee..f3621903 100755 --- a/webui.sh +++ b/webui.sh @@ -4,12 +4,6 @@ # change the variables in webui-user.sh instead # ################################################# - -use_venv=1 -if [[ $venv_dir == "-" ]]; then - use_venv=0 -fi - SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) @@ -28,6 +22,12 @@ then source "$SCRIPT_DIR"/webui-user.sh fi +# If $venv_dir is "-", then disable venv support +use_venv=1 +if [[ $venv_dir == "-" ]]; then + use_venv=0 +fi + # Set defaults # Install directory without trailing slash if [[ -z "${install_dir}" ]] From c2279da52260410ac2b4f64b7de7b2866f3a07a1 Mon Sep 17 00:00:00 2001 From: Won-Kyu Park Date: Tue, 3 Oct 2023 01:16:41 +0900 Subject: [PATCH 135/186] fix re_param_code (regression bug PR #13458) --- modules/generation_parameters_copypaste.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py index f1b0225e..0a606515 100644 --- a/modules/generation_parameters_copypaste.py +++ b/modules/generation_parameters_copypaste.py @@ -9,7 +9,7 @@ from modules.paths import data_path from modules import shared, ui_tempdir, script_callbacks, processing from PIL import Image -re_param_code = r'\s*(\w[\w -/]+):\s*("(?:\\.|[^\\"])+"|[^,]*)(?:,|$)' +re_param_code = r'\s*(\w[\w \-/]+):\s*("(?:\\.|[^\\"])+"|[^,]*)(?:,|$)' re_param = re.compile(re_param_code) re_imagesize = re.compile(r"^(\d+)x(\d+)$") re_hypernet_hash = re.compile("\(([0-9a-f]+)\)$") From 86a46e81892c72cc50f9a37dfb1ca285f189134d Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Mon, 2 Oct 2023 22:22:15 -0600 Subject: [PATCH 136/186] Fix accidentally closing popup dialogs --- javascript/extraNetworks.js | 11 ++++------- style.css | 14 ++++++++++---- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 493f31af..b1b85669 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -216,27 +216,24 @@ function extraNetworksSearchButton(tabs_id, event) { var globalPopup = null; var globalPopupInner = null; + function closePopup() { if (!globalPopup) return; - globalPopup.style.display = "none"; } + function popup(contents) { if (!globalPopup) { globalPopup = document.createElement('div'); - globalPopup.onclick = closePopup; globalPopup.classList.add('global-popup'); - + var close = document.createElement('div'); close.classList.add('global-popup-close'); - close.onclick = closePopup; + close.addEventListener("click", closePopup); close.title = "Close"; globalPopup.appendChild(close); globalPopupInner = document.createElement('div'); - globalPopupInner.onclick = function(event) { - event.stopPropagation(); return false; - }; globalPopupInner.classList.add('global-popup-inner'); globalPopup.appendChild(globalPopupInner); diff --git a/style.css b/style.css index fb4e2f1f..e034ecfd 100644 --- a/style.css +++ b/style.css @@ -581,7 +581,6 @@ table.popup-table .link{ width: 100%; height: 100%; overflow: auto; - background-color: rgba(20, 20, 20, 0.95); } .global-popup *{ @@ -590,9 +589,6 @@ table.popup-table .link{ .global-popup-close:before { content: "×"; -} - -.global-popup-close{ position: fixed; right: 0.25em; top: 0; @@ -601,10 +597,20 @@ table.popup-table .link{ font-size: 32pt; } +.global-popup-close{ + position: fixed; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(20, 20, 20, 0.95); +} + .global-popup-inner{ display: inline-block; margin: auto; padding: 2em; + z-index: 1001; } /* fullpage image viewer */ From e5381320b99f657aadad0bf8f414108a96567b3c Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Mon, 2 Oct 2023 22:33:03 -0600 Subject: [PATCH 137/186] Lint --- javascript/extraNetworks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index b1b85669..439e76a3 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -226,7 +226,7 @@ function popup(contents) { if (!globalPopup) { globalPopup = document.createElement('div'); globalPopup.classList.add('global-popup'); - + var close = document.createElement('div'); close.classList.add('global-popup-close'); close.addEventListener("click", closePopup); From 7d60076b8b275771a1aa98f017aff845ef68d964 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Tue, 3 Oct 2023 16:22:32 +0300 Subject: [PATCH 138/186] case-insensitive search for settings --- javascript/settings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/settings.js b/javascript/settings.js index 7889cf1c..4e79ec00 100644 --- a/javascript/settings.js +++ b/javascript/settings.js @@ -24,10 +24,10 @@ onUiLoaded(function() { var settings_tabs = gradioApp().querySelector('#settings div'); onEdit('settingsSearch', editTextarea, 250, function() { - var searchText = (editTextarea.value || "").trim(); + var searchText = (editTextarea.value || "").trim().toLowerCase(); gradioApp().querySelectorAll('#settings > div[id^=settings_] div[id^=column_settings_] > *').forEach(function(elem) { - var visible = elem.textContent.trim().indexOf(searchText) != -1; + var visible = elem.textContent.trim().toLowerCase().indexOf(searchText) != -1; elem.style.display = visible ? "" : "none"; }); From 35fd24e857e625c090e9af3fdcef145b88bef436 Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Tue, 3 Oct 2023 23:05:48 +0900 Subject: [PATCH 139/186] Less placeholder bug_report template --- .github/ISSUE_TEMPLATE/bug_report.yml | 44 +++++---------------------- 1 file changed, 8 insertions(+), 36 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index a423f052..5876e941 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -39,10 +39,7 @@ body: label: What happened? description: Tell us what happened in a very clear and simple way placeholder: | - I tried to use txt2img with the XYZ grid script, with DPM++ SDE, DPM++ 2M SDE samplers. - It should generate a grid of 2 images but I only got 1. - - (add screenshot or screen recording if necessary) + txt2img is not working as intended. validations: required: true - type: textarea @@ -51,9 +48,9 @@ body: label: Steps to reproduce the problem description: Please provide us with precise step by step instructions on how to reproduce the bug placeholder: | - 1. Go to txt2img tab, select XYZ grid script - 2. Set axis type to `Sampler`, and select DPM++ 2M SDE, DPM++ 3M SDE - 3. Set `Sampling steps` to 1, click generate button + 1. Go to ... + 2. Press ... + 3. ... validations: required: true - type: textarea @@ -62,8 +59,7 @@ body: label: What should have happened? description: Tell us what you think the normal behavior should be placeholder: | - It should generate a grid of 2 images. - This was working in webui version 1.x.x + WebUI should ... validations: required: true - type: dropdown @@ -97,37 +93,13 @@ body: label: Console logs description: Please provide **full** cmd/terminal logs from the moment you started UI to the end of it, after the bug occured. If it's very long, provide a link to pastebin or similar service. render: Shell - placeholder: | - generating image for xyz plot: UnboundLocalError - Traceback (most recent call last): - File "B:\GitHub\stable-diffusion-webui\scripts\xyz_grid.py", line 698, in cell - res = process_images(pc) - File "B:\GitHub\stable-diffusion-webui\modules\processing.py", line 732, in process_images - res = process_images_inner(p) - File "B:\GitHub\stable-diffusion-webui\modules\processing.py", line 867, in process_images_inner - samples_ddim = p.sample(conditioning=p.c, unconditional_conditioning=p.uc, seeds=p.seeds, subseeds=p.subseeds, subseed_strength=p.subseed_strength, prompts=p.prompts) - File "B:\GitHub\stable-diffusion-webui\modules\processing.py", line 1140, in sample - samples = self.sampler.sample(self, x, conditioning, unconditional_conditioning, image_conditioning=self.txt2img_image_conditioning(x)) - File "B:\GitHub\stable-diffusion-webui\modules\sd_samplers_kdiffusion.py", line 235, in sample - samples = self.launch_sampling(steps, lambda: self.func(self.model_wrap_cfg, x, extra_args=self.sampler_extra_args, disable=False, callback=self.callback_state, **extra_params_kwargs)) - File "B:\GitHub\stable-diffusion-webui\modules\sd_samplers_common.py", line 261, in launch_sampling - return func() - File "B:\GitHub\stable-diffusion-webui\modules\sd_samplers_kdiffusion.py", line 235, in - samples = self.launch_sampling(steps, lambda: self.func(self.model_wrap_cfg, x, extra_args=self.sampler_extra_args, disable=False, callback=self.callback_state, **extra_params_kwargs)) - File "B:\GitHub\stable-diffusion-webui\venv\lib\site-packages\torch\utils\_contextlib.py", line 115, in decorate_context - return func(*args, **kwargs) - File "B:\GitHub\stable-diffusion-webui\repositories\k-diffusion\k_diffusion\sampling.py", line 651, in sample_dpmpp_2m_sde - h_last = h - UnboundLocalError: local variable 'h' referenced before assignment validations: required: true - type: textarea id: misc attributes: label: Additional information - description: Please provide us with any relevant additional info or context. - placeholder: | + description: | + Please provide us with any relevant additional info or context. Examples: - I have updated the GPU driver recently. - I suspect the issue is caused by XXXXX. - I am using a VPN. +  I have updated my GPU driver recently. From e34949be52a89af21b2bcb0c18ca8d834e9cb562 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Fri, 6 Oct 2023 22:49:33 -0600 Subject: [PATCH 140/186] Edit-attention fixes --- javascript/edit-attention.js | 52 +++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index 794453bf..b7f3215d 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -26,6 +26,7 @@ function keyupEditAttention(event) { // Set the selection to the text between the parenthesis const parenContent = text.substring(beforeParen + 1, selectionStart + afterParen); + if (!/.*:[\d.]+/.test(parenContent)) return false; const lastColon = parenContent.lastIndexOf(":"); selectionStart = beforeParen + 1; selectionEnd = selectionStart + lastColon; @@ -66,40 +67,53 @@ function keyupEditAttention(event) { var closeCharacter = ')'; var delta = opts.keyedit_precision_attention; - if (selectionStart > 0 && text[selectionStart - 1] == '<') { + if (selectionStart > 0 && /<.*:[\d.]+>/.test(text.slice(selectionStart - 1, selectionEnd + text.slice(selectionEnd).indexOf(">") + 1))) { closeCharacter = '>'; delta = opts.keyedit_precision_extra; - } else if (selectionStart == 0 || text[selectionStart - 1] != "(") { - + } else if (selectionStart > 0 && /\(.*\)|\[.*\]/.test(text.slice(selectionStart - 1, selectionEnd + 1))) { + closeCharacter = null; + if (isPlus) { + text = text.slice(0, selectionStart) + text[selectionStart - 1] + text.slice(selectionStart, selectionEnd) + text[selectionEnd] + text.slice(selectionEnd); + selectionStart++; + selectionEnd++; + } else { + text = text.slice(0, selectionStart - 1) + text.slice(selectionStart, selectionEnd) + text.slice(selectionEnd + 1); + selectionStart--; + selectionEnd--; + } + } else if (selectionStart == 0 || !/\(.*:[\d.]+\)/.test(text.slice(selectionStart - 1, selectionEnd + text.slice(selectionEnd).indexOf(")") + 1))) { // do not include spaces at the end while (selectionEnd > selectionStart && text[selectionEnd - 1] == ' ') { - selectionEnd -= 1; + selectionEnd--; } + if (selectionStart == selectionEnd) { return; } text = text.slice(0, selectionStart) + "(" + text.slice(selectionStart, selectionEnd) + ":1.0)" + text.slice(selectionEnd); - selectionStart += 1; - selectionEnd += 1; + selectionStart++; + selectionEnd++; } - var end = text.slice(selectionEnd + 1).indexOf(closeCharacter) + 1; - var weight = parseFloat(text.slice(selectionEnd + 1, selectionEnd + end)); - if (isNaN(weight)) return; + if (closeCharacter) { + var end = text.slice(selectionEnd + 1).indexOf(closeCharacter) + 1; + var weight = parseFloat(text.slice(selectionEnd + 1, selectionEnd + end)); + if (isNaN(weight)) return; - weight += isPlus ? delta : -delta; - weight = parseFloat(weight.toPrecision(12)); - if (String(weight).length == 1) weight += ".0"; + weight += isPlus ? delta : -delta; + weight = parseFloat(weight.toPrecision(12)); + if (String(weight).length == 1) weight += ".0"; - if (closeCharacter == ')' && weight == 1) { - var endParenPos = text.substring(selectionEnd).indexOf(')'); - text = text.slice(0, selectionStart - 1) + text.slice(selectionStart, selectionEnd) + text.slice(selectionEnd + endParenPos + 1); - selectionStart--; - selectionEnd--; - } else { - text = text.slice(0, selectionEnd + 1) + weight + text.slice(selectionEnd + end); + if (closeCharacter == ')' && weight == 1) { + var endParenPos = text.substring(selectionEnd).indexOf(')'); + text = text.slice(0, selectionStart - 1) + text.slice(selectionStart, selectionEnd) + text.slice(selectionEnd + endParenPos + 1); + selectionStart--; + selectionEnd--; + } else { + text = text.slice(0, selectionEnd + 1) + weight + text.slice(selectionEnd + end); + } } target.focus(); From 76010a51ef1f3805a7487723599035bc2356c3fb Mon Sep 17 00:00:00 2001 From: wangqiuwen Date: Sat, 7 Oct 2023 15:36:01 +0800 Subject: [PATCH 141/186] up --- modules/sd_models.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/modules/sd_models.py b/modules/sd_models.py index eedb38c6..3a060ab6 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -1,4 +1,5 @@ import collections +import copy import os.path import sys import gc @@ -309,8 +310,6 @@ def get_checkpoint_state_dict(checkpoint_info: CheckpointInfo, timer): if checkpoint_info in checkpoints_loaded: # use checkpoint cache print(f"Loading weights [{sd_model_hash}] from cache") - # move to end as latest - checkpoints_loaded.move_to_end(checkpoint_info) return checkpoints_loaded[checkpoint_info] print(f"Loading weights [{sd_model_hash}] from {checkpoint_info.filename}") @@ -352,12 +351,12 @@ def load_model_weights(model, checkpoint_info: CheckpointInfo, state_dict, timer if model.is_sdxl: sd_models_xl.extend_sdxl(model) - model.load_state_dict(state_dict, strict=False) - timer.record("apply weights to model") - if shared.opts.sd_checkpoint_cache > 0: # cache newly loaded model - checkpoints_loaded[checkpoint_info] = state_dict + checkpoints_loaded[checkpoint_info] = copy.deepcopy(state_dict) + + model.load_state_dict(state_dict, strict=False) + timer.record("apply weights to model") del state_dict From 770ee23f18d12fb3b5627c636aa420f481e292ee Mon Sep 17 00:00:00 2001 From: wangqiuwen Date: Sat, 7 Oct 2023 15:38:50 +0800 Subject: [PATCH 142/186] reverst --- modules/sd_models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/sd_models.py b/modules/sd_models.py index 3a060ab6..8d63e7f1 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -310,6 +310,8 @@ def get_checkpoint_state_dict(checkpoint_info: CheckpointInfo, timer): if checkpoint_info in checkpoints_loaded: # use checkpoint cache print(f"Loading weights [{sd_model_hash}] from cache") + # move to end as latest + checkpoints_loaded.move_to_end(checkpoint_info) return checkpoints_loaded[checkpoint_info] print(f"Loading weights [{sd_model_hash}] from {checkpoint_info.filename}") From 09a2da835ef9c3dd12f3d842fbd5dd9e19ded859 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Sat, 7 Oct 2023 14:48:43 -0600 Subject: [PATCH 143/186] Add brackets, vertical bar to default delimiters --- modules/shared_options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/shared_options.py b/modules/shared_options.py index ab9b0072..48243234 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -256,7 +256,7 @@ options_templates.update(options_section(('ui', "User interface"), { "dimensions_and_batch_together": OptionInfo(True, "Show Width/Height and Batch sliders in same row").needs_reload_ui(), "keyedit_precision_attention": OptionInfo(0.1, "Ctrl+up/down precision when editing (attention:1.1)", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}), "keyedit_precision_extra": OptionInfo(0.05, "Ctrl+up/down precision when editing ", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}), - "keyedit_delimiters": OptionInfo(r".,\/!?%^*;:{}=`~() ", "Ctrl+up/down word delimiters"), + "keyedit_delimiters": OptionInfo(r".,\/!?%^*;:{}=`~()[]<>| ", "Ctrl+up/down word delimiters"), "keyedit_delimiters_whitespace": OptionInfo(["Tab", "Carriage Return", "Line Feed"], "Ctrl+up/down whitespace delimiters", gr.CheckboxGroup, lambda: {"choices": ["Tab", "Carriage Return", "Line Feed"]}), "keyedit_move": OptionInfo(True, "Alt+left/right moves prompt elements"), "quicksettings_list": OptionInfo(["sd_model_checkpoint"], "Quicksettings list", ui_components.DropdownMulti, lambda: {"choices": list(shared.opts.data_labels.keys())}).js("info", "settingsHintsShowQuicksettings").info("setting entries that appear at the top of page rather than in settings tab").needs_reload_ui(), From fd51b8501e1d9f9380c948cbf665c7708baef5d6 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Sat, 7 Oct 2023 15:28:25 -0600 Subject: [PATCH 144/186] Fix multi-line selections --- javascript/edit-attention.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index b7f3215d..d7b6001b 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -26,7 +26,7 @@ function keyupEditAttention(event) { // Set the selection to the text between the parenthesis const parenContent = text.substring(beforeParen + 1, selectionStart + afterParen); - if (!/.*:[\d.]+/.test(parenContent)) return false; + if (!/.*:[\d.]+/s.test(parenContent)) return false; const lastColon = parenContent.lastIndexOf(":"); selectionStart = beforeParen + 1; selectionEnd = selectionStart + lastColon; @@ -67,10 +67,10 @@ function keyupEditAttention(event) { var closeCharacter = ')'; var delta = opts.keyedit_precision_attention; - if (selectionStart > 0 && /<.*:[\d.]+>/.test(text.slice(selectionStart - 1, selectionEnd + text.slice(selectionEnd).indexOf(">") + 1))) { + if (selectionStart > 0 && /<.*:[\d.]+>/s.test(text.slice(selectionStart - 1, selectionEnd + text.slice(selectionEnd).indexOf(">") + 1))) { closeCharacter = '>'; delta = opts.keyedit_precision_extra; - } else if (selectionStart > 0 && /\(.*\)|\[.*\]/.test(text.slice(selectionStart - 1, selectionEnd + 1))) { + } else if (selectionStart > 0 && /\(.*\)|\[.*\]/s.test(text.slice(selectionStart - 1, selectionEnd + 1))) { closeCharacter = null; if (isPlus) { text = text.slice(0, selectionStart) + text[selectionStart - 1] + text.slice(selectionStart, selectionEnd) + text[selectionEnd] + text.slice(selectionEnd); @@ -81,7 +81,7 @@ function keyupEditAttention(event) { selectionStart--; selectionEnd--; } - } else if (selectionStart == 0 || !/\(.*:[\d.]+\)/.test(text.slice(selectionStart - 1, selectionEnd + text.slice(selectionEnd).indexOf(")") + 1))) { + } else if (selectionStart == 0 || !/\(.*:[\d.]+\)/s.test(text.slice(selectionStart - 1, selectionEnd + text.slice(selectionEnd).indexOf(")") + 1))) { // do not include spaces at the end while (selectionEnd > selectionStart && text[selectionEnd - 1] == ' ') { selectionEnd--; From 3562b0dc7427e92828001cd713ff47a83255ccc8 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Sat, 7 Oct 2023 15:52:16 -0600 Subject: [PATCH 145/186] Fix negative values --- javascript/edit-attention.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index d7b6001b..89b37aaf 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -26,7 +26,7 @@ function keyupEditAttention(event) { // Set the selection to the text between the parenthesis const parenContent = text.substring(beforeParen + 1, selectionStart + afterParen); - if (!/.*:[\d.]+/s.test(parenContent)) return false; + if (!/.*:-?[\d.]+/s.test(parenContent)) return false; const lastColon = parenContent.lastIndexOf(":"); selectionStart = beforeParen + 1; selectionEnd = selectionStart + lastColon; @@ -67,7 +67,7 @@ function keyupEditAttention(event) { var closeCharacter = ')'; var delta = opts.keyedit_precision_attention; - if (selectionStart > 0 && /<.*:[\d.]+>/s.test(text.slice(selectionStart - 1, selectionEnd + text.slice(selectionEnd).indexOf(">") + 1))) { + if (selectionStart > 0 && /<.*:-?[\d.]+>/s.test(text.slice(selectionStart - 1, selectionEnd + text.slice(selectionEnd).indexOf(">") + 1))) { closeCharacter = '>'; delta = opts.keyedit_precision_extra; } else if (selectionStart > 0 && /\(.*\)|\[.*\]/s.test(text.slice(selectionStart - 1, selectionEnd + 1))) { @@ -81,7 +81,7 @@ function keyupEditAttention(event) { selectionStart--; selectionEnd--; } - } else if (selectionStart == 0 || !/\(.*:[\d.]+\)/s.test(text.slice(selectionStart - 1, selectionEnd + text.slice(selectionEnd).indexOf(")") + 1))) { + } else if (selectionStart == 0 || !/\(.*:-?[\d.]+\)/s.test(text.slice(selectionStart - 1, selectionEnd + text.slice(selectionEnd).indexOf(")") + 1))) { // do not include spaces at the end while (selectionEnd > selectionStart && text[selectionEnd - 1] == ' ') { selectionEnd--; From 9821625a76177ebc8b62a1ee6d8ef39cf4805f99 Mon Sep 17 00:00:00 2001 From: Leon Date: Mon, 9 Oct 2023 18:36:48 +0800 Subject: [PATCH 146/186] fix the key error exception when adding an overwriting key which is defined in the extensions --- modules/processing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/processing.py b/modules/processing.py index 36bc94f7..fee2440f 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -711,7 +711,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed: if p.scripts is not None: p.scripts.before_process(p) - stored_opts = {k: opts.data[k] for k in p.override_settings.keys() if k in opts.data} + stored_opts = {k: opts.data[k] if k in opts.data else opts.get_default(k) for k in p.override_settings.keys() if k in opts.data} try: # if no checkpoint override or the override checkpoint can't be found, remove override entry and load opts checkpoint From 2aa485b5afb13fd6aab79777e4dfc488591b2f1c Mon Sep 17 00:00:00 2001 From: Kohaku-Blueleaf <59680068+KohakuBlueleaf@users.noreply.github.com> Date: Mon, 9 Oct 2023 22:52:09 +0800 Subject: [PATCH 147/186] add lora bundle system --- extensions-builtin/Lora/network.py | 1 + extensions-builtin/Lora/networks.py | 48 +++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/extensions-builtin/Lora/network.py b/extensions-builtin/Lora/network.py index d8e8dfb7..6021fd8d 100644 --- a/extensions-builtin/Lora/network.py +++ b/extensions-builtin/Lora/network.py @@ -93,6 +93,7 @@ class Network: # LoraModule self.unet_multiplier = 1.0 self.dyn_dim = None self.modules = {} + self.bundle_embeddings = {} self.mtime = None self.mentioned_name = None diff --git a/extensions-builtin/Lora/networks.py b/extensions-builtin/Lora/networks.py index 315682b3..652b8ebe 100644 --- a/extensions-builtin/Lora/networks.py +++ b/extensions-builtin/Lora/networks.py @@ -15,6 +15,7 @@ import torch from typing import Union from modules import shared, devices, sd_models, errors, scripts, sd_hijack +from modules.textual_inversion.textual_inversion import Embedding module_types = [ network_lora.ModuleTypeLora(), @@ -149,9 +150,15 @@ def load_network(name, network_on_disk): is_sd2 = 'model_transformer_resblocks' in shared.sd_model.network_layer_mapping matched_networks = {} + bundle_embeddings = {} for key_network, weight in sd.items(): key_network_without_network_parts, network_part = key_network.split(".", 1) + if key_network_without_network_parts == "bundle_emb": + emb_name, vec_name = network_part.split(".", 1) + emb_dict = bundle_embeddings.get(emb_name, {}) + emb_dict[vec_name] = weight + bundle_embeddings[emb_name] = emb_dict key = convert_diffusers_name_to_compvis(key_network_without_network_parts, is_sd2) sd_module = shared.sd_model.network_layer_mapping.get(key, None) @@ -195,6 +202,8 @@ def load_network(name, network_on_disk): net.modules[key] = net_module + net.bundle_embeddings = bundle_embeddings + if keys_failed_to_match: logging.debug(f"Network {network_on_disk.filename} didn't match keys: {keys_failed_to_match}") @@ -210,11 +219,14 @@ def purge_networks_from_memory(): def load_networks(names, te_multipliers=None, unet_multipliers=None, dyn_dims=None): + emb_db = sd_hijack.model_hijack.embedding_db already_loaded = {} for net in loaded_networks: if net.name in names: already_loaded[net.name] = net + for emb_name in net.bundle_embeddings: + emb_db.register_embedding_by_name(None, shared.sd_model, emb_name) loaded_networks.clear() @@ -257,6 +269,41 @@ def load_networks(names, te_multipliers=None, unet_multipliers=None, dyn_dims=No net.dyn_dim = dyn_dims[i] if dyn_dims else 1.0 loaded_networks.append(net) + for emb_name, data in net.bundle_embeddings.items(): + # textual inversion embeddings + if 'string_to_param' in data: + param_dict = data['string_to_param'] + param_dict = getattr(param_dict, '_parameters', param_dict) # fix for torch 1.12.1 loading saved file from torch 1.11 + assert len(param_dict) == 1, 'embedding file has multiple terms in it' + emb = next(iter(param_dict.items()))[1] + vec = emb.detach().to(devices.device, dtype=torch.float32) + shape = vec.shape[-1] + vectors = vec.shape[0] + elif type(data) == dict and 'clip_g' in data and 'clip_l' in data: # SDXL embedding + vec = {k: v.detach().to(devices.device, dtype=torch.float32) for k, v in data.items()} + shape = data['clip_g'].shape[-1] + data['clip_l'].shape[-1] + vectors = data['clip_g'].shape[0] + elif type(data) == dict and type(next(iter(data.values()))) == torch.Tensor: # diffuser concepts + assert len(data.keys()) == 1, 'embedding file has multiple terms in it' + + emb = next(iter(data.values())) + if len(emb.shape) == 1: + emb = emb.unsqueeze(0) + vec = emb.detach().to(devices.device, dtype=torch.float32) + shape = vec.shape[-1] + vectors = vec.shape[0] + else: + raise Exception(f"Couldn't identify {emb_name} in lora: {name} as neither textual inversion embedding nor diffuser concept.") + + embedding = Embedding(vec, emb_name) + embedding.vectors = vectors + embedding.shape = shape + + if emb_db.expected_shape == -1 or emb_db.expected_shape == embedding.shape: + emb_db.register_embedding(embedding, shared.sd_model) + else: + emb_db.skipped_embeddings[name] = embedding + if failed_to_load_networks: sd_hijack.model_hijack.comments.append("Networks not found: " + ", ".join(failed_to_load_networks)) @@ -565,6 +612,7 @@ extra_network_lora = None available_networks = {} available_network_aliases = {} loaded_networks = [] +loaded_bundle_embeddings = {} networks_in_memory = {} available_network_hash_lookup = {} forbidden_network_aliases = {} From 3d8b1af6beb9015f6b3573661d8ed00275f6129f Mon Sep 17 00:00:00 2001 From: Kohaku-Blueleaf <59680068+KohakuBlueleaf@users.noreply.github.com> Date: Tue, 10 Oct 2023 12:09:33 +0800 Subject: [PATCH 148/186] Support string_to_param nested dict format: bundle_emb.EMBNAME.string_to_param.KEYNAME --- extensions-builtin/Lora/networks.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/extensions-builtin/Lora/networks.py b/extensions-builtin/Lora/networks.py index 652b8ebe..ab3517d8 100644 --- a/extensions-builtin/Lora/networks.py +++ b/extensions-builtin/Lora/networks.py @@ -157,7 +157,11 @@ def load_network(name, network_on_disk): if key_network_without_network_parts == "bundle_emb": emb_name, vec_name = network_part.split(".", 1) emb_dict = bundle_embeddings.get(emb_name, {}) - emb_dict[vec_name] = weight + if vec_name.split('.')[0] == 'string_to_param': + _, k2 = vec_name.split('.', 1) + emb_dict['string_to_param'] = {k2: weight} + else: + emb_dict[vec_name] = weight bundle_embeddings[emb_name] = emb_dict key = convert_diffusers_name_to_compvis(key_network_without_network_parts, is_sd2) @@ -301,6 +305,7 @@ def load_networks(names, te_multipliers=None, unet_multipliers=None, dyn_dims=No if emb_db.expected_shape == -1 or emb_db.expected_shape == embedding.shape: emb_db.register_embedding(embedding, shared.sd_model) + print(f'registered bundle embedding: {embedding.name}') else: emb_db.skipped_embeddings[name] = embedding From 2282eb8dd5905e8ed71231a0b8fc77599d10c12f Mon Sep 17 00:00:00 2001 From: Kohaku-Blueleaf <59680068+KohakuBlueleaf@users.noreply.github.com> Date: Tue, 10 Oct 2023 12:11:00 +0800 Subject: [PATCH 149/186] Remove dev debug print --- extensions-builtin/Lora/networks.py | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions-builtin/Lora/networks.py b/extensions-builtin/Lora/networks.py index ab3517d8..465e24c8 100644 --- a/extensions-builtin/Lora/networks.py +++ b/extensions-builtin/Lora/networks.py @@ -305,7 +305,6 @@ def load_networks(names, te_multipliers=None, unet_multipliers=None, dyn_dims=No if emb_db.expected_shape == -1 or emb_db.expected_shape == embedding.shape: emb_db.register_embedding(embedding, shared.sd_model) - print(f'registered bundle embedding: {embedding.name}') else: emb_db.skipped_embeddings[name] = embedding From 81e94de3185d42dba4e7bb72cf836f683f28b03f Mon Sep 17 00:00:00 2001 From: Kohaku-Blueleaf <59680068+KohakuBlueleaf@users.noreply.github.com> Date: Tue, 10 Oct 2023 14:44:20 +0800 Subject: [PATCH 150/186] Add warning when meet emb name conflicting Choose standalone embedding (in /embeddings folder) first --- extensions-builtin/Lora/lora_logger.py | 33 +++++++++++ extensions-builtin/Lora/networks.py | 80 +++++++++++++++----------- 2 files changed, 81 insertions(+), 32 deletions(-) create mode 100644 extensions-builtin/Lora/lora_logger.py diff --git a/extensions-builtin/Lora/lora_logger.py b/extensions-builtin/Lora/lora_logger.py new file mode 100644 index 00000000..d50e90f0 --- /dev/null +++ b/extensions-builtin/Lora/lora_logger.py @@ -0,0 +1,33 @@ +import sys +import copy +import logging + + +class ColoredFormatter(logging.Formatter): + COLORS = { + "DEBUG": "\033[0;36m", # CYAN + "INFO": "\033[0;32m", # GREEN + "WARNING": "\033[0;33m", # YELLOW + "ERROR": "\033[0;31m", # RED + "CRITICAL": "\033[0;37;41m", # WHITE ON RED + "RESET": "\033[0m", # RESET COLOR + } + + def format(self, record): + colored_record = copy.copy(record) + levelname = colored_record.levelname + seq = self.COLORS.get(levelname, self.COLORS["RESET"]) + colored_record.levelname = f"{seq}{levelname}{self.COLORS['RESET']}" + return super().format(colored_record) + + +logger = logging.getLogger("lora") +logger.propagate = False + + +if not logger.handlers: + handler = logging.StreamHandler(sys.stdout) + handler.setFormatter( + ColoredFormatter("[%(name)s]-%(levelname)s: %(message)s") + ) + logger.addHandler(handler) \ No newline at end of file diff --git a/extensions-builtin/Lora/networks.py b/extensions-builtin/Lora/networks.py index 465e24c8..12f70576 100644 --- a/extensions-builtin/Lora/networks.py +++ b/extensions-builtin/Lora/networks.py @@ -17,6 +17,8 @@ from typing import Union from modules import shared, devices, sd_models, errors, scripts, sd_hijack from modules.textual_inversion.textual_inversion import Embedding +from lora_logger import logger + module_types = [ network_lora.ModuleTypeLora(), network_hada.ModuleTypeHada(), @@ -206,7 +208,40 @@ def load_network(name, network_on_disk): net.modules[key] = net_module - net.bundle_embeddings = bundle_embeddings + embeddings = {} + for emb_name, data in bundle_embeddings.items(): + # textual inversion embeddings + if 'string_to_param' in data: + param_dict = data['string_to_param'] + param_dict = getattr(param_dict, '_parameters', param_dict) # fix for torch 1.12.1 loading saved file from torch 1.11 + assert len(param_dict) == 1, 'embedding file has multiple terms in it' + emb = next(iter(param_dict.items()))[1] + vec = emb.detach().to(devices.device, dtype=torch.float32) + shape = vec.shape[-1] + vectors = vec.shape[0] + elif type(data) == dict and 'clip_g' in data and 'clip_l' in data: # SDXL embedding + vec = {k: v.detach().to(devices.device, dtype=torch.float32) for k, v in data.items()} + shape = data['clip_g'].shape[-1] + data['clip_l'].shape[-1] + vectors = data['clip_g'].shape[0] + elif type(data) == dict and type(next(iter(data.values()))) == torch.Tensor: # diffuser concepts + assert len(data.keys()) == 1, 'embedding file has multiple terms in it' + + emb = next(iter(data.values())) + if len(emb.shape) == 1: + emb = emb.unsqueeze(0) + vec = emb.detach().to(devices.device, dtype=torch.float32) + shape = vec.shape[-1] + vectors = vec.shape[0] + else: + raise Exception(f"Couldn't identify {emb_name} in lora: {name} as neither textual inversion embedding nor diffuser concept.") + + embedding = Embedding(vec, emb_name) + embedding.vectors = vectors + embedding.shape = shape + embedding.loaded = None + embeddings[emb_name] = embedding + + net.bundle_embeddings = embeddings if keys_failed_to_match: logging.debug(f"Network {network_on_disk.filename} didn't match keys: {keys_failed_to_match}") @@ -229,8 +264,9 @@ def load_networks(names, te_multipliers=None, unet_multipliers=None, dyn_dims=No for net in loaded_networks: if net.name in names: already_loaded[net.name] = net - for emb_name in net.bundle_embeddings: - emb_db.register_embedding_by_name(None, shared.sd_model, emb_name) + for emb_name, embedding in net.bundle_embeddings.items(): + if embedding.loaded: + emb_db.register_embedding_by_name(None, shared.sd_model, emb_name) loaded_networks.clear() @@ -273,37 +309,17 @@ def load_networks(names, te_multipliers=None, unet_multipliers=None, dyn_dims=No net.dyn_dim = dyn_dims[i] if dyn_dims else 1.0 loaded_networks.append(net) - for emb_name, data in net.bundle_embeddings.items(): - # textual inversion embeddings - if 'string_to_param' in data: - param_dict = data['string_to_param'] - param_dict = getattr(param_dict, '_parameters', param_dict) # fix for torch 1.12.1 loading saved file from torch 1.11 - assert len(param_dict) == 1, 'embedding file has multiple terms in it' - emb = next(iter(param_dict.items()))[1] - vec = emb.detach().to(devices.device, dtype=torch.float32) - shape = vec.shape[-1] - vectors = vec.shape[0] - elif type(data) == dict and 'clip_g' in data and 'clip_l' in data: # SDXL embedding - vec = {k: v.detach().to(devices.device, dtype=torch.float32) for k, v in data.items()} - shape = data['clip_g'].shape[-1] + data['clip_l'].shape[-1] - vectors = data['clip_g'].shape[0] - elif type(data) == dict and type(next(iter(data.values()))) == torch.Tensor: # diffuser concepts - assert len(data.keys()) == 1, 'embedding file has multiple terms in it' - - emb = next(iter(data.values())) - if len(emb.shape) == 1: - emb = emb.unsqueeze(0) - vec = emb.detach().to(devices.device, dtype=torch.float32) - shape = vec.shape[-1] - vectors = vec.shape[0] - else: - raise Exception(f"Couldn't identify {emb_name} in lora: {name} as neither textual inversion embedding nor diffuser concept.") - - embedding = Embedding(vec, emb_name) - embedding.vectors = vectors - embedding.shape = shape + for emb_name, embedding in net.bundle_embeddings.items(): + if embedding.loaded is None and emb_name in emb_db.word_embeddings: + logger.warning( + f'Skip bundle embedding: "{emb_name}"' + ' as it was already loaded from embeddings folder' + ) + continue + embedding.loaded = False if emb_db.expected_shape == -1 or emb_db.expected_shape == embedding.shape: + embedding.loaded = True emb_db.register_embedding(embedding, shared.sd_model) else: emb_db.skipped_embeddings[name] = embedding From 891ccb767c3815db48a124677d1cd0f204018ad4 Mon Sep 17 00:00:00 2001 From: Kohaku-Blueleaf <59680068+KohakuBlueleaf@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:07:25 +0800 Subject: [PATCH 151/186] Fix lint --- extensions-builtin/Lora/lora_logger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions-builtin/Lora/lora_logger.py b/extensions-builtin/Lora/lora_logger.py index d50e90f0..d51de297 100644 --- a/extensions-builtin/Lora/lora_logger.py +++ b/extensions-builtin/Lora/lora_logger.py @@ -30,4 +30,4 @@ if not logger.handlers: handler.setFormatter( ColoredFormatter("[%(name)s]-%(levelname)s: %(message)s") ) - logger.addHandler(handler) \ No newline at end of file + logger.addHandler(handler) From dbb10fbd8c2dd4f3ca83a1d2e15e188799074ce4 Mon Sep 17 00:00:00 2001 From: Won-Kyu Park Date: Sun, 1 Oct 2023 20:18:25 +0900 Subject: [PATCH 152/186] show the preview image in the modalview if available --- javascript/imageviewer.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/javascript/imageviewer.js b/javascript/imageviewer.js index c21d396e..e4dae91b 100644 --- a/javascript/imageviewer.js +++ b/javascript/imageviewer.js @@ -33,8 +33,11 @@ function updateOnBackgroundChange() { const modalImage = gradioApp().getElementById("modalImage"); if (modalImage && modalImage.offsetParent) { let currentButton = selected_gallery_button(); - - if (currentButton?.children?.length > 0 && modalImage.src != currentButton.children[0].src) { + let preview = gradioApp().querySelectorAll('.livePreview > img'); + if (preview.length > 0) { + // show preview image if available + modalImage.src = preview[preview.length - 1].src; + } else if (currentButton?.children?.length > 0 && modalImage.src != currentButton.children[0].src) { modalImage.src = currentButton.children[0].src; if (modalImage.style.display === 'none') { const modal = gradioApp().getElementById("lightboxModal"); From 906d1179e9a333eeb0f12a95b592dd5b44eb0aaa Mon Sep 17 00:00:00 2001 From: v0xie <28695009+v0xie@users.noreply.github.com> Date: Wed, 11 Oct 2023 21:26:58 -0700 Subject: [PATCH 153/186] support inference with LyCORIS GLora networks --- extensions-builtin/Lora/network_glora.py | 33 ++++++++++++++++++++++++ extensions-builtin/Lora/networks.py | 2 ++ 2 files changed, 35 insertions(+) create mode 100644 extensions-builtin/Lora/network_glora.py diff --git a/extensions-builtin/Lora/network_glora.py b/extensions-builtin/Lora/network_glora.py new file mode 100644 index 00000000..492d4870 --- /dev/null +++ b/extensions-builtin/Lora/network_glora.py @@ -0,0 +1,33 @@ + +import network + +class ModuleTypeGLora(network.ModuleType): + def create_module(self, net: network.Network, weights: network.NetworkWeights): + if all(x in weights.w for x in ["a1.weight", "a2.weight", "alpha", "b1.weight", "b2.weight"]): + return NetworkModuleGLora(net, weights) + + return None + +# adapted from https://github.com/KohakuBlueleaf/LyCORIS +class NetworkModuleGLora(network.NetworkModule): + def __init__(self, net: network.Network, weights: network.NetworkWeights): + super().__init__(net, weights) + + if hasattr(self.sd_module, 'weight'): + self.shape = self.sd_module.weight.shape + + self.w1a = weights.w["a1.weight"] + self.w1b = weights.w["b1.weight"] + self.w2a = weights.w["a2.weight"] + self.w2b = weights.w["b2.weight"] + + def calc_updown(self, orig_weight): + w1a = self.w1a.to(orig_weight.device, dtype=orig_weight.dtype) + w1b = self.w1b.to(orig_weight.device, dtype=orig_weight.dtype) + w2a = self.w2a.to(orig_weight.device, dtype=orig_weight.dtype) + w2b = self.w2b.to(orig_weight.device, dtype=orig_weight.dtype) + + output_shape = [w1a.size(0), w1b.size(1)] + updown = ((w2b @ w1b) + ((orig_weight @ w2a) @ w1a)) + + return self.finalize_updown(updown, orig_weight, output_shape) diff --git a/extensions-builtin/Lora/networks.py b/extensions-builtin/Lora/networks.py index 315682b3..ddab3c55 100644 --- a/extensions-builtin/Lora/networks.py +++ b/extensions-builtin/Lora/networks.py @@ -5,6 +5,7 @@ import re import lora_patches import network import network_lora +import network_glora import network_hada import network_ia3 import network_lokr @@ -23,6 +24,7 @@ module_types = [ network_lokr.ModuleTypeLokr(), network_full.ModuleTypeFull(), network_norm.ModuleTypeNorm(), + network_glora.ModuleTypeGLora(), ] From fbc8d213546047d8970b92809e15b33e8a1301be Mon Sep 17 00:00:00 2001 From: Won-Kyu Park Date: Sat, 14 Oct 2023 02:39:04 +0900 Subject: [PATCH 154/186] fix IndexError: list index out of range error interrupted while postprocess --- modules/processing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/processing.py b/modules/processing.py index 36bc94f7..df037fb0 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -820,6 +820,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: state.skipped = False if state.interrupted: + infotexts.append(Processed(p, []).infotext(p, 0)) break sd_models.reload_model_weights() # model can be changed for example by refiner From 44d14bc32e4a5501df04f844a00f9e18b777f7eb Mon Sep 17 00:00:00 2001 From: Gleb Alekseev Date: Fri, 13 Oct 2023 15:08:59 -0300 Subject: [PATCH 155/186] added option to play notification sound or not --- modules/shared_options.py | 1 + modules/ui.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/shared_options.py b/modules/shared_options.py index 00b273fa..afcbf9b8 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -22,6 +22,7 @@ restricted_opts = { } options_templates.update(options_section(('saving-images', "Saving images/grids"), { + "notification_audio": OptionInfo(True, "Play notification sound after image generation", comment_after="(notification.mp3 should be present in the root directory)").needs_reload_ui(), "samples_save": OptionInfo(True, "Always save all generated images"), "samples_format": OptionInfo('png', 'File format for images'), "samples_filename_pattern": OptionInfo("", "Images filename pattern", component_args=hide_dirs).link("wiki", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Custom-Images-Filename-Name-and-Subdirectory"), diff --git a/modules/ui.py b/modules/ui.py index 579bab98..df327891 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -1286,7 +1286,7 @@ def create_ui(): loadsave.setup_ui() - if os.path.exists(os.path.join(script_path, "notification.mp3")): + if os.path.exists(os.path.join(script_path, "notification.mp3")) and shared.opts.notification_audio: gr.Audio(interactive=False, value=os.path.join(script_path, "notification.mp3"), elem_id="audio_notification", visible=False) footer = shared.html("footer.html") From 954499a49409582085ed288b94b837ecae7ff86c Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Fri, 13 Oct 2023 16:44:01 -0600 Subject: [PATCH 156/186] Convert (emphasis) to (emphasis:1.1) per @SirVeggie's suggestion --- javascript/edit-attention.js | 53 ++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index 89b37aaf..ee48eb99 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -71,16 +71,23 @@ function keyupEditAttention(event) { closeCharacter = '>'; delta = opts.keyedit_precision_extra; } else if (selectionStart > 0 && /\(.*\)|\[.*\]/s.test(text.slice(selectionStart - 1, selectionEnd + 1))) { - closeCharacter = null; - if (isPlus) { - text = text.slice(0, selectionStart) + text[selectionStart - 1] + text.slice(selectionStart, selectionEnd) + text[selectionEnd] + text.slice(selectionEnd); - selectionStart++; - selectionEnd++; - } else { - text = text.slice(0, selectionStart - 1) + text.slice(selectionStart, selectionEnd) + text.slice(selectionEnd + 1); - selectionStart--; - selectionEnd--; + let start = text[selectionStart - 1]; + let end = text[selectionEnd]; + let numParen = 0; + + while (text[selectionStart - numParen - 1] == start && text[selectionEnd + numParen] == end) { + numParen++; } + + if (start == "(") { + weight = 1.1 ** numParen; + } else { + weight = 0.9 ** numParen; + } + + text = text.slice(0, selectionStart - numParen) + "(" + text.slice(selectionStart, selectionEnd) + ":" + weight + ")" + text.slice(selectionEnd + numParen); + selectionStart -= numParen - 1; + selectionEnd -= numParen - 1; } else if (selectionStart == 0 || !/\(.*:-?[\d.]+\)/s.test(text.slice(selectionStart - 1, selectionEnd + text.slice(selectionEnd).indexOf(")") + 1))) { // do not include spaces at the end while (selectionEnd > selectionStart && text[selectionEnd - 1] == ' ') { @@ -97,23 +104,21 @@ function keyupEditAttention(event) { selectionEnd++; } - if (closeCharacter) { - var end = text.slice(selectionEnd + 1).indexOf(closeCharacter) + 1; - var weight = parseFloat(text.slice(selectionEnd + 1, selectionEnd + end)); - if (isNaN(weight)) return; + var end = text.slice(selectionEnd + 1).indexOf(closeCharacter) + 1; + var weight = parseFloat(text.slice(selectionEnd + 1, selectionEnd + end)); + if (isNaN(weight)) return; - weight += isPlus ? delta : -delta; - weight = parseFloat(weight.toPrecision(12)); - if (String(weight).length == 1) weight += ".0"; + weight += isPlus ? delta : -delta; + weight = parseFloat(weight.toPrecision(12)); + if (Number.isInteger(weight)) weight += ".0"; - if (closeCharacter == ')' && weight == 1) { - var endParenPos = text.substring(selectionEnd).indexOf(')'); - text = text.slice(0, selectionStart - 1) + text.slice(selectionStart, selectionEnd) + text.slice(selectionEnd + endParenPos + 1); - selectionStart--; - selectionEnd--; - } else { - text = text.slice(0, selectionEnd + 1) + weight + text.slice(selectionEnd + end); - } + if (closeCharacter == ')' && weight == 1) { + var endParenPos = text.substring(selectionEnd).indexOf(')'); + text = text.slice(0, selectionStart - 1) + text.slice(selectionStart, selectionEnd) + text.slice(selectionEnd + endParenPos + 1); + selectionStart--; + selectionEnd--; + } else { + text = text.slice(0, selectionEnd + 1) + weight + text.slice(selectionEnd + end); } target.focus(); From fff1a0c74fb769701ade393cda005bf5975ec5f4 Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Fri, 13 Oct 2023 17:18:02 -0600 Subject: [PATCH 157/186] Make attention conversion optional Fix square brackets multiplier --- javascript/edit-attention.js | 63 ++++++++++++++++++++++-------------- modules/shared_options.py | 2 +- 2 files changed, 40 insertions(+), 25 deletions(-) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index ee48eb99..8b6dd240 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -73,21 +73,34 @@ function keyupEditAttention(event) { } else if (selectionStart > 0 && /\(.*\)|\[.*\]/s.test(text.slice(selectionStart - 1, selectionEnd + 1))) { let start = text[selectionStart - 1]; let end = text[selectionEnd]; - let numParen = 0; + if (opts.keyedit_convert) { + let numParen = 0; - while (text[selectionStart - numParen - 1] == start && text[selectionEnd + numParen] == end) { - numParen++; - } + while (text[selectionStart - numParen - 1] == start && text[selectionEnd + numParen] == end) { + numParen++; + } - if (start == "(") { - weight = 1.1 ** numParen; + if (start == "(") { + weight = 1.1 ** numParen; + } else { + weight = (1 / 1.1) ** numParen; + } + + text = text.slice(0, selectionStart - numParen) + "(" + text.slice(selectionStart, selectionEnd) + ":" + weight + ")" + text.slice(selectionEnd + numParen); + selectionStart -= numParen - 1; + selectionEnd -= numParen - 1; } else { - weight = 0.9 ** numParen; + closeCharacter = null; + if (isPlus) { + text = text.slice(0, selectionStart) + start + text.slice(selectionStart, selectionEnd) + end + text.slice(selectionEnd); + selectionStart++; + selectionEnd++; + } else { + text = text.slice(0, selectionStart - 1) + text.slice(selectionStart, selectionEnd) + text.slice(selectionEnd + 1); + selectionStart--; + selectionEnd--; + } } - - text = text.slice(0, selectionStart - numParen) + "(" + text.slice(selectionStart, selectionEnd) + ":" + weight + ")" + text.slice(selectionEnd + numParen); - selectionStart -= numParen - 1; - selectionEnd -= numParen - 1; } else if (selectionStart == 0 || !/\(.*:-?[\d.]+\)/s.test(text.slice(selectionStart - 1, selectionEnd + text.slice(selectionEnd).indexOf(")") + 1))) { // do not include spaces at the end while (selectionEnd > selectionStart && text[selectionEnd - 1] == ' ') { @@ -104,21 +117,23 @@ function keyupEditAttention(event) { selectionEnd++; } - var end = text.slice(selectionEnd + 1).indexOf(closeCharacter) + 1; - var weight = parseFloat(text.slice(selectionEnd + 1, selectionEnd + end)); - if (isNaN(weight)) return; + if (closeCharacter) { + var end = text.slice(selectionEnd + 1).indexOf(closeCharacter) + 1; + var weight = parseFloat(text.slice(selectionEnd + 1, selectionEnd + end)); + if (isNaN(weight)) return; - weight += isPlus ? delta : -delta; - weight = parseFloat(weight.toPrecision(12)); - if (Number.isInteger(weight)) weight += ".0"; + weight += isPlus ? delta : -delta; + weight = parseFloat(weight.toPrecision(12)); + if (Number.isInteger(weight)) weight += ".0"; - if (closeCharacter == ')' && weight == 1) { - var endParenPos = text.substring(selectionEnd).indexOf(')'); - text = text.slice(0, selectionStart - 1) + text.slice(selectionStart, selectionEnd) + text.slice(selectionEnd + endParenPos + 1); - selectionStart--; - selectionEnd--; - } else { - text = text.slice(0, selectionEnd + 1) + weight + text.slice(selectionEnd + end); + if (closeCharacter == ')' && weight == 1) { + var endParenPos = text.substring(selectionEnd).indexOf(')'); + text = text.slice(0, selectionStart - 1) + text.slice(selectionStart, selectionEnd) + text.slice(selectionEnd + endParenPos + 1); + selectionStart--; + selectionEnd--; + } else { + text = text.slice(0, selectionEnd + 1) + weight + text.slice(selectionEnd + end); + } } target.focus(); diff --git a/modules/shared_options.py b/modules/shared_options.py index 48243234..a674d3da 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -258,6 +258,7 @@ options_templates.update(options_section(('ui', "User interface"), { "keyedit_precision_extra": OptionInfo(0.05, "Ctrl+up/down precision when editing ", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}), "keyedit_delimiters": OptionInfo(r".,\/!?%^*;:{}=`~()[]<>| ", "Ctrl+up/down word delimiters"), "keyedit_delimiters_whitespace": OptionInfo(["Tab", "Carriage Return", "Line Feed"], "Ctrl+up/down whitespace delimiters", gr.CheckboxGroup, lambda: {"choices": ["Tab", "Carriage Return", "Line Feed"]}), + "keyedit_convert": OptionInfo(True, "Convert (attention) to (attention:1.1)"), "keyedit_move": OptionInfo(True, "Alt+left/right moves prompt elements"), "quicksettings_list": OptionInfo(["sd_model_checkpoint"], "Quicksettings list", ui_components.DropdownMulti, lambda: {"choices": list(shared.opts.data_labels.keys())}).js("info", "settingsHintsShowQuicksettings").info("setting entries that appear at the top of page rather than in settings tab").needs_reload_ui(), "ui_tab_order": OptionInfo([], "UI tab order", ui_components.DropdownMulti, lambda: {"choices": list(shared.tab_names)}).needs_reload_ui(), @@ -332,4 +333,3 @@ options_templates.update(options_section((None, "Hidden options"), { "restore_config_state_file": OptionInfo("", "Config state file to restore from, under 'config-states/' folder"), "sd_checkpoint_hash": OptionInfo("", "SHA256 hash of the current checkpoint"), })) - From 3a66c3c9e1cecab5095f37964d40a5f4cde317af Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 14 Oct 2023 07:35:06 +0300 Subject: [PATCH 158/186] put notification.mp3 option at the end of the page --- modules/shared_options.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/shared_options.py b/modules/shared_options.py index cb356638..ce395302 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -22,7 +22,6 @@ restricted_opts = { } options_templates.update(options_section(('saving-images', "Saving images/grids"), { - "notification_audio": OptionInfo(True, "Play notification sound after image generation", comment_after="(notification.mp3 should be present in the root directory)").needs_reload_ui(), "samples_save": OptionInfo(True, "Always save all generated images"), "samples_format": OptionInfo('png', 'File format for images'), "samples_filename_pattern": OptionInfo("", "Images filename pattern", component_args=hide_dirs).link("wiki", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Custom-Images-Filename-Name-and-Subdirectory"), @@ -63,6 +62,8 @@ options_templates.update(options_section(('saving-images', "Saving images/grids" "clean_temp_dir_at_start": OptionInfo(False, "Cleanup non-default temporary directory when starting webui"), "save_incomplete_images": OptionInfo(False, "Save incomplete images").info("save images that has been interrupted in mid-generation; even if not saved, they will still show up in webui output."), + + "notification_audio": OptionInfo(True, "Play notification sound after image generation").info("notification.mp3 should be present in the root directory").needs_reload_ui(), })) options_templates.update(options_section(('saving-paths', "Paths for saving"), { From a109c7aeb8871fe0ae201794f140f8f2e9b5c3ac Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 14 Oct 2023 07:49:03 +0300 Subject: [PATCH 159/186] more general case of adding an infotext when no images have been generated --- modules/processing.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/processing.py b/modules/processing.py index df037fb0..816f5fc7 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -820,7 +820,6 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: state.skipped = False if state.interrupted: - infotexts.append(Processed(p, []).infotext(p, 0)) break sd_models.reload_model_weights() # model can be changed for example by refiner @@ -961,6 +960,9 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: state.nextjob() + if not infotexts: + infotexts.append(Processed(p, []).infotext(p, 0)) + p.color_corrections = None index_of_first_image = 0 From 0619df9835833079f8ba5cb5a510b55c4433acaf Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 14 Oct 2023 08:01:04 +0300 Subject: [PATCH 160/186] use shallow copy for #13535 --- modules/sd_models.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/sd_models.py b/modules/sd_models.py index 2b43868e..c8efeedc 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -1,5 +1,4 @@ import collections -import copy import os.path import sys import gc @@ -360,7 +359,7 @@ def load_model_weights(model, checkpoint_info: CheckpointInfo, state_dict, timer if shared.opts.sd_checkpoint_cache > 0: # cache newly loaded model - checkpoints_loaded[checkpoint_info] = copy.deepcopy(state_dict) + checkpoints_loaded[checkpoint_info] = state_dict.copy() model.load_state_dict(state_dict, strict=False) timer.record("apply weights to model") From a8cbe50c9fa324ed887089e4333452ecc4355c92 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 14 Oct 2023 12:14:56 +0300 Subject: [PATCH 161/186] remove duplicated code --- extensions-builtin/Lora/networks.py | 31 +------- .../textual_inversion/textual_inversion.py | 74 ++++++++++--------- 2 files changed, 42 insertions(+), 63 deletions(-) diff --git a/extensions-builtin/Lora/networks.py b/extensions-builtin/Lora/networks.py index 12f70576..d5f0f9f1 100644 --- a/extensions-builtin/Lora/networks.py +++ b/extensions-builtin/Lora/networks.py @@ -15,7 +15,7 @@ import torch from typing import Union from modules import shared, devices, sd_models, errors, scripts, sd_hijack -from modules.textual_inversion.textual_inversion import Embedding +import modules.textual_inversion.textual_inversion as textual_inversion from lora_logger import logger @@ -210,34 +210,7 @@ def load_network(name, network_on_disk): embeddings = {} for emb_name, data in bundle_embeddings.items(): - # textual inversion embeddings - if 'string_to_param' in data: - param_dict = data['string_to_param'] - param_dict = getattr(param_dict, '_parameters', param_dict) # fix for torch 1.12.1 loading saved file from torch 1.11 - assert len(param_dict) == 1, 'embedding file has multiple terms in it' - emb = next(iter(param_dict.items()))[1] - vec = emb.detach().to(devices.device, dtype=torch.float32) - shape = vec.shape[-1] - vectors = vec.shape[0] - elif type(data) == dict and 'clip_g' in data and 'clip_l' in data: # SDXL embedding - vec = {k: v.detach().to(devices.device, dtype=torch.float32) for k, v in data.items()} - shape = data['clip_g'].shape[-1] + data['clip_l'].shape[-1] - vectors = data['clip_g'].shape[0] - elif type(data) == dict and type(next(iter(data.values()))) == torch.Tensor: # diffuser concepts - assert len(data.keys()) == 1, 'embedding file has multiple terms in it' - - emb = next(iter(data.values())) - if len(emb.shape) == 1: - emb = emb.unsqueeze(0) - vec = emb.detach().to(devices.device, dtype=torch.float32) - shape = vec.shape[-1] - vectors = vec.shape[0] - else: - raise Exception(f"Couldn't identify {emb_name} in lora: {name} as neither textual inversion embedding nor diffuser concept.") - - embedding = Embedding(vec, emb_name) - embedding.vectors = vectors - embedding.shape = shape + embedding = textual_inversion.create_embedding_from_data(data, emb_name, filename=network_on_disk.filename + "/" + emb_name) embedding.loaded = None embeddings[emb_name] = embedding diff --git a/modules/textual_inversion/textual_inversion.py b/modules/textual_inversion/textual_inversion.py index 401a0a2a..04dda585 100644 --- a/modules/textual_inversion/textual_inversion.py +++ b/modules/textual_inversion/textual_inversion.py @@ -181,40 +181,7 @@ class EmbeddingDatabase: else: return - - # textual inversion embeddings - if 'string_to_param' in data: - param_dict = data['string_to_param'] - param_dict = getattr(param_dict, '_parameters', param_dict) # fix for torch 1.12.1 loading saved file from torch 1.11 - assert len(param_dict) == 1, 'embedding file has multiple terms in it' - emb = next(iter(param_dict.items()))[1] - vec = emb.detach().to(devices.device, dtype=torch.float32) - shape = vec.shape[-1] - vectors = vec.shape[0] - elif type(data) == dict and 'clip_g' in data and 'clip_l' in data: # SDXL embedding - vec = {k: v.detach().to(devices.device, dtype=torch.float32) for k, v in data.items()} - shape = data['clip_g'].shape[-1] + data['clip_l'].shape[-1] - vectors = data['clip_g'].shape[0] - elif type(data) == dict and type(next(iter(data.values()))) == torch.Tensor: # diffuser concepts - assert len(data.keys()) == 1, 'embedding file has multiple terms in it' - - emb = next(iter(data.values())) - if len(emb.shape) == 1: - emb = emb.unsqueeze(0) - vec = emb.detach().to(devices.device, dtype=torch.float32) - shape = vec.shape[-1] - vectors = vec.shape[0] - else: - raise Exception(f"Couldn't identify {filename} as neither textual inversion embedding nor diffuser concept.") - - embedding = Embedding(vec, name) - embedding.step = data.get('step', None) - embedding.sd_checkpoint = data.get('sd_checkpoint', None) - embedding.sd_checkpoint_name = data.get('sd_checkpoint_name', None) - embedding.vectors = vectors - embedding.shape = shape - embedding.filename = path - embedding.set_hash(hashes.sha256(embedding.filename, "textual_inversion/" + name) or '') + embedding = create_embedding_from_data(data, name, filename=filename, filepath=path) if self.expected_shape == -1 or self.expected_shape == embedding.shape: self.register_embedding(embedding, shared.sd_model) @@ -313,6 +280,45 @@ def create_embedding(name, num_vectors_per_token, overwrite_old, init_text='*'): return fn +def create_embedding_from_data(data, name, filename='unknown embedding file', filepath=None): + if 'string_to_param' in data: # textual inversion embeddings + param_dict = data['string_to_param'] + param_dict = getattr(param_dict, '_parameters', param_dict) # fix for torch 1.12.1 loading saved file from torch 1.11 + assert len(param_dict) == 1, 'embedding file has multiple terms in it' + emb = next(iter(param_dict.items()))[1] + vec = emb.detach().to(devices.device, dtype=torch.float32) + shape = vec.shape[-1] + vectors = vec.shape[0] + elif type(data) == dict and 'clip_g' in data and 'clip_l' in data: # SDXL embedding + vec = {k: v.detach().to(devices.device, dtype=torch.float32) for k, v in data.items()} + shape = data['clip_g'].shape[-1] + data['clip_l'].shape[-1] + vectors = data['clip_g'].shape[0] + elif type(data) == dict and type(next(iter(data.values()))) == torch.Tensor: # diffuser concepts + assert len(data.keys()) == 1, 'embedding file has multiple terms in it' + + emb = next(iter(data.values())) + if len(emb.shape) == 1: + emb = emb.unsqueeze(0) + vec = emb.detach().to(devices.device, dtype=torch.float32) + shape = vec.shape[-1] + vectors = vec.shape[0] + else: + raise Exception(f"Couldn't identify {filename} as neither textual inversion embedding nor diffuser concept.") + + embedding = Embedding(vec, name) + embedding.step = data.get('step', None) + embedding.sd_checkpoint = data.get('sd_checkpoint', None) + embedding.sd_checkpoint_name = data.get('sd_checkpoint_name', None) + embedding.vectors = vectors + embedding.shape = shape + + if filepath: + embedding.filename = filepath + embedding.set_hash(hashes.sha256(filepath, "textual_inversion/" + name) or '') + + return embedding + + def write_loss(log_directory, filename, step, epoch_len, values): if shared.opts.training_write_csv_every == 0: return From 117ec7199416b56a16cbc5591f13fe6cce521d9e Mon Sep 17 00:00:00 2001 From: Won-Kyu Park Date: Sat, 14 Oct 2023 21:58:28 +0900 Subject: [PATCH 162/186] support webui.settings.bat --- webui.bat | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/webui.bat b/webui.bat index a630ea4d..e2c9079d 100644 --- a/webui.bat +++ b/webui.bat @@ -1,5 +1,9 @@ @echo off +if exist webui.settings.bat ( + call webui.settings.bat +) + if not defined PYTHON (set PYTHON=python) if defined GIT (set "GIT_PYTHON_GIT_EXECUTABLE=%GIT%") if not defined VENV_DIR (set "VENV_DIR=%~dp0%venv") From f00eaa4d000856964f810aa4b651200861bafc6f Mon Sep 17 00:00:00 2001 From: Khachatur Avanesian Date: Sun, 15 Oct 2023 02:34:03 +0300 Subject: [PATCH 163/186] Start / Restart generation by Ctrl (Alt) + Enter Add ability to interrupt current generation and start generation again by Ctrl (Alt) + Enter --- script.js | 328 +++++++++++++++++++++++++++--------------------------- 1 file changed, 165 insertions(+), 163 deletions(-) diff --git a/script.js b/script.js index 34cca765..8bf1257d 100644 --- a/script.js +++ b/script.js @@ -1,163 +1,165 @@ -function gradioApp() { - const elems = document.getElementsByTagName('gradio-app'); - const elem = elems.length == 0 ? document : elems[0]; - - if (elem !== document) { - elem.getElementById = function(id) { - return document.getElementById(id); - }; - } - return elem.shadowRoot ? elem.shadowRoot : elem; -} - -/** - * Get the currently selected top-level UI tab button (e.g. the button that says "Extras"). - */ -function get_uiCurrentTab() { - return gradioApp().querySelector('#tabs > .tab-nav > button.selected'); -} - -/** - * Get the first currently visible top-level UI tab content (e.g. the div hosting the "txt2img" UI). - */ -function get_uiCurrentTabContent() { - return gradioApp().querySelector('#tabs > .tabitem[id^=tab_]:not([style*="display: none"])'); -} - -var uiUpdateCallbacks = []; -var uiAfterUpdateCallbacks = []; -var uiLoadedCallbacks = []; -var uiTabChangeCallbacks = []; -var optionsChangedCallbacks = []; -var uiAfterUpdateTimeout = null; -var uiCurrentTab = null; - -/** - * Register callback to be called at each UI update. - * The callback receives an array of MutationRecords as an argument. - */ -function onUiUpdate(callback) { - uiUpdateCallbacks.push(callback); -} - -/** - * Register callback to be called soon after UI updates. - * The callback receives no arguments. - * - * This is preferred over `onUiUpdate` if you don't need - * access to the MutationRecords, as your function will - * not be called quite as often. - */ -function onAfterUiUpdate(callback) { - uiAfterUpdateCallbacks.push(callback); -} - -/** - * Register callback to be called when the UI is loaded. - * The callback receives no arguments. - */ -function onUiLoaded(callback) { - uiLoadedCallbacks.push(callback); -} - -/** - * Register callback to be called when the UI tab is changed. - * The callback receives no arguments. - */ -function onUiTabChange(callback) { - uiTabChangeCallbacks.push(callback); -} - -/** - * Register callback to be called when the options are changed. - * The callback receives no arguments. - * @param callback - */ -function onOptionsChanged(callback) { - optionsChangedCallbacks.push(callback); -} - -function executeCallbacks(queue, arg) { - for (const callback of queue) { - try { - callback(arg); - } catch (e) { - console.error("error running callback", callback, ":", e); - } - } -} - -/** - * Schedule the execution of the callbacks registered with onAfterUiUpdate. - * The callbacks are executed after a short while, unless another call to this function - * is made before that time. IOW, the callbacks are executed only once, even - * when there are multiple mutations observed. - */ -function scheduleAfterUiUpdateCallbacks() { - clearTimeout(uiAfterUpdateTimeout); - uiAfterUpdateTimeout = setTimeout(function() { - executeCallbacks(uiAfterUpdateCallbacks); - }, 200); -} - -var executedOnLoaded = false; - -document.addEventListener("DOMContentLoaded", function() { - var mutationObserver = new MutationObserver(function(m) { - if (!executedOnLoaded && gradioApp().querySelector('#txt2img_prompt')) { - executedOnLoaded = true; - executeCallbacks(uiLoadedCallbacks); - } - - executeCallbacks(uiUpdateCallbacks, m); - scheduleAfterUiUpdateCallbacks(); - const newTab = get_uiCurrentTab(); - if (newTab && (newTab !== uiCurrentTab)) { - uiCurrentTab = newTab; - executeCallbacks(uiTabChangeCallbacks); - } - }); - mutationObserver.observe(gradioApp(), {childList: true, subtree: true}); -}); - -/** - * Add a ctrl+enter as a shortcut to start a generation - */ -document.addEventListener('keydown', function(e) { - var handled = false; - if (e.key !== undefined) { - if ((e.key == "Enter" && (e.metaKey || e.ctrlKey || e.altKey))) handled = true; - } else if (e.keyCode !== undefined) { - if ((e.keyCode == 13 && (e.metaKey || e.ctrlKey || e.altKey))) handled = true; - } - if (handled) { - var button = get_uiCurrentTabContent().querySelector('button[id$=_generate]'); - if (button) { - button.click(); - } - e.preventDefault(); - } -}); - -/** - * checks that a UI element is not in another hidden element or tab content - */ -function uiElementIsVisible(el) { - if (el === document) { - return true; - } - - const computedStyle = getComputedStyle(el); - const isVisible = computedStyle.display !== 'none'; - - if (!isVisible) return false; - return uiElementIsVisible(el.parentNode); -} - -function uiElementInSight(el) { - const clRect = el.getBoundingClientRect(); - const windowHeight = window.innerHeight; - const isOnScreen = clRect.bottom > 0 && clRect.top < windowHeight; - - return isOnScreen; -} +function gradioApp() { + const elems = document.getElementsByTagName('gradio-app'); + const elem = elems.length == 0 ? document : elems[0]; + + if (elem !== document) { + elem.getElementById = function(id) { + return document.getElementById(id); + }; + } + return elem.shadowRoot ? elem.shadowRoot : elem; +} + +/** + * Get the currently selected top-level UI tab button (e.g. the button that says "Extras"). + */ +function get_uiCurrentTab() { + return gradioApp().querySelector('#tabs > .tab-nav > button.selected'); +} + +/** + * Get the first currently visible top-level UI tab content (e.g. the div hosting the "txt2img" UI). + */ +function get_uiCurrentTabContent() { + return gradioApp().querySelector('#tabs > .tabitem[id^=tab_]:not([style*="display: none"])'); +} + +var uiUpdateCallbacks = []; +var uiAfterUpdateCallbacks = []; +var uiLoadedCallbacks = []; +var uiTabChangeCallbacks = []; +var optionsChangedCallbacks = []; +var uiAfterUpdateTimeout = null; +var uiCurrentTab = null; + +/** + * Register callback to be called at each UI update. + * The callback receives an array of MutationRecords as an argument. + */ +function onUiUpdate(callback) { + uiUpdateCallbacks.push(callback); +} + +/** + * Register callback to be called soon after UI updates. + * The callback receives no arguments. + * + * This is preferred over `onUiUpdate` if you don't need + * access to the MutationRecords, as your function will + * not be called quite as often. + */ +function onAfterUiUpdate(callback) { + uiAfterUpdateCallbacks.push(callback); +} + +/** + * Register callback to be called when the UI is loaded. + * The callback receives no arguments. + */ +function onUiLoaded(callback) { + uiLoadedCallbacks.push(callback); +} + +/** + * Register callback to be called when the UI tab is changed. + * The callback receives no arguments. + */ +function onUiTabChange(callback) { + uiTabChangeCallbacks.push(callback); +} + +/** + * Register callback to be called when the options are changed. + * The callback receives no arguments. + * @param callback + */ +function onOptionsChanged(callback) { + optionsChangedCallbacks.push(callback); +} + +function executeCallbacks(queue, arg) { + for (const callback of queue) { + try { + callback(arg); + } catch (e) { + console.error("error running callback", callback, ":", e); + } + } +} + +/** + * Schedule the execution of the callbacks registered with onAfterUiUpdate. + * The callbacks are executed after a short while, unless another call to this function + * is made before that time. IOW, the callbacks are executed only once, even + * when there are multiple mutations observed. + */ +function scheduleAfterUiUpdateCallbacks() { + clearTimeout(uiAfterUpdateTimeout); + uiAfterUpdateTimeout = setTimeout(function() { + executeCallbacks(uiAfterUpdateCallbacks); + }, 200); +} + +var executedOnLoaded = false; + +document.addEventListener("DOMContentLoaded", function() { + var mutationObserver = new MutationObserver(function(m) { + if (!executedOnLoaded && gradioApp().querySelector('#txt2img_prompt')) { + executedOnLoaded = true; + executeCallbacks(uiLoadedCallbacks); + } + + executeCallbacks(uiUpdateCallbacks, m); + scheduleAfterUiUpdateCallbacks(); + const newTab = get_uiCurrentTab(); + if (newTab && (newTab !== uiCurrentTab)) { + uiCurrentTab = newTab; + executeCallbacks(uiTabChangeCallbacks); + } + }); + mutationObserver.observe(gradioApp(), {childList: true, subtree: true}); +}); + +/** + * Add a Ctrl (Alt) + Enter as a shortcut to start / restart a generation + */ +document.addEventListener('keydown', (e) => { + const isEnter = e.key === 'Enter' || e.keyCode === 13 + const isModifierKey = e.metaKey || e.ctrlKey || e.altKey + + const interruptButton = get_uiCurrentTabContent().querySelector('button[id$=_interrupt]') + const generateButton = get_uiCurrentTabContent().querySelector('button[id$=_generate]') + + if (isEnter && isModifierKey) { + if (interruptButton.style.display === 'block') { + interruptButton.click() + setTimeout(() => generateButton.click(), 500) + } else { + generateButton.click() + } + e.preventDefault() + } +}) + +/** + * checks that a UI element is not in another hidden element or tab content + */ +function uiElementIsVisible(el) { + if (el === document) { + return true; + } + + const computedStyle = getComputedStyle(el); + const isVisible = computedStyle.display !== 'none'; + + if (!isVisible) return false; + return uiElementIsVisible(el.parentNode); +} + +function uiElementInSight(el) { + const clRect = el.getBoundingClientRect(); + const windowHeight = window.innerHeight; + const isOnScreen = clRect.bottom > 0 && clRect.top < windowHeight; + + return isOnScreen; +} From 0d65d0eabded4a79c3168ed14599db3970d414c5 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 15 Oct 2023 08:45:38 +0300 Subject: [PATCH 164/186] add an option to not print stack traces on ctrl+c. --- modules/initialize_util.py | 6 +++++- modules/shared_options.py | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/initialize_util.py b/modules/initialize_util.py index 2894eee4..2e9b6d89 100644 --- a/modules/initialize_util.py +++ b/modules/initialize_util.py @@ -150,10 +150,14 @@ def dumpstacks(): def configure_sigint_handler(): # make the program just exit at ctrl+c without waiting for anything + + from modules import shared + def sigint_handler(sig, frame): print(f'Interrupted with signal {sig} in {frame}') - dumpstacks() + if shared.opts.dump_stacks_on_signal: + dumpstacks() os._exit(0) diff --git a/modules/shared_options.py b/modules/shared_options.py index ce395302..6ef9fdd5 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -112,6 +112,7 @@ options_templates.update(options_section(('system', "System"), { "list_hidden_files": OptionInfo(True, "Load models/files in hidden directories").info("directory is hidden if its name starts with \".\""), "disable_mmap_load_safetensors": OptionInfo(False, "Disable memmapping for loading .safetensors files.").info("fixes very slow loading speed in some cases"), "hide_ldm_prints": OptionInfo(True, "Prevent Stability-AI's ldm/sgm modules from printing noise to console."), + "dump_stacks_on_signal": OptionInfo(False, "Print stack traces before exiting the program with ctrl+c."), })) options_templates.update(options_section(('API', "API"), { From 282903bb6798f49af66f6935ee4dc0015895cf7c Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 15 Oct 2023 09:41:02 +0300 Subject: [PATCH 165/186] repair unload sd checkpoint button --- modules/api/api.py | 11 +++++------ modules/sd_models.py | 13 +------------ modules/ui_settings.py | 24 +++++++++++++++++------- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index efedafa4..09083874 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -17,15 +17,14 @@ from fastapi.encoders import jsonable_encoder from secrets import compare_digest import modules.shared as shared -from modules import sd_samplers, deepbooru, sd_hijack, images, scripts, ui, postprocessing, errors, restart, shared_items, script_callbacks, generation_parameters_copypaste +from modules import sd_samplers, deepbooru, sd_hijack, images, scripts, ui, postprocessing, errors, restart, shared_items, script_callbacks, generation_parameters_copypaste, sd_models from modules.api import models from modules.shared import opts from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img, process_images from modules.textual_inversion.textual_inversion import create_embedding, train_embedding from modules.textual_inversion.preprocess import preprocess from modules.hypernetworks.hypernetwork import create_hypernetwork, train_hypernetwork -from PIL import PngImagePlugin,Image -from modules.sd_models import unload_model_weights, reload_model_weights, checkpoint_aliases +from PIL import PngImagePlugin, Image from modules.sd_models_config import find_checkpoint_config_near_filename from modules.realesrgan_model import get_realesrgan_models from modules import devices @@ -541,12 +540,12 @@ class Api: return {} def unloadapi(self): - unload_model_weights() + sd_models.unload_model_weights() return {} def reloadapi(self): - reload_model_weights() + sd_models.send_model_to_device(shared.sd_model) return {} @@ -566,7 +565,7 @@ class Api: def set_config(self, req: dict[str, Any]): checkpoint_name = req.get("sd_model_checkpoint", None) - if checkpoint_name is not None and checkpoint_name not in checkpoint_aliases: + if checkpoint_name is not None and checkpoint_name not in sd_models.checkpoint_aliases: raise RuntimeError(f"model {checkpoint_name!r} not found") for k, v in req.items(): diff --git a/modules/sd_models.py b/modules/sd_models.py index c8efeedc..3b6cdea1 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -1,7 +1,6 @@ import collections import os.path import sys -import gc import threading import torch @@ -798,17 +797,7 @@ def reload_model_weights(sd_model=None, info=None): def unload_model_weights(sd_model=None, info=None): - timer = Timer() - - if model_data.sd_model: - model_data.sd_model.to(devices.cpu) - sd_hijack.model_hijack.undo_hijack(model_data.sd_model) - model_data.sd_model = None - sd_model = None - gc.collect() - devices.torch_gc() - - print(f"Unloaded weights {timer.summary()}.") + send_model_to_cpu(sd_model or shared.sd_model) return sd_model diff --git a/modules/ui_settings.py b/modules/ui_settings.py index 74a3aef3..e054d00a 100644 --- a/modules/ui_settings.py +++ b/modules/ui_settings.py @@ -1,6 +1,6 @@ import gradio as gr -from modules import ui_common, shared, script_callbacks, scripts, sd_models, sysinfo +from modules import ui_common, shared, script_callbacks, scripts, sd_models, sysinfo, timer from modules.call_queue import wrap_gradio_call from modules.shared import opts from modules.ui_components import FormRow @@ -177,8 +177,8 @@ class UiSettings: download_localization = gr.Button(value='Download localization template', elem_id="download_localization") reload_script_bodies = gr.Button(value='Reload custom script bodies (No ui updates, No restart)', variant='secondary', elem_id="settings_reload_script_bodies") with gr.Row(): - unload_sd_model = gr.Button(value='Unload SD checkpoint to free VRAM', elem_id="sett_unload_sd_model") - reload_sd_model = gr.Button(value='Reload the last SD checkpoint back into VRAM', elem_id="sett_reload_sd_model") + unload_sd_model = gr.Button(value='Unload SD checkpoint to RAM', elem_id="sett_unload_sd_model") + reload_sd_model = gr.Button(value='Load SD checkpoint to VRAM from RAM', elem_id="sett_reload_sd_model") with gr.Row(): calculate_all_checkpoint_hash = gr.Button(value='Calculate hash for all checkpoint', elem_id="calculate_all_checkpoint_hash") calculate_all_checkpoint_hash_threads = gr.Number(value=1, label="Number of parallel calculations", elem_id="calculate_all_checkpoint_hash_threads", precision=0, minimum=1) @@ -194,16 +194,26 @@ class UiSettings: self.text_settings = gr.Textbox(elem_id="settings_json", value=lambda: opts.dumpjson(), visible=False) + def call_func_and_return_text(func, text): + def handler(): + t = timer.Timer() + func() + t.record(text) + + return f'{text} in {t.total:.1f}s' + + return handler + unload_sd_model.click( - fn=sd_models.unload_model_weights, + fn=call_func_and_return_text(sd_models.unload_model_weights, 'Unloaded the checkpoint'), inputs=[], - outputs=[] + outputs=[self.result] ) reload_sd_model.click( - fn=sd_models.reload_model_weights, + fn=call_func_and_return_text(lambda: sd_models.send_model_to_device(shared.sd_model), 'Loaded the checkpoint'), inputs=[], - outputs=[] + outputs=[self.result] ) request_notifications.click( From 2f6ea8b10312e700f8d01f90dff15d17690ce49c Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 15 Oct 2023 10:12:38 +0300 Subject: [PATCH 166/186] respect keyedit_precision_attention setting when converting from old (((attention))) syntax --- javascript/edit-attention.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index 8b6dd240..45d9a788 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -86,6 +86,8 @@ function keyupEditAttention(event) { weight = (1 / 1.1) ** numParen; } + weight = Math.round(weight / opts.keyedit_precision_attention) * opts.keyedit_precision_attention; + text = text.slice(0, selectionStart - numParen) + "(" + text.slice(selectionStart, selectionEnd) + ":" + weight + ")" + text.slice(selectionEnd + numParen); selectionStart -= numParen - 1; selectionEnd -= numParen - 1; From 77bd953da2f96845b196f242f7ba51138cd54e3b Mon Sep 17 00:00:00 2001 From: Khachatur Avanesian Date: Sun, 15 Oct 2023 10:25:36 +0300 Subject: [PATCH 167/186] Update script.js Exclude lambda --- script.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/script.js b/script.js index 8bf1257d..2fab1554 100644 --- a/script.js +++ b/script.js @@ -123,23 +123,25 @@ document.addEventListener("DOMContentLoaded", function() { /** * Add a Ctrl (Alt) + Enter as a shortcut to start / restart a generation */ -document.addEventListener('keydown', (e) => { - const isEnter = e.key === 'Enter' || e.keyCode === 13 - const isModifierKey = e.metaKey || e.ctrlKey || e.altKey +document.addEventListener('keydown', function(e) { + const isEnter = e.key === 'Enter' || e.keyCode === 13; + const isModifierKey = e.metaKey || e.ctrlKey || e.altKey; - const interruptButton = get_uiCurrentTabContent().querySelector('button[id$=_interrupt]') - const generateButton = get_uiCurrentTabContent().querySelector('button[id$=_generate]') + const interruptButton = get_uiCurrentTabContent().querySelector('button[id$=_interrupt]'); + const generateButton = get_uiCurrentTabContent().querySelector('button[id$=_generate]'); if (isEnter && isModifierKey) { if (interruptButton.style.display === 'block') { - interruptButton.click() - setTimeout(() => generateButton.click(), 500) + interruptButton.click(); + setTimeout(function() { + generateButton.click(); + }, 500); } else { - generateButton.click() + generateButton.click(); } - e.preventDefault() + e.preventDefault(); } -}) +}); /** * checks that a UI element is not in another hidden element or tab content From d295e97a0d7ca985ab21e935fd933ce629fba2bf Mon Sep 17 00:00:00 2001 From: Khachatur Avanesian Date: Sun, 15 Oct 2023 10:37:48 +0300 Subject: [PATCH 168/186] Update script.js LF instead CRLF --- script.js | 130 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 68 insertions(+), 62 deletions(-) diff --git a/script.js b/script.js index 2fab1554..8af9773f 100644 --- a/script.js +++ b/script.js @@ -1,43 +1,45 @@ function gradioApp() { - const elems = document.getElementsByTagName('gradio-app'); - const elem = elems.length == 0 ? document : elems[0]; + const elems = document.getElementsByTagName('gradio-app') + const elem = elems.length == 0 ? document : elems[0] if (elem !== document) { - elem.getElementById = function(id) { - return document.getElementById(id); - }; + elem.getElementById = function (id) { + return document.getElementById(id) + } } - return elem.shadowRoot ? elem.shadowRoot : elem; + return elem.shadowRoot ? elem.shadowRoot : elem } /** * Get the currently selected top-level UI tab button (e.g. the button that says "Extras"). */ function get_uiCurrentTab() { - return gradioApp().querySelector('#tabs > .tab-nav > button.selected'); + return gradioApp().querySelector('#tabs > .tab-nav > button.selected') } /** * Get the first currently visible top-level UI tab content (e.g. the div hosting the "txt2img" UI). */ function get_uiCurrentTabContent() { - return gradioApp().querySelector('#tabs > .tabitem[id^=tab_]:not([style*="display: none"])'); + return gradioApp().querySelector( + '#tabs > .tabitem[id^=tab_]:not([style*="display: none"])' + ) } -var uiUpdateCallbacks = []; -var uiAfterUpdateCallbacks = []; -var uiLoadedCallbacks = []; -var uiTabChangeCallbacks = []; -var optionsChangedCallbacks = []; -var uiAfterUpdateTimeout = null; -var uiCurrentTab = null; +var uiUpdateCallbacks = [] +var uiAfterUpdateCallbacks = [] +var uiLoadedCallbacks = [] +var uiTabChangeCallbacks = [] +var optionsChangedCallbacks = [] +var uiAfterUpdateTimeout = null +var uiCurrentTab = null /** * Register callback to be called at each UI update. * The callback receives an array of MutationRecords as an argument. */ function onUiUpdate(callback) { - uiUpdateCallbacks.push(callback); + uiUpdateCallbacks.push(callback) } /** @@ -49,7 +51,7 @@ function onUiUpdate(callback) { * not be called quite as often. */ function onAfterUiUpdate(callback) { - uiAfterUpdateCallbacks.push(callback); + uiAfterUpdateCallbacks.push(callback) } /** @@ -57,7 +59,7 @@ function onAfterUiUpdate(callback) { * The callback receives no arguments. */ function onUiLoaded(callback) { - uiLoadedCallbacks.push(callback); + uiLoadedCallbacks.push(callback) } /** @@ -65,7 +67,7 @@ function onUiLoaded(callback) { * The callback receives no arguments. */ function onUiTabChange(callback) { - uiTabChangeCallbacks.push(callback); + uiTabChangeCallbacks.push(callback) } /** @@ -74,15 +76,15 @@ function onUiTabChange(callback) { * @param callback */ function onOptionsChanged(callback) { - optionsChangedCallbacks.push(callback); + optionsChangedCallbacks.push(callback) } function executeCallbacks(queue, arg) { for (const callback of queue) { try { - callback(arg); + callback(arg) } catch (e) { - console.error("error running callback", callback, ":", e); + console.error('error running callback', callback, ':', e) } } } @@ -94,74 +96,78 @@ function executeCallbacks(queue, arg) { * when there are multiple mutations observed. */ function scheduleAfterUiUpdateCallbacks() { - clearTimeout(uiAfterUpdateTimeout); - uiAfterUpdateTimeout = setTimeout(function() { - executeCallbacks(uiAfterUpdateCallbacks); - }, 200); + clearTimeout(uiAfterUpdateTimeout) + uiAfterUpdateTimeout = setTimeout(function () { + executeCallbacks(uiAfterUpdateCallbacks) + }, 200) } -var executedOnLoaded = false; +var executedOnLoaded = false -document.addEventListener("DOMContentLoaded", function() { - var mutationObserver = new MutationObserver(function(m) { +document.addEventListener('DOMContentLoaded', function () { + var mutationObserver = new MutationObserver(function (m) { if (!executedOnLoaded && gradioApp().querySelector('#txt2img_prompt')) { - executedOnLoaded = true; - executeCallbacks(uiLoadedCallbacks); + executedOnLoaded = true + executeCallbacks(uiLoadedCallbacks) } - executeCallbacks(uiUpdateCallbacks, m); - scheduleAfterUiUpdateCallbacks(); - const newTab = get_uiCurrentTab(); - if (newTab && (newTab !== uiCurrentTab)) { - uiCurrentTab = newTab; - executeCallbacks(uiTabChangeCallbacks); + executeCallbacks(uiUpdateCallbacks, m) + scheduleAfterUiUpdateCallbacks() + const newTab = get_uiCurrentTab() + if (newTab && newTab !== uiCurrentTab) { + uiCurrentTab = newTab + executeCallbacks(uiTabChangeCallbacks) } - }); - mutationObserver.observe(gradioApp(), {childList: true, subtree: true}); -}); + }) + mutationObserver.observe(gradioApp(), {childList: true, subtree: true}) +}) /** - * Add a Ctrl (Alt) + Enter as a shortcut to start / restart a generation + * Add a ctrl+enter as a shortcut to start a generation */ -document.addEventListener('keydown', function(e) { - const isEnter = e.key === 'Enter' || e.keyCode === 13; - const isModifierKey = e.metaKey || e.ctrlKey || e.altKey; +document.addEventListener('keydown', function (e) { + const isEnter = e.key === 'Enter' || e.keyCode === 13 + const isModifierKey = e.metaKey || e.ctrlKey || e.altKey - const interruptButton = get_uiCurrentTabContent().querySelector('button[id$=_interrupt]'); - const generateButton = get_uiCurrentTabContent().querySelector('button[id$=_generate]'); + const interruptButton = get_uiCurrentTabContent().querySelector( + 'button[id$=_interrupt]' + ) + const generateButton = get_uiCurrentTabContent().querySelector( + 'button[id$=_generate]' + ) if (isEnter && isModifierKey) { if (interruptButton.style.display === 'block') { - interruptButton.click(); - setTimeout(function() { - generateButton.click(); - }, 500); + interruptButton.click() + setTimeout(function () { + generateButton.click() + }, 500) } else { - generateButton.click(); + generateButton.click() } - e.preventDefault(); + e.preventDefault() } -}); +}) /** * checks that a UI element is not in another hidden element or tab content */ function uiElementIsVisible(el) { if (el === document) { - return true; + return true } - const computedStyle = getComputedStyle(el); - const isVisible = computedStyle.display !== 'none'; + const computedStyle = getComputedStyle(el) + const isVisible = computedStyle.display !== 'none' - if (!isVisible) return false; - return uiElementIsVisible(el.parentNode); + if (!isVisible) return false + return uiElementIsVisible(el.parentNode) } function uiElementInSight(el) { - const clRect = el.getBoundingClientRect(); - const windowHeight = window.innerHeight; - const isOnScreen = clRect.bottom > 0 && clRect.top < windowHeight; + const clRect = el.getBoundingClientRect() + const windowHeight = window.innerHeight + const isOnScreen = clRect.bottom > 0 && clRect.top < windowHeight - return isOnScreen; + return isOnScreen } From 3e223523ced2a19347d0b42b662129947152dc49 Mon Sep 17 00:00:00 2001 From: Khachatur Avanesian Date: Sun, 15 Oct 2023 10:48:50 +0300 Subject: [PATCH 169/186] Update script.js --- script.js | 128 ++++++++++++++++++++++++++---------------------------- 1 file changed, 61 insertions(+), 67 deletions(-) diff --git a/script.js b/script.js index 8af9773f..0f63ee93 100644 --- a/script.js +++ b/script.js @@ -1,45 +1,43 @@ function gradioApp() { - const elems = document.getElementsByTagName('gradio-app') - const elem = elems.length == 0 ? document : elems[0] + const elems = document.getElementsByTagName('gradio-app'); + const elem = elems.length == 0 ? document : elems[0]; if (elem !== document) { - elem.getElementById = function (id) { - return document.getElementById(id) - } + elem.getElementById = function(id) { + return document.getElementById(id); + }; } - return elem.shadowRoot ? elem.shadowRoot : elem + return elem.shadowRoot ? elem.shadowRoot : elem; } /** * Get the currently selected top-level UI tab button (e.g. the button that says "Extras"). */ function get_uiCurrentTab() { - return gradioApp().querySelector('#tabs > .tab-nav > button.selected') + return gradioApp().querySelector('#tabs > .tab-nav > button.selected'); } /** * Get the first currently visible top-level UI tab content (e.g. the div hosting the "txt2img" UI). */ function get_uiCurrentTabContent() { - return gradioApp().querySelector( - '#tabs > .tabitem[id^=tab_]:not([style*="display: none"])' - ) + return gradioApp().querySelector('#tabs > .tabitem[id^=tab_]:not([style*="display: none"])'); } -var uiUpdateCallbacks = [] -var uiAfterUpdateCallbacks = [] -var uiLoadedCallbacks = [] -var uiTabChangeCallbacks = [] -var optionsChangedCallbacks = [] -var uiAfterUpdateTimeout = null -var uiCurrentTab = null +var uiUpdateCallbacks = []; +var uiAfterUpdateCallbacks = []; +var uiLoadedCallbacks = []; +var uiTabChangeCallbacks = []; +var optionsChangedCallbacks = []; +var uiAfterUpdateTimeout = null; +var uiCurrentTab = null; /** * Register callback to be called at each UI update. * The callback receives an array of MutationRecords as an argument. */ function onUiUpdate(callback) { - uiUpdateCallbacks.push(callback) + uiUpdateCallbacks.push(callback); } /** @@ -51,7 +49,7 @@ function onUiUpdate(callback) { * not be called quite as often. */ function onAfterUiUpdate(callback) { - uiAfterUpdateCallbacks.push(callback) + uiAfterUpdateCallbacks.push(callback); } /** @@ -59,7 +57,7 @@ function onAfterUiUpdate(callback) { * The callback receives no arguments. */ function onUiLoaded(callback) { - uiLoadedCallbacks.push(callback) + uiLoadedCallbacks.push(callback); } /** @@ -67,7 +65,7 @@ function onUiLoaded(callback) { * The callback receives no arguments. */ function onUiTabChange(callback) { - uiTabChangeCallbacks.push(callback) + uiTabChangeCallbacks.push(callback); } /** @@ -76,15 +74,15 @@ function onUiTabChange(callback) { * @param callback */ function onOptionsChanged(callback) { - optionsChangedCallbacks.push(callback) + optionsChangedCallbacks.push(callback); } function executeCallbacks(queue, arg) { for (const callback of queue) { try { - callback(arg) + callback(arg); } catch (e) { - console.error('error running callback', callback, ':', e) + console.error("error running callback", callback, ":", e); } } } @@ -96,78 +94,74 @@ function executeCallbacks(queue, arg) { * when there are multiple mutations observed. */ function scheduleAfterUiUpdateCallbacks() { - clearTimeout(uiAfterUpdateTimeout) - uiAfterUpdateTimeout = setTimeout(function () { - executeCallbacks(uiAfterUpdateCallbacks) - }, 200) + clearTimeout(uiAfterUpdateTimeout); + uiAfterUpdateTimeout = setTimeout(function() { + executeCallbacks(uiAfterUpdateCallbacks); + }, 200); } -var executedOnLoaded = false +var executedOnLoaded = false; -document.addEventListener('DOMContentLoaded', function () { - var mutationObserver = new MutationObserver(function (m) { +document.addEventListener("DOMContentLoaded", function() { + var mutationObserver = new MutationObserver(function(m) { if (!executedOnLoaded && gradioApp().querySelector('#txt2img_prompt')) { - executedOnLoaded = true - executeCallbacks(uiLoadedCallbacks) + executedOnLoaded = true; + executeCallbacks(uiLoadedCallbacks); } - executeCallbacks(uiUpdateCallbacks, m) - scheduleAfterUiUpdateCallbacks() - const newTab = get_uiCurrentTab() - if (newTab && newTab !== uiCurrentTab) { - uiCurrentTab = newTab - executeCallbacks(uiTabChangeCallbacks) + executeCallbacks(uiUpdateCallbacks, m); + scheduleAfterUiUpdateCallbacks(); + const newTab = get_uiCurrentTab(); + if (newTab && (newTab !== uiCurrentTab)) { + uiCurrentTab = newTab; + executeCallbacks(uiTabChangeCallbacks); } - }) - mutationObserver.observe(gradioApp(), {childList: true, subtree: true}) -}) + }); + mutationObserver.observe(gradioApp(), {childList: true, subtree: true}); +}); /** * Add a ctrl+enter as a shortcut to start a generation */ -document.addEventListener('keydown', function (e) { - const isEnter = e.key === 'Enter' || e.keyCode === 13 - const isModifierKey = e.metaKey || e.ctrlKey || e.altKey +document.addEventListener('keydown', function(e) { + const isEnter = e.key === 'Enter' || e.keyCode === 13; + const isModifierKey = e.metaKey || e.ctrlKey || e.altKey; - const interruptButton = get_uiCurrentTabContent().querySelector( - 'button[id$=_interrupt]' - ) - const generateButton = get_uiCurrentTabContent().querySelector( - 'button[id$=_generate]' - ) + const interruptButton = get_uiCurrentTabContent().querySelector('button[id$=_interrupt]'); + const generateButton = get_uiCurrentTabContent().querySelector('button[id$=_generate]'); if (isEnter && isModifierKey) { if (interruptButton.style.display === 'block') { - interruptButton.click() - setTimeout(function () { - generateButton.click() - }, 500) + interruptButton.click(); + setTimeout(function() { + generateButton.click(); + }, 500); } else { - generateButton.click() + generateButton.click(); } - e.preventDefault() + e.preventDefault(); } -}) +}); /** * checks that a UI element is not in another hidden element or tab content */ function uiElementIsVisible(el) { if (el === document) { - return true + return true; } - const computedStyle = getComputedStyle(el) - const isVisible = computedStyle.display !== 'none' + const computedStyle = getComputedStyle(el); + const isVisible = computedStyle.display !== 'none'; - if (!isVisible) return false - return uiElementIsVisible(el.parentNode) + if (!isVisible) return false; + return uiElementIsVisible(el.parentNode); } function uiElementInSight(el) { - const clRect = el.getBoundingClientRect() - const windowHeight = window.innerHeight - const isOnScreen = clRect.bottom > 0 && clRect.top < windowHeight + const clRect = el.getBoundingClientRect(); + const windowHeight = window.innerHeight; + const isOnScreen = clRect.bottom > 0 && clRect.top < windowHeight; - return isOnScreen + return isOnScreen; } From d33cb2b8122f259002ce6ef2e7f5cf30dbe069b5 Mon Sep 17 00:00:00 2001 From: Khachatur Avanesian Date: Sun, 15 Oct 2023 11:01:45 +0300 Subject: [PATCH 170/186] Add files via upload LF --- script.js | 334 +++++++++++++++++++++++++++--------------------------- 1 file changed, 167 insertions(+), 167 deletions(-) diff --git a/script.js b/script.js index 0f63ee93..5f6ee354 100644 --- a/script.js +++ b/script.js @@ -1,167 +1,167 @@ -function gradioApp() { - const elems = document.getElementsByTagName('gradio-app'); - const elem = elems.length == 0 ? document : elems[0]; - - if (elem !== document) { - elem.getElementById = function(id) { - return document.getElementById(id); - }; - } - return elem.shadowRoot ? elem.shadowRoot : elem; -} - -/** - * Get the currently selected top-level UI tab button (e.g. the button that says "Extras"). - */ -function get_uiCurrentTab() { - return gradioApp().querySelector('#tabs > .tab-nav > button.selected'); -} - -/** - * Get the first currently visible top-level UI tab content (e.g. the div hosting the "txt2img" UI). - */ -function get_uiCurrentTabContent() { - return gradioApp().querySelector('#tabs > .tabitem[id^=tab_]:not([style*="display: none"])'); -} - -var uiUpdateCallbacks = []; -var uiAfterUpdateCallbacks = []; -var uiLoadedCallbacks = []; -var uiTabChangeCallbacks = []; -var optionsChangedCallbacks = []; -var uiAfterUpdateTimeout = null; -var uiCurrentTab = null; - -/** - * Register callback to be called at each UI update. - * The callback receives an array of MutationRecords as an argument. - */ -function onUiUpdate(callback) { - uiUpdateCallbacks.push(callback); -} - -/** - * Register callback to be called soon after UI updates. - * The callback receives no arguments. - * - * This is preferred over `onUiUpdate` if you don't need - * access to the MutationRecords, as your function will - * not be called quite as often. - */ -function onAfterUiUpdate(callback) { - uiAfterUpdateCallbacks.push(callback); -} - -/** - * Register callback to be called when the UI is loaded. - * The callback receives no arguments. - */ -function onUiLoaded(callback) { - uiLoadedCallbacks.push(callback); -} - -/** - * Register callback to be called when the UI tab is changed. - * The callback receives no arguments. - */ -function onUiTabChange(callback) { - uiTabChangeCallbacks.push(callback); -} - -/** - * Register callback to be called when the options are changed. - * The callback receives no arguments. - * @param callback - */ -function onOptionsChanged(callback) { - optionsChangedCallbacks.push(callback); -} - -function executeCallbacks(queue, arg) { - for (const callback of queue) { - try { - callback(arg); - } catch (e) { - console.error("error running callback", callback, ":", e); - } - } -} - -/** - * Schedule the execution of the callbacks registered with onAfterUiUpdate. - * The callbacks are executed after a short while, unless another call to this function - * is made before that time. IOW, the callbacks are executed only once, even - * when there are multiple mutations observed. - */ -function scheduleAfterUiUpdateCallbacks() { - clearTimeout(uiAfterUpdateTimeout); - uiAfterUpdateTimeout = setTimeout(function() { - executeCallbacks(uiAfterUpdateCallbacks); - }, 200); -} - -var executedOnLoaded = false; - -document.addEventListener("DOMContentLoaded", function() { - var mutationObserver = new MutationObserver(function(m) { - if (!executedOnLoaded && gradioApp().querySelector('#txt2img_prompt')) { - executedOnLoaded = true; - executeCallbacks(uiLoadedCallbacks); - } - - executeCallbacks(uiUpdateCallbacks, m); - scheduleAfterUiUpdateCallbacks(); - const newTab = get_uiCurrentTab(); - if (newTab && (newTab !== uiCurrentTab)) { - uiCurrentTab = newTab; - executeCallbacks(uiTabChangeCallbacks); - } - }); - mutationObserver.observe(gradioApp(), {childList: true, subtree: true}); -}); - -/** - * Add a ctrl+enter as a shortcut to start a generation - */ -document.addEventListener('keydown', function(e) { - const isEnter = e.key === 'Enter' || e.keyCode === 13; - const isModifierKey = e.metaKey || e.ctrlKey || e.altKey; - - const interruptButton = get_uiCurrentTabContent().querySelector('button[id$=_interrupt]'); - const generateButton = get_uiCurrentTabContent().querySelector('button[id$=_generate]'); - - if (isEnter && isModifierKey) { - if (interruptButton.style.display === 'block') { - interruptButton.click(); - setTimeout(function() { - generateButton.click(); - }, 500); - } else { - generateButton.click(); - } - e.preventDefault(); - } -}); - -/** - * checks that a UI element is not in another hidden element or tab content - */ -function uiElementIsVisible(el) { - if (el === document) { - return true; - } - - const computedStyle = getComputedStyle(el); - const isVisible = computedStyle.display !== 'none'; - - if (!isVisible) return false; - return uiElementIsVisible(el.parentNode); -} - -function uiElementInSight(el) { - const clRect = el.getBoundingClientRect(); - const windowHeight = window.innerHeight; - const isOnScreen = clRect.bottom > 0 && clRect.top < windowHeight; - - return isOnScreen; -} +function gradioApp() { + const elems = document.getElementsByTagName('gradio-app'); + const elem = elems.length == 0 ? document : elems[0]; + + if (elem !== document) { + elem.getElementById = function(id) { + return document.getElementById(id); + }; + } + return elem.shadowRoot ? elem.shadowRoot : elem; +} + +/** + * Get the currently selected top-level UI tab button (e.g. the button that says "Extras"). + */ +function get_uiCurrentTab() { + return gradioApp().querySelector('#tabs > .tab-nav > button.selected'); +} + +/** + * Get the first currently visible top-level UI tab content (e.g. the div hosting the "txt2img" UI). + */ +function get_uiCurrentTabContent() { + return gradioApp().querySelector('#tabs > .tabitem[id^=tab_]:not([style*="display: none"])'); +} + +var uiUpdateCallbacks = []; +var uiAfterUpdateCallbacks = []; +var uiLoadedCallbacks = []; +var uiTabChangeCallbacks = []; +var optionsChangedCallbacks = []; +var uiAfterUpdateTimeout = null; +var uiCurrentTab = null; + +/** + * Register callback to be called at each UI update. + * The callback receives an array of MutationRecords as an argument. + */ +function onUiUpdate(callback) { + uiUpdateCallbacks.push(callback); +} + +/** + * Register callback to be called soon after UI updates. + * The callback receives no arguments. + * + * This is preferred over `onUiUpdate` if you don't need + * access to the MutationRecords, as your function will + * not be called quite as often. + */ +function onAfterUiUpdate(callback) { + uiAfterUpdateCallbacks.push(callback); +} + +/** + * Register callback to be called when the UI is loaded. + * The callback receives no arguments. + */ +function onUiLoaded(callback) { + uiLoadedCallbacks.push(callback); +} + +/** + * Register callback to be called when the UI tab is changed. + * The callback receives no arguments. + */ +function onUiTabChange(callback) { + uiTabChangeCallbacks.push(callback); +} + +/** + * Register callback to be called when the options are changed. + * The callback receives no arguments. + * @param callback + */ +function onOptionsChanged(callback) { + optionsChangedCallbacks.push(callback); +} + +function executeCallbacks(queue, arg) { + for (const callback of queue) { + try { + callback(arg); + } catch (e) { + console.error("error running callback", callback, ":", e); + } + } +} + +/** + * Schedule the execution of the callbacks registered with onAfterUiUpdate. + * The callbacks are executed after a short while, unless another call to this function + * is made before that time. IOW, the callbacks are executed only once, even + * when there are multiple mutations observed. + */ +function scheduleAfterUiUpdateCallbacks() { + clearTimeout(uiAfterUpdateTimeout); + uiAfterUpdateTimeout = setTimeout(function() { + executeCallbacks(uiAfterUpdateCallbacks); + }, 200); +} + +var executedOnLoaded = false; + +document.addEventListener("DOMContentLoaded", function() { + var mutationObserver = new MutationObserver(function(m) { + if (!executedOnLoaded && gradioApp().querySelector('#txt2img_prompt')) { + executedOnLoaded = true; + executeCallbacks(uiLoadedCallbacks); + } + + executeCallbacks(uiUpdateCallbacks, m); + scheduleAfterUiUpdateCallbacks(); + const newTab = get_uiCurrentTab(); + if (newTab && (newTab !== uiCurrentTab)) { + uiCurrentTab = newTab; + executeCallbacks(uiTabChangeCallbacks); + } + }); + mutationObserver.observe(gradioApp(), {childList: true, subtree: true}); +}); + +/** + * Add a ctrl+enter as a shortcut to start a generation + */ +document.addEventListener('keydown', function(e) { + const isEnter = e.key === 'Enter' || e.keyCode === 13; + const isModifierKey = e.metaKey || e.ctrlKey || e.altKey; + + const interruptButton = get_uiCurrentTabContent().querySelector('button[id$=_interrupt]'); + const generateButton = get_uiCurrentTabContent().querySelector('button[id$=_generate]'); + + if (isEnter && isModifierKey) { + if (interruptButton.style.display === 'block') { + interruptButton.click(); + setTimeout(function() { + generateButton.click(); + }, 500); + } else { + generateButton.click(); + } + e.preventDefault(); + } +}); + +/** + * checks that a UI element is not in another hidden element or tab content + */ +function uiElementIsVisible(el) { + if (el === document) { + return true; + } + + const computedStyle = getComputedStyle(el); + const isVisible = computedStyle.display !== 'none'; + + if (!isVisible) return false; + return uiElementIsVisible(el.parentNode); +} + +function uiElementInSight(el) { + const clRect = el.getBoundingClientRect(); + const windowHeight = window.innerHeight; + const isOnScreen = clRect.bottom > 0 && clRect.top < windowHeight; + + return isOnScreen; +} From 384fab9627942dc7a5771368180bab9cfe0c2877 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 21 Oct 2023 08:45:51 +0300 Subject: [PATCH 171/186] rework some of changes for emphasis editing keys, force conversion of old-style emphasis --- javascript/edit-attention.js | 97 ++++++++++++++++-------------------- modules/shared_options.py | 3 +- 2 files changed, 45 insertions(+), 55 deletions(-) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index 45d9a788..f3af9a4c 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -26,10 +26,15 @@ function keyupEditAttention(event) { // Set the selection to the text between the parenthesis const parenContent = text.substring(beforeParen + 1, selectionStart + afterParen); - if (!/.*:-?[\d.]+/s.test(parenContent)) return false; - const lastColon = parenContent.lastIndexOf(":"); - selectionStart = beforeParen + 1; - selectionEnd = selectionStart + lastColon; + if (/.*:-?[\d.]+/s.test(parenContent)) { + const lastColon = parenContent.lastIndexOf(":"); + selectionStart = beforeParen + 1; + selectionEnd = selectionStart + lastColon; + } else { + selectionStart = beforeParen + 1; + selectionEnd = selectionStart + parenContent.length; + } + target.setSelectionRange(selectionStart, selectionEnd); return true; } @@ -58,7 +63,7 @@ function keyupEditAttention(event) { } // If the user hasn't selected anything, let's select their current parenthesis block or word - if (!selectCurrentParenthesisBlock('<', '>') && !selectCurrentParenthesisBlock('(', ')')) { + if (!selectCurrentParenthesisBlock('<', '>') && !selectCurrentParenthesisBlock('(', ')') && !selectCurrentParenthesisBlock('[', ']')) { selectCurrentWord(); } @@ -66,44 +71,31 @@ function keyupEditAttention(event) { var closeCharacter = ')'; var delta = opts.keyedit_precision_attention; + var start = selectionStart > 0 ? text[selectionStart - 1] : ""; + var end = text[selectionEnd]; - if (selectionStart > 0 && /<.*:-?[\d.]+>/s.test(text.slice(selectionStart - 1, selectionEnd + text.slice(selectionEnd).indexOf(">") + 1))) { + if (start == '<') { closeCharacter = '>'; delta = opts.keyedit_precision_extra; - } else if (selectionStart > 0 && /\(.*\)|\[.*\]/s.test(text.slice(selectionStart - 1, selectionEnd + 1))) { - let start = text[selectionStart - 1]; - let end = text[selectionEnd]; - if (opts.keyedit_convert) { - let numParen = 0; + } else if (start == '(' && end == ')' || start == '[' && end == ']') { // convert old-style (((emphasis))) + let numParen = 0; - while (text[selectionStart - numParen - 1] == start && text[selectionEnd + numParen] == end) { - numParen++; - } - - if (start == "(") { - weight = 1.1 ** numParen; - } else { - weight = (1 / 1.1) ** numParen; - } - - weight = Math.round(weight / opts.keyedit_precision_attention) * opts.keyedit_precision_attention; - - text = text.slice(0, selectionStart - numParen) + "(" + text.slice(selectionStart, selectionEnd) + ":" + weight + ")" + text.slice(selectionEnd + numParen); - selectionStart -= numParen - 1; - selectionEnd -= numParen - 1; - } else { - closeCharacter = null; - if (isPlus) { - text = text.slice(0, selectionStart) + start + text.slice(selectionStart, selectionEnd) + end + text.slice(selectionEnd); - selectionStart++; - selectionEnd++; - } else { - text = text.slice(0, selectionStart - 1) + text.slice(selectionStart, selectionEnd) + text.slice(selectionEnd + 1); - selectionStart--; - selectionEnd--; - } + while (text[selectionStart - numParen - 1] == start && text[selectionEnd + numParen] == end) { + numParen++; } - } else if (selectionStart == 0 || !/\(.*:-?[\d.]+\)/s.test(text.slice(selectionStart - 1, selectionEnd + text.slice(selectionEnd).indexOf(")") + 1))) { + + if (start == "[") { + weight = (1 / 1.1) ** numParen; + } else { + weight = 1.1 ** numParen; + } + + weight = Math.round(weight / opts.keyedit_precision_attention) * opts.keyedit_precision_attention; + + text = text.slice(0, selectionStart - numParen) + "(" + text.slice(selectionStart, selectionEnd) + ":" + weight + ")" + text.slice(selectionEnd + numParen); + selectionStart -= numParen - 1; + selectionEnd -= numParen - 1; + } else if (start != '(') { // do not include spaces at the end while (selectionEnd > selectionStart && text[selectionEnd - 1] == ' ') { selectionEnd--; @@ -119,23 +111,22 @@ function keyupEditAttention(event) { selectionEnd++; } - if (closeCharacter) { - var end = text.slice(selectionEnd + 1).indexOf(closeCharacter) + 1; - var weight = parseFloat(text.slice(selectionEnd + 1, selectionEnd + end)); - if (isNaN(weight)) return; + if (text[selectionEnd] != ':') return; + var weightLength = text.slice(selectionEnd + 1).indexOf(closeCharacter) + 1; + var weight = parseFloat(text.slice(selectionEnd + 1, selectionEnd + weightLength)); + if (isNaN(weight)) return; - weight += isPlus ? delta : -delta; - weight = parseFloat(weight.toPrecision(12)); - if (Number.isInteger(weight)) weight += ".0"; + weight += isPlus ? delta : -delta; + weight = parseFloat(weight.toPrecision(12)); + if (Number.isInteger(weight)) weight += ".0"; - if (closeCharacter == ')' && weight == 1) { - var endParenPos = text.substring(selectionEnd).indexOf(')'); - text = text.slice(0, selectionStart - 1) + text.slice(selectionStart, selectionEnd) + text.slice(selectionEnd + endParenPos + 1); - selectionStart--; - selectionEnd--; - } else { - text = text.slice(0, selectionEnd + 1) + weight + text.slice(selectionEnd + end); - } + if (closeCharacter == ')' && weight == 1) { + var endParenPos = text.substring(selectionEnd).indexOf(')'); + text = text.slice(0, selectionStart - 1) + text.slice(selectionStart, selectionEnd) + text.slice(selectionEnd + endParenPos + 1); + selectionStart--; + selectionEnd--; + } else { + text = text.slice(0, selectionEnd + 1) + weight + text.slice(selectionEnd + weightLength); } target.focus(); diff --git a/modules/shared_options.py b/modules/shared_options.py index 32bf7353..0a82216f 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -259,9 +259,8 @@ options_templates.update(options_section(('ui', "User interface"), { "dimensions_and_batch_together": OptionInfo(True, "Show Width/Height and Batch sliders in same row").needs_reload_ui(), "keyedit_precision_attention": OptionInfo(0.1, "Ctrl+up/down precision when editing (attention:1.1)", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}), "keyedit_precision_extra": OptionInfo(0.05, "Ctrl+up/down precision when editing ", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}), - "keyedit_delimiters": OptionInfo(r".,\/!?%^*;:{}=`~()[]<>| ", "Ctrl+up/down word delimiters"), + "keyedit_delimiters": OptionInfo(r".,\/!?%^*;:{}=`~() ", "Ctrl+up/down word delimiters"), "keyedit_delimiters_whitespace": OptionInfo(["Tab", "Carriage Return", "Line Feed"], "Ctrl+up/down whitespace delimiters", gr.CheckboxGroup, lambda: {"choices": ["Tab", "Carriage Return", "Line Feed"]}), - "keyedit_convert": OptionInfo(True, "Convert (attention) to (attention:1.1)"), "keyedit_move": OptionInfo(True, "Alt+left/right moves prompt elements"), "quicksettings_list": OptionInfo(["sd_model_checkpoint"], "Quicksettings list", ui_components.DropdownMulti, lambda: {"choices": list(shared.opts.data_labels.keys())}).js("info", "settingsHintsShowQuicksettings").info("setting entries that appear at the top of page rather than in settings tab").needs_reload_ui(), "ui_tab_order": OptionInfo([], "UI tab order", ui_components.DropdownMulti, lambda: {"choices": list(shared.tab_names)}).needs_reload_ui(), From 464fbcd92118bf00173b9982325fe6348201313e Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sat, 21 Oct 2023 09:09:32 +0300 Subject: [PATCH 172/186] fix the situation with emphasis editing (aaaa:1.1) bbbb (cccc:1.1) --- javascript/edit-attention.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index f3af9a4c..04464100 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -19,11 +19,17 @@ function keyupEditAttention(event) { let beforeParen = before.lastIndexOf(OPEN); if (beforeParen == -1) return false; + let beforeClosingParen = before.lastIndexOf(CLOSE); + if (beforeClosingParen != -1 && beforeClosingParen > beforeParen) return false; + // Find closing parenthesis around current cursor const after = text.substring(selectionStart); let afterParen = after.indexOf(CLOSE); if (afterParen == -1) return false; + let afterOpeningParen = after.indexOf(OPEN); + if (afterOpeningParen != -1 && afterOpeningParen < beforeParen) return false; + // Set the selection to the text between the parenthesis const parenContent = text.substring(beforeParen + 1, selectionStart + afterParen); if (/.*:-?[\d.]+/s.test(parenContent)) { From 443ca983ade333721930ea2f18f80b45762e2aea Mon Sep 17 00:00:00 2001 From: avantcontra Date: Sun, 22 Oct 2023 03:21:23 +0800 Subject: [PATCH 173/186] fix bug when using --gfpgan-models-path --- modules/gfpgan_model.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/modules/gfpgan_model.py b/modules/gfpgan_model.py index 8e0f13bd..93567253 100644 --- a/modules/gfpgan_model.py +++ b/modules/gfpgan_model.py @@ -9,6 +9,7 @@ from modules import paths, shared, devices, modelloader, errors model_dir = "GFPGAN" user_path = None model_path = os.path.join(paths.models_path, model_dir) +model_file_path = None model_url = "https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.4.pth" have_gfpgan = False loaded_gfpgan_model = None @@ -17,24 +18,32 @@ loaded_gfpgan_model = None def gfpgann(): global loaded_gfpgan_model global model_path + global model_file_path if loaded_gfpgan_model is not None: loaded_gfpgan_model.gfpgan.to(devices.device_gfpgan) return loaded_gfpgan_model if gfpgan_constructor is None: return None + + models = modelloader.load_models(model_path, model_url, user_path, ext_filter=['.pth']) - models = modelloader.load_models(model_path, model_url, user_path, ext_filter="GFPGAN") if len(models) == 1 and models[0].startswith("http"): model_file = models[0] elif len(models) != 0: - latest_file = max(models, key=os.path.getctime) + gfp_models = [] + for item in models: + if 'GFPGAN' in os.path.basename(item): + gfp_models.append(item) + latest_file = max(gfp_models, key=os.path.getctime) model_file = latest_file else: print("Unable to load gfpgan model!") return None + if hasattr(facexlib.detection.retinaface, 'device'): facexlib.detection.retinaface.device = devices.device_gfpgan + model_file_path = model_file model = gfpgan_constructor(model_path=model_file, upscale=1, arch='clean', channel_multiplier=2, bg_upsampler=None, device=devices.device_gfpgan) loaded_gfpgan_model = model @@ -77,19 +86,25 @@ def setup_model(dirname): global user_path global have_gfpgan global gfpgan_constructor + global model_file_path + + facexlib_path = model_path + + if dirname is not None: + facexlib_path = dirname load_file_from_url_orig = gfpgan.utils.load_file_from_url facex_load_file_from_url_orig = facexlib.detection.load_file_from_url facex_load_file_from_url_orig2 = facexlib.parsing.load_file_from_url def my_load_file_from_url(**kwargs): - return load_file_from_url_orig(**dict(kwargs, model_dir=model_path)) + return load_file_from_url_orig(**dict(kwargs, model_dir=model_file_path)) def facex_load_file_from_url(**kwargs): - return facex_load_file_from_url_orig(**dict(kwargs, save_dir=model_path, model_dir=None)) + return facex_load_file_from_url_orig(**dict(kwargs, save_dir=facexlib_path, model_dir=None)) def facex_load_file_from_url2(**kwargs): - return facex_load_file_from_url_orig2(**dict(kwargs, save_dir=model_path, model_dir=None)) + return facex_load_file_from_url_orig2(**dict(kwargs, save_dir=facexlib_path, model_dir=None)) gfpgan.utils.load_file_from_url = my_load_file_from_url facexlib.detection.load_file_from_url = facex_load_file_from_url From 236dd55dbe895ba72a64567482ee67ab680c5344 Mon Sep 17 00:00:00 2001 From: avantcontra Date: Sun, 22 Oct 2023 04:32:13 +0800 Subject: [PATCH 174/186] fix Blank line contains whitespace --- modules/gfpgan_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gfpgan_model.py b/modules/gfpgan_model.py index 93567253..01d668ec 100644 --- a/modules/gfpgan_model.py +++ b/modules/gfpgan_model.py @@ -25,7 +25,7 @@ def gfpgann(): if gfpgan_constructor is None: return None - + models = modelloader.load_models(model_path, model_url, user_path, ext_filter=['.pth']) if len(models) == 1 and models[0].startswith("http"): From 88b2ef3b04c37ec068fdfea9ba2596645e981b46 Mon Sep 17 00:00:00 2001 From: David Benson Date: Mon, 23 Oct 2023 08:16:26 -0400 Subject: [PATCH 175/186] Update prompts_from_file script to allow concatenating entries with the general prompt. --- scripts/prompts_from_file.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/scripts/prompts_from_file.py b/scripts/prompts_from_file.py index 50320d55..1aadf113 100644 --- a/scripts/prompts_from_file.py +++ b/scripts/prompts_from_file.py @@ -108,6 +108,7 @@ class Script(scripts.Script): def ui(self, is_img2img): checkbox_iterate = gr.Checkbox(label="Iterate seed every line", value=False, elem_id=self.elem_id("checkbox_iterate")) checkbox_iterate_batch = gr.Checkbox(label="Use same random seed for all lines", value=False, elem_id=self.elem_id("checkbox_iterate_batch")) + prompt_position = gr.Radio(["start", "end"], label="Insert prompts at the", elem_id=self.elem_id("prompt_position"), value="start") prompt_txt = gr.Textbox(label="List of prompt inputs", lines=1, elem_id=self.elem_id("prompt_txt")) file = gr.File(label="Upload prompt inputs", type='binary', elem_id=self.elem_id("file")) @@ -118,9 +119,9 @@ class Script(scripts.Script): # We don't shrink back to 1, because that causes the control to ignore [enter], and it may # be unclear to the user that shift-enter is needed. prompt_txt.change(lambda tb: gr.update(lines=7) if ("\n" in tb) else gr.update(lines=2), inputs=[prompt_txt], outputs=[prompt_txt], show_progress=False) - return [checkbox_iterate, checkbox_iterate_batch, prompt_txt] + return [checkbox_iterate, checkbox_iterate_batch, prompt_position, prompt_txt] - def run(self, p, checkbox_iterate, checkbox_iterate_batch, prompt_txt: str): + def run(self, p, checkbox_iterate, checkbox_iterate_batch, prompt_position, prompt_txt: str): lines = [x for x in (x.strip() for x in prompt_txt.splitlines()) if x] p.do_not_save_grid = True @@ -158,6 +159,18 @@ class Script(scripts.Script): for k, v in args.items(): setattr(copy_p, k, v) + if args.get("prompt") and p.prompt: + if prompt_position == "start": + copy_p.prompt = args.get("prompt") + " " + p.prompt + else: + copy_p.prompt = p.prompt + " " + args.get("prompt") + + if args.get("negative_prompt") and p.negative_prompt: + if prompt_position == "start": + copy_p.negative_prompt = args.get("negative_prompt") + " " + p.negative_prompt + else: + copy_p.negative_prompt = p.negative_prompt + " " + args.get("negative_prompt") + proc = process_images(copy_p) images += proc.images From dfc4c27b2402a35a1820ffa549e74bb79873aaaa Mon Sep 17 00:00:00 2001 From: David Benson Date: Mon, 23 Oct 2023 08:26:40 -0400 Subject: [PATCH 176/186] linting issue --- scripts/prompts_from_file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/prompts_from_file.py b/scripts/prompts_from_file.py index 1aadf113..3c09bb97 100644 --- a/scripts/prompts_from_file.py +++ b/scripts/prompts_from_file.py @@ -164,7 +164,7 @@ class Script(scripts.Script): copy_p.prompt = args.get("prompt") + " " + p.prompt else: copy_p.prompt = p.prompt + " " + args.get("prompt") - + if args.get("negative_prompt") and p.negative_prompt: if prompt_position == "start": copy_p.negative_prompt = args.get("negative_prompt") + " " + p.negative_prompt From 5121846d34d74aee9b55d48d35c1559a710051b0 Mon Sep 17 00:00:00 2001 From: Won-Kyu Park Date: Wed, 25 Oct 2023 21:37:55 +0900 Subject: [PATCH 177/186] call state.jobnext() before postproces*() --- modules/processing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/processing.py b/modules/processing.py index 40598f5c..70ad1ebe 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -886,6 +886,8 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: devices.torch_gc() + state.nextjob() + if p.scripts is not None: p.scripts.postprocess_batch(p, x_samples_ddim, batch_number=n) @@ -958,8 +960,6 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: devices.torch_gc() - state.nextjob() - if not infotexts: infotexts.append(Processed(p, []).infotext(p, 0)) From fbc5c531b9cfa949d60dae19420d01f8af186b55 Mon Sep 17 00:00:00 2001 From: Meerkov Date: Sun, 29 Oct 2023 15:37:08 -0700 Subject: [PATCH 178/186] Fix #13796 Fix comment error that makes understanding scheduling more confusing. --- modules/prompt_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/prompt_parser.py b/modules/prompt_parser.py index 334efeef..86b7acb5 100644 --- a/modules/prompt_parser.py +++ b/modules/prompt_parser.py @@ -5,7 +5,7 @@ from collections import namedtuple from typing import List import lark -# a prompt like this: "fantasy landscape with a [mountain:lake:0.25] and [an oak:a christmas tree:0.75][ in foreground::0.6][ in background:0.25] [shoddy:masterful:0.5]" +# a prompt like this: "fantasy landscape with a [mountain:lake:0.25] and [an oak:a christmas tree:0.75][ in foreground::0.6][: in background:0.25] [shoddy:masterful:0.5]" # will be represented with prompt_schedule like this (assuming steps=100): # [25, 'fantasy landscape with a mountain and an oak in foreground shoddy'] # [50, 'fantasy landscape with a lake and an oak in foreground in background shoddy'] From 8052a4971e1be48e1df2535284a7791cd1ad39ae Mon Sep 17 00:00:00 2001 From: missionfloyd Date: Fri, 3 Nov 2023 00:59:19 -0600 Subject: [PATCH 179/186] Fix parenthesis auto selection Fixes #13813 --- javascript/edit-attention.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/edit-attention.js b/javascript/edit-attention.js index 04464100..688c2f11 100644 --- a/javascript/edit-attention.js +++ b/javascript/edit-attention.js @@ -28,7 +28,7 @@ function keyupEditAttention(event) { if (afterParen == -1) return false; let afterOpeningParen = after.indexOf(OPEN); - if (afterOpeningParen != -1 && afterOpeningParen < beforeParen) return false; + if (afterOpeningParen != -1 && afterOpeningParen < afterParen) return false; // Set the selection to the text between the parenthesis const parenContent = text.substring(beforeParen + 1, selectionStart + afterParen); From cc80a09d82afae793800a033a1f525f5dc797cff Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Sat, 4 Nov 2023 00:50:30 +0900 Subject: [PATCH 180/186] Update requirements_versions.txt --- requirements_versions.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements_versions.txt b/requirements_versions.txt index 7d27f2be..cb7403a9 100644 --- a/requirements_versions.txt +++ b/requirements_versions.txt @@ -29,3 +29,4 @@ torch torchdiffeq==0.2.3 torchsde==0.2.6 transformers==4.30.2 +httpx==0.24.1 From 2b06cefe66684ed2648d3221efbc36aeaae99a2f Mon Sep 17 00:00:00 2001 From: gibiee <37574274+gibiee@users.noreply.github.com> Date: Sun, 5 Nov 2023 11:37:23 +0900 Subject: [PATCH 181/186] correct a typo modify "defaul" to "default" --- 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 4e602a84..a9fb9bfa 100644 --- a/modules/cmd_args.py +++ b/modules/cmd_args.py @@ -107,7 +107,7 @@ parser.add_argument("--tls-certfile", type=str, help="Partially enables TLS, req parser.add_argument("--disable-tls-verify", action="store_false", help="When passed, enables the use of self-signed certificates.", default=None) parser.add_argument("--server-name", type=str, help="Sets hostname of server", default=None) parser.add_argument("--gradio-queue", action='store_true', help="does not do anything", default=True) -parser.add_argument("--no-gradio-queue", action='store_true', help="Disables gradio queue; causes the webpage to use http requests instead of websockets; was the defaul in earlier versions") +parser.add_argument("--no-gradio-queue", action='store_true', help="Disables gradio queue; causes the webpage to use http requests instead of websockets; was the default in earlier versions") parser.add_argument("--skip-version-check", action='store_true', help="Do not check versions of torch and xformers") parser.add_argument("--no-hashing", action='store_true', help="disable sha256 hashing of checkpoints to help loading performance", default=False) parser.add_argument("--no-download-sd-model", action='store_true', help="don't download SD1.5 model even if no model is found in --ckpt-dir", default=False) From 6b8c661c49796bba093ca8a8301e81d28afb9832 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 5 Nov 2023 08:55:54 +0300 Subject: [PATCH 182/186] add a visible checkbox to input accordion --- javascript/inputAccordion.js | 87 ++++++++++++++++++++++++------------ style.css | 5 +++ 2 files changed, 63 insertions(+), 29 deletions(-) diff --git a/javascript/inputAccordion.js b/javascript/inputAccordion.js index f2839852..8fc01230 100644 --- a/javascript/inputAccordion.js +++ b/javascript/inputAccordion.js @@ -1,37 +1,66 @@ -var observerAccordionOpen = new MutationObserver(function(mutations) { - mutations.forEach(function(mutationRecord) { - var elem = mutationRecord.target; - var open = elem.classList.contains('open'); - - var accordion = elem.parentNode; - accordion.classList.toggle('input-accordion-open', open); - - var checkbox = gradioApp().querySelector('#' + accordion.id + "-checkbox input"); - checkbox.checked = open; - updateInput(checkbox); - - var extra = gradioApp().querySelector('#' + accordion.id + "-extra"); - if (extra) { - extra.style.display = open ? "" : "none"; - } - }); -}); - function inputAccordionChecked(id, checked) { - var label = gradioApp().querySelector('#' + id + " .label-wrap"); - if (label.classList.contains('open') != checked) { - label.click(); + var accordion = gradioApp().getElementById(id); + accordion.visibleCheckbox.checked = checked; + accordion.onVisibleCheckboxChange(); +} + +function setupAccordion(accordion){ + var labelWrap = accordion.querySelector('.label-wrap'); + var gradioCheckbox = gradioApp().querySelector('#' + accordion.id + "-checkbox input"); + var extra = gradioApp().querySelector('#' + accordion.id + "-extra"); + var span = labelWrap.querySelector('span'); + var linked = true; + + var isOpen = function(){ return labelWrap.classList.contains('open'); } + + var observerAccordionOpen = new MutationObserver(function(mutations) { + mutations.forEach(function(mutationRecord) { + accordion.classList.toggle('input-accordion-open', isOpen()); + + if(linked){ + accordion.visibleCheckbox.checked = isOpen(); + accordion.onVisibleCheckboxChange(); + } + }); + }); + observerAccordionOpen.observe(labelWrap, {attributes: true, attributeFilter: ['class']}); + + if (extra) { + labelWrap.insertBefore(extra, labelWrap.lastElementChild); } + + accordion.onChecked = function(checked){ + if (isOpen() != checked) { + labelWrap.click(); + } + } + + var visibleCheckbox = document.createElement('INPUT'); + visibleCheckbox.type = 'checkbox'; + visibleCheckbox.checked = isOpen(); + visibleCheckbox.id = accordion.id + "-visible-checkbox"; + visibleCheckbox.className = gradioCheckbox.className + " input-accordion-checkbox"; + span.insertBefore(visibleCheckbox, span.firstChild); + + accordion.visibleCheckbox = visibleCheckbox; + accordion.onVisibleCheckboxChange = function(){ + if(linked && isOpen() != visibleCheckbox.checked) { + labelWrap.click(); + } + + gradioCheckbox.checked = visibleCheckbox.checked; + updateInput(gradioCheckbox); + }; + + visibleCheckbox.addEventListener('click', function(event){ + linked = false; + event.stopPropagation(); + }); + visibleCheckbox.addEventListener('input', accordion.onVisibleCheckboxChange); } onUiLoaded(function() { for (var accordion of gradioApp().querySelectorAll('.input-accordion')) { - var labelWrap = accordion.querySelector('.label-wrap'); - observerAccordionOpen.observe(labelWrap, {attributes: true, attributeFilter: ['class']}); - - var extra = gradioApp().querySelector('#' + accordion.id + "-extra"); - if (extra) { - labelWrap.insertBefore(extra, labelWrap.lastElementChild); - } + setupAccordion(accordion); } }); diff --git a/style.css b/style.css index 115626cd..9a1181e8 100644 --- a/style.css +++ b/style.css @@ -204,6 +204,11 @@ div.block.gradio-accordion { padding: 8px 8px; } +input[type="checkbox"].input-accordion-checkbox{ + vertical-align: sub; + margin-right: 0.5em; +} + /* txt2img/img2img specific */ From 16ab17429016a1154b9aa83244cdbfc7ba463d72 Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 5 Nov 2023 09:20:05 +0300 Subject: [PATCH 183/186] eslint --- javascript/inputAccordion.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/javascript/inputAccordion.js b/javascript/inputAccordion.js index 8fc01230..7570309a 100644 --- a/javascript/inputAccordion.js +++ b/javascript/inputAccordion.js @@ -4,20 +4,22 @@ function inputAccordionChecked(id, checked) { accordion.onVisibleCheckboxChange(); } -function setupAccordion(accordion){ +function setupAccordion(accordion) { var labelWrap = accordion.querySelector('.label-wrap'); var gradioCheckbox = gradioApp().querySelector('#' + accordion.id + "-checkbox input"); var extra = gradioApp().querySelector('#' + accordion.id + "-extra"); var span = labelWrap.querySelector('span'); var linked = true; - var isOpen = function(){ return labelWrap.classList.contains('open'); } + var isOpen = function() { + return labelWrap.classList.contains('open'); + }; var observerAccordionOpen = new MutationObserver(function(mutations) { mutations.forEach(function(mutationRecord) { accordion.classList.toggle('input-accordion-open', isOpen()); - if(linked){ + if (linked) { accordion.visibleCheckbox.checked = isOpen(); accordion.onVisibleCheckboxChange(); } @@ -29,22 +31,22 @@ function setupAccordion(accordion){ labelWrap.insertBefore(extra, labelWrap.lastElementChild); } - accordion.onChecked = function(checked){ + accordion.onChecked = function(checked) { if (isOpen() != checked) { labelWrap.click(); } - } + }; var visibleCheckbox = document.createElement('INPUT'); visibleCheckbox.type = 'checkbox'; visibleCheckbox.checked = isOpen(); - visibleCheckbox.id = accordion.id + "-visible-checkbox"; + visibleCheckbox.id = accordion.id + "-visible-checkbox"; visibleCheckbox.className = gradioCheckbox.className + " input-accordion-checkbox"; span.insertBefore(visibleCheckbox, span.firstChild); accordion.visibleCheckbox = visibleCheckbox; - accordion.onVisibleCheckboxChange = function(){ - if(linked && isOpen() != visibleCheckbox.checked) { + accordion.onVisibleCheckboxChange = function() { + if (linked && isOpen() != visibleCheckbox.checked) { labelWrap.click(); } @@ -52,7 +54,7 @@ function setupAccordion(accordion){ updateInput(gradioCheckbox); }; - visibleCheckbox.addEventListener('click', function(event){ + visibleCheckbox.addEventListener('click', function(event) { linked = false; event.stopPropagation(); }); From d9499f4301018ebd2977685d098381aa4111d2ae Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 5 Nov 2023 10:12:50 +0300 Subject: [PATCH 184/186] properly apply sort order for extra network cards when selected from dropdown allow selection of default sort order in settings remove 'Default' sort order, replace with 'Name' --- javascript/extraNetworks.js | 27 ++++++++++++++++----------- modules/shared_options.py | 2 ++ modules/ui_extra_networks.py | 6 ++++-- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index ac26718f..a4d1d9d9 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -27,7 +27,6 @@ function setupExtraNetworksForTab(tabname) { var showDirsDiv = gradioApp().getElementById(tabname + '_extra_show_dirs'); var showDirs = gradioApp().querySelector('#' + tabname + '_extra_show_dirs input'); - sort.dataset.sortkey = 'sortDefault'; tabs.appendChild(searchDiv); tabs.appendChild(sort); tabs.appendChild(sortOrder); @@ -49,20 +48,23 @@ function setupExtraNetworksForTab(tabname) { elem.style.display = visible ? "" : "none"; }); + + applySort(); }; var applySort = function() { + var cards = gradioApp().querySelectorAll('#' + tabname + '_extra_tabs div.card'); + var reverse = sortOrder.classList.contains("sortReverse"); - var sortKey = sort.querySelector("input").value.toLowerCase().replace("sort", "").replaceAll(" ", "_").replace(/_+$/, "").trim(); - sortKey = sortKey ? "sort" + sortKey.charAt(0).toUpperCase() + sortKey.slice(1) : ""; - var sortKeyStore = sortKey ? sortKey + (reverse ? "Reverse" : "") : ""; - if (!sortKey || sortKeyStore == sort.dataset.sortkey) { + var sortKey = sort.querySelector("input").value.toLowerCase().replace("sort", "").replaceAll(" ", "_").replace(/_+$/, "").trim() || "name"; + sortKey = "sort" + sortKey.charAt(0).toUpperCase() + sortKey.slice(1); + var sortKeyStore = sortKey + "-" + (reverse ? "Descending" : "Ascending") + "-" + cards.length; + + if (sortKeyStore == sort.dataset.sortkey) { return; } - sort.dataset.sortkey = sortKeyStore; - var cards = gradioApp().querySelectorAll('#' + tabname + '_extra_tabs div.card'); cards.forEach(function(card) { card.originalParentElement = card.parentElement; }); @@ -88,15 +90,13 @@ function setupExtraNetworksForTab(tabname) { }; search.addEventListener("input", applyFilter); - applyFilter(); - ["change", "blur", "click"].forEach(function(evt) { - sort.querySelector("input").addEventListener(evt, applySort); - }); sortOrder.addEventListener("click", function() { sortOrder.classList.toggle("sortReverse"); applySort(); }); + applyFilter(); + extraNetworksApplySort[tabname] = applySort; extraNetworksApplyFilter[tabname] = applyFilter; var showDirsUpdate = function() { @@ -113,7 +113,12 @@ function applyExtraNetworkFilter(tabname) { setTimeout(extraNetworksApplyFilter[tabname], 1); } +function applyExtraNetworkSort(tabname) { + setTimeout(extraNetworksApplySort[tabname], 1); +} + var extraNetworksApplyFilter = {}; +var extraNetworksApplySort = {}; var activePromptTextarea = {}; function setupExtraNetworks() { diff --git a/modules/shared_options.py b/modules/shared_options.py index 0a82216f..6543e440 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -234,6 +234,8 @@ options_templates.update(options_section(('extra_networks', "Extra Networks"), { "extra_networks_card_height": OptionInfo(0, "Card height for Extra Networks").info("in pixels"), "extra_networks_card_text_scale": OptionInfo(1.0, "Card text scale", gr.Slider, {"minimum": 0.0, "maximum": 2.0, "step": 0.01}).info("1 = original size"), "extra_networks_card_show_desc": OptionInfo(True, "Show description on card"), + "extra_networks_card_order_field": OptionInfo("Name", "Default order field for Extra Networks cards", gr.Dropdown, {"choices": ['Name', 'Date Created', 'Date Modified']}).needs_reload_ui(), + "extra_networks_card_order": OptionInfo("Ascending", "Default order for Extra Networks cards", gr.Dropdown, {"choices": ['Ascending', 'Descending']}).needs_reload_ui(), "extra_networks_add_text_separator": OptionInfo(" ", "Extra networks separator").info("extra text to add before <...> when adding extra network to prompt"), "ui_extra_networks_tab_reorder": OptionInfo("", "Extra networks tab order").needs_reload_ui(), "textual_inversion_print_at_load": OptionInfo(False, "Print a list of Textual Inversion embeddings when loading model"), diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 59d6ecc6..fc729917 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -381,8 +381,8 @@ def create_ui(interface: gr.Blocks, unrelated_tabs, tabname): related_tabs.append(tab) edit_search = gr.Textbox('', show_label=False, elem_id=tabname+"_extra_search", elem_classes="search", placeholder="Search...", visible=False, interactive=True) - dropdown_sort = gr.Dropdown(choices=['Default Sort', 'Date Created', 'Date Modified', 'Name'], value='Default Sort', elem_id=tabname+"_extra_sort", elem_classes="sort", multiselect=False, visible=False, show_label=False, interactive=True, label=tabname+"_extra_sort_order") - button_sortorder = ToolButton(switch_values_symbol, elem_id=tabname+"_extra_sortorder", elem_classes="sortorder", visible=False, tooltip="Invert sort order") + dropdown_sort = gr.Dropdown(choices=['Name', 'Date Created', 'Date Modified', ], value=shared.opts.extra_networks_card_order_field, elem_id=tabname+"_extra_sort", elem_classes="sort", multiselect=False, visible=False, show_label=False, interactive=True, label=tabname+"_extra_sort_order") + button_sortorder = ToolButton(switch_values_symbol, elem_id=tabname+"_extra_sortorder", elem_classes=["sortorder"] + ([] if shared.opts.extra_networks_card_order == "Ascending" else ["sortReverse"]), visible=False, tooltip="Invert sort order") button_refresh = gr.Button('Refresh', elem_id=tabname+"_extra_refresh", visible=False) checkbox_show_dirs = gr.Checkbox(True, label='Show dirs', elem_id=tabname+"_extra_show_dirs", elem_classes="show-dirs", visible=False) @@ -395,6 +395,8 @@ def create_ui(interface: gr.Blocks, unrelated_tabs, tabname): for tab in related_tabs: tab.select(fn=lambda: [gr.update(visible=True) for _ in range(5)], inputs=[], outputs=[edit_search, dropdown_sort, button_sortorder, button_refresh, checkbox_show_dirs], show_progress=False) + dropdown_sort.change(fn=lambda: None, _js="function(){ applyExtraNetworkSort('" + tabname + "'); }") + def pages_html(): if not ui.pages_contents: return refresh() From 4d4a9e733219f8c065a4ab6c5ab42836db7330fe Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 5 Nov 2023 19:19:55 +0300 Subject: [PATCH 185/186] added compact prompt option --- .../mobile/javascript/mobile.js | 2 + javascript/extraNetworks.js | 33 +++ modules/shared_items.py | 2 + modules/shared_options.py | 1 + modules/ui.py | 247 +++++++----------- modules/ui_common.py | 15 +- modules/ui_extra_networks.py | 18 +- modules/ui_extra_networks_checkpoints.py | 2 + modules/ui_toprow.py | 141 ++++++++++ style.css | 23 +- 10 files changed, 315 insertions(+), 169 deletions(-) create mode 100644 modules/ui_toprow.py diff --git a/extensions-builtin/mobile/javascript/mobile.js b/extensions-builtin/mobile/javascript/mobile.js index 652f07ac..bff1aced 100644 --- a/extensions-builtin/mobile/javascript/mobile.js +++ b/extensions-builtin/mobile/javascript/mobile.js @@ -12,6 +12,8 @@ function isMobile() { } function reportWindowSize() { + if (gradioApp().querySelector('.toprow-compact-tools')) return; // not applicable for compact prompt layout + var currentlyMobile = isMobile(); if (currentlyMobile == isSetupForMobile) return; isSetupForMobile = currentlyMobile; diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index a4d1d9d9..a1bf29a8 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -26,6 +26,8 @@ function setupExtraNetworksForTab(tabname) { var refresh = gradioApp().getElementById(tabname + '_extra_refresh'); var showDirsDiv = gradioApp().getElementById(tabname + '_extra_show_dirs'); var showDirs = gradioApp().querySelector('#' + tabname + '_extra_show_dirs input'); + var promptContainer = gradioApp().querySelector('.prompt-container-compact#' + tabname + '_prompt_container'); + var negativePrompt = gradioApp().querySelector('#' + tabname + '_neg_prompt'); tabs.appendChild(searchDiv); tabs.appendChild(sort); @@ -109,6 +111,37 @@ function setupExtraNetworksForTab(tabname) { showDirsUpdate(); } +function extraNetworksMovePromptToTab(tabname, id, showPrompt, showNegativePrompt) { + if (!gradioApp().querySelector('.toprow-compact-tools')) return; // only applicable for compact prompt layout + + var promptContainer = gradioApp().getElementById(tabname + '_prompt_container'); + var prompt = gradioApp().getElementById(tabname + '_prompt_row'); + var negPrompt = gradioApp().getElementById(tabname + '_neg_prompt_row'); + var elem = id ? gradioApp().getElementById(id) : null; + + if (showNegativePrompt && elem) { + elem.insertBefore(negPrompt, elem.firstChild); + } else { + promptContainer.insertBefore(negPrompt, promptContainer.firstChild); + } + + if (showPrompt && elem) { + elem.insertBefore(prompt, elem.firstChild); + } else { + promptContainer.insertBefore(prompt, promptContainer.firstChild); + } +} + + +function extraNetworksUrelatedTabSelected(tabname) { // called from python when user selects an unrelated tab (generate) + extraNetworksMovePromptToTab(tabname, '', false, false); +} + +function extraNetworksTabSelected(tabname, id, showPrompt, showNegativePrompt) { // called from python when user selects an extra networks tab + extraNetworksMovePromptToTab(tabname, id, showPrompt, showNegativePrompt); + +} + function applyExtraNetworkFilter(tabname) { setTimeout(extraNetworksApplyFilter[tabname], 1); } diff --git a/modules/shared_items.py b/modules/shared_items.py index b1459f8c..5024b426 100644 --- a/modules/shared_items.py +++ b/modules/shared_items.py @@ -67,6 +67,8 @@ def reload_hypernetworks(): ui_reorder_categories_builtin_items = [ + "prompt", + "image", "inpaint", "sampler", "accordions", diff --git a/modules/shared_options.py b/modules/shared_options.py index 6543e440..4e3d7541 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -272,6 +272,7 @@ options_templates.update(options_section(('ui', "User interface"), { "hires_fix_show_sampler": OptionInfo(False, "Hires fix: show hires checkpoint and sampler selection").needs_reload_ui(), "hires_fix_show_prompts": OptionInfo(False, "Hires fix: show hires prompt and negative prompt").needs_reload_ui(), "disable_token_counters": OptionInfo(False, "Disable prompt token counters").needs_reload_ui(), + "compact_prompt_box": OptionInfo(True, "Compact prompt layout").info("puts prompt and negative prompt inside the Generate tab, leaving more vertical space for the image on the right").needs_reload_ui(), })) diff --git a/modules/ui.py b/modules/ui.py index bcf39199..2454eb36 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -12,7 +12,7 @@ from PIL import Image, PngImagePlugin # noqa: F401 from modules.call_queue import wrap_gradio_gpu_call, wrap_queued_call, wrap_gradio_call from modules import gradio_extensons # noqa: F401 -from modules import sd_hijack, sd_models, script_callbacks, ui_extensions, deepbooru, extra_networks, ui_common, ui_postprocessing, progress, ui_loadsave, shared_items, ui_settings, timer, sysinfo, ui_checkpoint_merger, ui_prompt_styles, scripts, sd_samplers, processing, ui_extra_networks +from modules import sd_hijack, sd_models, script_callbacks, ui_extensions, deepbooru, extra_networks, ui_common, ui_postprocessing, progress, ui_loadsave, shared_items, ui_settings, timer, sysinfo, ui_checkpoint_merger, scripts, sd_samplers, processing, ui_extra_networks, ui_toprow from modules.ui_components import FormRow, FormGroup, ToolButton, FormHTML, InputAccordion, ResizeHandleRow from modules.paths import script_path from modules.ui_common import create_refresh_button @@ -25,7 +25,6 @@ import modules.hypernetworks.ui as hypernetworks_ui import modules.textual_inversion.ui as textual_inversion_ui import modules.textual_inversion.textual_inversion as textual_inversion import modules.shared as shared -import modules.images from modules import prompt_parser from modules.sd_hijack import model_hijack from modules.generation_parameters_copypaste import image_from_url_text @@ -177,79 +176,6 @@ def update_negative_prompt_token_counter(text, steps): return update_token_counter(text, steps, is_positive=False) -class Toprow: - """Creates a top row UI with prompts, generate button, styles, extra little buttons for things, and enables some functionality related to their operation""" - - def __init__(self, is_img2img): - id_part = "img2img" if is_img2img else "txt2img" - self.id_part = id_part - - with gr.Row(elem_id=f"{id_part}_toprow", variant="compact"): - with gr.Column(elem_id=f"{id_part}_prompt_container", scale=6): - with gr.Row(): - with gr.Column(scale=80): - with gr.Row(): - self.prompt = gr.Textbox(label="Prompt", elem_id=f"{id_part}_prompt", show_label=False, lines=3, placeholder="Prompt (press Ctrl+Enter or Alt+Enter to generate)", elem_classes=["prompt"]) - self.prompt_img = gr.File(label="", elem_id=f"{id_part}_prompt_image", file_count="single", type="binary", visible=False) - - with gr.Row(): - with gr.Column(scale=80): - with gr.Row(): - self.negative_prompt = gr.Textbox(label="Negative prompt", elem_id=f"{id_part}_neg_prompt", show_label=False, lines=3, placeholder="Negative prompt (press Ctrl+Enter or Alt+Enter to generate)", elem_classes=["prompt"]) - - self.button_interrogate = None - self.button_deepbooru = None - if is_img2img: - with gr.Column(scale=1, elem_classes="interrogate-col"): - self.button_interrogate = gr.Button('Interrogate\nCLIP', elem_id="interrogate") - self.button_deepbooru = gr.Button('Interrogate\nDeepBooru', elem_id="deepbooru") - - with gr.Column(scale=1, elem_id=f"{id_part}_actions_column"): - with gr.Row(elem_id=f"{id_part}_generate_box", elem_classes="generate-box"): - self.interrupt = gr.Button('Interrupt', elem_id=f"{id_part}_interrupt", elem_classes="generate-box-interrupt") - self.skip = gr.Button('Skip', elem_id=f"{id_part}_skip", elem_classes="generate-box-skip") - self.submit = gr.Button('Generate', elem_id=f"{id_part}_generate", variant='primary') - - self.skip.click( - fn=lambda: shared.state.skip(), - inputs=[], - outputs=[], - ) - - self.interrupt.click( - fn=lambda: shared.state.interrupt(), - inputs=[], - outputs=[], - ) - - with gr.Row(elem_id=f"{id_part}_tools"): - self.paste = ToolButton(value=paste_symbol, elem_id="paste", tooltip="Read generation parameters from prompt or last generation if prompt is empty into user interface.") - self.clear_prompt_button = ToolButton(value=clear_prompt_symbol, elem_id=f"{id_part}_clear_prompt", tooltip="Clear prompt") - self.apply_styles = ToolButton(value=ui_prompt_styles.styles_materialize_symbol, elem_id=f"{id_part}_style_apply", tooltip="Apply all selected styles to prompts.") - self.restore_progress_button = ToolButton(value=restore_progress_symbol, elem_id=f"{id_part}_restore_progress", visible=False, tooltip="Restore progress") - - self.token_counter = gr.HTML(value="0/75", elem_id=f"{id_part}_token_counter", elem_classes=["token-counter"]) - self.token_button = gr.Button(visible=False, elem_id=f"{id_part}_token_button") - self.negative_token_counter = gr.HTML(value="0/75", elem_id=f"{id_part}_negative_token_counter", elem_classes=["token-counter"]) - self.negative_token_button = gr.Button(visible=False, elem_id=f"{id_part}_negative_token_button") - - self.clear_prompt_button.click( - fn=lambda *x: x, - _js="confirm_clear_prompt", - inputs=[self.prompt, self.negative_prompt], - outputs=[self.prompt, self.negative_prompt], - ) - - self.ui_styles = ui_prompt_styles.UiPromptStyles(id_part, self.prompt, self.negative_prompt) - self.ui_styles.setup_apply_button(self.apply_styles) - - self.prompt_img.change( - fn=modules.images.image_data, - inputs=[self.prompt_img], - outputs=[self.prompt, self.prompt_img], - show_progress=False, - ) - def setup_progressbar(*args, **kwargs): pass @@ -288,8 +214,8 @@ def apply_setting(key, value): return getattr(opts, key) -def create_output_panel(tabname, outdir): - return ui_common.create_output_panel(tabname, outdir) +def create_output_panel(tabname, outdir, toprow=None): + return ui_common.create_output_panel(tabname, outdir, toprow) def create_sampler_and_steps_selection(choices, tabname): @@ -336,7 +262,7 @@ def create_ui(): scripts.scripts_txt2img.initialize_scripts(is_img2img=False) with gr.Blocks(analytics_enabled=False) as txt2img_interface: - toprow = Toprow(is_img2img=False) + toprow = ui_toprow.Toprow(is_img2img=False, is_compact=shared.opts.compact_prompt_box) dummy_component = gr.Label(visible=False) @@ -348,6 +274,9 @@ def create_ui(): scripts.scripts_txt2img.prepare_ui() for category in ordered_ui_categories(): + if category == "prompt": + toprow.create_inline_toprow_prompts() + if category == "sampler": steps, sampler_name = create_sampler_and_steps_selection(sd_samplers.visible_sampler_names(), "txt2img") @@ -442,7 +371,7 @@ def create_ui(): show_progress=False, ) - txt2img_gallery, generation_info, html_info, html_log = create_output_panel("txt2img", opts.outdir_txt2img_samples) + txt2img_gallery, generation_info, html_info, html_log = create_output_panel("txt2img", opts.outdir_txt2img_samples, toprow) txt2img_args = dict( fn=wrap_gradio_gpu_call(modules.txt2img.txt2img, extra_outputs=[None, '', '']), @@ -554,7 +483,7 @@ def create_ui(): scripts.scripts_img2img.initialize_scripts(is_img2img=True) with gr.Blocks(analytics_enabled=False) as img2img_interface: - toprow = Toprow(is_img2img=True) + toprow = ui_toprow.Toprow(is_img2img=True, is_compact=shared.opts.compact_prompt_box) extra_tabs = gr.Tabs(elem_id="img2img_extra_tabs") extra_tabs.__enter__() @@ -577,85 +506,89 @@ def create_ui(): button = gr.Button(title) copy_image_buttons.append((button, name, elem)) - with gr.Tabs(elem_id="mode_img2img"): - img2img_selected_tab = gr.State(0) - - with gr.TabItem('img2img', id='img2img', elem_id="img2img_img2img_tab") as tab_img2img: - init_img = gr.Image(label="Image for img2img", elem_id="img2img_image", show_label=False, source="upload", interactive=True, type="pil", tool="editor", image_mode="RGBA", height=opts.img2img_editor_height) - add_copy_image_controls('img2img', init_img) - - with gr.TabItem('Sketch', id='img2img_sketch', elem_id="img2img_img2img_sketch_tab") as tab_sketch: - sketch = gr.Image(label="Image for img2img", elem_id="img2img_sketch", show_label=False, source="upload", interactive=True, type="pil", tool="color-sketch", image_mode="RGB", height=opts.img2img_editor_height, brush_color=opts.img2img_sketch_default_brush_color) - add_copy_image_controls('sketch', sketch) - - with gr.TabItem('Inpaint', id='inpaint', elem_id="img2img_inpaint_tab") as tab_inpaint: - init_img_with_mask = gr.Image(label="Image for inpainting with mask", show_label=False, elem_id="img2maskimg", source="upload", interactive=True, type="pil", tool="sketch", image_mode="RGBA", height=opts.img2img_editor_height, brush_color=opts.img2img_inpaint_mask_brush_color) - add_copy_image_controls('inpaint', init_img_with_mask) - - with gr.TabItem('Inpaint sketch', id='inpaint_sketch', elem_id="img2img_inpaint_sketch_tab") as tab_inpaint_color: - inpaint_color_sketch = gr.Image(label="Color sketch inpainting", show_label=False, elem_id="inpaint_sketch", source="upload", interactive=True, type="pil", tool="color-sketch", image_mode="RGB", height=opts.img2img_editor_height, brush_color=opts.img2img_inpaint_sketch_default_brush_color) - inpaint_color_sketch_orig = gr.State(None) - add_copy_image_controls('inpaint_sketch', inpaint_color_sketch) - - def update_orig(image, state): - if image is not None: - same_size = state is not None and state.size == image.size - has_exact_match = np.any(np.all(np.array(image) == np.array(state), axis=-1)) - edited = same_size and has_exact_match - return image if not edited or state is None else state - - inpaint_color_sketch.change(update_orig, [inpaint_color_sketch, inpaint_color_sketch_orig], inpaint_color_sketch_orig) - - with gr.TabItem('Inpaint upload', id='inpaint_upload', elem_id="img2img_inpaint_upload_tab") as tab_inpaint_upload: - init_img_inpaint = gr.Image(label="Image for img2img", show_label=False, source="upload", interactive=True, type="pil", elem_id="img_inpaint_base") - init_mask_inpaint = gr.Image(label="Mask", source="upload", interactive=True, type="pil", image_mode="RGBA", elem_id="img_inpaint_mask") - - with gr.TabItem('Batch', id='batch', elem_id="img2img_batch_tab") as tab_batch: - hidden = '
Disabled when launched with --hide-ui-dir-config.' if shared.cmd_opts.hide_ui_dir_config else '' - gr.HTML( - "

Process images in a directory on the same machine where the server is running." + - "
Use an empty output directory to save pictures normally instead of writing to the output directory." + - f"
Add inpaint batch mask directory to enable inpaint batch processing." - f"{hidden}

" - ) - img2img_batch_input_dir = gr.Textbox(label="Input directory", **shared.hide_dirs, elem_id="img2img_batch_input_dir") - img2img_batch_output_dir = gr.Textbox(label="Output directory", **shared.hide_dirs, elem_id="img2img_batch_output_dir") - img2img_batch_inpaint_mask_dir = gr.Textbox(label="Inpaint batch mask directory (required for inpaint batch processing only)", **shared.hide_dirs, elem_id="img2img_batch_inpaint_mask_dir") - with gr.Accordion("PNG info", open=False): - img2img_batch_use_png_info = gr.Checkbox(label="Append png info to prompts", **shared.hide_dirs, elem_id="img2img_batch_use_png_info") - img2img_batch_png_info_dir = gr.Textbox(label="PNG info directory", **shared.hide_dirs, placeholder="Leave empty to use input directory", elem_id="img2img_batch_png_info_dir") - img2img_batch_png_info_props = gr.CheckboxGroup(["Prompt", "Negative prompt", "Seed", "CFG scale", "Sampler", "Steps", "Model hash"], label="Parameters to take from png info", info="Prompts from png info will be appended to prompts set in ui.") - - img2img_tabs = [tab_img2img, tab_sketch, tab_inpaint, tab_inpaint_color, tab_inpaint_upload, tab_batch] - - for i, tab in enumerate(img2img_tabs): - tab.select(fn=lambda tabnum=i: tabnum, inputs=[], outputs=[img2img_selected_tab]) - - def copy_image(img): - if isinstance(img, dict) and 'image' in img: - return img['image'] - - return img - - for button, name, elem in copy_image_buttons: - button.click( - fn=copy_image, - inputs=[elem], - outputs=[copy_image_destinations[name]], - ) - button.click( - fn=lambda: None, - _js=f"switch_to_{name.replace(' ', '_')}", - inputs=[], - outputs=[], - ) - - with FormRow(): - resize_mode = gr.Radio(label="Resize mode", elem_id="resize_mode", choices=["Just resize", "Crop and resize", "Resize and fill", "Just resize (latent upscale)"], type="index", value="Just resize") - scripts.scripts_img2img.prepare_ui() for category in ordered_ui_categories(): + if category == "prompt": + toprow.create_inline_toprow_prompts() + + if category == "image": + with gr.Tabs(elem_id="mode_img2img"): + img2img_selected_tab = gr.State(0) + + with gr.TabItem('img2img', id='img2img', elem_id="img2img_img2img_tab") as tab_img2img: + init_img = gr.Image(label="Image for img2img", elem_id="img2img_image", show_label=False, source="upload", interactive=True, type="pil", tool="editor", image_mode="RGBA", height=opts.img2img_editor_height) + add_copy_image_controls('img2img', init_img) + + with gr.TabItem('Sketch', id='img2img_sketch', elem_id="img2img_img2img_sketch_tab") as tab_sketch: + sketch = gr.Image(label="Image for img2img", elem_id="img2img_sketch", show_label=False, source="upload", interactive=True, type="pil", tool="color-sketch", image_mode="RGB", height=opts.img2img_editor_height, brush_color=opts.img2img_sketch_default_brush_color) + add_copy_image_controls('sketch', sketch) + + with gr.TabItem('Inpaint', id='inpaint', elem_id="img2img_inpaint_tab") as tab_inpaint: + init_img_with_mask = gr.Image(label="Image for inpainting with mask", show_label=False, elem_id="img2maskimg", source="upload", interactive=True, type="pil", tool="sketch", image_mode="RGBA", height=opts.img2img_editor_height, brush_color=opts.img2img_inpaint_mask_brush_color) + add_copy_image_controls('inpaint', init_img_with_mask) + + with gr.TabItem('Inpaint sketch', id='inpaint_sketch', elem_id="img2img_inpaint_sketch_tab") as tab_inpaint_color: + inpaint_color_sketch = gr.Image(label="Color sketch inpainting", show_label=False, elem_id="inpaint_sketch", source="upload", interactive=True, type="pil", tool="color-sketch", image_mode="RGB", height=opts.img2img_editor_height, brush_color=opts.img2img_inpaint_sketch_default_brush_color) + inpaint_color_sketch_orig = gr.State(None) + add_copy_image_controls('inpaint_sketch', inpaint_color_sketch) + + def update_orig(image, state): + if image is not None: + same_size = state is not None and state.size == image.size + has_exact_match = np.any(np.all(np.array(image) == np.array(state), axis=-1)) + edited = same_size and has_exact_match + return image if not edited or state is None else state + + inpaint_color_sketch.change(update_orig, [inpaint_color_sketch, inpaint_color_sketch_orig], inpaint_color_sketch_orig) + + with gr.TabItem('Inpaint upload', id='inpaint_upload', elem_id="img2img_inpaint_upload_tab") as tab_inpaint_upload: + init_img_inpaint = gr.Image(label="Image for img2img", show_label=False, source="upload", interactive=True, type="pil", elem_id="img_inpaint_base") + init_mask_inpaint = gr.Image(label="Mask", source="upload", interactive=True, type="pil", image_mode="RGBA", elem_id="img_inpaint_mask") + + with gr.TabItem('Batch', id='batch', elem_id="img2img_batch_tab") as tab_batch: + hidden = '
Disabled when launched with --hide-ui-dir-config.' if shared.cmd_opts.hide_ui_dir_config else '' + gr.HTML( + "

Process images in a directory on the same machine where the server is running." + + "
Use an empty output directory to save pictures normally instead of writing to the output directory." + + f"
Add inpaint batch mask directory to enable inpaint batch processing." + f"{hidden}

" + ) + img2img_batch_input_dir = gr.Textbox(label="Input directory", **shared.hide_dirs, elem_id="img2img_batch_input_dir") + img2img_batch_output_dir = gr.Textbox(label="Output directory", **shared.hide_dirs, elem_id="img2img_batch_output_dir") + img2img_batch_inpaint_mask_dir = gr.Textbox(label="Inpaint batch mask directory (required for inpaint batch processing only)", **shared.hide_dirs, elem_id="img2img_batch_inpaint_mask_dir") + with gr.Accordion("PNG info", open=False): + img2img_batch_use_png_info = gr.Checkbox(label="Append png info to prompts", **shared.hide_dirs, elem_id="img2img_batch_use_png_info") + img2img_batch_png_info_dir = gr.Textbox(label="PNG info directory", **shared.hide_dirs, placeholder="Leave empty to use input directory", elem_id="img2img_batch_png_info_dir") + img2img_batch_png_info_props = gr.CheckboxGroup(["Prompt", "Negative prompt", "Seed", "CFG scale", "Sampler", "Steps", "Model hash"], label="Parameters to take from png info", info="Prompts from png info will be appended to prompts set in ui.") + + img2img_tabs = [tab_img2img, tab_sketch, tab_inpaint, tab_inpaint_color, tab_inpaint_upload, tab_batch] + + for i, tab in enumerate(img2img_tabs): + tab.select(fn=lambda tabnum=i: tabnum, inputs=[], outputs=[img2img_selected_tab]) + + def copy_image(img): + if isinstance(img, dict) and 'image' in img: + return img['image'] + + return img + + for button, name, elem in copy_image_buttons: + button.click( + fn=copy_image, + inputs=[elem], + outputs=[copy_image_destinations[name]], + ) + button.click( + fn=lambda: None, + _js=f"switch_to_{name.replace(' ', '_')}", + inputs=[], + outputs=[], + ) + + with FormRow(): + resize_mode = gr.Radio(label="Resize mode", elem_id="resize_mode", choices=["Just resize", "Crop and resize", "Resize and fill", "Just resize (latent upscale)"], type="index", value="Just resize") + if category == "sampler": steps, sampler_name = create_sampler_and_steps_selection(sd_samplers.visible_sampler_names(), "img2img") @@ -769,7 +702,7 @@ def create_ui(): if category not in {"accordions"}: scripts.scripts_img2img.setup_ui_for_section(category) - img2img_gallery, generation_info, html_info, html_log = create_output_panel("img2img", opts.outdir_img2img_samples) + img2img_gallery, generation_info, html_info, html_log = create_output_panel("img2img", opts.outdir_img2img_samples, toprow) img2img_args = dict( fn=wrap_gradio_gpu_call(modules.img2img.img2img, extra_outputs=[None, '', '']), diff --git a/modules/ui_common.py b/modules/ui_common.py index 84a7d7f2..032ec4af 100644 --- a/modules/ui_common.py +++ b/modules/ui_common.py @@ -104,7 +104,7 @@ def save_files(js_data, images, do_make_zip, index): return gr.File.update(value=fullfns, visible=True), plaintext_to_html(f"Saved: {filenames[0]}") -def create_output_panel(tabname, outdir): +def create_output_panel(tabname, outdir, toprow=None): def open_folder(f): if not os.path.exists(f): @@ -130,12 +130,15 @@ Requested path was: {f} else: sp.Popen(["xdg-open", path]) - with gr.Column(variant='panel', elem_id=f"{tabname}_results"): - with gr.Group(elem_id=f"{tabname}_gallery_container"): - result_gallery = gr.Gallery(label='Output', show_label=False, elem_id=f"{tabname}_gallery", columns=4, preview=True, height=shared.opts.gallery_height or None) + with gr.Column(elem_id=f"{tabname}_results"): + if toprow: + toprow.create_inline_toprow_image() - generation_info = None - with gr.Column(): + with gr.Column(variant='panel', elem_id=f"{tabname}_results_panel"): + with gr.Group(elem_id=f"{tabname}_gallery_container"): + result_gallery = gr.Gallery(label='Output', show_label=False, elem_id=f"{tabname}_gallery", columns=4, preview=True, height=shared.opts.gallery_height or None) + + generation_info = None with gr.Row(elem_id=f"image_buttons_{tabname}", elem_classes="image-buttons"): open_folder_button = ToolButton(folder_symbol, elem_id=f'{tabname}_open_folder', visible=not shared.cmd_opts.hide_ui_dir_config, tooltip="Open images output directory.") diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index fc729917..7907cd63 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -103,6 +103,7 @@ class ExtraNetworksPage: self.name = title.lower() self.id_page = self.name.replace(" ", "_") self.card_page = shared.html("extra-networks-card.html") + self.allow_prompt = True self.allow_negative_prompt = False self.metadata = {} self.items = {} @@ -367,7 +368,7 @@ def create_ui(interface: gr.Blocks, unrelated_tabs, tabname): related_tabs = [] for page in ui.stored_extra_pages: - with gr.Tab(page.title, id=page.id_page) as tab: + with gr.Tab(page.title, elem_id=f"{tabname}_{page.id_page}", elem_classes=["extra-page"]) as tab: elem_id = f"{tabname}_{page.id_page}_cards_html" page_elem = gr.HTML('Loading...', elem_id=elem_id) ui.pages.append(page_elem) @@ -389,11 +390,18 @@ def create_ui(interface: gr.Blocks, unrelated_tabs, tabname): ui.button_save_preview = gr.Button('Save preview', elem_id=tabname+"_save_preview", visible=False) ui.preview_target_filename = gr.Textbox('Preview save filename', elem_id=tabname+"_preview_filename", visible=False) - for tab in unrelated_tabs: - tab.select(fn=lambda: [gr.update(visible=False) for _ in range(5)], inputs=[], outputs=[edit_search, dropdown_sort, button_sortorder, button_refresh, checkbox_show_dirs], show_progress=False) + tab_controls = [edit_search, dropdown_sort, button_sortorder, button_refresh, checkbox_show_dirs] - for tab in related_tabs: - tab.select(fn=lambda: [gr.update(visible=True) for _ in range(5)], inputs=[], outputs=[edit_search, dropdown_sort, button_sortorder, button_refresh, checkbox_show_dirs], show_progress=False) + for tab in unrelated_tabs: + tab.select(fn=lambda: [gr.update(visible=False) for _ in tab_controls], _js='function(){ extraNetworksUrelatedTabSelected("' + tabname + '"); }', inputs=[], outputs=tab_controls, show_progress=False) + + for page, tab in zip(ui.stored_extra_pages, related_tabs): + allow_prompt = "true" if page.allow_prompt else "false" + allow_negative_prompt = "true" if page.allow_negative_prompt else "false" + + jscode = 'extraNetworksTabSelected("' + tabname + '", "' + f"{tabname}_{page.id_page}" + '", ' + allow_prompt + ', ' + allow_negative_prompt + ');' + + tab.select(fn=lambda: [gr.update(visible=True) for _ in tab_controls], _js='function(){ ' + jscode + ' }', inputs=[], outputs=tab_controls, show_progress=False) dropdown_sort.change(fn=lambda: None, _js="function(){ applyExtraNetworkSort('" + tabname + "'); }") diff --git a/modules/ui_extra_networks_checkpoints.py b/modules/ui_extra_networks_checkpoints.py index ca6c2607..2fc0ed43 100644 --- a/modules/ui_extra_networks_checkpoints.py +++ b/modules/ui_extra_networks_checkpoints.py @@ -10,6 +10,8 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage): def __init__(self): super().__init__('Checkpoints') + self.allow_prompt = False + def refresh(self): shared.refresh_checkpoints() diff --git a/modules/ui_toprow.py b/modules/ui_toprow.py new file mode 100644 index 00000000..985b5a2d --- /dev/null +++ b/modules/ui_toprow.py @@ -0,0 +1,141 @@ +import gradio as gr + +from modules import shared, ui_prompt_styles +import modules.images + +from modules.ui_components import ToolButton + + +class Toprow: + """Creates a top row UI with prompts, generate button, styles, extra little buttons for things, and enables some functionality related to their operation""" + + prompt = None + prompt_img = None + negative_prompt = None + + button_interrogate = None + button_deepbooru = None + + interrupt = None + skip = None + submit = None + + paste = None + clear_prompt_button = None + apply_styles = None + restore_progress_button = None + + token_counter = None + token_button = None + negative_token_counter = None + negative_token_button = None + + ui_styles = None + + submit_box = None + + def __init__(self, is_img2img, is_compact=False): + id_part = "img2img" if is_img2img else "txt2img" + self.id_part = id_part + self.is_img2img = is_img2img + self.is_compact = is_compact + + if not is_compact: + with gr.Row(elem_id=f"{id_part}_toprow", variant="compact"): + self.create_classic_toprow() + else: + self.create_submit_box() + + def create_classic_toprow(self): + self.create_prompts() + + with gr.Column(scale=1, elem_id=f"{self.id_part}_actions_column"): + self.create_submit_box() + + self.create_tools_row() + + self.create_styles_ui() + + def create_inline_toprow_prompts(self): + if not self.is_compact: + return + + self.create_prompts() + + with gr.Row(elem_classes=["toprow-compact-stylerow"]): + with gr.Column(elem_classes=["toprow-compact-tools"]): + self.create_tools_row() + with gr.Column(): + self.create_styles_ui() + + def create_inline_toprow_image(self): + if not self.is_compact: + return + + self.submit_box.render() + + def create_prompts(self): + with gr.Column(elem_id=f"{self.id_part}_prompt_container", elem_classes=["prompt-container-compact"] if self.is_compact else [], scale=6): + with gr.Row(elem_id=f"{self.id_part}_prompt_row", elem_classes=["prompt-row"]): + self.prompt = gr.Textbox(label="Prompt", elem_id=f"{self.id_part}_prompt", show_label=False, lines=3, placeholder="Prompt (press Ctrl+Enter or Alt+Enter to generate)", elem_classes=["prompt"]) + self.prompt_img = gr.File(label="", elem_id=f"{self.id_part}_prompt_image", file_count="single", type="binary", visible=False) + + with gr.Row(elem_id=f"{self.id_part}_neg_prompt_row", elem_classes=["prompt-row"]): + self.negative_prompt = gr.Textbox(label="Negative prompt", elem_id=f"{self.id_part}_neg_prompt", show_label=False, lines=3, placeholder="Negative prompt (press Ctrl+Enter or Alt+Enter to generate)", elem_classes=["prompt"]) + + self.prompt_img.change( + fn=modules.images.image_data, + inputs=[self.prompt_img], + outputs=[self.prompt, self.prompt_img], + show_progress=False, + ) + + def create_submit_box(self): + with gr.Row(elem_id=f"{self.id_part}_generate_box", elem_classes=["generate-box"] + (["generate-box-compact"] if self.is_compact else []), render=not self.is_compact) as submit_box: + self.submit_box = submit_box + + self.interrupt = gr.Button('Interrupt', elem_id=f"{self.id_part}_interrupt", elem_classes="generate-box-interrupt") + self.skip = gr.Button('Skip', elem_id=f"{self.id_part}_skip", elem_classes="generate-box-skip") + self.submit = gr.Button('Generate', elem_id=f"{self.id_part}_generate", variant='primary') + + self.skip.click( + fn=lambda: shared.state.skip(), + inputs=[], + outputs=[], + ) + + self.interrupt.click( + fn=lambda: shared.state.interrupt(), + inputs=[], + outputs=[], + ) + + def create_tools_row(self): + with gr.Row(elem_id=f"{self.id_part}_tools"): + from modules.ui import paste_symbol, clear_prompt_symbol, restore_progress_symbol + + self.paste = ToolButton(value=paste_symbol, elem_id="paste", tooltip="Read generation parameters from prompt or last generation if prompt is empty into user interface.") + self.clear_prompt_button = ToolButton(value=clear_prompt_symbol, elem_id=f"{self.id_part}_clear_prompt", tooltip="Clear prompt") + self.apply_styles = ToolButton(value=ui_prompt_styles.styles_materialize_symbol, elem_id=f"{self.id_part}_style_apply", tooltip="Apply all selected styles to prompts.") + + if self.is_img2img: + self.button_interrogate = ToolButton('📎', tooltip='Interrogate CLIP - use CLIP neural network to create a text describing the image, and put it into the prompt field', elem_id="interrogate") + self.button_deepbooru = ToolButton('📦', tooltip='Interrogate DeepBooru - use DeepBooru neural network to create a text describing the image, and put it into the prompt field', elem_id="deepbooru") + + self.restore_progress_button = ToolButton(value=restore_progress_symbol, elem_id=f"{self.id_part}_restore_progress", visible=False, tooltip="Restore progress") + + self.token_counter = gr.HTML(value="0/75", elem_id=f"{self.id_part}_token_counter", elem_classes=["token-counter"]) + self.token_button = gr.Button(visible=False, elem_id=f"{self.id_part}_token_button") + self.negative_token_counter = gr.HTML(value="0/75", elem_id=f"{self.id_part}_negative_token_counter", elem_classes=["token-counter"]) + self.negative_token_button = gr.Button(visible=False, elem_id=f"{self.id_part}_negative_token_button") + + self.clear_prompt_button.click( + fn=lambda *x: x, + _js="confirm_clear_prompt", + inputs=[self.prompt, self.negative_prompt], + outputs=[self.prompt, self.negative_prompt], + ) + + def create_styles_ui(self): + self.ui_styles = ui_prompt_styles.UiPromptStyles(self.id_part, self.prompt, self.negative_prompt) + self.ui_styles.setup_apply_button(self.apply_styles) diff --git a/style.css b/style.css index 9a1181e8..73162022 100644 --- a/style.css +++ b/style.css @@ -296,6 +296,13 @@ input[type="checkbox"].input-accordion-checkbox{ min-height: 4.5em; } +#txt2img_generate, #img2img_generate { + min-height: 4.5em; +} +.generate-box-compact #txt2img_generate, .generate-box-compact #img2img_generate { + min-height: 3em; +} + @media screen and (min-width: 2500px) { #txt2img_gallery, #img2img_gallery { min-height: 768px; @@ -403,6 +410,15 @@ div#extras_scale_to_tab div.form{ min-width: 0.5em; } +div.toprow-compact-stylerow{ + margin: 0.5em 0; +} + +div.toprow-compact-tools{ + min-width: fit-content !important; + max-width: fit-content; +} + /* settings */ #quicksettings { align-items: end; @@ -525,7 +541,8 @@ table.popup-table .link{ height: 20px; background: #b4c0cc; border-radius: 3px !important; - top: -20px; + top: -14px; + left: 0px; width: 100%; } @@ -823,6 +840,10 @@ footer { /* extra networks UI */ +.extra-page .prompt{ + margin: 0 0 0.5em 0; +} + .extra-network-cards{ height: calc(100vh - 24rem); overflow: clip scroll; From c3699d4fd185d5a7285c5519f9bb4b6fec236d9f Mon Sep 17 00:00:00 2001 From: AUTOMATIC1111 <16777216c@gmail.com> Date: Sun, 5 Nov 2023 19:23:48 +0300 Subject: [PATCH 186/186] compact prompt option disabled by default --- modules/shared_options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/shared_options.py b/modules/shared_options.py index 4e3d7541..a9964fcb 100644 --- a/modules/shared_options.py +++ b/modules/shared_options.py @@ -272,7 +272,7 @@ options_templates.update(options_section(('ui', "User interface"), { "hires_fix_show_sampler": OptionInfo(False, "Hires fix: show hires checkpoint and sampler selection").needs_reload_ui(), "hires_fix_show_prompts": OptionInfo(False, "Hires fix: show hires prompt and negative prompt").needs_reload_ui(), "disable_token_counters": OptionInfo(False, "Disable prompt token counters").needs_reload_ui(), - "compact_prompt_box": OptionInfo(True, "Compact prompt layout").info("puts prompt and negative prompt inside the Generate tab, leaving more vertical space for the image on the right").needs_reload_ui(), + "compact_prompt_box": OptionInfo(False, "Compact prompt layout").info("puts prompt and negative prompt inside the Generate tab, leaving more vertical space for the image on the right").needs_reload_ui(), }))