mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(config): add CLI arg to specify config file
This allows users to create simple "profiles" via separate `invokeai.yaml` files. - Remove `InvokeAIAppConfig.set_root()`, it's extraneous - Remove `InvokeAIAppConfig.merge_from_file()`, it's extraneous - Add `--config` to the app arg parser, add `InvokeAIAppConfig._config_file`, and consume in the config singleton getter - `InvokeAIAppConfig.init_file_path` -> `InvokeAIAppConfig.config_file_path`
This commit is contained in:
@ -121,6 +121,7 @@ class InvokeAIAppConfig(BaseSettings):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
_root: Optional[Path] = PrivateAttr(default=None)
|
_root: Optional[Path] = PrivateAttr(default=None)
|
||||||
|
_config_file: Optional[Path] = PrivateAttr(default=None)
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
|
|
||||||
@ -251,24 +252,6 @@ class InvokeAIAppConfig(BaseSettings):
|
|||||||
if len(config_dict) > 0:
|
if len(config_dict) > 0:
|
||||||
file.write(yaml.dump(config_dict, sort_keys=False))
|
file.write(yaml.dump(config_dict, sort_keys=False))
|
||||||
|
|
||||||
def merge_from_file(self, source_path: Optional[Path] = None) -> None:
|
|
||||||
"""Read the config from the `invokeai.yaml` file, migrating it if necessary and merging it into the singleton config.
|
|
||||||
|
|
||||||
This function will write to the `invokeai.yaml` file if the config is migrated.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
source_path: Path to the config file. If not provided, the default path is used.
|
|
||||||
"""
|
|
||||||
path = source_path or self.init_file_path
|
|
||||||
config_from_file = load_and_migrate_config(path)
|
|
||||||
# Clobbering here will overwrite any settings that were set via environment variables
|
|
||||||
self.update_config(config_from_file, clobber=False)
|
|
||||||
|
|
||||||
def set_root(self, root: Path) -> None:
|
|
||||||
"""Set the runtime root directory. This is typically set using a CLI arg."""
|
|
||||||
assert isinstance(root, Path)
|
|
||||||
self._root = root
|
|
||||||
|
|
||||||
def _resolve(self, partial_path: Path) -> Path:
|
def _resolve(self, partial_path: Path) -> Path:
|
||||||
return (self.root_path / partial_path).resolve()
|
return (self.root_path / partial_path).resolve()
|
||||||
|
|
||||||
@ -283,9 +266,9 @@ class InvokeAIAppConfig(BaseSettings):
|
|||||||
return root.resolve()
|
return root.resolve()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def init_file_path(self) -> Path:
|
def config_file_path(self) -> Path:
|
||||||
"""Path to invokeai.yaml, resolved to an absolute path.."""
|
"""Path to invokeai.yaml, resolved to an absolute path.."""
|
||||||
resolved_path = self._resolve(INIT_FILE)
|
resolved_path = self._resolve(self._config_file or INIT_FILE)
|
||||||
assert resolved_path is not None
|
assert resolved_path is not None
|
||||||
return resolved_path
|
return resolved_path
|
||||||
|
|
||||||
@ -441,9 +424,11 @@ def get_config() -> InvokeAIAppConfig:
|
|||||||
if not InvokeAIArgs.did_parse:
|
if not InvokeAIArgs.did_parse:
|
||||||
return config
|
return config
|
||||||
|
|
||||||
# CLI args trump environment variables
|
# Set CLI args
|
||||||
if root := getattr(args, "root", None):
|
if root := getattr(args, "root", None):
|
||||||
config.set_root(Path(root))
|
config._root = Path(root)
|
||||||
|
if config_file := getattr(args, "config_file", None):
|
||||||
|
config._config_file = Path(config_file)
|
||||||
|
|
||||||
# Log in to HF
|
# Log in to HF
|
||||||
hf_login()
|
hf_login()
|
||||||
@ -452,9 +437,11 @@ def get_config() -> InvokeAIAppConfig:
|
|||||||
configs_src = Path(model_configs.__path__[0]) # pyright: ignore [reportUnknownMemberType, reportUnknownArgumentType, reportAttributeAccessIssue]
|
configs_src = Path(model_configs.__path__[0]) # pyright: ignore [reportUnknownMemberType, reportUnknownArgumentType, reportAttributeAccessIssue]
|
||||||
shutil.copytree(configs_src, config.legacy_conf_path, dirs_exist_ok=True)
|
shutil.copytree(configs_src, config.legacy_conf_path, dirs_exist_ok=True)
|
||||||
|
|
||||||
if config.init_file_path.exists():
|
if config.config_file_path.exists():
|
||||||
config.merge_from_file()
|
incoming_config = load_and_migrate_config(config.config_file_path)
|
||||||
|
# Clobbering here will overwrite any settings that were set via environment variables
|
||||||
|
config.update_config(incoming_config, clobber=False)
|
||||||
else:
|
else:
|
||||||
config.write_file(config.init_file_path)
|
config.write_file(config.config_file_path)
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
@ -343,7 +343,7 @@ class ModelInstallService(ModelInstallServiceBase):
|
|||||||
|
|
||||||
# Remove `legacy_models_yaml_path` from the config file - we are done with it either way
|
# Remove `legacy_models_yaml_path` from the config file - we are done with it either way
|
||||||
self._app_config.legacy_models_yaml_path = None
|
self._app_config.legacy_models_yaml_path = None
|
||||||
self._app_config.write_file(self._app_config.init_file_path)
|
self._app_config.write_file(self._app_config.config_file_path)
|
||||||
|
|
||||||
def scan_directory(self, scan_dir: Path, install: bool = False) -> List[str]: # noqa D102
|
def scan_directory(self, scan_dir: Path, install: bool = False) -> List[str]: # noqa D102
|
||||||
self._cached_model_paths = {Path(x.path).resolve() for x in self.record_store.all_models()}
|
self._cached_model_paths = {Path(x.path).resolve() for x in self.record_store.all_models()}
|
||||||
|
@ -3,14 +3,16 @@ from typing import Optional
|
|||||||
|
|
||||||
from invokeai.version import __version__
|
from invokeai.version import __version__
|
||||||
|
|
||||||
_root_help = r"""Sets a root directory for the app.
|
_root_help = r"""Path to the runtime root directory. If omitted, the app will search for the root directory in the following order:
|
||||||
If omitted, the app will search for the root directory in the following order:
|
|
||||||
- The `$INVOKEAI_ROOT` environment variable
|
- The `$INVOKEAI_ROOT` environment variable
|
||||||
- The currently active virtual environment's parent directory
|
- The currently active virtual environment's parent directory
|
||||||
- `$HOME/invokeai`"""
|
- `$HOME/invokeai`"""
|
||||||
|
|
||||||
|
_config_file_help = r"""Path to the invokeai.yaml configuration file. If omitted, the app will search for the file in the root directory."""
|
||||||
|
|
||||||
_parser = ArgumentParser(description="Invoke Studio", formatter_class=RawTextHelpFormatter)
|
_parser = ArgumentParser(description="Invoke Studio", formatter_class=RawTextHelpFormatter)
|
||||||
_parser.add_argument("--root", type=str, help=_root_help)
|
_parser.add_argument("--root", type=str, help=_root_help)
|
||||||
|
_parser.add_argument("--config", dest="config_file", type=str, help=_config_file_help)
|
||||||
_parser.add_argument("--version", action="version", version=__version__, help="Displays the version and exits.")
|
_parser.add_argument("--version", action="version", version=__version__, help="Displays the version and exits.")
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ def store(
|
|||||||
datadir: Any,
|
datadir: Any,
|
||||||
) -> ModelRecordServiceSQL:
|
) -> ModelRecordServiceSQL:
|
||||||
config = InvokeAIAppConfig()
|
config = InvokeAIAppConfig()
|
||||||
config.set_root(datadir)
|
config._root = datadir
|
||||||
logger = InvokeAILogger.get_logger(config=config)
|
logger = InvokeAILogger.get_logger(config=config)
|
||||||
db = create_mock_sqlite_database(config, logger)
|
db = create_mock_sqlite_database(config, logger)
|
||||||
return ModelRecordServiceSQL(db)
|
return ModelRecordServiceSQL(db)
|
||||||
|
@ -94,7 +94,7 @@ def diffusers_dir(mm2_model_files: Path) -> Path:
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mm2_app_config(mm2_root_dir: Path) -> InvokeAIAppConfig:
|
def mm2_app_config(mm2_root_dir: Path) -> InvokeAIAppConfig:
|
||||||
app_config = InvokeAIAppConfig(models_dir=mm2_root_dir / "models", log_level="info")
|
app_config = InvokeAIAppConfig(models_dir=mm2_root_dir / "models", log_level="info")
|
||||||
app_config.set_root(mm2_root_dir)
|
app_config._root = mm2_root_dir
|
||||||
return app_config
|
return app_config
|
||||||
|
|
||||||
|
|
||||||
|
@ -170,7 +170,7 @@ def test_set_and_resolve_paths():
|
|||||||
"""Test setting root and resolving paths based on it."""
|
"""Test setting root and resolving paths based on it."""
|
||||||
with TemporaryDirectory() as tmpdir:
|
with TemporaryDirectory() as tmpdir:
|
||||||
config = InvokeAIAppConfig()
|
config = InvokeAIAppConfig()
|
||||||
config.set_root(Path(tmpdir))
|
config._root = Path(tmpdir)
|
||||||
assert config.models_path == Path(tmpdir).resolve() / "models"
|
assert config.models_path == Path(tmpdir).resolve() / "models"
|
||||||
assert config.db_path == Path(tmpdir).resolve() / "databases" / "invokeai.db"
|
assert config.db_path == Path(tmpdir).resolve() / "databases" / "invokeai.db"
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user