diff --git a/extensions-builtin/sd-extension-system-info b/extensions-builtin/sd-extension-system-info index 9b07adb37..a8fabaa73 160000 --- a/extensions-builtin/sd-extension-system-info +++ b/extensions-builtin/sd-extension-system-info @@ -1 +1 @@ -Subproject commit 9b07adb37d7955d21506cdc29def2d8abd9d2fb4 +Subproject commit a8fabaa731fd7c05985960ed8c982500d22ad290 diff --git a/extensions-builtin/stable-diffusion-webui-images-browser b/extensions-builtin/stable-diffusion-webui-images-browser index 62cf14133..f711a6929 160000 --- a/extensions-builtin/stable-diffusion-webui-images-browser +++ b/extensions-builtin/stable-diffusion-webui-images-browser @@ -1 +1 @@ -Subproject commit 62cf141330d932f6d49ef4a32891f4c17636e585 +Subproject commit f711a692942d478660824ec5cd046fe22173f88b diff --git a/launch.py b/launch.py index 1fb48a477..b943fed22 100644 --- a/launch.py +++ b/launch.py @@ -7,8 +7,6 @@ import shlex import platform import argparse import json -import warnings -from rich import print parser = argparse.ArgumentParser(add_help=False) parser.add_argument("--ui-settings-file", type=str, default='config.json') @@ -17,6 +15,7 @@ args, _ = parser.parse_known_args(sys.argv) script_path = os.path.dirname(__file__) data_path = os.getcwd() + dir_repos = "repositories" dir_extensions = "extensions" python = sys.executable @@ -25,7 +24,6 @@ index_url = os.environ.get('INDEX_URL', "") stored_commit_hash = None skip_install = False -warnings.filterwarnings(action="ignore", category=UserWarning) def check_python_version(): is_windows = platform.system() == "Windows" @@ -36,11 +34,26 @@ def check_python_version(): if is_windows: supported_minors = [10] else: - supported_minors = [9, 10, 11] + supported_minors = [7, 8, 9, 10, 11] if not (major == 3 and minor in supported_minors): import modules.errors - modules.errors.print_error_explanation(f"Incompatible Python version: {major}.{minor}.{micro} required 3.9-3.11") + + modules.errors.print_error_explanation(f""" +INCOMPATIBLE PYTHON VERSION + +This program is tested with 3.10.6 Python, but you have {major}.{minor}.{micro}. +If you encounter an error with "RuntimeError: Couldn't install torch." message, +or any other error regarding unsuccessful package (library) installation, +please downgrade (or upgrade) to the latest version of 3.10 Python +and delete current Python and "venv" folder in WebUI's directory. + +You can download 3.10 Python from here: https://www.python.org/downloads/release/python-3109/ + +{"Alternatively, use a binary release of WebUI: https://github.com/AUTOMATIC1111/stable-diffusion-webui/releases" if is_windows else ""} + +Use --skip-python-version-check to suppress this warning. +""") def commit_hash(): @@ -167,6 +180,23 @@ def git_pull_recursive(dir): print(f"Couldn't perform 'git pull' on repository in '{subdir}':\n{e.output.decode('utf-8').strip()}\n") +def version_check(commit): + try: + import requests + commits = requests.get('https://api.github.com/repos/AUTOMATIC1111/stable-diffusion-webui/branches/master').json() + if commit != "" and commits['commit']['sha'] != commit: + print("--------------------------------------------------------") + print("| You are not up to date with the most recent release. |") + print("| Consider running `git pull` to update. |") + print("--------------------------------------------------------") + elif commits['commit']['sha'] == commit: + print("You are up to date with the most recent release.") + else: + print("Not a git clone, can't perform version check.") + except Exception as e: + print("version check failed", e) + + def run_extension_installer(extension_dir): path_installer = os.path.join(extension_dir, "install.py") if not os.path.isfile(path_installer): @@ -176,9 +206,7 @@ def run_extension_installer(extension_dir): env = os.environ.copy() env['PYTHONPATH'] = os.path.abspath(".") - stdout = run(f'"{python}" "{path_installer}"', errdesc=f"Error running install.py for extension {extension_dir}", custom_env=env) - if stdout is not None: - print(stdout) + print(run(f'"{python}" "{path_installer}"', errdesc=f"Error running install.py for extension {extension_dir}", custom_env=env)) except Exception as e: print(e, file=sys.stderr) @@ -209,9 +237,11 @@ def run_extensions_installers(settings_file): def prepare_environment(): global skip_install + torch_command = os.environ.get('TORCH_COMMAND', "pip install torch==1.13.1+cu117 torchvision==0.14.1+cu117 --extra-index-url https://download.pytorch.org/whl/cu117") requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt") commandline_args = os.environ.get('COMMANDLINE_ARGS', "") + xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.16rc425') gfpgan_package = os.environ.get('GFPGAN_PACKAGE', "git+https://github.com/TencentARC/GFPGAN.git@8d2447a2d918f8eba5a4a01463fd48e45126a379") clip_package = os.environ.get('CLIP_PACKAGE', "git+https://github.com/openai/CLIP.git@d50d76daa670286dd6cacf3bcd80b5e4823fc8e1") openclip_package = os.environ.get('OPENCLIP_PACKAGE', "git+https://github.com/mlfoundations/open_clip.git@bb6e834e9c70d9c27d0dc3ecedeebeaeb1ffad6b") @@ -222,10 +252,9 @@ def prepare_environment(): codeformer_repo = os.environ.get('CODEFORMER_REPO', 'https://github.com/sczhou/CodeFormer.git') blip_repo = os.environ.get('BLIP_REPO', 'https://github.com/salesforce/BLIP.git') - stable_diffusion_commit_hash = os.environ.get('STABLE_DIFFUSION_COMMIT_HASH', "fc1488421a2761937b9d54784194157882cbc3b1") - taming_transformers_commit_hash = os.environ.get('TAMING_TRANSFORMERS_COMMIT_HASH', "3ba01b241669f5ade541ce990f7650a3b8f65318") - # k_diffusion_commit_hash = os.environ.get('K_DIFFUSION_COMMIT_HASH', "5b3af030dd83e0297272d861c19477735d0317ec") - k_diffusion_commit_hash = os.environ.get('K_DIFFUSION_COMMIT_HASH', "b43db16749d51055f813255eea2fdf1def801919") + stable_diffusion_commit_hash = os.environ.get('STABLE_DIFFUSION_COMMIT_HASH', "47b6b607fdd31875c9279cd2f4f16b92e4ea958e") + taming_transformers_commit_hash = os.environ.get('TAMING_TRANSFORMERS_COMMIT_HASH', "24268930bf1dce879235a7fddd0b2355b84d7ea6") + k_diffusion_commit_hash = os.environ.get('K_DIFFUSION_COMMIT_HASH', "5b3af030dd83e0297272d861c19477735d0317ec") codeformer_commit_hash = os.environ.get('CODEFORMER_COMMIT_HASH', "c5b4593074ba6214284d6acd5f1719b6c5d739af") blip_commit_hash = os.environ.get('BLIP_COMMIT_HASH', "48211a1594f1321b00f14c9f7a5b4813144b2fb9") @@ -233,8 +262,14 @@ def prepare_environment(): sys.argv, _ = extract_arg(sys.argv, '-f') sys.argv, update_all_extensions = extract_arg(sys.argv, '--update-all-extensions') + sys.argv, skip_torch_cuda_test = extract_arg(sys.argv, '--skip-torch-cuda-test') sys.argv, skip_python_version_check = extract_arg(sys.argv, '--skip-python-version-check') + sys.argv, reinstall_xformers = extract_arg(sys.argv, '--reinstall-xformers') + sys.argv, reinstall_torch = extract_arg(sys.argv, '--reinstall-torch') + sys.argv, update_check = extract_arg(sys.argv, '--update-check') + sys.argv, run_tests, test_dir = extract_opt(sys.argv, '--tests') sys.argv, skip_install = extract_arg(sys.argv, '--skip-install') + xformers = '--xformers' in sys.argv ngrok = '--ngrok' in sys.argv if not skip_python_version_check: @@ -242,6 +277,15 @@ def prepare_environment(): commit = commit_hash() + print(f"Python {sys.version}") + print(f"Commit hash: {commit}") + + if reinstall_torch or not is_installed("torch") or not is_installed("torchvision"): + run(f'"{python}" -m {torch_command}', "Installing torch and torchvision", "Couldn't install torch", live=True) + + if not skip_torch_cuda_test: + run_python("import torch; assert torch.cuda.is_available(), 'Torch is not able to use GPU; add --skip-torch-cuda-test to COMMANDLINE_ARGS variable to disable this check'") + if not is_installed("gfpgan"): run_pip(f"install {gfpgan_package}", "gfpgan") @@ -251,6 +295,18 @@ def prepare_environment(): if not is_installed("open_clip"): run_pip(f"install {openclip_package}", "open_clip") + if (not is_installed("xformers") or reinstall_xformers) and xformers: + if platform.system() == "Windows": + if platform.python_version().startswith("3.10"): + run_pip(f"install -U -I --no-deps {xformers_package}", "xformers") + 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 {xformers_package}", "xformers") + if not is_installed("pyngrok") and ngrok: run_pip("install pyngrok", "ngrok") @@ -269,31 +325,50 @@ def prepare_environment(): requirements_file = os.path.join(script_path, requirements_file) run_pip(f"install -r \"{requirements_file}\"", "requirements for Web UI") - if "--exit" in sys.argv: - exit(0) - run_extensions_installers(settings_file=args.ui_settings_file) + if update_check: + version_check(commit) + if update_all_extensions: git_pull_recursive(os.path.join(data_path, dir_extensions)) + if "--exit" in sys.argv: + print("Exiting because of --exit argument") + exit(0) + + if run_tests: + exitcode = tests(test_dir) + exit(exitcode) + + +def tests(test_dir): + if "--api" not in sys.argv: + sys.argv.append("--api") + if "--ckpt" not in sys.argv: + sys.argv.append("--ckpt") + sys.argv.append(os.path.join(script_path, "test/test_files/empty.pt")) + if "--skip-torch-cuda-test" not in sys.argv: + sys.argv.append("--skip-torch-cuda-test") + if "--disable-nan-check" not in sys.argv: + sys.argv.append("--disable-nan-check") + + print(f"Launching Web UI in another process for testing with arguments: {' '.join(sys.argv[1:])}") + + os.environ['COMMANDLINE_ARGS'] = "" + with open(os.path.join(script_path, 'test/stdout.txt'), "w", encoding="utf8") as stdout, open(os.path.join(script_path, 'test/stderr.txt'), "w", encoding="utf8") as stderr: + proc = subprocess.Popen([sys.executable, *sys.argv], stdout=stdout, stderr=stderr) + + import test.server_poll + exitcode = test.server_poll.run_tests(proc, test_dir) + + print(f"Stopping Web UI process with id {proc.pid}") + proc.kill() + return exitcode + def start(): - print(f"Launching server with arguments: {' '.join(sys.argv[1:])}") - - rich_installed = False - try: - from rich.pretty import install as pretty_install - from rich.traceback import install as traceback_install - from rich.console import Console - console = Console(log_time=True, log_time_format='%H:%M:%S-%f') - pretty_install(console=console) - traceback_install(console=console, extra_lines=1, width=console.width, word_wrap=False, indent_guides=False, show_locals=True, max_frames=2) - rich_installed = True - except: - import traceback - pass # if rich is not installed do nothing - + print(f"Launching {'API server' if '--nowebui' in sys.argv else 'Web UI'} with arguments: {' '.join(sys.argv[1:])}") import webui if '--nowebui' in sys.argv: webui.api_only() diff --git a/modules/sd_models.py b/modules/sd_models.py index db19f4182..f0cb12400 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -9,7 +9,6 @@ from omegaconf import OmegaConf from os import mkdir from urllib import request import ldm.modules.midas as midas -import io from ldm.util import instantiate_from_config @@ -18,9 +17,6 @@ from modules.paths import models_path from modules.sd_hijack_inpainting import do_inpainting_hijack from modules.timer import Timer -import rich -from rich import print - model_dir = "Stable-diffusion" model_path = os.path.abspath(os.path.join(paths.models_path, model_dir)) @@ -238,18 +234,16 @@ def read_metadata_from_safetensors(filename): return res -def read_state_dict(checkpoint_file): - if 'v1-5-pruned-emaonly.safetensors' in checkpoint_file: - pl_sd = safetensors.torch.load_file(checkpoint_file, device='cpu') +def read_state_dict(checkpoint_file, print_global_state=False, map_location=None): + _, extension = os.path.splitext(checkpoint_file) + if extension.lower() == ".safetensors": + device = map_location or shared.weight_load_location or devices.get_optimal_device_name() + pl_sd = safetensors.torch.load_file(checkpoint_file, device=device) else: - _, extension = os.path.splitext(checkpoint_file) - with rich.progress.open(checkpoint_file, 'rb') as f: - if extension.lower() == ".safetensors": - buffer = f.read() - pl_sd = safetensors.torch.load(buffer) - else: - buffer = io.BytesIO(f.read()) - pl_sd = torch.load(buffer, map_location='cpu') + pl_sd = torch.load(checkpoint_file, map_location=map_location or shared.weight_load_location) + + if print_global_state and "global_step" in pl_sd: + print(f"Global Step: {pl_sd['global_step']}") sd = get_state_dict_from_checkpoint(pl_sd) return sd @@ -261,12 +255,12 @@ def get_checkpoint_state_dict(checkpoint_info: CheckpointInfo, timer): if checkpoint_info in checkpoints_loaded: # use checkpoint cache - print(f"Loading weights from cache") + print(f"Loading weights [{sd_model_hash}] from cache") return checkpoints_loaded[checkpoint_info] - print(f"Loading weights from {checkpoint_info.filename}") + print(f"Loading weights [{sd_model_hash}] from {checkpoint_info.filename}") res = read_state_dict(checkpoint_info.filename) - timer.record("load weights") + timer.record("load weights from disk") return res @@ -282,7 +276,7 @@ def load_model_weights(model, checkpoint_info: CheckpointInfo, state_dict, timer model.load_state_dict(state_dict, strict=False) del state_dict - timer.record("apply weights") + timer.record("apply weights to model") if shared.opts.sd_checkpoint_cache > 0: # cache newly loaded model @@ -308,12 +302,15 @@ def load_model_weights(model, checkpoint_info: CheckpointInfo, state_dict, timer if depth_model: model.depth_model = depth_model + timer.record("apply half()") + devices.dtype = torch.float32 if shared.cmd_opts.no_half else torch.float16 devices.dtype_vae = torch.float32 if shared.cmd_opts.no_half or shared.cmd_opts.no_half_vae else torch.float16 devices.dtype_unet = model.model.diffusion_model.dtype devices.unet_needs_upcast = shared.cmd_opts.upcast_sampling and devices.dtype == torch.float16 and devices.dtype_unet == torch.float16 model.first_stage_model.to(devices.dtype_vae) + timer.record("apply dtype to VAE") # clean up cache if limit is reached while len(checkpoints_loaded) > shared.opts.sd_checkpoint_cache: @@ -330,7 +327,7 @@ def load_model_weights(model, checkpoint_info: CheckpointInfo, state_dict, timer sd_vae.clear_loaded_vae() vae_file, vae_source = sd_vae.resolve_vae(checkpoint_info.filename) sd_vae.load_vae(model, vae_file, vae_source) - timer.record("load vae") + timer.record("load VAE") def enable_midas_autodownload(): @@ -443,7 +440,7 @@ def load_model(checkpoint_info=None, already_loaded_state_dict=None, time_taken_ else: sd_model.to(shared.device) - timer.record("device move") + timer.record("move model to device") sd_hijack.model_hijack.hijack(sd_model) @@ -497,7 +494,7 @@ def reload_model_weights(sd_model=None, info=None): if sd_model is None or checkpoint_config != sd_model.used_config: del sd_model checkpoints_loaded.clear() - load_model(checkpoint_info, already_loaded_state_dict=state_dict, time_taken_to_load_state_dict=timer.records["load weights"]) + load_model(checkpoint_info, already_loaded_state_dict=state_dict, time_taken_to_load_state_dict=timer.records["load weights from disk"]) return shared.sd_model try: @@ -515,7 +512,7 @@ def reload_model_weights(sd_model=None, info=None): if not shared.cmd_opts.lowvram and not shared.cmd_opts.medvram: sd_model.to(devices.device) - timer.record("device move") + timer.record("move model to device") print(f"Weights loaded in {timer.summary()}.") diff --git a/modules/shared.py b/modules/shared.py index d84ee5244..f28a12ccc 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -5,6 +5,7 @@ import os import sys import time +from PIL import Image import gradio as gr import tqdm @@ -14,7 +15,6 @@ import modules.styles import modules.devices as devices from modules import localization, extensions, script_loading, errors, ui_components, shared_items from modules.paths import models_path, script_path, data_path -from rich import print demo = None @@ -37,7 +37,7 @@ parser.add_argument("--no-half-vae", action='store_true', help="do not switch th parser.add_argument("--no-progressbar-hiding", action='store_true', help="do not hide progressbar in gradio UI (we hide it because it slows down ML if you have hardware acceleration in browser)") parser.add_argument("--max-batch-count", type=int, default=16, help="maximum batch count value for the UI") parser.add_argument("--embeddings-dir", type=str, default=os.path.join(data_path, 'embeddings'), help="embeddings directory for textual inversion (default: embeddings)") -parser.add_argument("--textual-inversion-templates-dir", type=str, default=os.path.join(script_path, 'train/templates'), help="directory with textual inversion templates") +parser.add_argument("--textual-inversion-templates-dir", type=str, default=os.path.join(script_path, 'textual_inversion_templates'), help="directory with textual inversion templates") parser.add_argument("--hypernetwork-dir", type=str, default=os.path.join(models_path, 'hypernetworks'), help="hypernetwork directory") parser.add_argument("--localizations-dir", type=str, default=os.path.join(script_path, 'localizations'), help="localizations directory") parser.add_argument("--allow-code", action='store_true', help="allow custom script execution from webui") @@ -48,7 +48,6 @@ parser.add_argument("--always-batch-cond-uncond", action='store_true', help="dis parser.add_argument("--unload-gfpgan", action='store_true', help="does not do anything.") parser.add_argument("--precision", type=str, help="evaluate at this precision", choices=["full", "autocast"], default="autocast") parser.add_argument("--upcast-sampling", action='store_true', help="upcast sampling. No effect with --no-half. Usually produces similar results to --no-half with better performance while using less memory.") -parser.add_argument("--profile", action='store_true', help="run profiler") parser.add_argument("--share", action='store_true', help="use share=True for gradio and make the UI accessible through their site") parser.add_argument("--ngrok", type=str, help="ngrok authtoken, alternative to gradio --share", default=None) parser.add_argument("--ngrok-region", type=str, help="The region in which ngrok should start.", default="us") @@ -59,7 +58,7 @@ parser.add_argument("--esrgan-models-path", type=str, help="Path to directory wi parser.add_argument("--bsrgan-models-path", type=str, help="Path to directory with BSRGAN model file(s).", default=os.path.join(models_path, 'BSRGAN')) parser.add_argument("--realesrgan-models-path", type=str, help="Path to directory with RealESRGAN model file(s).", default=os.path.join(models_path, 'RealESRGAN')) parser.add_argument("--clip-models-path", type=str, help="Path to directory with CLIP model file(s).", default=None) -parser.add_argument("--xformers", action='store_true', help="enable xformers for cross attention layers", default=True) +parser.add_argument("--xformers", action='store_true', help="enable xformers for cross attention layers") parser.add_argument("--force-enable-xformers", action='store_true', help="enable xformers for cross attention layers regardless of whether the checking code thinks you can run it; do not make bug reports if this fails to work") parser.add_argument("--xformers-flash-attention", action='store_true', help="enable xformers with Flash Attention to improve reproducibility (supported for SD2.x or variant only)") parser.add_argument("--deepdanbooru", action='store_true', help="does not do anything") @@ -70,10 +69,10 @@ parser.add_argument("--sub-quad-kv-chunk-size", type=int, help="kv chunk size fo parser.add_argument("--sub-quad-chunk-threshold", type=int, help="the percentage of VRAM threshold for the sub-quadratic cross-attention layer optimization to use chunking", default=None) parser.add_argument("--opt-split-attention-invokeai", action='store_true', help="force-enables InvokeAI's cross-attention layer optimization. By default, it's on when cuda is unavailable.") parser.add_argument("--opt-split-attention-v1", action='store_true', help="enable older version of split attention optimization that does not consume all the VRAM it can find") -parser.add_argument("--opt-sdp-attention", action='store_true', help="enable scaled dot product cross-attention layer optimization; requires PyTorch 2.*", default=True) +parser.add_argument("--opt-sdp-attention", action='store_true', help="enable scaled dot product cross-attention layer optimization; requires PyTorch 2.*") parser.add_argument("--opt-sdp-no-mem-attention", action='store_true', help="enable scaled dot product cross-attention layer optimization without memory efficient attention, makes image generation deterministic; requires PyTorch 2.*") parser.add_argument("--disable-opt-split-attention", action='store_true', help="force-disables cross-attention layer optimization") -parser.add_argument("--disable-nan-check", action='store_true', help="do not check if produced images/latent spaces have nans; useful for running without a checkpoint in CI", default=True) +parser.add_argument("--disable-nan-check", action='store_true', help="do not check if produced images/latent spaces have nans; useful for running without a checkpoint in CI") parser.add_argument("--use-cpu", nargs='+', help="use CPU as torch device for specified modules", default=[], type=str.lower) parser.add_argument("--listen", action='store_true', help="launch gradio with 0.0.0.0 as server name, allowing to respond to network requests") parser.add_argument("--port", type=int, help="launch gradio with given server port, you need root/admin rights for ports < 1024, defaults to 7860 if available", default=None) @@ -90,13 +89,13 @@ parser.add_argument("--gradio-inpaint-tool", type=str, help="does not do anythin parser.add_argument("--opt-channelslast", action='store_true', help="change memory type for stable diffusion to channels last") parser.add_argument("--styles-file", type=str, help="filename to use for styles", default=os.path.join(data_path, 'styles.csv')) parser.add_argument("--autolaunch", action='store_true', help="open the webui URL in the system's default browser upon launch", default=False) -parser.add_argument("--theme", type=str, help="launches the UI with light or dark theme", default='dark') +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=True) +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('--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=True) -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)", default=True) +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)") parser.add_argument("--api-auth", type=str, help='Set authentication for API like "username:password"; or comma-delimit multiple like "u1:p1,u2:p2,u3:p3"', default=None) parser.add_argument("--api-log", action='store_true', help="use api-log=True to enable logging of all API requests") parser.add_argument("--nowebui", action='store_true', help="use api=True to launch the API instead of the webui") @@ -108,7 +107,7 @@ parser.add_argument("--cors-allow-origins-regex", type=str, help="Allowed CORS o parser.add_argument("--tls-keyfile", type=str, help="Partially enables TLS, requires --tls-certfile to fully function", default=None) parser.add_argument("--tls-certfile", type=str, help="Partially enables TLS, requires --tls-keyfile to fully function", 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="Uses gradio queue; experimental option; breaks restart UI button", default=True) +parser.add_argument("--gradio-queue", action='store_true', help="Uses gradio queue; experimental option; breaks restart UI button") 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) @@ -153,6 +152,7 @@ devices.device, devices.device_interrogate, devices.device_gfpgan, devices.devic (devices.cpu if any(y in cmd_opts.use_cpu for y in [x, 'all']) else devices.get_optimal_device() for x in ['sd', 'interrogate', 'gfpgan', 'esrgan', 'codeformer']) device = devices.device +weight_load_location = None if cmd_opts.lowram else "cpu" batch_cond_uncond = cmd_opts.always_batch_cond_uncond or not (cmd_opts.lowvram or cmd_opts.medvram) parallel_processing_allowed = not cmd_opts.lowvram and not cmd_opts.medvram @@ -692,7 +692,7 @@ class TotalTQDM: def reset(self): self._tqdm = tqdm.tqdm( - desc="Total", + desc="Total progress", total=state.job_count * state.sampling_steps, position=1, file=progress_print_out @@ -742,20 +742,3 @@ def html(filename): return file.read() return "" - -try: - from rich.pretty import install as pretty_install - from rich.traceback import install as traceback_install - from rich.console import Console - console = Console(log_time=True, log_time_format='%H:%M:%S-%f') - pretty_install(console=console) - traceback_install(console=console, extra_lines=1, width=console.width, word_wrap=False, indent_guides=False, show_locals=True, max_frames=2) -except: - console = None - import traceback - -def exception(): - if console is not None: - console.print_exception(show_locals=True, max_frames=10, extra_lines=1, suppress=[gr], word_wrap=False, width=min([console.width, 200])) - else: - print(traceback.format_exc(), file=sys.stderr) diff --git a/modules/ui.py b/modules/ui.py index 0c2766d10..7e6033327 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -8,6 +8,7 @@ import random import sys import tempfile import time +import traceback from functools import partial, reduce import warnings @@ -41,6 +42,8 @@ import modules.hypernetworks.ui from modules.generation_parameters_copypaste import image_from_url_text import modules.extras +warnings.filterwarnings("default" if opts.show_warnings else "ignore", category=UserWarning) + # this is a fix for Windows users. Without it, javascript files will be served with text/html content-type and the browser will not show any UI mimetypes.init() mimetypes.add_type('application/javascript', '.js') @@ -183,18 +186,18 @@ def create_seed_inputs(target_interface): reuse_seed = gr.Button(reuse_symbol, elem_id=target_interface + '_reuse_seed') with gr.Group(elem_id=target_interface + '_subseed_show_box'): - seed_checkbox = gr.Checkbox(label='Extra', elem_id=target_interface + '_subseed_show', value=True) + seed_checkbox = gr.Checkbox(label='Extra', elem_id=target_interface + '_subseed_show', value=False) # Components to show/hide based on the 'Extra' checkbox seed_extras = [] - with FormRow(visible=True, elem_id=target_interface + '_subseed_row') as seed_extra_row_1: + with FormRow(visible=False, elem_id=target_interface + '_subseed_row') as seed_extra_row_1: seed_extras.append(seed_extra_row_1) subseed = gr.Number(label='Variation seed', value=-1, elem_id=target_interface + '_subseed') subseed.style(container=False) random_subseed = gr.Button(random_symbol, elem_id=target_interface + '_random_subseed') reuse_subseed = gr.Button(reuse_symbol, elem_id=target_interface + '_reuse_subseed') - subseed_strength = gr.Slider(label='strength', value=0.0, minimum=0, maximum=1, step=0.01, elem_id=target_interface + '_subseed_strength') + subseed_strength = gr.Slider(label='Variation strength', value=0.0, minimum=0, maximum=1, step=0.01, elem_id=target_interface + '_subseed_strength') with FormRow(visible=False) as seed_extra_row_2: seed_extras.append(seed_extra_row_2) @@ -473,27 +476,26 @@ def create_ui(): elif category == "dimensions": with FormRow(): with gr.Column(elem_id="txt2img_column_size", scale=4): - with FormRow(elem_id="txt2img_row_dimension"): - width = gr.Slider(minimum=128, maximum=2048, step=8, label="Width", value=512, elem_id="txt2img_width") - height = gr.Slider(minimum=128, maximum=2048, step=8, label="Height", value=512, elem_id="txt2img_height") + width = gr.Slider(minimum=64, maximum=2048, step=8, label="Width", value=512, elem_id="txt2img_width") + height = gr.Slider(minimum=64, maximum=2048, step=8, label="Height", value=512, elem_id="txt2img_height") res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="txt2img_res_switch_btn") if opts.dimensions_and_batch_together: with gr.Column(elem_id="txt2img_column_batch"): - with FormRow(elem_id="txt2img_row_batch"): - batch_count = gr.Slider(minimum=1, step=1, label='Batch count', value=1, elem_id="txt2img_batch_count") - batch_size = gr.Slider(minimum=1, maximum=8, step=1, label='Batch size', value=1, elem_id="txt2img_batch_size") + batch_count = gr.Slider(minimum=1, step=1, label='Batch count', value=1, elem_id="txt2img_batch_count") + batch_size = gr.Slider(minimum=1, maximum=8, step=1, label='Batch size', value=1, elem_id="txt2img_batch_size") + elif category == "cfg": + cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.5, label='CFG Scale', value=7.0, elem_id="txt2img_cfg_scale") elif category == "seed": seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w, seed_checkbox = create_seed_inputs('txt2img') elif category == "checkboxes": with FormRow(elem_id="txt2img_checkboxes", variant="compact"): - cfg_scale = gr.Slider(minimum=1.0, maximum=30.0, step=0.5, label='CFG Scale', value=7.0, elem_id="txt2img_cfg_scale") restore_faces = gr.Checkbox(label='Restore faces', value=False, visible=len(shared.face_restorers) > 1, elem_id="txt2img_restore_faces") tiling = gr.Checkbox(label='Tiling', value=False, elem_id="txt2img_tiling") - enable_hr = gr.Checkbox(label='Hi-res tiling', value=False, elem_id="txt2img_enable_hr") + enable_hr = gr.Checkbox(label='Hires. fix', value=False, elem_id="txt2img_enable_hr") hr_final_resolution = FormHTML(value="", elem_id="txtimg_hr_finalres", label="Upscaled resolution", interactive=False) elif category == "hires_fix": @@ -743,7 +745,7 @@ def create_ui(): ) with FormRow(): - resize_mode = gr.Radio(label="Resize mode", elem_id="resize_mode", choices=["Just resize", "Crop and resize", "Resize and fill", "Latent upscale"], type="index", value="Just resize") + 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") for category in ordered_ui_categories(): if category == "sampler": @@ -752,16 +754,14 @@ def create_ui(): elif category == "dimensions": with FormRow(): with gr.Column(elem_id="img2img_column_size", scale=4): - with FormRow(elem_id="img2img_row_size"): - 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") + 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") res_switch_btn = ToolButton(value=switch_values_symbol, elem_id="img2img_res_switch_btn") if opts.dimensions_and_batch_together: with gr.Column(elem_id="img2img_column_batch"): - with FormRow(elem_id="img2img_row_batch"): - batch_count = gr.Slider(minimum=1, step=1, label='Batch count', value=1, elem_id="img2img_batch_count") - batch_size = gr.Slider(minimum=1, maximum=8, step=1, label='Batch size', value=1, elem_id="img2img_batch_size") + batch_count = gr.Slider(minimum=1, step=1, label='Batch count', value=1, elem_id="img2img_batch_count") + batch_size = gr.Slider(minimum=1, maximum=8, step=1, label='Batch size', value=1, elem_id="img2img_batch_size") elif category == "cfg": with FormGroup(): @@ -1006,6 +1006,48 @@ def create_ui(): } return interp_descriptions[value] + with gr.Blocks(analytics_enabled=False) as modelmerger_interface: + with gr.Row().style(equal_height=False): + with gr.Column(variant='compact'): + interp_description = gr.HTML(value=update_interp_description("Weighted sum"), elem_id="modelmerger_interp_description") + + with FormRow(elem_id="modelmerger_models"): + primary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_primary_model_name", label="Primary model (A)") + create_refresh_button(primary_model_name, modules.sd_models.list_models, lambda: {"choices": modules.sd_models.checkpoint_tiles()}, "refresh_checkpoint_A") + + secondary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_secondary_model_name", label="Secondary model (B)") + create_refresh_button(secondary_model_name, modules.sd_models.list_models, lambda: {"choices": modules.sd_models.checkpoint_tiles()}, "refresh_checkpoint_B") + + tertiary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_tertiary_model_name", label="Tertiary model (C)") + create_refresh_button(tertiary_model_name, modules.sd_models.list_models, lambda: {"choices": modules.sd_models.checkpoint_tiles()}, "refresh_checkpoint_C") + + custom_name = gr.Textbox(label="Custom Name (Optional)", elem_id="modelmerger_custom_name") + interp_amount = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Multiplier (M) - set to 0 to get model A', value=0.3, elem_id="modelmerger_interp_amount") + interp_method = gr.Radio(choices=["No interpolation", "Weighted sum", "Add difference"], value="Weighted sum", label="Interpolation Method", elem_id="modelmerger_interp_method") + interp_method.change(fn=update_interp_description, inputs=[interp_method], outputs=[interp_description]) + + with FormRow(): + checkpoint_format = gr.Radio(choices=["ckpt", "safetensors"], value="ckpt", label="Checkpoint format", elem_id="modelmerger_checkpoint_format") + save_as_half = gr.Checkbox(value=False, label="Save as float16", elem_id="modelmerger_save_as_half") + + with FormRow(): + with gr.Column(): + config_source = gr.Radio(choices=["A, B or C", "B", "C", "Don't"], value="A, B or C", label="Copy config from", type="index", elem_id="modelmerger_config_method") + + with gr.Column(): + with FormRow(): + bake_in_vae = gr.Dropdown(choices=["None"] + list(sd_vae.vae_dict), value="None", label="Bake in VAE", elem_id="modelmerger_bake_in_vae") + create_refresh_button(bake_in_vae, sd_vae.refresh_vae_list, lambda: {"choices": ["None"] + list(sd_vae.vae_dict)}, "modelmerger_refresh_bake_in_vae") + + with FormRow(): + discard_weights = gr.Textbox(value="", label="Discard weights with matching name", elem_id="modelmerger_discard_weights") + + with gr.Row(): + modelmerger_merge = gr.Button(elem_id="modelmerger_merge", value="Merge", variant='primary') + + with gr.Column(variant='compact', elem_id="modelmerger_results_container"): + with gr.Group(elem_id="modelmerger_results_panel"): + modelmerger_result = gr.HTML(elem_id="modelmerger_result", show_label=False) with gr.Blocks(analytics_enabled=False) as train_interface: with gr.Row().style(equal_height=False): @@ -1014,49 +1056,6 @@ def create_ui(): with gr.Row(variant="compact").style(equal_height=False): with gr.Tabs(elem_id="train_tabs"): - with gr.Tab(label="Merge models") as modelmerger_interface: - with gr.Row().style(equal_height=False): - with gr.Column(variant='compact'): - interp_description = gr.HTML(value=update_interp_description("Weighted sum"), elem_id="modelmerger_interp_description") - - with FormRow(elem_id="modelmerger_models"): - primary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_primary_model_name", label="Primary model (A)") - create_refresh_button(primary_model_name, modules.sd_models.list_models, lambda: {"choices": modules.sd_models.checkpoint_tiles()}, "refresh_checkpoint_A") - - secondary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_secondary_model_name", label="Secondary model (B)") - create_refresh_button(secondary_model_name, modules.sd_models.list_models, lambda: {"choices": modules.sd_models.checkpoint_tiles()}, "refresh_checkpoint_B") - - tertiary_model_name = gr.Dropdown(modules.sd_models.checkpoint_tiles(), elem_id="modelmerger_tertiary_model_name", label="Tertiary model (C)") - create_refresh_button(tertiary_model_name, modules.sd_models.list_models, lambda: {"choices": modules.sd_models.checkpoint_tiles()}, "refresh_checkpoint_C") - - custom_name = gr.Textbox(label="Custom Name (Optional)", elem_id="modelmerger_custom_name") - interp_amount = gr.Slider(minimum=0.0, maximum=1.0, step=0.05, label='Multiplier (M) - set to 0 to get model A', value=0.3, elem_id="modelmerger_interp_amount") - interp_method = gr.Radio(choices=["No interpolation", "Weighted sum", "Add difference"], value="Weighted sum", label="Interpolation Method", elem_id="modelmerger_interp_method") - interp_method.change(fn=update_interp_description, inputs=[interp_method], outputs=[interp_description]) - - with FormRow(): - checkpoint_format = gr.Radio(choices=["ckpt", "safetensors"], value="ckpt", label="Checkpoint format", elem_id="modelmerger_checkpoint_format") - save_as_half = gr.Checkbox(value=False, label="Save as float16", elem_id="modelmerger_save_as_half") - - with FormRow(): - with gr.Column(): - config_source = gr.Radio(choices=["A, B or C", "B", "C", "Don't"], value="A, B or C", label="Copy config from", type="index", elem_id="modelmerger_config_method") - - with gr.Column(): - with FormRow(): - bake_in_vae = gr.Dropdown(choices=["None"] + list(sd_vae.vae_dict), value="None", label="Bake in VAE", elem_id="modelmerger_bake_in_vae") - create_refresh_button(bake_in_vae, sd_vae.refresh_vae_list, lambda: {"choices": ["None"] + list(sd_vae.vae_dict)}, "modelmerger_refresh_bake_in_vae") - - with FormRow(): - discard_weights = gr.Textbox(value="", label="Discard weights with matching name", elem_id="modelmerger_discard_weights") - - with gr.Row(): - modelmerger_merge = gr.Button(elem_id="modelmerger_merge", value="Merge", variant='primary') - - with gr.Column(variant='compact', elem_id="modelmerger_results_container"): - with gr.Group(elem_id="modelmerger_results_panel"): - modelmerger_result = gr.HTML(elem_id="modelmerger_result", show_label=False) - with gr.Tab(label="Create embedding"): new_embedding_name = gr.Textbox(label="Name", elem_id="train_new_embedding_name") initialization_text = gr.Textbox(label="Initialization text", value="*", elem_id="train_initialization_text") @@ -1443,7 +1442,7 @@ def create_ui(): with gr.Column(scale=6): settings_submit = gr.Button(value="Apply settings", variant='primary', elem_id="settings_submit") with gr.Column(): - restart_gradio = gr.Button(value='Reload UI', variant='primary', elem_id="settings_restart_gradio", visible=False) + restart_gradio = gr.Button(value='Reload UI', variant='primary', elem_id="settings_restart_gradio") result = gr.HTML(elem_id="settings_result") @@ -1534,11 +1533,11 @@ def create_ui(): ) interfaces = [ - (txt2img_interface, "From Text", "txt2img"), - (img2img_interface, "From Image", "img2img"), - (extras_interface, "Process", "extras"), - (pnginfo_interface, "Image Info", "pnginfo"), - # (modelmerger_interface, "Checkpoint Merger", "modelmerger"), + (txt2img_interface, "txt2img", "txt2img"), + (img2img_interface, "img2img", "img2img"), + (extras_interface, "Extras", "extras"), + (pnginfo_interface, "PNG Info", "pnginfo"), + (modelmerger_interface, "Checkpoint Merger", "modelmerger"), (train_interface, "Train", "ti"), ] @@ -1636,7 +1635,7 @@ def create_ui(): results = modules.extras.run_modelmerger(*args) except Exception as e: print("Error loading/saving model file:", file=sys.stderr) - shared.exception() + print(traceback.format_exc(), file=sys.stderr) modules.sd_models.list_models() # to remove the potentially missing models from the list return [*[gr.Dropdown.update(choices=modules.sd_models.checkpoint_tiles()) for _ in range(4)], f"Error merging checkpoints: {e}"] return results @@ -1680,7 +1679,7 @@ def create_ui(): except Exception: error_loading = True print("Error loading settings:", file=sys.stderr) - shared.exception() + print(traceback.format_exc(), file=sys.stderr) def loadsave(path, x): def apply_field(obj, field, condition=None, init_field=None): @@ -1782,9 +1781,11 @@ if not hasattr(shared, 'GradioTemplateResponseOriginal'): def versions_html(): import torch - from launch import commit_hash + import launch python_version = ".".join([str(x) for x in sys.version_info[0:3]]) + commit = launch.commit_hash() + short_commit = commit[0:8] if shared.xformers_available: import xformers @@ -1792,11 +1793,6 @@ def versions_html(): else: xformers_version = "N/A" - try: - torch_version = torch.__long_version__ - except: - torch_version = torch.__version__ - return f""" python: {python_version}  •  @@ -1806,7 +1802,7 @@ xformers: {xformers_version}  •  gradio: {gr.__version__}  •  -commit: {commit_hash()[0:8]} +commit: {short_commit}  •  checkpoint: N/A """ diff --git a/requirements.txt b/requirements.txt index a104c7e17..6d53f0893 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,46 +1,32 @@ -accelerate -aenum -basicsr -bitsandbytes blendmodes -clean-fid -colormap -diffusers -easydev -einops -extcolors -facexlib -fastapi -filetype -font-roboto +accelerate +basicsr fonts +font-roboto gfpgan -GitPython -gradio -inflection +gradio==3.16.2 invisible-watermark -jsonmerge -kornia -lark -mediapipe numpy omegaconf opencv-contrib-python +requests piexif Pillow -psutil -pyngrok -pytorch_lightning +pytorch_lightning==1.7.7 realesrgan -requests -resize-right -rich -safetensors -scikit-image -timm -toml +scikit-image>=0.19 +timm==0.4.12 +transformers==4.25.1 torch +einops +jsonmerge +clean-fid +resize-right torchdiffeq +kornia +lark +inflection +GitPython torchsde -transformers -voluptuous +safetensors +psutil diff --git a/requirements_versions.txt b/requirements_versions.txt index b1821d079..0031c6162 100644 --- a/requirements_versions.txt +++ b/requirements_versions.txt @@ -1,17 +1,30 @@ -accelerate==0.17.1 -clip_interrogator==0.6.0 -diffusers==0.14.0 -einops==0.4.1 -fastapi==0.94.0 +blendmodes==2022 +transformers==4.25.1 +accelerate==0.12.0 +basicsr==1.4.2 gfpgan==1.3.8 -GitPython==3.1.31 gradio==3.16.2 -numexpr==2.8.4 -omegaconf==2.3.0 -pandas==1.5.3 +numpy==1.23.3 Pillow==9.4.0 -protobuf==3.20.3 -pytorch_lightning==1.9.4 realesrgan==0.3.0 -safetensors==0.3.0 -transformers==4.27.1 +torch +omegaconf==2.2.3 +pytorch_lightning==1.7.6 +scikit-image==0.19.2 +fonts +font-roboto +timm==0.6.7 +piexif==1.1.3 +einops==0.4.1 +jsonmerge==1.8.0 +clean-fid==0.1.29 +resize-right==0.0.2 +torchdiffeq==0.2.3 +kornia==0.6.7 +lark==1.1.2 +inflection==0.5.1 +GitPython==3.1.30 +torchsde==0.2.5 +safetensors==0.2.7 +httpcore<=0.15 +fastapi==0.94.0 diff --git a/scripts/postprocessing_upscale.py b/scripts/postprocessing_upscale.py index 069dfd1ac..8842bd91c 100644 --- a/scripts/postprocessing_upscale.py +++ b/scripts/postprocessing_upscale.py @@ -20,7 +20,6 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): with gr.Tabs(elem_id="extras_resize_mode"): with gr.TabItem('Scale by', elem_id="extras_scale_by_tab") as tab_scale_by: upscaling_resize = gr.Slider(minimum=1.0, maximum=8.0, step=0.05, label="Resize", value=4, elem_id="extras_upscaling_resize") - extras_upscaler_1 = gr.Dropdown(label='Upscaler 1', elem_id="extras_upscaler_1", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name) with gr.TabItem('Scale to', elem_id="extras_scale_to_tab") as tab_scale_to: with FormRow(): @@ -28,6 +27,9 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): upscaling_resize_h = gr.Number(label="Height", value=512, precision=0, elem_id="extras_upscaling_resize_h") upscaling_crop = gr.Checkbox(label='Crop to fit', value=True, elem_id="extras_upscaling_crop") + with FormRow(): + extras_upscaler_1 = gr.Dropdown(label='Upscaler 1', elem_id="extras_upscaler_1", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name) + with FormRow(): extras_upscaler_2 = gr.Dropdown(label='Upscaler 2', elem_id="extras_upscaler_2", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name) extras_upscaler_2_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Upscaler 2 visibility", value=0.0, elem_id="extras_upscaler_2_visibility") diff --git a/style.css b/style.css index 8d488700d..3eac2b176 100644 --- a/style.css +++ b/style.css @@ -58,6 +58,9 @@ margin-right: 0; } +.performance .vram { +} + #txt2img_generate, #img2img_generate { min-height: 4.5em; } @@ -302,6 +305,9 @@ input[type="range"]{ margin-bottom: -0.75em; } +#txt2img_negative_prompt, #img2img_negative_prompt{ +} + /* gradio 3.8 adds opacity to progressbar which makes it blink; disable it here */ .transition.opacity-20 { opacity: 1 !important; @@ -804,6 +810,9 @@ footer { overflow: visible; } +.gr-compact > *{ +} + .gr-compact .gr-block, .gr-compact .gr-form{ border: none; box-shadow: none; @@ -822,6 +831,9 @@ footer { border: none; } +[id*='img2img_copy_to_'] > button { +} + [id*='img2img_label_copy_to_'] { font-size: 1.0em; font-weight: bold; diff --git a/webui.py b/webui.py index f786cd739..aaec79fda 100644 --- a/webui.py +++ b/webui.py @@ -4,12 +4,10 @@ import time import importlib import signal import re -import warnings from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.gzip import GZipMiddleware from packaging import version -from rich import print import logging logging.getLogger("xformers").addFilter(lambda record: 'A matching Triton is not available' not in record.getMessage()) @@ -19,12 +17,13 @@ from modules import paths, timer, import_hook, errors startup_timer = timer.Timer() import torch -import pytorch_lightning # pytorch_lightning should be imported after torch, but it re-enables warnings on import so import once to disable them -warnings.filterwarnings(action="ignore", category=DeprecationWarning, module="pytorch_lightning") startup_timer.record("import torch") import gradio +startup_timer.record("import gradio") + import ldm.modules.encoders.modules +startup_timer.record("import ldm") from modules import extra_networks, ui_extra_networks_checkpoints from modules import extra_networks_hypernet, ui_extra_networks_hypernets, ui_extra_networks_textual_inversion @@ -35,7 +34,7 @@ if ".dev" in torch.__version__ or "+git" in torch.__version__: torch.__long_version__ = torch.__version__ torch.__version__ = re.search(r'[\d.]+[\d]', torch.__version__).group(0) -from modules import shared, devices, sd_samplers, upscaler, extensions, ui_tempdir, ui_extra_networks +from modules import shared, devices, sd_samplers, upscaler, extensions, localization, ui_tempdir, ui_extra_networks import modules.codeformer_model as codeformer import modules.face_restoration import modules.gfpgan_model as gfpgan @@ -56,7 +55,7 @@ from modules import modelloader from modules.shared import cmd_opts import modules.hypernetworks.hypernetwork -startup_timer.record("import libraries") +startup_timer.record("other imports") if cmd_opts.server_name: @@ -65,18 +64,42 @@ else: server_name = "0.0.0.0" if cmd_opts.listen else None +def check_versions(): + if shared.cmd_opts.skip_version_check: + return + + expected_torch_version = "1.13.1" + + if version.parse(torch.__version__) < version.parse(expected_torch_version): + errors.print_error_explanation(f""" +You are running torch {torch.__version__}. +The program is tested to work with torch {expected_torch_version}. +To reinstall the desired version, run with commandline flag --reinstall-torch. +Beware that this will cause a lot of large files to be downloaded, as well as +there are reports of issues with training tab on the latest version. + +Use --skip-version-check commandline argument to disable this check. + """.strip()) + + expected_xformers_version = "0.0.16rc425" + if shared.xformers_available: + import xformers + + if version.parse(xformers.__version__) < version.parse(expected_xformers_version): + errors.print_error_explanation(f""" +You are running xformers {xformers.__version__}. +The program is tested to work with xformers {expected_xformers_version}. +To reinstall the desired version, run with commandline flag --reinstall-xformers. + +Use --skip-version-check commandline argument to disable this check. + """.strip()) + + def initialize(): - if torch.cuda.is_available(): - if torch.version.cuda: cuda_version = f'CUDA {torch.version.cuda} cuDNN {torch.backends.cudnn.version()}' - elif torch.version.hip: cuda_version = f'HIP {torch.version.hip}' - else: cuda_version = '' - print(f'Torch {getattr(torch, "__long_version__", torch.__version__)} {cuda_version}') - for device in [torch.cuda.device(i) for i in range(torch.cuda.device_count())]: - print(f'GPU {torch.cuda.get_device_name(device)} VRAM {round(torch.cuda.get_device_properties(device).total_memory / 1024 / 1024)} Arch {torch.cuda.get_device_capability(device)} Cores {torch.cuda.get_device_properties(device).multi_processor_count}') - else: - print(f'Torch {getattr(torch, "__long_version__", torch.__version__)} running on CPU') + check_versions() extensions.list_extensions() + localization.list_localizations(cmd_opts.localizations_dir) startup_timer.record("list extensions") if cmd_opts.ui_debug_mode: @@ -86,7 +109,7 @@ def initialize(): modelloader.cleanup_models() modules.sd_models.setup_model() - startup_timer.record("list models") + startup_timer.record("list SD models") codeformer.setup_model(cmd_opts.codeformer_models_path) startup_timer.record("setup codeformer") @@ -109,6 +132,18 @@ def initialize(): modules.textual_inversion.textual_inversion.list_textual_inversion_templates() startup_timer.record("refresh textual inversion templates") + try: + modules.sd_models.load_model() + except Exception as e: + errors.display(e, "loading stable diffusion model") + print("", file=sys.stderr) + print("Stable diffusion model failed to load, exiting", file=sys.stderr) + exit(1) + startup_timer.record("load SD checkpoint") + + shared.opts.data["sd_model_checkpoint"] = shared.sd_model.sd_checkpoint_info.title + + shared.opts.onchange("sd_model_checkpoint", wrap_queued_call(lambda: modules.sd_models.reload_model_weights())) shared.opts.onchange("sd_vae", wrap_queued_call(lambda: modules.sd_vae.reload_vae_weights()), call=False) shared.opts.onchange("sd_vae_as_default", wrap_queued_call(lambda: modules.sd_vae.reload_vae_weights()), call=False) shared.opts.onchange("temp_dir", ui_tempdir.on_tmpdir_changed) @@ -141,32 +176,16 @@ def initialize(): startup_timer.record("TLS") # make the program just exit at ctrl+c without waiting for anything - def sigint_handler(_sig, _frame): - print('Exiting') + def sigint_handler(sig, frame): + print(f'Interrupted with signal {sig} in {frame}') os._exit(0) signal.signal(signal.SIGINT, sigint_handler) -def load_model(): - shared.state.begin() - shared.state.job = 'load model' - try: - modules.sd_models.load_model() - except Exception as e: - errors.display(e, "loading stable diffusion model") - print("", file=sys.stderr) - print("Stable diffusion model failed to load, exiting", file=sys.stderr) - exit(1) - shared.opts.data["sd_model_checkpoint"] = shared.sd_model.sd_checkpoint_info.title - shared.opts.onchange("sd_model_checkpoint", wrap_queued_call(lambda: modules.sd_models.reload_model_weights())) - shared.state.end() - startup_timer.record("load checkpoint") - - def setup_middleware(app): app.middleware_stack = None # reset current middleware to allow modifying user provided list - app.add_middleware(GZipMiddleware, minimum_size=1024) + app.add_middleware(GZipMiddleware, minimum_size=1000) if cmd_opts.cors_allow_origins and cmd_opts.cors_allow_origins_regex: app.add_middleware(CORSMiddleware, allow_origins=cmd_opts.cors_allow_origins.split(','), allow_origin_regex=cmd_opts.cors_allow_origins_regex, allow_methods=['*'], allow_credentials=True, allow_headers=['*']) elif cmd_opts.cors_allow_origins: @@ -182,9 +201,19 @@ def create_api(app): return api +def wait_on_server(demo=None): + while 1: + time.sleep(0.5) + if shared.state.need_restart: + shared.state.need_restart = False + time.sleep(0.5) + demo.close() + time.sleep(0.5) + break + + def api_only(): initialize() - load_model() app = FastAPI() setup_middleware(app) @@ -200,71 +229,105 @@ def webui(): launch_api = cmd_opts.api initialize() - if shared.opts.clean_temp_dir_at_start: - ui_tempdir.cleanup_tmpdr() - startup_timer.record("cleanup temp dir") + while 1: + if shared.opts.clean_temp_dir_at_start: + ui_tempdir.cleanup_tmpdr() + startup_timer.record("cleanup temp dir") - modules.script_callbacks.before_ui_callback() - startup_timer.record("scripts before_ui_callback") + modules.script_callbacks.before_ui_callback() + startup_timer.record("scripts before_ui_callback") - shared.demo = modules.ui.create_ui() - startup_timer.record("create ui") + shared.demo = modules.ui.create_ui() + startup_timer.record("create ui") - if cmd_opts.gradio_queue: - shared.demo.queue(16) + if cmd_opts.gradio_queue: + shared.demo.queue(64) - gradio_auth_creds = [] - if cmd_opts.gradio_auth: - gradio_auth_creds += [x.strip() for x in cmd_opts.gradio_auth.strip('"').replace('\n', '').split(',') if x.strip()] - if cmd_opts.gradio_auth_path: - with open(cmd_opts.gradio_auth_path, 'r', encoding="utf8") as file: - for line in file.readlines(): - gradio_auth_creds += [x.strip() for x in line.split(',') if x.strip()] + gradio_auth_creds = [] + if cmd_opts.gradio_auth: + gradio_auth_creds += [x.strip() for x in cmd_opts.gradio_auth.strip('"').replace('\n', '').split(',') if x.strip()] + if cmd_opts.gradio_auth_path: + with open(cmd_opts.gradio_auth_path, 'r', encoding="utf8") as file: + for line in file.readlines(): + gradio_auth_creds += [x.strip() for x in line.split(',') if x.strip()] - app, _local_url, _share_url = shared.demo.launch( - share=cmd_opts.share, - server_name=server_name, - server_port=cmd_opts.port, - ssl_keyfile=cmd_opts.tls_keyfile, - ssl_certfile=cmd_opts.tls_certfile, - debug=cmd_opts.gradio_debug, - auth=[tuple(cred.split(':')) for cred in gradio_auth_creds] if gradio_auth_creds else None, - inbrowser=cmd_opts.autolaunch, - prevent_thread_lock=True, - favicon_path='automatic.ico', - ) - for dep in shared.demo.dependencies: - dep['show_progress'] = False # disable gradio css animation on component update + app, local_url, share_url = shared.demo.launch( + share=cmd_opts.share, + server_name=server_name, + server_port=cmd_opts.port, + ssl_keyfile=cmd_opts.tls_keyfile, + ssl_certfile=cmd_opts.tls_certfile, + debug=cmd_opts.gradio_debug, + auth=[tuple(cred.split(':')) for cred in gradio_auth_creds] if gradio_auth_creds else None, + inbrowser=cmd_opts.autolaunch, + prevent_thread_lock=True + ) + # after initial launch, disable --autolaunch for subsequent restarts + cmd_opts.autolaunch = False - # app is instance of FastAPI server - # shared.demo.server is instance of gradio class which inherits from uvicorn.Server - # shared.demo.config is instance of uvicorn.Config - # shared.demo.app is instance of ASGIApp + startup_timer.record("gradio launch") - cmd_opts.autolaunch = False + # gradio uses a very open CORS policy via app.user_middleware, which makes it possible for + # an attacker to trick the user into opening a malicious HTML page, which makes a request to the + # running web ui and do whatever the attacker wants, including installing an extension and + # running its code. We disable this here. Suggested by RyotaK. + app.user_middleware = [x for x in app.user_middleware if x.cls.__name__ != 'CORSMiddleware'] - startup_timer.record("gradio launch") + setup_middleware(app) - app.user_middleware = [x for x in app.user_middleware if x.cls.__name__ != 'CORSMiddleware'] + modules.progress.setup_progress_api(app) - setup_middleware(app) + if launch_api: + create_api(app) - modules.progress.setup_progress_api(app) + ui_extra_networks.add_pages_to_demo(app) - if launch_api: - create_api(app) + modules.script_callbacks.app_started_callback(shared.demo, app) + startup_timer.record("scripts app_started_callback") - ui_extra_networks.add_pages_to_demo(app) + print(f"Startup time: {startup_timer.summary()}.") - modules.script_callbacks.app_started_callback(shared.demo, app) - startup_timer.record("scripts app_started_callback") + wait_on_server(shared.demo) + print('Restarting UI...') - load_model() + startup_timer.reset() - print(f"Startup time: {startup_timer.summary()}.") + sd_samplers.set_samplers() - while True: - time.sleep(0.1) + modules.script_callbacks.script_unloaded_callback() + extensions.list_extensions() + startup_timer.record("list extensions") + + localization.list_localizations(cmd_opts.localizations_dir) + + modelloader.forbid_loaded_nonbuiltin_upscalers() + modules.scripts.reload_scripts() + startup_timer.record("load scripts") + + modules.script_callbacks.model_loaded_callback(shared.sd_model) + startup_timer.record("model loaded callback") + + modelloader.load_upscalers() + startup_timer.record("load upscalers") + + for module in [module for name, module in sys.modules.items() if name.startswith("modules.ui")]: + importlib.reload(module) + startup_timer.record("reload script modules") + + modules.sd_models.list_models() + startup_timer.record("list SD models") + + shared.reload_hypernetworks() + startup_timer.record("reload hypernetworks") + + ui_extra_networks.intialize() + ui_extra_networks.register_page(ui_extra_networks_textual_inversion.ExtraNetworksPageTextualInversion()) + ui_extra_networks.register_page(ui_extra_networks_hypernets.ExtraNetworksPageHypernetworks()) + ui_extra_networks.register_page(ui_extra_networks_checkpoints.ExtraNetworksPageCheckpoints()) + + extra_networks.initialize() + extra_networks.register_extra_network(extra_networks_hypernet.ExtraNetworkHypernet()) + startup_timer.record("initialize extra networks") if __name__ == "__main__":