Merge branch 'main' into patch-1

This commit is contained in:
Lincoln Stein 2023-02-05 21:43:13 -05:00 committed by GitHub
commit 719a5de506
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 111 additions and 85 deletions

View File

@ -47,7 +47,7 @@ jobs:
type=semver,pattern={{version}} type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}} type=semver,pattern={{major}}
type=raw,value='sha'-{{sha}}-${{ matrix.flavor}} type=sha,enable=true,prefix=sha-,suffix=${{ matrix.flavor}},format=short
type=raw,value={{branch}}-${{ matrix.flavor }} type=raw,value={{branch}}-${{ matrix.flavor }}
flavor: | flavor: |
latest=${{ matrix.flavor == 'cuda' && github.ref == 'refs/heads/main' }} latest=${{ matrix.flavor == 'cuda' && github.ref == 'refs/heads/main' }}

View File

@ -1,28 +1,20 @@
# Stable Diffusion Web UI # InvokeAI UI dev setup
## Run The UI is in `invokeai/frontend`.
- `python scripts/dream.py --web` serves both frontend and backend at ## Environment set up
http://localhost:9090
## Evironment Install [node](https://nodejs.org/en/download/) (includes npm) and
Install [node](https://nodejs.org/en/download/) (includes npm) and optionally
[yarn](https://yarnpkg.com/getting-started/install). [yarn](https://yarnpkg.com/getting-started/install).
From `frontend/` run `npm install` / `yarn install` to install the frontend From `invokeai/frontend/` run `yarn install` to get everything set up.
packages.
## Dev ## Dev
1. From `frontend/`, run `npm dev` / `yarn dev` to start the dev server. 1. Start the dev server: `yarn dev`
2. Run `python scripts/dream.py --web`. 2. Start the InvokeAI UI per usual: `invokeai --web`
3. Navigate to the dev server address e.g. `http://localhost:5173/`. 3. Point your browser to the dev server address e.g. `http://localhost:5173/`
To build for dev: `npm build-dev` / `yarn build-dev` To build for dev: `yarn build-dev`
To build for production: `npm build` / `yarn build` To build for production: `yarn build`
## TODO
- Search repo for "TODO"

View File

@ -15,20 +15,18 @@ from pathlib import Path
from typing import List, Union from typing import List, Union
import npyscreen import npyscreen
from diffusers import DiffusionPipeline, logging as dlogging from diffusers import DiffusionPipeline
from diffusers import logging as dlogging
from npyscreen import widget
from omegaconf import OmegaConf from omegaconf import OmegaConf
from ldm.invoke.globals import ( from ldm.invoke.globals import (Globals, global_cache_dir, global_config_file,
Globals, global_models_dir, global_set_root)
global_cache_dir,
global_config_file,
global_models_dir,
global_set_root,
)
from ldm.invoke.model_manager import ModelManager from ldm.invoke.model_manager import ModelManager
DEST_MERGED_MODEL_DIR = "merged_models" DEST_MERGED_MODEL_DIR = "merged_models"
def merge_diffusion_models( def merge_diffusion_models(
model_ids_or_paths: List[Union[str, Path]], model_ids_or_paths: List[Union[str, Path]],
alpha: float = 0.5, alpha: float = 0.5,
@ -48,10 +46,10 @@ def merge_diffusion_models(
cache_dir, resume_download, force_download, proxies, local_files_only, use_auth_token, revision, torch_dtype, device_map cache_dir, resume_download, force_download, proxies, local_files_only, use_auth_token, revision, torch_dtype, device_map
""" """
with warnings.catch_warnings(): with warnings.catch_warnings():
warnings.simplefilter('ignore') warnings.simplefilter("ignore")
verbosity = dlogging.get_verbosity() verbosity = dlogging.get_verbosity()
dlogging.set_verbosity_error() dlogging.set_verbosity_error()
pipe = DiffusionPipeline.from_pretrained( pipe = DiffusionPipeline.from_pretrained(
model_ids_or_paths[0], model_ids_or_paths[0],
cache_dir=kwargs.get("cache_dir", global_cache_dir()), cache_dir=kwargs.get("cache_dir", global_cache_dir()),
@ -188,13 +186,12 @@ class FloatTitleSlider(npyscreen.TitleText):
class mergeModelsForm(npyscreen.FormMultiPageAction): class mergeModelsForm(npyscreen.FormMultiPageAction):
interpolations = ["weighted_sum", "sigmoid", "inv_sigmoid", "add_difference"] interpolations = ["weighted_sum", "sigmoid", "inv_sigmoid", "add_difference"]
def __init__(self, parentApp, name): def __init__(self, parentApp, name):
self.parentApp = parentApp self.parentApp = parentApp
self.ALLOW_RESIZE=True self.ALLOW_RESIZE = True
self.FIX_MINIMUM_SIZE_WHEN_CREATED=False self.FIX_MINIMUM_SIZE_WHEN_CREATED = False
super().__init__(parentApp, name) super().__init__(parentApp, name)
@property @property
@ -205,29 +202,29 @@ class mergeModelsForm(npyscreen.FormMultiPageAction):
self.parentApp.setNextForm(None) self.parentApp.setNextForm(None)
def create(self): def create(self):
window_height,window_width=curses.initscr().getmaxyx() window_height, window_width = curses.initscr().getmaxyx()
self.model_names = self.get_model_names() self.model_names = self.get_model_names()
max_width = max([len(x) for x in self.model_names]) max_width = max([len(x) for x in self.model_names])
max_width += 6 max_width += 6
horizontal_layout = max_width*3 < window_width horizontal_layout = max_width * 3 < window_width
self.add_widget_intelligent( self.add_widget_intelligent(
npyscreen.FixedText, npyscreen.FixedText,
color='CONTROL', color="CONTROL",
value=f"Select two models to merge and optionally a third.", value=f"Select two models to merge and optionally a third.",
editable=False, editable=False,
) )
self.add_widget_intelligent( self.add_widget_intelligent(
npyscreen.FixedText, npyscreen.FixedText,
color='CONTROL', color="CONTROL",
value=f"Use up and down arrows to move, <space> to select an item, <tab> and <shift-tab> to move from one field to the next.", value=f"Use up and down arrows to move, <space> to select an item, <tab> and <shift-tab> to move from one field to the next.",
editable=False, editable=False,
) )
self.add_widget_intelligent( self.add_widget_intelligent(
npyscreen.FixedText, npyscreen.FixedText,
value='MODEL 1', value="MODEL 1",
color='GOOD', color="GOOD",
editable=False, editable=False,
rely=4 if horizontal_layout else None, rely=4 if horizontal_layout else None,
) )
@ -242,57 +239,57 @@ class mergeModelsForm(npyscreen.FormMultiPageAction):
) )
self.add_widget_intelligent( self.add_widget_intelligent(
npyscreen.FixedText, npyscreen.FixedText,
value='MODEL 2', value="MODEL 2",
color='GOOD', color="GOOD",
editable=False, editable=False,
relx=max_width+3 if horizontal_layout else None, relx=max_width + 3 if horizontal_layout else None,
rely=4 if horizontal_layout else None, rely=4 if horizontal_layout else None,
) )
self.model2 = self.add_widget_intelligent( self.model2 = self.add_widget_intelligent(
npyscreen.SelectOne, npyscreen.SelectOne,
name='(2)', name="(2)",
values=self.model_names, values=self.model_names,
value=1, value=1,
max_height=len(self.model_names), max_height=len(self.model_names),
max_width=max_width, max_width=max_width,
relx=max_width+3 if horizontal_layout else None, relx=max_width + 3 if horizontal_layout else None,
rely=5 if horizontal_layout else None, rely=5 if horizontal_layout else None,
scroll_exit=True, scroll_exit=True,
) )
self.add_widget_intelligent( self.add_widget_intelligent(
npyscreen.FixedText, npyscreen.FixedText,
value='MODEL 3', value="MODEL 3",
color='GOOD', color="GOOD",
editable=False, editable=False,
relx=max_width*2+3 if horizontal_layout else None, relx=max_width * 2 + 3 if horizontal_layout else None,
rely=4 if horizontal_layout else None, rely=4 if horizontal_layout else None,
) )
models_plus_none = self.model_names.copy() models_plus_none = self.model_names.copy()
models_plus_none.insert(0,'None') models_plus_none.insert(0, "None")
self.model3 = self.add_widget_intelligent( self.model3 = self.add_widget_intelligent(
npyscreen.SelectOne, npyscreen.SelectOne,
name='(3)', name="(3)",
values=models_plus_none, values=models_plus_none,
value=0, value=0,
max_height=len(self.model_names)+1, max_height=len(self.model_names) + 1,
max_width=max_width, max_width=max_width,
scroll_exit=True, scroll_exit=True,
relx=max_width*2+3 if horizontal_layout else None, relx=max_width * 2 + 3 if horizontal_layout else None,
rely=5 if horizontal_layout else None, rely=5 if horizontal_layout else None,
) )
for m in [self.model1,self.model2,self.model3]: for m in [self.model1, self.model2, self.model3]:
m.when_value_edited = self.models_changed m.when_value_edited = self.models_changed
self.merged_model_name = self.add_widget_intelligent( self.merged_model_name = self.add_widget_intelligent(
npyscreen.TitleText, npyscreen.TitleText,
name="Name for merged model:", name="Name for merged model:",
labelColor='CONTROL', labelColor="CONTROL",
value="", value="",
scroll_exit=True, scroll_exit=True,
) )
self.force = self.add_widget_intelligent( self.force = self.add_widget_intelligent(
npyscreen.Checkbox, npyscreen.Checkbox,
name="Force merge of incompatible models", name="Force merge of incompatible models",
labelColor='CONTROL', labelColor="CONTROL",
value=False, value=False,
scroll_exit=True, scroll_exit=True,
) )
@ -301,7 +298,7 @@ class mergeModelsForm(npyscreen.FormMultiPageAction):
name="Merge Method:", name="Merge Method:",
values=self.interpolations, values=self.interpolations,
value=0, value=0,
labelColor='CONTROL', labelColor="CONTROL",
max_height=len(self.interpolations) + 1, max_height=len(self.interpolations) + 1,
scroll_exit=True, scroll_exit=True,
) )
@ -312,7 +309,7 @@ class mergeModelsForm(npyscreen.FormMultiPageAction):
step=0.05, step=0.05,
lowest=0, lowest=0,
value=0.5, value=0.5,
labelColor='CONTROL', labelColor="CONTROL",
scroll_exit=True, scroll_exit=True,
) )
self.model1.editing = True self.model1.editing = True
@ -322,43 +319,43 @@ class mergeModelsForm(npyscreen.FormMultiPageAction):
selected_model1 = self.model1.value[0] selected_model1 = self.model1.value[0]
selected_model2 = self.model2.value[0] selected_model2 = self.model2.value[0]
selected_model3 = self.model3.value[0] selected_model3 = self.model3.value[0]
merged_model_name = f'{models[selected_model1]}+{models[selected_model2]}' merged_model_name = f"{models[selected_model1]}+{models[selected_model2]}"
self.merged_model_name.value = merged_model_name self.merged_model_name.value = merged_model_name
if selected_model3 > 0: if selected_model3 > 0:
self.merge_method.values=['add_difference'], self.merge_method.values = (["add_difference"],)
self.merged_model_name.value += f'+{models[selected_model3]}' self.merged_model_name.value += f"+{models[selected_model3]}"
else: else:
self.merge_method.values=self.interpolations self.merge_method.values = self.interpolations
self.merge_method.value=0 self.merge_method.value = 0
def on_ok(self): def on_ok(self):
if self.validate_field_values() and self.check_for_overwrite(): if self.validate_field_values() and self.check_for_overwrite():
self.parentApp.setNextForm(None) self.parentApp.setNextForm(None)
self.editing = False self.editing = False
self.parentApp.merge_arguments = self.marshall_arguments() self.parentApp.merge_arguments = self.marshall_arguments()
npyscreen.notify('Starting the merge...') npyscreen.notify("Starting the merge...")
else: else:
self.editing = True self.editing = True
def on_cancel(self): def on_cancel(self):
sys.exit(0) sys.exit(0)
def marshall_arguments(self)->dict: def marshall_arguments(self) -> dict:
model_names = self.model_names model_names = self.model_names
models = [ models = [
model_names[self.model1.value[0]], model_names[self.model1.value[0]],
model_names[self.model2.value[0]], model_names[self.model2.value[0]],
] ]
if self.model3.value[0] > 0: if self.model3.value[0] > 0:
models.append(model_names[self.model3.value[0]-1]) models.append(model_names[self.model3.value[0] - 1])
args = dict( args = dict(
models=models, models=models,
alpha = self.alpha.value, alpha=self.alpha.value,
interp = self.interpolations[self.merge_method.value[0]], interp=self.interpolations[self.merge_method.value[0]],
force = self.force.value, force=self.force.value,
merged_model_name = self.merged_model_name.value, merged_model_name=self.merged_model_name.value,
) )
return args return args
@ -371,18 +368,22 @@ class mergeModelsForm(npyscreen.FormMultiPageAction):
f"The chosen merged model destination, {model_out}, is already in use. Overwrite?" 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 = [] bad_fields = []
model_names = self.model_names model_names = self.model_names
selected_models = set((model_names[self.model1.value[0]],model_names[self.model2.value[0]])) selected_models = set(
(model_names[self.model1.value[0]], model_names[self.model2.value[0]])
)
if self.model3.value[0] > 0: if self.model3.value[0] > 0:
selected_models.add(model_names[self.model3.value[0]-1]) selected_models.add(model_names[self.model3.value[0] - 1])
if len(selected_models) < 2: if len(selected_models) < 2:
bad_fields.append(f'Please select two or three DIFFERENT models to compare. You selected {selected_models}') bad_fields.append(
f"Please select two or three DIFFERENT models to compare. You selected {selected_models}"
)
if len(bad_fields) > 0: 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: for problem in bad_fields:
message += f'\n* {problem}' message += f"\n* {problem}"
npyscreen.notify_confirm(message) npyscreen.notify_confirm(message)
return False return False
else: else:
@ -410,6 +411,7 @@ class Mergeapp(npyscreen.NPSAppManaged):
npyscreen.setTheme(npyscreen.Themes.ElegantTheme) npyscreen.setTheme(npyscreen.Themes.ElegantTheme)
self.main = self.addForm("MAIN", mergeModelsForm, name="Merge Models Settings") self.main = self.addForm("MAIN", mergeModelsForm, name="Merge Models Settings")
def run_gui(args: Namespace): def run_gui(args: Namespace):
mergeapp = Mergeapp() mergeapp = Mergeapp()
mergeapp.run() mergeapp.run()
@ -450,5 +452,27 @@ def main():
] = cache_dir # because not clear the merge pipeline is honoring cache_dir ] = cache_dir # because not clear the merge pipeline is honoring cache_dir
args.cache_dir = 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 widget.NotEnoughSpaceForWidget as e:
if str(e).startswith("Height of 1 allocated"):
print(
"** You need to have at least two diffusers models defined in models.yaml in order to merge"
)
else:
print(f"** A layout error has occurred: {str(e)}")
sys.exit(-1)
except Exception as e:
print(">> An error occurred:")
traceback.print_exc()
sys.exit(-1)
except KeyboardInterrupt:
sys.exit(-1)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -17,6 +17,7 @@ from pathlib import Path
from typing import List, Tuple from typing import List, Tuple
import npyscreen import npyscreen
from npyscreen import widget
from omegaconf import OmegaConf from omegaconf import OmegaConf
from ldm.invoke.globals import Globals, global_set_root from ldm.invoke.globals import Globals, global_set_root
@ -295,7 +296,7 @@ class textualInversionForm(npyscreen.FormMultiPageAction):
for idx in range(len(model_names)) for idx in range(len(model_names))
if "default" in conf[model_names[idx]] if "default" in conf[model_names[idx]]
] ]
default = defaults[0] if len(defaults)>0 else 0 default = defaults[0] if len(defaults) > 0 else 0
return (model_names, default) return (model_names, default)
def marshall_arguments(self) -> dict: def marshall_arguments(self) -> dict:
@ -438,11 +439,20 @@ def main():
do_front_end(args) do_front_end(args)
else: else:
do_textual_inversion_training(**vars(args)) do_textual_inversion_training(**vars(args))
except widget.NotEnoughSpaceForWidget as e:
if str(e).startswith("Height of 1 allocated"):
print(
"** You need to have at least one diffusers models defined in models.yaml in order to train"
)
else:
print(f"** A layout error has occurred: {str(e)}")
sys.exit(-1)
except AssertionError as e: except AssertionError as e:
print(str(e)) print(str(e))
sys.exit(-1) sys.exit(-1)
except KeyboardInterrupt: except KeyboardInterrupt:
pass pass
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -1,26 +1,26 @@
import requests as request import requests
import ldm.invoke._version as version from ldm.invoke import __app_name__, __version__
local_version = str(version.__version__) local_version = str(__version__).replace("-", "")
package_name = str(__app_name__)
def get_pypi_versions(package_name="InvokeAI") -> list[str]: def get_pypi_versions(package_name=package_name) -> list[str]:
"""Get the versions of the package from PyPI""" """Get the versions of the package from PyPI"""
url = f"https://pypi.org/pypi/{package_name}/json" url = f"https://pypi.org/pypi/{package_name}/json"
response = request.get(url).json() response = requests.get(url).json()
versions: list[str] = list(response["releases"].keys()) versions: list[str] = list(response["releases"].keys())
return versions return versions
def local_on_pypi(package_name="InvokeAI", local_version=local_version) -> bool: def local_on_pypi(package_name=package_name, local_version=local_version) -> bool:
"""Compare the versions of the package from PyPI and the local package""" """Compare the versions of the package from PyPI and the local package"""
pypi_versions = get_pypi_versions(package_name) pypi_versions = get_pypi_versions(package_name)
return local_version in pypi_versions return local_version in pypi_versions
if __name__ == "__main__": if __name__ == "__main__":
package_name = "InvokeAI"
if local_on_pypi(): if local_on_pypi():
print(f"Package {package_name} is up to date") print(f"Package {package_name} is up to date")
else: else: