From b9aef33ae860761105bb3669a22c6ff5f8a783d0 Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Thu, 2 Feb 2023 20:26:45 -0500 Subject: [PATCH 1/7] enhance console gui for invokeai-merge - Added modest adaptive behavior; if the screen is wide enough the three checklists of models will be arranged in a horizontal row. - Added color support --- ldm/invoke/merge_diffusers.py | 187 ++++++++++++++++++++++++---------- 1 file changed, 135 insertions(+), 52 deletions(-) diff --git a/ldm/invoke/merge_diffusers.py b/ldm/invoke/merge_diffusers.py index 85eac1077c..00d2599e69 100644 --- a/ldm/invoke/merge_diffusers.py +++ b/ldm/invoke/merge_diffusers.py @@ -5,6 +5,7 @@ used to merge 2-3 models together and create a new InvokeAI-registered diffusion Copyright (c) 2023 Lincoln Stein and the InvokeAI Development Team """ import argparse +import curses import os import sys from argparse import Namespace @@ -12,6 +13,7 @@ from pathlib import Path from typing import List, Union import npyscreen +import warnings from diffusers import DiffusionPipeline from omegaconf import OmegaConf @@ -26,7 +28,6 @@ from ldm.invoke.model_manager import ModelManager DEST_MERGED_MODEL_DIR = "merged_models" - def merge_diffusion_models( model_ids_or_paths: List[Union[str, Path]], alpha: float = 0.5, @@ -185,6 +186,8 @@ class mergeModelsForm(npyscreen.FormMultiPageAction): def __init__(self, parentApp, name): self.parentApp = parentApp + self.ALLOW_RESIZE=True + self.FIX_MINIMUM_SIZE_WHEN_CREATED=False super().__init__(parentApp, name) @property @@ -195,29 +198,94 @@ class mergeModelsForm(npyscreen.FormMultiPageAction): self.parentApp.setNextForm(None) def create(self): + window_height,window_width=curses.initscr().getmaxyx() + self.model_names = self.get_model_names() - + max_width = max([len(x) for x in self.model_names]) + max_width += 6 + horizontal_layout = max_width*3 < window_width + self.add_widget_intelligent( - npyscreen.FixedText, name="Select up to three models to merge", value="" + npyscreen.FixedText, + color='CONTROL', + value=f"Select two models to merge and optionally a third.", + editable=False, ) - self.models = self.add_widget_intelligent( - npyscreen.TitleMultiSelect, - name="Select two to three models to merge:", + self.add_widget_intelligent( + npyscreen.FixedText, + color='CONTROL', + value=f"Use up and down arrows to move, to select an item, and to move from one field to the next.", + editable=False, + ) + self.add_widget_intelligent( + npyscreen.FixedText, + value='MODEL 1', + color='GOOD', + editable=False, + rely=4 if horizontal_layout else None, + ) + self.model1 = self.add_widget_intelligent( + npyscreen.SelectOne, values=self.model_names, - value=None, - max_height=len(self.model_names) + 1, + value=0, + max_height=len(self.model_names), + max_width=max_width, + scroll_exit=True, + rely=5, + ) + self.add_widget_intelligent( + npyscreen.FixedText, + value='MODEL 2', + color='GOOD', + editable=False, + relx=max_width+3 if horizontal_layout else None, + rely=4 if horizontal_layout else None, + ) + self.model2 = self.add_widget_intelligent( + npyscreen.SelectOne, + name='(2)', + values=self.model_names, + value=1, + max_height=len(self.model_names), + max_width=max_width, + relx=max_width+3 if horizontal_layout else None, + rely=5 if horizontal_layout else None, scroll_exit=True, ) - self.models.when_value_edited = self.models_changed + self.add_widget_intelligent( + npyscreen.FixedText, + value='MODEL 3', + color='GOOD', + editable=False, + relx=max_width*2+3 if horizontal_layout else None, + rely=4 if horizontal_layout else None, + ) + models_plus_none = self.model_names.copy() + models_plus_none.insert(0,'None') + self.model3 = self.add_widget_intelligent( + npyscreen.SelectOne, + name='(3)', + values=models_plus_none, + value=0, + max_height=len(self.model_names)+1, + max_width=max_width, + scroll_exit=True, + relx=max_width*2+3 if horizontal_layout else None, + rely=5 if horizontal_layout else None, + ) + for m in [self.model1,self.model2,self.model3]: + m.when_value_edited = self.models_changed self.merged_model_name = self.add_widget_intelligent( npyscreen.TitleText, name="Name for merged model:", + labelColor='CONTROL', value="", scroll_exit=True, ) self.force = self.add_widget_intelligent( npyscreen.Checkbox, name="Force merge of incompatible models", + labelColor='CONTROL', value=False, scroll_exit=True, ) @@ -226,6 +294,7 @@ class mergeModelsForm(npyscreen.FormMultiPageAction): name="Merge Method:", values=self.interpolations, value=0, + labelColor='CONTROL', max_height=len(self.interpolations) + 1, scroll_exit=True, ) @@ -236,47 +305,53 @@ class mergeModelsForm(npyscreen.FormMultiPageAction): step=0.05, lowest=0, value=0.5, + labelColor='CONTROL', scroll_exit=True, ) - self.models.editing = True + self.model1.editing = True def models_changed(self): - model_names = self.models.values - selected_models = self.models.value - if len(selected_models) > 3: - npyscreen.notify_confirm( - "Too many models selected for merging. Select two to three." - ) - return - elif len(selected_models) > 2: - self.merge_method.values = ["add_difference"] - self.merge_method.value = 0 + models = self.model1.values + selected_model1 = self.model1.value[0] + selected_model2 = self.model2.value[0] + selected_model3 = self.model3.value[0] + merged_model_name = f'{models[selected_model1]}+{models[selected_model2]}' + self.merged_model_name.value = merged_model_name + + if selected_model3 > 0: + self.merge_method.values=['add_difference'], + self.merged_model_name.value += f'+{models[selected_model3]}' else: - self.merge_method.values = self.interpolations - self.merged_model_name.value = "+".join( - [model_names[x] for x in selected_models] - ) + self.merge_method.values=self.interpolations + self.merge_method.value=0 def on_ok(self): if self.validate_field_values() and self.check_for_overwrite(): self.parentApp.setNextForm(None) self.editing = False self.parentApp.merge_arguments = self.marshall_arguments() - npyscreen.notify("Starting the merge...") + npyscreen.notify('Starting the merge...') else: self.editing = True def on_cancel(self): sys.exit(0) - def marshall_arguments(self) -> dict: - models = [self.models.values[x] for x in self.models.value] + def marshall_arguments(self)->dict: + model_names = self.model_names + models = [ + model_names[self.model1.value[0]], + model_names[self.model2.value[0]], + ] + if self.model3.value[0] > 0: + models.append(model_names[self.model3.value[0]-1]) + args = dict( models=models, - alpha=self.alpha.value, - interp=self.interpolations[self.merge_method.value[0]], - force=self.force.value, - merged_model_name=self.merged_model_name.value, + alpha = self.alpha.value, + interp = self.interpolations[self.merge_method.value[0]], + force = self.force.value, + merged_model_name = self.merged_model_name.value, ) return args @@ -289,15 +364,18 @@ class mergeModelsForm(npyscreen.FormMultiPageAction): f"The chosen merged model destination, {model_out}, is already in use. Overwrite?" ) - def validate_field_values(self) -> bool: + def validate_field_values(self)->bool: bad_fields = [] - selected_models = self.models.value - if len(selected_models) < 2 or len(selected_models) > 3: - bad_fields.append("Please select two or three models to merge.") + model_names = self.model_names + selected_models = set((model_names[self.model1.value[0]],model_names[self.model2.value[0]])) + if self.model3.value[0] > 0: + selected_models.add(model_names[self.model3.value[0]-1]) + if len(selected_models) < 2: + bad_fields.append(f'Please select two or three DIFFERENT models to compare. You selected {selected_models}') if len(bad_fields) > 0: - message = "The following problems were detected and must be corrected:" + message = 'The following problems were detected and must be corrected:' for problem in bad_fields: - message += f"\n* {problem}" + message += f'\n* {problem}' npyscreen.notify_confirm(message) return False else: @@ -322,10 +400,9 @@ class Mergeapp(npyscreen.NPSAppManaged): ) # precision doesn't really matter here def onStart(self): - npyscreen.setTheme(npyscreen.Themes.DefaultTheme) + npyscreen.setTheme(npyscreen.Themes.ElegantTheme) self.main = self.addForm("MAIN", mergeModelsForm, name="Merge Models Settings") - def run_gui(args: Namespace): mergeapp = Mergeapp() mergeapp.run() @@ -338,8 +415,8 @@ def run_gui(args: Namespace): def run_cli(args: Namespace): assert args.alpha >= 0 and args.alpha <= 1.0, "alpha must be between 0 and 1" assert ( - len(args.models) >= 1 and len(args.models) <= 3 - ), "provide 2 or 3 models to merge" + args.models and len(args.models) >= 1 and len(args.models) <= 3 + ), "Please provide the --models argument to list 2 to 3 models to merge. Use --help for full usage." if not args.merged_model_name: args.merged_model_name = "+".join(args.models) @@ -353,6 +430,7 @@ def run_cli(args: Namespace): ), f'A model named "{args.merged_model_name}" already exists. Use --clobber to overwrite.' merge_diffusion_models_and_commit(**vars(args)) + print(f'>> Models merged into new model: "{args.merged_model_name}".') def main(): @@ -365,17 +443,22 @@ def main(): ] = cache_dir # because not clear the merge pipeline is honoring cache_dir args.cache_dir = cache_dir - try: - if args.front_end: - run_gui(args) - else: - run_cli(args) - print(f">> Conversion successful. New model is named {args.merged_model_name}") - except Exception as e: - print(f"** An error occurred while merging the pipelines: {str(e)}") - sys.exit(-1) - except KeyboardInterrupt: - sys.exit(-1) + with warnings.catch_warnings(): + warnings.simplefilter('ignore') + try: + if args.front_end: + run_gui(args) + else: + run_cli(args) + print(f'>> Conversion successful.') + except Exception as e: + if str(e).startswith('Not enough space'): + print('** Not enough horizontal space! Try making the window wider, or relaunch with a smaller starting size.') + else: + print(f"** An error occurred while merging the pipelines: {str(e)}") + sys.exit(-1) + except KeyboardInterrupt: + sys.exit(-1) if __name__ == "__main__": main() From 3b43f3a5a1440b1c2d2d9043764bf795f042275a Mon Sep 17 00:00:00 2001 From: Eugene Brodsky Date: Fri, 3 Feb 2023 00:36:26 -0500 Subject: [PATCH 2/7] (installer) fix failure to create venv over an existing venv if reinstalling over an existing installation where the .venv was created with symlinks to system python instead of copies of the python executable, the installer would raise a SameFileError, because it would attempt to copy Python over itself. This fixes the issue. --- installer/installer.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/installer/installer.py b/installer/installer.py index 888ed42b8b..e9d2cc392c 100644 --- a/installer/installer.py +++ b/installer/installer.py @@ -129,7 +129,14 @@ class Installer: else: venv_dir = self.dest / ".venv" - venv.create(venv_dir, with_pip=True) + # Prefer to copy python executables + # so that updates to system python don't break InvokeAI + try: + venv.create(venv_dir, with_pip=True) + # If installing over an existing environment previously created with symlinks, + # the executables will fail to copy. Keep symlinks in that case + except shutil.SameFileError: + venv.create(venv_dir, with_pip=True, symlinks=True) # upgrade pip in Python 3.9 environments if int(platform.python_version_tuple()[1]) == 9: From c38b0b906d976e07cfe438f4383a5945b13e4855 Mon Sep 17 00:00:00 2001 From: Eugene Brodsky Date: Fri, 3 Feb 2023 08:00:05 -0500 Subject: [PATCH 3/7] (config) fix invokeai-configure path handling after manual install --- ldm/invoke/config/configure_invokeai.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ldm/invoke/config/configure_invokeai.py b/ldm/invoke/config/configure_invokeai.py index bb3658005f..1130afc99e 100755 --- a/ldm/invoke/config/configure_invokeai.py +++ b/ldm/invoke/config/configure_invokeai.py @@ -528,8 +528,8 @@ def update_config_file(successfully_downloaded: dict, opt: dict): # this check is ignored if opt.config_file is specified - user is assumed to know what they # are doing if they are passing a custom config file from elsewhere. if config_file is Default_config_file and not config_file.parent.exists(): - configs_src = Path(__file__).parent / "configs" - configs_dest = Path(Globals.root) / "configs" + configs_src = Dataset_path.parent + configs_dest = Default_config_file.parent shutil.copytree(configs_src, configs_dest, dirs_exist_ok=True) yaml = new_config_file_contents(successfully_downloaded, config_file) From 64f9fbda2f4ffd3d009a3b10138c6df76517f307 Mon Sep 17 00:00:00 2001 From: Eugene Brodsky Date: Fri, 3 Feb 2023 08:51:46 -0500 Subject: [PATCH 4/7] (docs) update manual install documentation --- docs/installation/020_INSTALL_MANUAL.md | 155 ++++++++++-------------- 1 file changed, 66 insertions(+), 89 deletions(-) diff --git a/docs/installation/020_INSTALL_MANUAL.md b/docs/installation/020_INSTALL_MANUAL.md index 960624d29e..e4fefde7c5 100644 --- a/docs/installation/020_INSTALL_MANUAL.md +++ b/docs/installation/020_INSTALL_MANUAL.md @@ -14,22 +14,15 @@ title: Installing Manually ## Introduction -You have two choices for manual installation. The [first one](#pip-Install) uses -basic Python virtual environment (`venv`) command and `pip` package manager. The -[second one](#Conda-method) uses Anaconda3 package manager (`conda`). Both -methods require you to enter commands on the terminal, also known as the -"console". - -Note that the `conda` installation method is currently deprecated and will not -be supported at some point in the future. +!!! tip As of InvokeAI v2.3.0 installation using the `conda` package manager +is no longer being supported. It will likely still work, but we are not testing +this installation method. On Windows systems, you are encouraged to install and use the [PowerShell](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-windows?view=powershell-7.3), which provides compatibility with Linux and Mac shells and nice features such as command-line completion. -## pip Install - To install InvokeAI with virtual environments and the PIP package manager, please follow these steps: @@ -50,44 +43,64 @@ please follow these steps: This will create InvokeAI folder where you will follow the rest of the steps. -3. From within the InvokeAI top-level directory, create and activate a virtual - environment named `.venv` and prompt displaying `InvokeAI`: - - ```bash - python -m venv .venv \ - --prompt InvokeAI \ - --upgrade-deps - source .venv/bin/activate - ``` - -4. Make sure that pip is installed in your virtual environment an up to date: - - ```bash - python -m ensurepip \ - --upgrade - python -m pip install \ - --upgrade pip - ``` - -5. Install Package - - ```bash - pip install --use-pep517 . - ``` - -6. Set up the runtime directory - - In this step you will initialize a runtime directory that will contain the - models, model config files, directory for textual inversion embeddings, and - your outputs. This keeps the runtime directory separate from the source code - and aids in updating. - - You may pick any location for this directory using the `--root_dir` option - (abbreviated --root). If you don't pass this option, it will default to - `~/invokeai`. +3. Create a directory of to contain your InvokeAI installation (known as the "runtime" + or "root" directory). This is where your models, configs, and outputs will live + by default. Please keep in mind the disk space requirements - you will need at + least 18GB (as of this writing) for the models and the virtual environment. + From now on we will refer to this directory as `INVOKEAI_ROOT`. This keeps the + runtime directory separate from the source code and aids in updating. ```bash - invokeai-configure --root_dir ~/Programs/invokeai + export INVOKEAI_ROOT="~/invokeai" + mkdir ${INVOKEAI_ROOT} + ``` + +4. From within the InvokeAI top-level directory, create and activate a virtual + environment named `.venv` and prompt displaying `InvokeAI`: + + ```bash + python -m venv ${INVOKEAI_ROOT}/.venv \ + --prompt invokeai \ + --upgrade-deps \ + --copies + source ${INVOKEAI_ROOT}/.venv/bin/activate + ``` + + !!! warning + + You **may** create your virtual environment anywhere on the filesystem. + But IF you choose a location that is *not* inside the `$INVOKEAI_ROOT` directory, + then you must set the `INVOKEAI_ROOT` environment variable in your shell environment, + for example, by editing `~/.bashrc` or `~/.zshrc` files, or setting the Windows environment + variable. Refer to your operating system / shell documentation for the correct way of doing so. + +5. Make sure that pip is installed in your virtual environment an up to date: + + ```bash + python -m pip install --upgrade pip + ``` + +6. Install Package + + ```bash + pip install --use-pep517 . + ``` + + Deactivate and reactivate your runtime directory so that the invokeai-specific commands + become available in the environment + + ``` + deactivate && source ${INVOKEAI_ROOT}/.venv/bin/activate + ``` + +7. Set up the runtime directory + + In this step you will initialize your runtime directory with the downloaded + models, model config files, directory for textual inversion embeddings, and + your outputs. + + ```bash + invokeai-configure --root ${INVOKEAI_ROOT} ``` The script `invokeai-configure` will interactively guide you through the @@ -101,11 +114,6 @@ please follow these steps: If you get an error message about a module not being installed, check that the `invokeai` environment is active and if not, repeat step 5. - Note that `invokeai-configure` and `invokeai` should be installed under your - virtual environment directory and the system should find them on the PATH. - If this isn't working on your system, you can call the scripts directory - using `python scripts/configure_invokeai.py` and `python scripts/invoke.py`. - !!! tip If you have already downloaded the weights file(s) for another Stable @@ -127,19 +135,19 @@ please follow these steps: === "CLI" ```bash - invoke.py --root ~/Programs/invokeai + invokeai --root ~/invokeai ``` === "local Webserver" ```bash - invoke.py --web --root ~/Programs/invokeai + invokeai --web --root ~/invokeai ``` === "Public Webserver" ```bash - invoke.py --web --host 0.0.0.0 --root ~/Programs/invokeai + invokeai --web --host 0.0.0.0 --root ~/invokeai ``` If you choose the run the web interface, point your browser at @@ -147,7 +155,8 @@ please follow these steps: !!! tip - You can permanently set the location of the runtime directory by setting the environment variable INVOKEAI_ROOT to the path of the directory. + You can permanently set the location of the runtime directory by setting the environment variable `INVOKEAI_ROOT` to the path of the directory. As mentioned previously, this is + **required** if your virtual environment is located outside of your runtime directory. 8. Render away! @@ -163,38 +172,6 @@ please follow these steps: then launch `invokeai` command. If you forget to activate the virtual environment you will most likeley receive a `command not found` error. - !!! tip - - Do not move the source code repository after installation. The virtual environment directory has absolute paths in it that get confused if the directory is moved. - -## Creating an "install" version of InvokeAI - -If you wish you can install InvokeAI and all its dependencies in the runtime -directory. This allows you to delete the source code repository and eliminates -the need to provide `--root_dir` at startup time. Note that this method only -works with the PIP method. - -1. Follow the instructions for the PIP install, but in step #2 put the virtual - environment into the runtime directory. For example, assuming the runtime - directory lives in `~/Programs/invokeai`, you'd run: - - ```bash - python -m venv ~/Programs/invokeai - ``` - -2. Now follow steps 3 to 5 in the PIP recipe, ending with the `pip install` - step. - -3. Run one additional step while you are in the source code repository directory - - ``` - pip install --use-pep517 . # note the dot in the end!!! - ``` - -4. That's all! Now, whenever you activate the virtual environment, `invokeai` - will know where to look for the runtime directory without needing a - `--root_dir` argument. In addition, you can now move or delete the source - code repository entirely. - - (Don't move the runtime directory!) + !!! warning + Do not move the runtime directory after installation. The virtual environment has absolute paths in it that get confused if the directory is moved. From 31964c7c4ce6d0c8d9368590c096581eb37150cf Mon Sep 17 00:00:00 2001 From: Eugene Brodsky Date: Fri, 3 Feb 2023 09:01:30 -0500 Subject: [PATCH 5/7] (docs) remove an obsolete manual install doc --- docs/installation/INSTALL_MANUAL.md | 374 ---------------------------- 1 file changed, 374 deletions(-) delete mode 100644 docs/installation/INSTALL_MANUAL.md diff --git a/docs/installation/INSTALL_MANUAL.md b/docs/installation/INSTALL_MANUAL.md deleted file mode 100644 index f45463c535..0000000000 --- a/docs/installation/INSTALL_MANUAL.md +++ /dev/null @@ -1,374 +0,0 @@ ---- -title: Manual Installation ---- - -
-# :fontawesome-brands-linux: Linux | :fontawesome-brands-apple: macOS | :fontawesome-brands-windows: Windows -
- -!!! warning "This is for advanced Users" - - who are already experienced with using conda or pip - -## Introduction - -You have two choices for manual installation, the [first one](#Conda_method) -based on the Anaconda3 package manager (`conda`), and -[a second one](#PIP_method) which uses basic Python virtual environment (`venv`) -commands and the PIP package manager. Both methods require you to enter commands -on the terminal, also known as the "console". - -On Windows systems you are encouraged to install and use the -[Powershell](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-windows?view=powershell-7.3), -which provides compatibility with Linux and Mac shells and nice features such as -command-line completion. - -### Conda method - -1. Check that your system meets the - [hardware requirements](index.md#Hardware_Requirements) and has the - appropriate GPU drivers installed. In particular, if you are a Linux user - with an AMD GPU installed, you may need to install the - [ROCm driver](https://rocmdocs.amd.com/en/latest/Installation_Guide/Installation-Guide.html). - - InvokeAI does not yet support Windows machines with AMD GPUs due to the lack - of ROCm driver support on this platform. - - To confirm that the appropriate drivers are installed, run `nvidia-smi` on - NVIDIA/CUDA systems, and `rocm-smi` on AMD systems. These should return - information about the installed video card. - - Macintosh users with MPS acceleration, or anybody with a CPU-only system, - can skip this step. - -2. You will need to install Anaconda3 and Git if they are not already - available. Use your operating system's preferred package manager, or - download the installers manually. You can find them here: - - - [Anaconda3](https://www.anaconda.com/) - - [git](https://git-scm.com/downloads) - -3. Clone the [InvokeAI](https://github.com/invoke-ai/InvokeAI) source code from - GitHub: - - ```bash - git clone https://github.com/invoke-ai/InvokeAI.git - ``` - - This will create InvokeAI folder where you will follow the rest of the - steps. - -4. Enter the newly-created InvokeAI folder: - - ```bash - cd InvokeAI - ``` - - From this step forward make sure that you are working in the InvokeAI - directory! - -5. Select the appropriate environment file: - - We have created a series of environment files suited for different operating - systems and GPU hardware. They are located in the - `environments-and-requirements` directory: - -
- - | filename | OS | - | :----------------------: | :----------------------------: | - | environment-lin-amd.yml | Linux with an AMD (ROCm) GPU | - | environment-lin-cuda.yml | Linux with an NVIDIA CUDA GPU | - | environment-mac.yml | Macintosh | - | environment-win-cuda.yml | Windows with an NVIDA CUDA GPU | - -
- - Choose the appropriate environment file for your system and link or copy it - to `environment.yml` in InvokeAI's top-level directory. To do so, run - following command from the repository-root: - - !!! Example "" - - === "Macintosh and Linux" - - !!! todo "Replace `xxx` and `yyy` with the appropriate OS and GPU codes as seen in the table above" - - ```bash - ln -sf environments-and-requirements/environment-xxx-yyy.yml environment.yml - ``` - - When this is done, confirm that a file `environment.yml` has been linked in - the InvokeAI root directory and that it points to the correct file in the - `environments-and-requirements`. - - ```bash - ls -la - ``` - - === "Windows" - - !!! todo " Since it requires admin privileges to create links, we will use the copy command to create your `environment.yml`" - - ```cmd - copy environments-and-requirements\environment-win-cuda.yml environment.yml - ``` - - Afterwards verify that the file `environment.yml` has been created, either via the - explorer or by using the command `dir` from the terminal - - ```cmd - dir - ``` - - !!! warning "Do not try to run conda on directly on the subdirectory environments file. This won't work. Instead, copy or link it to the top-level directory as shown." - -6. Create the conda environment: - - ```bash - conda env update - ``` - - This will create a new environment named `invokeai` and install all InvokeAI - dependencies into it. If something goes wrong you should take a look at - [troubleshooting](#troubleshooting). - -7. Activate the `invokeai` environment: - - In order to use the newly created environment you will first need to - activate it - - ```bash - conda activate invokeai - ``` - - Your command-line prompt should change to indicate that `invokeai` is active - by prepending `(invokeai)`. - -8. Pre-Load the model weights files: - - !!! tip - - If you have already downloaded the weights file(s) for another Stable - Diffusion distribution, you may skip this step (by selecting "skip" when - prompted) and configure InvokeAI to use the previously-downloaded files. The - process for this is described in [here](050_INSTALLING_MODELS.md). - - ```bash - python scripts/configure_invokeai.py - ``` - - The script `configure_invokeai.py` will interactively guide you through the - process of downloading and installing the weights files needed for InvokeAI. - Note that the main Stable Diffusion weights file is protected by a license - agreement that you have to agree to. The script will list the steps you need - to take to create an account on the site that hosts the weights files, - accept the agreement, and provide an access token that allows InvokeAI to - legally download and install the weights files. - - If you get an error message about a module not being installed, check that - the `invokeai` environment is active and if not, repeat step 5. - -9. Run the command-line- or the web- interface: - - !!! example "" - - !!! warning "Make sure that the conda environment is activated, which should create `(invokeai)` in front of your prompt!" - - === "CLI" - - ```bash - python scripts/invoke.py - ``` - - === "local Webserver" - - ```bash - python scripts/invoke.py --web - ``` - - === "Public Webserver" - - ```bash - python scripts/invoke.py --web --host 0.0.0.0 - ``` - - If you choose the run the web interface, point your browser at - http://localhost:9090 in order to load the GUI. - -10. Render away! - - Browse the [features](../features/CLI.md) section to learn about all the things you - can do with InvokeAI. - - Note that some GPUs are slow to warm up. In particular, when using an AMD - card with the ROCm driver, you may have to wait for over a minute the first - time you try to generate an image. Fortunately, after the warm up period - rendering will be fast. - -11. Subsequently, to relaunch the script, be sure to run "conda activate - invokeai", enter the `InvokeAI` directory, and then launch the invoke - script. If you forget to activate the 'invokeai' environment, the script - will fail with multiple `ModuleNotFound` errors. - -## Updating to newer versions of the script - -This distribution is changing rapidly. If you used the `git clone` method -(step 5) to download the InvokeAI directory, then to update to the latest and -greatest version, launch the Anaconda window, enter `InvokeAI` and type: - -```bash -git pull -conda env update -python scripts/configure_invokeai.py --no-interactive #optional -``` - -This will bring your local copy into sync with the remote one. The last step may -be needed to take advantage of new features or released models. The -`--no-interactive` flag will prevent the script from prompting you to download -the big Stable Diffusion weights files. - -## pip Install - -To install InvokeAI with only the PIP package manager, please follow these -steps: - -1. Make sure you are using Python 3.9 or higher. The rest of the install - procedure depends on this: - - ```bash - python -V - ``` - -2. Install the `virtualenv` tool if you don't have it already: - - ```bash - pip install virtualenv - ``` - -3. From within the InvokeAI top-level directory, create and activate a virtual - environment named `invokeai`: - - ```bash - virtualenv invokeai - source invokeai/bin/activate - ``` - -4. Run PIP - - ```bash - pip --python invokeai install --use-pep517 . - ``` - ---- - -## Troubleshooting - -Here are some common issues and their suggested solutions. - -### Conda - -#### Conda fails before completing `conda update` - -The usual source of these errors is a package incompatibility. While we have -tried to minimize these, over time packages get updated and sometimes introduce -incompatibilities. - -We suggest that you search -[Issues](https://github.com/invoke-ai/InvokeAI/issues) or the "bugs-and-support" -channel of the [InvokeAI Discord](https://discord.gg/ZmtBAhwWhy). - -You may also try to install the broken packages manually using PIP. To do this, -activate the `invokeai` environment, and run `pip install` with the name and -version of the package that is causing the incompatibility. For example: - -```bash -pip install test-tube==0.7.5 -``` - -You can keep doing this until all requirements are satisfied and the `invoke.py` -script runs without errors. Please report to -[Issues](https://github.com/invoke-ai/InvokeAI/issues) what you were able to do -to work around the problem so that others can benefit from your investigation. - -### Create Conda Environment fails on MacOS - -If conda create environment fails with lmdb error, this is most likely caused by Clang. -Run brew config to see which Clang is installed on your Mac. If Clang isn't installed, that's causing the error. -Start by installing additional XCode command line tools, followed by brew install llvm. - -```bash -xcode-select --install -brew install llvm -``` - -If brew config has Clang installed, update to the latest llvm and try creating the environment again. - -#### `configure_invokeai.py` or `invoke.py` crashes at an early stage - -This is usually due to an incomplete or corrupted Conda install. Make sure you -have linked to the correct environment file and run `conda update` again. - -If the problem persists, a more extreme measure is to clear Conda's caches and -remove the `invokeai` environment: - -```bash -conda deactivate -conda env remove -n invokeai -conda clean -a -conda update -``` - -This removes all cached library files, including ones that may have been -corrupted somehow. (This is not supposed to happen, but does anyway). - -#### `invoke.py` crashes at a later stage - -If the CLI or web site had been working ok, but something unexpected happens -later on during the session, you've encountered a code bug that is probably -unrelated to an install issue. Please search -[Issues](https://github.com/invoke-ai/InvokeAI/issues), file a bug report, or -ask for help on [Discord](https://discord.gg/ZmtBAhwWhy) - -#### My renders are running very slowly - -You may have installed the wrong torch (machine learning) package, and the -system is running on CPU rather than the GPU. To check, look at the log messages -that appear when `invoke.py` is first starting up. One of the earlier lines -should say `Using device type cuda`. On AMD systems, it will also say "cuda", -and on Macintoshes, it should say "mps". If instead the message says it is -running on "cpu", then you may need to install the correct torch library. - -You may be able to fix this by installing a different torch library. Here are -the magic incantations for Conda and PIP. - -!!! todo "For CUDA systems" - - - conda - - ```bash - conda install pytorch torchvision torchaudio pytorch-cuda=11.6 -c pytorch -c nvidia - ``` - - - pip - - ```bash - pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu116 - ``` - -!!! todo "For AMD systems" - - - conda - - ```bash - conda activate invokeai - pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/rocm5.2/ - ``` - - - pip - - ```bash - pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/rocm5.2/ - ``` - -More information and troubleshooting tips can be found at https://pytorch.org. From 4b9be6113d03a5cbc85953d7ed3ccbd1e542f957 Mon Sep 17 00:00:00 2001 From: Eugene Brodsky Date: Fri, 3 Feb 2023 09:01:54 -0500 Subject: [PATCH 6/7] (docs) remove an obsolete symlink to a documentation file --- docs/installation/INSTALL_AUTOMATED.md | 1 - 1 file changed, 1 deletion(-) delete mode 120000 docs/installation/INSTALL_AUTOMATED.md diff --git a/docs/installation/INSTALL_AUTOMATED.md b/docs/installation/INSTALL_AUTOMATED.md deleted file mode 120000 index 1818736494..0000000000 --- a/docs/installation/INSTALL_AUTOMATED.md +++ /dev/null @@ -1 +0,0 @@ -010_INSTALL_AUTOMATED.md \ No newline at end of file From 01a2b8c05bfd73308843152b8b2eefcfb1666356 Mon Sep 17 00:00:00 2001 From: Matthias Wild <40327258+mauwii@users.noreply.github.com> Date: Fri, 3 Feb 2023 23:34:47 +0100 Subject: [PATCH 7/7] Adapt latest changes to Dockerfile (#2478) * remove non maintained Dockerfile * adapt Docker related files to latest changes - also build the frontend when building the image - skip user response if INVOKE_MODEL_RECONFIGURE is set - split INVOKE_MODEL_RECONFIGURE to support more than one argument * rename `docker-build` dir to `docker` * update build-container.yml - rename image to invokeai - add cpu flavor - add metadata to build summary - enable caching - remove build-cloud-img.yml * fix yarn cache path, link copyjob --- .dockerignore | 16 ++--- .github/CODEOWNERS | 2 +- .github/workflows/build-cloud-img.yml | 88 --------------------------- .github/workflows/build-container.yml | 29 +++++++-- docker-build/Dockerfile.cloud | 86 -------------------------- docker-build/Makefile | 44 -------------- docker-build/env.sh | 10 --- {docker-build => docker}/Dockerfile | 72 ++++++++++++++++------ {docker-build => docker}/build.sh | 27 ++++---- docker/env.sh | 35 +++++++++++ {docker-build => docker}/run.sh | 17 +++--- ldm/invoke/CLI.py | 16 +++-- 12 files changed, 154 insertions(+), 288 deletions(-) delete mode 100644 .github/workflows/build-cloud-img.yml delete mode 100644 docker-build/Dockerfile.cloud delete mode 100644 docker-build/Makefile delete mode 100644 docker-build/env.sh rename {docker-build => docker}/Dockerfile (52%) rename {docker-build => docker}/build.sh (59%) create mode 100644 docker/env.sh rename {docker-build => docker}/run.sh (58%) diff --git a/.dockerignore b/.dockerignore index d64ce825dc..3d59dece86 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,18 +1,20 @@ +# use this file as a whitelist * -!assets/caution.png !backend -!frontend/dist +!invokeai !ldm !pyproject.toml !README.md !scripts # Guard against pulling in any models that might exist in the directory tree -**.pt* +**/*.pt* +**/*.ckpt -# unignore configs, but only ignore the custom models.yaml, in case it exists -!configs -configs/models.yaml -configs/models.yaml.orig +# whitelist frontend, but ignore node_modules +invokeai/frontend/node_modules +# ignore python cache **/__pycache__ +**/*.py[cod] +**/*.egg-info diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6648a0279e..9271600ba3 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,4 +4,4 @@ scripts/legacy_api.py @CapableWeb tests/legacy_tests.sh @CapableWeb installer/ @ebr .github/workflows/ @mauwii -docker_build/ @mauwii +docker/ @mauwii diff --git a/.github/workflows/build-cloud-img.yml b/.github/workflows/build-cloud-img.yml deleted file mode 100644 index a866cc20ad..0000000000 --- a/.github/workflows/build-cloud-img.yml +++ /dev/null @@ -1,88 +0,0 @@ -name: Build and push cloud image -on: - workflow_dispatch: - # push: - # branches: - # - main - # tags: - # - v* - # # we will NOT push the image on pull requests, only test buildability. - # pull_request: - # branches: - # - main - -permissions: - contents: read - packages: write - -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} - -jobs: - docker: - if: github.event.pull_request.draft == false - strategy: - fail-fast: false - matrix: - arch: - - x86_64 - # requires resolving a patchmatch issue - # - aarch64 - runs-on: ubuntu-latest - name: ${{ matrix.arch }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - if: matrix.arch == 'aarch64' - - - name: Docker meta - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - # see https://github.com/docker/metadata-action - # will push the following tags: - # :edge - # :main (+ any other branches enabled in the workflow) - # : - # :1.2.3 (for semver tags) - # :1.2 (for semver tags) - # : - tags: | - type=edge,branch=main - type=ref,event=branch - type=ref,event=tag - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=sha - # suffix image tags with architecture - flavor: | - latest=auto - suffix=-${{ matrix.arch }},latest=true - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - # do not login to container registry on PRs - - if: github.event_name != 'pull_request' - name: Docker login - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push cloud image - uses: docker/build-push-action@v3 - with: - context: . - file: docker-build/Dockerfile.cloud - platforms: Linux/${{ matrix.arch }} - # do not push the image on PRs - push: false - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/build-container.yml b/.github/workflows/build-container.yml index 84c0b66f9b..6612dff2d3 100644 --- a/.github/workflows/build-container.yml +++ b/.github/workflows/build-container.yml @@ -15,14 +15,19 @@ jobs: flavor: - amd - cuda + - cpu include: - flavor: amd pip-extra-index-url: 'https://download.pytorch.org/whl/rocm5.2' - dockerfile: docker-build/Dockerfile + dockerfile: docker/Dockerfile platforms: linux/amd64,linux/arm64 - flavor: cuda pip-extra-index-url: '' - dockerfile: docker-build/Dockerfile + dockerfile: docker/Dockerfile + platforms: linux/amd64,linux/arm64 + - flavor: cpu + pip-extra-index-url: 'https://download.pytorch.org/whl/cpu' + dockerfile: docker/Dockerfile platforms: linux/amd64,linux/arm64 runs-on: ubuntu-latest name: ${{ matrix.flavor }} @@ -34,7 +39,8 @@ jobs: id: meta uses: docker/metadata-action@v4 with: - images: ghcr.io/${{ github.repository }}-${{ matrix.flavor }} + github-token: ${{ secrets.GITHUB_TOKEN }} + images: ghcr.io/${{ github.repository }} tags: | type=ref,event=branch type=ref,event=tag @@ -43,7 +49,8 @@ jobs: type=semver,pattern={{major}} type=sha flavor: | - latest=true + latest=${{ matrix.flavor == 'cuda' && github.ref == 'refs/heads/main' }} + suffix=${{ matrix.flavor }},onlatest=false - name: Set up QEMU uses: docker/setup-qemu-action@v2 @@ -69,5 +76,15 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} build-args: PIP_EXTRA_INDEX_URL=${{ matrix.pip-extra-index-url }} - # cache-from: type=gha - # cache-to: type=gha,mode=max + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Output image, digest and metadata to summary + run: | + { + echo imageid: "${{ steps.docker_build.outputs.imageid }}" + echo digest: "${{ steps.docker_build.outputs.digest }}" + echo labels: "${{ steps.meta.outputs.labels }}" + echo tags: "${{ steps.meta.outputs.tags }}" + echo version: "${{ steps.meta.outputs.version }}" + } >> "$GITHUB_STEP_SUMMARY" diff --git a/docker-build/Dockerfile.cloud b/docker-build/Dockerfile.cloud deleted file mode 100644 index b40fabdccc..0000000000 --- a/docker-build/Dockerfile.cloud +++ /dev/null @@ -1,86 +0,0 @@ -####################### -#### Builder stage #### - -FROM library/ubuntu:22.04 AS builder - -ARG DEBIAN_FRONTEND=noninteractive -RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - --mount=type=cache,target=/var/lib/apt,sharing=locked \ - apt update && apt-get install -y \ - git \ - libglib2.0-0 \ - libgl1-mesa-glx \ - python3-venv \ - python3-pip \ - build-essential \ - python3-opencv \ - libopencv-dev - -# This is needed for patchmatch support -RUN cd /usr/lib/x86_64-linux-gnu/pkgconfig/ &&\ - ln -sf opencv4.pc opencv.pc - -ARG WORKDIR=/invokeai -WORKDIR ${WORKDIR} - -ENV VIRTUAL_ENV=${WORKDIR}/.venv -ENV PATH="$VIRTUAL_ENV/bin:$PATH" - -RUN --mount=type=cache,target=/root/.cache/pip \ - python3 -m venv ${VIRTUAL_ENV} &&\ - pip install --extra-index-url https://download.pytorch.org/whl/cu116 \ - torch==1.12.0+cu116 \ - torchvision==0.13.0+cu116 &&\ - pip install -e git+https://github.com/invoke-ai/PyPatchMatch@0.1.3#egg=pypatchmatch - -COPY . . -RUN --mount=type=cache,target=/root/.cache/pip \ - cp environments-and-requirements/requirements-lin-cuda.txt requirements.txt && \ - pip install -r requirements.txt &&\ - pip install -e . - - -####################### -#### Runtime stage #### - -FROM library/ubuntu:22.04 as runtime - -ARG DEBIAN_FRONTEND=noninteractive -ENV PYTHONUNBUFFERED=1 -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - --mount=type=cache,target=/var/lib/apt,sharing=locked \ - apt update && apt install -y --no-install-recommends \ - git \ - curl \ - ncdu \ - iotop \ - bzip2 \ - libglib2.0-0 \ - libgl1-mesa-glx \ - python3-venv \ - python3-pip \ - build-essential \ - python3-opencv \ - libopencv-dev &&\ - apt-get clean && apt-get autoclean - -ARG WORKDIR=/invokeai -WORKDIR ${WORKDIR} - -ENV INVOKEAI_ROOT=/mnt/invokeai -ENV VIRTUAL_ENV=${WORKDIR}/.venv -ENV PATH="$VIRTUAL_ENV/bin:$PATH" - -COPY --from=builder ${WORKDIR} ${WORKDIR} -COPY --from=builder /usr/lib/x86_64-linux-gnu/pkgconfig /usr/lib/x86_64-linux-gnu/pkgconfig - -# build patchmatch -RUN python -c "from patchmatch import patch_match" - -## workaround for non-existent initfile when runtime directory is mounted; see #1613 -RUN touch /root/.invokeai - -ENTRYPOINT ["bash"] - -CMD ["-c", "python3 scripts/invoke.py --web --host 0.0.0.0"] diff --git a/docker-build/Makefile b/docker-build/Makefile deleted file mode 100644 index 963caee9e4..0000000000 --- a/docker-build/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -# Directory in the container where the INVOKEAI_ROOT (runtime dir) will be mounted -INVOKEAI_ROOT=/mnt/invokeai -# Host directory to contain the runtime dir. Will be mounted at INVOKEAI_ROOT path in the container -HOST_MOUNT_PATH=${HOME}/invokeai - -IMAGE=local/invokeai:latest - -USER=$(shell id -u) -GROUP=$(shell id -g) - -# All downloaded models, config, etc will end up in ${HOST_MOUNT_PATH} on the host. -# This is consistent with the expected non-Docker behaviour. -# Contents can be moved to a persistent storage and used to prime the cache on another host. - -build: - DOCKER_BUILDKIT=1 docker build -t local/invokeai:latest -f Dockerfile.cloud .. - -configure: - docker run --rm -it --runtime=nvidia --gpus=all \ - -v ${HOST_MOUNT_PATH}:${INVOKEAI_ROOT} \ - -e INVOKEAI_ROOT=${INVOKEAI_ROOT} \ - ${IMAGE} -c "python scripts/configure_invokeai.py" - -# Run the container with the runtime dir mounted and the web server exposed on port 9090 -web: - docker run --rm -it --runtime=nvidia --gpus=all \ - -v ${HOST_MOUNT_PATH}:${INVOKEAI_ROOT} \ - -e INVOKEAI_ROOT=${INVOKEAI_ROOT} \ - -p 9090:9090 \ - ${IMAGE} -c "python scripts/invoke.py --web --host 0.0.0.0" - -# Run the cli with the runtime dir mounted -cli: - docker run --rm -it --runtime=nvidia --gpus=all \ - -v ${HOST_MOUNT_PATH}:${INVOKEAI_ROOT} \ - -e INVOKEAI_ROOT=${INVOKEAI_ROOT} \ - ${IMAGE} -c "python scripts/invoke.py" - -# Run the container with the runtime dir mounted and open a bash shell -shell: - docker run --rm -it --runtime=nvidia --gpus=all \ - -v ${HOST_MOUNT_PATH}:${INVOKEAI_ROOT} ${IMAGE} -- - -.PHONY: build configure web cli shell diff --git a/docker-build/env.sh b/docker-build/env.sh deleted file mode 100644 index 6d2f71f9ec..0000000000 --- a/docker-build/env.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash - -# Variables shared by build.sh and run.sh -REPOSITORY_NAME=${REPOSITORY_NAME:-$(basename "$(git rev-parse --show-toplevel)")} -VOLUMENAME=${VOLUMENAME:-${REPOSITORY_NAME,,}_data} -ARCH=${ARCH:-$(uname -m)} -PLATFORM=${PLATFORM:-Linux/${ARCH}} -CONTAINER_FLAVOR=${CONTAINER_FLAVOR:-cuda} -INVOKEAI_BRANCH=$(git branch --show) -INVOKEAI_TAG=${REPOSITORY_NAME,,}-${CONTAINER_FLAVOR}:${INVOKEAI_TAG:-${INVOKEAI_BRANCH##*/}} diff --git a/docker-build/Dockerfile b/docker/Dockerfile similarity index 52% rename from docker-build/Dockerfile rename to docker/Dockerfile index 909f806915..475f8dc55e 100644 --- a/docker-build/Dockerfile +++ b/docker/Dockerfile @@ -1,8 +1,12 @@ # syntax=docker/dockerfile:1 -FROM python:3.9-slim AS python-base -# use bash -SHELL [ "/bin/bash", "-c" ] +# Maintained by Matthias Wild + +ARG PYTHON_VERSION=3.9 +################## +### base image ### +################## +FROM python:${PYTHON_VERSION}-slim AS python-base # Install necesarry packages RUN \ @@ -17,12 +21,39 @@ RUN \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* -ARG APPDIR=/usr/src/app -ENV APPDIR ${APPDIR} +# set working directory and path +ARG APPDIR=/usr/src +ARG APPNAME=InvokeAI WORKDIR ${APPDIR} +ENV PATH=${APPDIR}/${APPNAME}/bin:$PATH -FROM python-base AS builder +###################### +### build frontend ### +###################### +FROM node:lts as frontend-builder +# Copy Sources +ARG APPDIR=/usr/src +WORKDIR ${APPDIR} +COPY --link . . + +# install dependencies and build frontend +WORKDIR ${APPDIR}/invokeai/frontend +RUN \ + --mount=type=cache,target=/usr/local/share/.cache/yarn/v6 \ + yarn install \ + --prefer-offline \ + --frozen-lockfile \ + --non-interactive \ + --production=false \ + && yarn build + +################################### +### install python dependencies ### +################################### +FROM python-base AS pyproject-builder + +# Install dependencies RUN \ --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ @@ -34,25 +65,28 @@ RUN \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* +# create virtual environment +RUN python3 -m venv "${APPNAME}" \ + --upgrade-deps + # copy sources -COPY --link . . +COPY --from=frontend-builder ${APPDIR} . + +# install pyproject.toml ARG PIP_EXTRA_INDEX_URL ENV PIP_EXTRA_INDEX_URL ${PIP_EXTRA_INDEX_URL} +RUN --mount=type=cache,target=/root/.cache/pip,sharing=locked \ + "${APPDIR}/${APPNAME}/bin/pip" install \ + --use-pep517 \ + . -# install requirements -RUN python3 -m venv invokeai \ - && ${APPDIR}/invokeai/bin/pip \ - install \ - --no-cache-dir \ - --use-pep517 \ - . - +##################### +### runtime image ### +##################### FROM python-base AS runtime # setup environment -COPY --link . . -COPY --from=builder ${APPDIR}/invokeai ${APPDIR}/invokeai -ENV PATH=${APPDIR}/invokeai/bin:$PATH +COPY --from=pyproject-builder ${APPDIR}/${APPNAME} ${APPDIR}/${APPNAME} ENV INVOKEAI_ROOT=/data ENV INVOKE_MODEL_RECONFIGURE="--yes --default_only" @@ -73,6 +107,6 @@ RUN \ && rm -rf /var/lib/apt/lists/* # set Entrypoint and default CMD -ENTRYPOINT [ "invoke" ] +ENTRYPOINT [ "invokeai" ] CMD [ "--web", "--host=0.0.0.0" ] VOLUME [ "/data" ] diff --git a/docker-build/build.sh b/docker/build.sh similarity index 59% rename from docker-build/build.sh rename to docker/build.sh index 02cd279280..4d3fdd85e6 100755 --- a/docker-build/build.sh +++ b/docker/build.sh @@ -2,30 +2,31 @@ set -e # How to use: https://invoke-ai.github.io/InvokeAI/installation/INSTALL_DOCKER/#setup -# # Some possible pip extra-index urls (cuda 11.7 is available without extra url): -# # CUDA 11.6: https://download.pytorch.org/whl/cu116 # ROCm 5.2: https://download.pytorch.org/whl/rocm5.2 # CPU: https://download.pytorch.org/whl/cpu -# # as found on https://pytorch.org/get-started/locally/ -cd "$(dirname "$0")" || exit 1 +SCRIPTDIR=$(dirname "$0") +cd "$SCRIPTDIR" || exit 1 source ./env.sh -DOCKERFILE=${INVOKE_DOCKERFILE:-"./Dockerfile"} +DOCKERFILE=${INVOKE_DOCKERFILE:-Dockerfile} # print the settings echo -e "You are using these values:\n" -echo -e "Dockerfile:\t ${DOCKERFILE}" -echo -e "extra-index-url: ${PIP_EXTRA_INDEX_URL:-none}" -echo -e "Volumename:\t ${VOLUMENAME}" -echo -e "arch:\t\t ${ARCH}" -echo -e "Platform:\t ${PLATFORM}" -echo -e "Invokeai_tag:\t ${INVOKEAI_TAG}\n" +echo -e "Dockerfile: \t${DOCKERFILE}" +echo -e "index-url: \t${PIP_EXTRA_INDEX_URL:-none}" +echo -e "Volumename: \t${VOLUMENAME}" +echo -e "Platform: \t${PLATFORM}" +echo -e "Registry: \t${CONTAINER_REGISTRY}" +echo -e "Repository: \t${CONTAINER_REPOSITORY}" +echo -e "Container Tag: \t${CONTAINER_TAG}" +echo -e "Container Image: ${CONTAINER_IMAGE}\n" +# Create docker volume if [[ -n "$(docker volume ls -f name="${VOLUMENAME}" -q)" ]]; then echo -e "Volume already exists\n" else @@ -36,7 +37,7 @@ fi # Build Container docker build \ --platform="${PLATFORM}" \ - --tag="${INVOKEAI_TAG}" \ - ${PIP_EXTRA_INDEX_URL:+--build-arg=PIP_EXTRA_INDEX_URL="${PIP_EXTRA_INDEX_URL}"} \ + --tag="${CONTAINER_IMAGE}" \ + ${PIP_EXTRA_INDEX_URL:+--build-arg="PIP_EXTRA_INDEX_URL=${PIP_EXTRA_INDEX_URL}"} \ --file="${DOCKERFILE}" \ .. diff --git a/docker/env.sh b/docker/env.sh new file mode 100644 index 0000000000..eab878577e --- /dev/null +++ b/docker/env.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +if [[ -z "$PIP_EXTRA_INDEX_URL" ]]; then + # Decide which container flavor to build if not specified + if [[ -z "$CONTAINER_FLAVOR" ]]; then + # Check for CUDA and ROCm + CUDA_AVAILABLE=$(python -c "import torch;print(torch.cuda.is_available())") + ROCM_AVAILABLE=$(python -c "import torch;print(torch.version.hip is not None)") + if [[ "$(uname -s)" != "Darwin" && "${CUDA_AVAILABLE}" == "True" ]]; then + CONTAINER_FLAVOR=cuda + elif [[ "$(uname -s)" != "Darwin" && "${ROCM_AVAILABLE}" == "True" ]]; then + CONTAINER_FLAVOR="rocm" + else + CONTAINER_FLAVOR="cpu" + fi + fi + # Set PIP_EXTRA_INDEX_URL based on container flavor + if [[ "$CONTAINER_FLAVOR" == "rocm" ]]; then + PIP_EXTRA_INDEX_URL="${PIP_EXTRA_INDEX_URL-"https://download.pytorch.org/whl/rocm"}" + elif CONTAINER_FLAVOR=cpu; then + PIP_EXTRA_INDEX_URL="${PIP_EXTRA_INDEX_URL-"https://download.pytorch.org/whl/cpu"}" + fi +fi + +# Variables shared by build.sh and run.sh +REPOSITORY_NAME="${REPOSITORY_NAME-$(basename "$(git rev-parse --show-toplevel)")}" +VOLUMENAME="${VOLUMENAME-"${REPOSITORY_NAME,,}_data"}" +ARCH="${ARCH-$(uname -m)}" +PLATFORM="${PLATFORM-Linux/${ARCH}}" +INVOKEAI_BRANCH="${INVOKEAI_BRANCH-$(git branch --show)}" +CONTAINER_REGISTRY="${CONTAINER_REGISTRY-"ghcr.io"}" +CONTAINER_REPOSITORY="${CONTAINER_REPOSITORY-"$(whoami)/${REPOSITORY_NAME}"}" +CONTAINER_TAG="${CONTAINER_TAG-"${INVOKEAI_BRANCH##*/}-${CONTAINER_FLAVOR}"}" +CONTAINER_IMAGE="${CONTAINER_REGISTRY}/${CONTAINER_REPOSITORY}:${CONTAINER_TAG}" +CONTAINER_IMAGE="${CONTAINER_IMAGE,,}" diff --git a/docker-build/run.sh b/docker/run.sh similarity index 58% rename from docker-build/run.sh rename to docker/run.sh index 23d85d9790..5593faaa3e 100755 --- a/docker-build/run.sh +++ b/docker/run.sh @@ -4,27 +4,28 @@ set -e # How to use: https://invoke-ai.github.io/InvokeAI/installation/INSTALL_DOCKER/#run-the-container # IMPORTANT: You need to have a token on huggingface.co to be able to download the checkpoints!!! -cd "$(dirname "$0")" || exit 1 +SCRIPTDIR=$(dirname "$0") +cd "$SCRIPTDIR" || exit 1 source ./env.sh echo -e "You are using these values:\n" echo -e "Volumename:\t${VOLUMENAME}" -echo -e "Invokeai_tag:\t${INVOKEAI_TAG}" +echo -e "Invokeai_tag:\t${CONTAINER_IMAGE}" echo -e "local Models:\t${MODELSPATH:-unset}\n" docker run \ --interactive \ --tty \ --rm \ - --platform="$PLATFORM" \ + --platform="${PLATFORM}" \ --name="${REPOSITORY_NAME,,}" \ --hostname="${REPOSITORY_NAME,,}" \ - --mount=source="$VOLUMENAME",target=/data \ + --mount=source="${VOLUMENAME}",target=/data \ ${MODELSPATH:+-u "$(id -u):$(id -g)"} \ - ${MODELSPATH:+--mount=type=bind,source=${MODELSPATH},target=/data/models} \ - ${HUGGING_FACE_HUB_TOKEN:+--env=HUGGING_FACE_HUB_TOKEN=${HUGGING_FACE_HUB_TOKEN}} \ + ${MODELSPATH:+--mount="type=bind,source=${MODELSPATH},target=/data/models"} \ + ${HUGGING_FACE_HUB_TOKEN:+--env="HUGGING_FACE_HUB_TOKEN=${HUGGING_FACE_HUB_TOKEN}"} \ --publish=9090:9090 \ --cap-add=sys_nice \ - ${GPU_FLAGS:+--gpus=${GPU_FLAGS}} \ - "$INVOKEAI_TAG" ${1:+$@} + ${GPU_FLAGS:+--gpus="${GPU_FLAGS}"} \ + "${CONTAINER_IMAGE}" ${1:+$@} diff --git a/ldm/invoke/CLI.py b/ldm/invoke/CLI.py index b433e063d1..2d673a9112 100644 --- a/ldm/invoke/CLI.py +++ b/ldm/invoke/CLI.py @@ -50,7 +50,7 @@ def main(): Globals.internet_available = args.internet_available and check_internet() Globals.disable_xformers = not args.xformers Globals.ckpt_convert = args.ckpt_convert - + print(f'>> Internet connectivity is {Globals.internet_available}') if not args.conf: @@ -1111,9 +1111,13 @@ def write_commands(opt, file_path:str, outfilepath:str): def report_model_error(opt:Namespace, e:Exception): print(f'** An error occurred while attempting to initialize the model: "{str(e)}"') print('** This can be caused by a missing or corrupted models file, and can sometimes be fixed by (re)installing the models.') - response = input('Do you want to run invokeai-configure script to select and/or reinstall models? [y] ') - if response.startswith(('n','N')): - return + yes_to_all = os.environ.get('INVOKE_MODEL_RECONFIGURE') + if yes_to_all: + print('** Reconfiguration is being forced by environment variable INVOKE_MODEL_RECONFIGURE') + else: + response = input('Do you want to run invokeai-configure script to select and/or reinstall models? [y] ') + if response.startswith(('n', 'N')): + return print('invokeai-configure is launching....\n') @@ -1121,13 +1125,13 @@ def report_model_error(opt:Namespace, e:Exception): # only the arguments accepted by the configuration script are parsed root_dir = ["--root", opt.root_dir] if opt.root_dir is not None else [] config = ["--config", opt.conf] if opt.conf is not None else [] - yes_to_all = os.environ.get('INVOKE_MODEL_RECONFIGURE') previous_args = sys.argv sys.argv = [ 'invokeai-configure' ] sys.argv.extend(root_dir) sys.argv.extend(config) if yes_to_all is not None: - sys.argv.append(yes_to_all) + for arg in yes_to_all.split(): + sys.argv.append(arg) from ldm.invoke.config import configure_invokeai configure_invokeai.main()