mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
fix(install): resolve config-related issues with configurator
- Do not use the singleton app config, this causes a lot of weirdness - Update logic to use new config object - Remove unused code
This commit is contained in:
parent
ce9aeeece3
commit
cb180909f7
@ -18,7 +18,7 @@ from argparse import Namespace
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from shutil import copy, get_terminal_size, move
|
from shutil import copy, get_terminal_size, move
|
||||||
from typing import Any, Optional, Set, Tuple, Type, get_args, get_type_hints
|
from typing import Any, Optional, Tuple, Type, get_args, get_type_hints
|
||||||
from urllib import request
|
from urllib import request
|
||||||
|
|
||||||
import npyscreen
|
import npyscreen
|
||||||
@ -32,9 +32,8 @@ from huggingface_hub import login as hf_hub_login
|
|||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
from transformers import AutoFeatureExtractor
|
from transformers import AutoFeatureExtractor
|
||||||
|
|
||||||
import invokeai.configs as configs
|
import invokeai.configs as model_configs
|
||||||
from invokeai.app.services.config import InvokeAIAppConfig
|
from invokeai.app.services.config import InvokeAIAppConfig
|
||||||
from invokeai.app.services.config.config_default import get_config
|
|
||||||
from invokeai.backend.install.install_helper import InstallHelper, InstallSelections
|
from invokeai.backend.install.install_helper import InstallHelper, InstallSelections
|
||||||
from invokeai.backend.model_manager import ModelType
|
from invokeai.backend.model_manager import ModelType
|
||||||
from invokeai.backend.util import choose_precision, choose_torch_device
|
from invokeai.backend.util import choose_precision, choose_torch_device
|
||||||
@ -64,7 +63,8 @@ def get_literal_fields(field: str) -> Tuple[Any]:
|
|||||||
|
|
||||||
# --------------------------globals-----------------------
|
# --------------------------globals-----------------------
|
||||||
|
|
||||||
config = get_config()
|
# Start from a fresh config object - we will read the user's config from file later, and update it with their choices
|
||||||
|
config = InvokeAIAppConfig()
|
||||||
|
|
||||||
PRECISION_CHOICES = get_literal_fields("precision")
|
PRECISION_CHOICES = get_literal_fields("precision")
|
||||||
DEVICE_CHOICES = get_literal_fields("device")
|
DEVICE_CHOICES = get_literal_fields("device")
|
||||||
@ -98,7 +98,7 @@ class DummyWidgetValue(Enum):
|
|||||||
|
|
||||||
|
|
||||||
# --------------------------------------------
|
# --------------------------------------------
|
||||||
def postscript(errors: Set[str]) -> None:
|
def postscript(errors: set[str]) -> None:
|
||||||
if not any(errors):
|
if not any(errors):
|
||||||
message = f"""
|
message = f"""
|
||||||
** INVOKEAI INSTALLATION SUCCESSFUL **
|
** INVOKEAI INSTALLATION SUCCESSFUL **
|
||||||
@ -515,7 +515,7 @@ Use cursor arrows to make a checkbox selection, and space to toggle.
|
|||||||
self.autoimport_dirs["autoimport_dir"] = self.add_widget_intelligent(
|
self.autoimport_dirs["autoimport_dir"] = self.add_widget_intelligent(
|
||||||
FileBox,
|
FileBox,
|
||||||
name="Optional folder to scan for new checkpoints, ControlNets, LoRAs and TI models",
|
name="Optional folder to scan for new checkpoints, ControlNets, LoRAs and TI models",
|
||||||
value=str(config.autoimport_path) if config.autoimport_dir else "",
|
value=str(config.autoimport_path),
|
||||||
select_dir=True,
|
select_dir=True,
|
||||||
must_exist=False,
|
must_exist=False,
|
||||||
use_two_lines=False,
|
use_two_lines=False,
|
||||||
@ -652,7 +652,7 @@ class EditOptApplication(npyscreen.NPSAppManaged):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def default_ramcache() -> float:
|
def get_default_ram_cache_size() -> float:
|
||||||
"""Run a heuristic for the default RAM cache based on installed RAM."""
|
"""Run a heuristic for the default RAM cache based on installed RAM."""
|
||||||
|
|
||||||
# Note that on my 64 GB machine, psutil.virtual_memory().total gives 62 GB,
|
# Note that on my 64 GB machine, psutil.virtual_memory().total gives 62 GB,
|
||||||
@ -662,11 +662,12 @@ def default_ramcache() -> float:
|
|||||||
) # 2.1 is just large enough for sd 1.5 ;-)
|
) # 2.1 is just large enough for sd 1.5 ;-)
|
||||||
|
|
||||||
|
|
||||||
def default_startup_options(init_file: Path) -> InvokeAIAppConfig:
|
def get_default_config() -> InvokeAIAppConfig:
|
||||||
opts = get_config()
|
"""Builds a new config object, setting the ram and precision using the appropriate heuristic."""
|
||||||
opts.ram = default_ramcache()
|
config = InvokeAIAppConfig()
|
||||||
opts.precision = "float32" if FORCE_FULL_PRECISION else choose_precision(torch.device(choose_torch_device()))
|
config.ram = get_default_ram_cache_size()
|
||||||
return opts
|
config.precision = "float32" if FORCE_FULL_PRECISION else choose_precision(torch.device(choose_torch_device()))
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
def default_user_selections(program_opts: Namespace, install_helper: InstallHelper) -> InstallSelections:
|
def default_user_selections(program_opts: Namespace, install_helper: InstallHelper) -> InstallSelections:
|
||||||
@ -696,7 +697,7 @@ def initialize_rootdir(root: Path, yes_to_all: bool = False):
|
|||||||
for model_type in ModelType:
|
for model_type in ModelType:
|
||||||
Path(root, "autoimport", model_type.value).mkdir(parents=True, exist_ok=True)
|
Path(root, "autoimport", model_type.value).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
configs_src = Path(configs.__path__[0])
|
configs_src = Path(model_configs.__path__[0])
|
||||||
configs_dest = root / "configs"
|
configs_dest = root / "configs"
|
||||||
if not os.path.samefile(configs_src, configs_dest):
|
if not os.path.samefile(configs_src, configs_dest):
|
||||||
shutil.copytree(configs_src, configs_dest, dirs_exist_ok=True)
|
shutil.copytree(configs_src, configs_dest, dirs_exist_ok=True)
|
||||||
@ -707,19 +708,19 @@ def initialize_rootdir(root: Path, yes_to_all: bool = False):
|
|||||||
|
|
||||||
# -------------------------------------
|
# -------------------------------------
|
||||||
def run_console_ui(
|
def run_console_ui(
|
||||||
program_opts: Namespace, initfile: Path, install_helper: InstallHelper
|
program_opts: Namespace, install_helper: InstallHelper
|
||||||
) -> Tuple[Optional[Namespace], Optional[InstallSelections]]:
|
) -> Tuple[Optional[Namespace], Optional[InstallSelections]]:
|
||||||
first_time = not (config.root_path / "invokeai.yaml").exists()
|
first_time = not config.init_file_path.exists()
|
||||||
invokeai_opts = default_startup_options(initfile) if first_time else config
|
config_opts = get_default_config() if first_time else config
|
||||||
if program_opts.root:
|
if program_opts.root:
|
||||||
invokeai_opts.set_root(Path(program_opts.root))
|
config_opts.set_root(Path(program_opts.root))
|
||||||
|
|
||||||
if not set_min_terminal_size(MIN_COLS, MIN_LINES):
|
if not set_min_terminal_size(MIN_COLS, MIN_LINES):
|
||||||
raise WindowTooSmallException(
|
raise WindowTooSmallException(
|
||||||
"Could not increase terminal size. Try running again with a larger window or smaller font size."
|
"Could not increase terminal size. Try running again with a larger window or smaller font size."
|
||||||
)
|
)
|
||||||
|
|
||||||
editApp = EditOptApplication(program_opts, invokeai_opts, install_helper)
|
editApp = EditOptApplication(program_opts, config_opts, install_helper)
|
||||||
editApp.run()
|
editApp.run()
|
||||||
if editApp.user_cancelled:
|
if editApp.user_cancelled:
|
||||||
return (None, None)
|
return (None, None)
|
||||||
@ -727,34 +728,11 @@ def run_console_ui(
|
|||||||
return (editApp.new_opts, editApp.install_selections)
|
return (editApp.new_opts, editApp.install_selections)
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------------
|
|
||||||
def write_opts(opts: InvokeAIAppConfig, init_file: Path) -> None:
|
|
||||||
"""
|
|
||||||
Update the invokeai.yaml file with values from current settings.
|
|
||||||
"""
|
|
||||||
new_config = get_config()
|
|
||||||
new_config.set_root(config.root_path)
|
|
||||||
|
|
||||||
# Remove any fields that are not in the model_fields list, like `hf_token`
|
|
||||||
cleaned_opts = {k: v for k, v in vars(opts).items() if k in new_config.model_fields}
|
|
||||||
new_config.update_config(cleaned_opts)
|
|
||||||
new_config.write_file(init_file)
|
|
||||||
|
|
||||||
if hasattr(opts, "hf_token") and opts.hf_token:
|
|
||||||
HfLogin(opts.hf_token)
|
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------------
|
# -------------------------------------
|
||||||
def default_output_dir() -> Path:
|
def default_output_dir() -> Path:
|
||||||
return config.root_path / "outputs"
|
return config.root_path / "outputs"
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------------
|
|
||||||
def write_default_options(program_opts: Namespace, initfile: Path) -> None:
|
|
||||||
opt = default_startup_options(initfile)
|
|
||||||
write_opts(opt, initfile)
|
|
||||||
|
|
||||||
|
|
||||||
def is_v2_install(root: Path) -> bool:
|
def is_v2_install(root: Path) -> bool:
|
||||||
# We check for to see if the runtime directory is correctly initialized.
|
# We check for to see if the runtime directory is correctly initialized.
|
||||||
old_init_file = root / "invokeai.init"
|
old_init_file = root / "invokeai.init"
|
||||||
@ -802,14 +780,6 @@ def main() -> None:
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
help="when --yes specified, only install the default model",
|
help="when --yes specified, only install the default model",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
|
||||||
"--config_file",
|
|
||||||
"-c",
|
|
||||||
dest="config_file",
|
|
||||||
type=str,
|
|
||||||
default=None,
|
|
||||||
help="path to configuration file to create",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--root_dir",
|
"--root_dir",
|
||||||
dest="root",
|
dest="root",
|
||||||
@ -823,13 +793,22 @@ def main() -> None:
|
|||||||
config.set_root(Path(opt.root))
|
config.set_root(Path(opt.root))
|
||||||
if opt.full_precision:
|
if opt.full_precision:
|
||||||
updates["precision"] = "float32"
|
updates["precision"] = "float32"
|
||||||
config.merge_from_file()
|
|
||||||
|
try:
|
||||||
|
# Attempt to read the config file into the config object
|
||||||
|
config.merge_from_file()
|
||||||
|
except FileNotFoundError:
|
||||||
|
# No config file, first time running the app
|
||||||
|
pass
|
||||||
|
|
||||||
config.update_config(updates)
|
config.update_config(updates)
|
||||||
logger = InvokeAILogger().get_logger(config=config)
|
logger = InvokeAILogger().get_logger(config=config)
|
||||||
|
|
||||||
errors = set()
|
errors: set[str] = set()
|
||||||
FORCE_FULL_PRECISION = opt.full_precision # FIXME global
|
FORCE_FULL_PRECISION = opt.full_precision # FIXME global
|
||||||
new_init_file = config.root_path / "invokeai.yaml"
|
|
||||||
|
# Before we write anything else, make a backup of the existing init file
|
||||||
|
new_init_file = config.init_file_path
|
||||||
backup_init_file = new_init_file.with_suffix(".bak")
|
backup_init_file = new_init_file.with_suffix(".bak")
|
||||||
if new_init_file.exists():
|
if new_init_file.exists():
|
||||||
copy(new_init_file, backup_init_file)
|
copy(new_init_file, backup_init_file)
|
||||||
@ -849,13 +828,20 @@ def main() -> None:
|
|||||||
models_to_download = default_user_selections(opt, install_helper)
|
models_to_download = default_user_selections(opt, install_helper)
|
||||||
|
|
||||||
if opt.yes_to_all:
|
if opt.yes_to_all:
|
||||||
write_default_options(opt, new_init_file)
|
# We will not show the UI - just write the default config to the file and move on to installing models.
|
||||||
init_options = Namespace(precision="float32" if opt.full_precision else "float16")
|
get_default_config().write_file(new_init_file)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
init_options, models_to_download = run_console_ui(opt, new_init_file, install_helper)
|
# Run the UI to get the user's options & model choices
|
||||||
if init_options:
|
user_opts, models_to_download = run_console_ui(opt, install_helper)
|
||||||
write_opts(init_options, new_init_file)
|
if user_opts:
|
||||||
|
# Create a dict of the user's opts, omitting any fields that are not config settings (like `hf_token`)
|
||||||
|
user_opts_dict = {k: v for k, v in vars(user_opts).items() if k in config.model_fields}
|
||||||
|
# Merge the user's opts back into the config object & write it
|
||||||
|
config.update_config(user_opts_dict)
|
||||||
|
config.write_file(config.init_file_path)
|
||||||
|
|
||||||
|
if hasattr(user_opts, "hf_token") and user_opts.hf_token:
|
||||||
|
HfLogin(user_opts.hf_token)
|
||||||
else:
|
else:
|
||||||
logger.info('\n** CANCELLED AT USER\'S REQUEST. USE THE "invoke.sh" LAUNCHER TO RUN LATER **\n')
|
logger.info('\n** CANCELLED AT USER\'S REQUEST. USE THE "invoke.sh" LAUNCHER TO RUN LATER **\n')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
@ -868,7 +854,6 @@ def main() -> None:
|
|||||||
|
|
||||||
if opt.skip_sd_weights:
|
if opt.skip_sd_weights:
|
||||||
logger.warning("Skipping diffusion weights download per user request")
|
logger.warning("Skipping diffusion weights download per user request")
|
||||||
|
|
||||||
elif models_to_download:
|
elif models_to_download:
|
||||||
install_helper.add_or_delete(models_to_download)
|
install_helper.add_or_delete(models_to_download)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user