mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
remove globals, args, generate and the legacy CLI
This commit is contained in:
parent
90054ddf0d
commit
15ffb53e59
@ -2,7 +2,6 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import torch
|
import torch
|
||||||
from argparse import Namespace
|
from argparse import Namespace
|
||||||
from invokeai.backend import Args
|
|
||||||
from omegaconf import OmegaConf
|
from omegaconf import OmegaConf
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import types
|
from typing import types
|
||||||
@ -13,7 +12,7 @@ from ...backend import ModelManager
|
|||||||
from ...backend.util import choose_precision, choose_torch_device
|
from ...backend.util import choose_precision, choose_torch_device
|
||||||
|
|
||||||
# TODO: Replace with an abstract class base ModelManagerBase
|
# TODO: Replace with an abstract class base ModelManagerBase
|
||||||
def get_model_manager(config: Args, logger: types.ModuleType) -> ModelManager:
|
def get_model_manager(config: InvokeAISettings, logger: types.ModuleType) -> ModelManager:
|
||||||
model_config = config.model_conf_path
|
model_config = config.model_conf_path
|
||||||
if not model_config.exists():
|
if not model_config.exists():
|
||||||
report_model_error(
|
report_model_error(
|
||||||
@ -44,7 +43,7 @@ def get_model_manager(config: Args, logger: types.ModuleType) -> ModelManager:
|
|||||||
else choose_precision(device)
|
else choose_precision(device)
|
||||||
|
|
||||||
model_manager = ModelManager(
|
model_manager = ModelManager(
|
||||||
OmegaConf.load(config.conf),
|
OmegaConf.load(config.model_conf_path),
|
||||||
precision=precision,
|
precision=precision,
|
||||||
device_type=device,
|
device_type=device,
|
||||||
max_loaded_models=config.max_loaded_models,
|
max_loaded_models=config.max_loaded_models,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
Initialization file for invokeai.backend
|
Initialization file for invokeai.backend
|
||||||
"""
|
"""
|
||||||
from .generate import Generate
|
|
||||||
from .generator import (
|
from .generator import (
|
||||||
InvokeAIGeneratorBasicParams,
|
InvokeAIGeneratorBasicParams,
|
||||||
InvokeAIGenerator,
|
InvokeAIGenerator,
|
||||||
@ -12,5 +11,3 @@ from .generator import (
|
|||||||
)
|
)
|
||||||
from .model_management import ModelManager, SDModelComponent
|
from .model_management import ModelManager, SDModelComponent
|
||||||
from .safety_checker import SafetyChecker
|
from .safety_checker import SafetyChecker
|
||||||
from .args import Args
|
|
||||||
from .globals import Globals
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,135 +0,0 @@
|
|||||||
"""
|
|
||||||
invokeai.backend.globals defines a small number of global variables that would
|
|
||||||
otherwise have to be passed through long and complex call chains.
|
|
||||||
|
|
||||||
It defines a Namespace object named "Globals" that contains
|
|
||||||
the attributes:
|
|
||||||
|
|
||||||
- root - the root directory under which "models" and "outputs" can be found
|
|
||||||
- initfile - path to the initialization file
|
|
||||||
- try_patchmatch - option to globally disable loading of 'patchmatch' module
|
|
||||||
- always_use_cpu - force use of CPU even if GPU is available
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import os.path as osp
|
|
||||||
from argparse import Namespace
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import Union
|
|
||||||
from pydantic import BaseSettings
|
|
||||||
|
|
||||||
Globals = Namespace()
|
|
||||||
|
|
||||||
# Where to look for the initialization file and other key components
|
|
||||||
Globals.initfile = "invokeai.init"
|
|
||||||
Globals.models_file = "models.yaml"
|
|
||||||
Globals.models_dir = "models"
|
|
||||||
Globals.config_dir = "configs"
|
|
||||||
Globals.autoscan_dir = "weights"
|
|
||||||
Globals.converted_ckpts_dir = "converted_ckpts"
|
|
||||||
|
|
||||||
# Set the default root directory. This can be overwritten by explicitly
|
|
||||||
# passing the `--root <directory>` argument on the command line.
|
|
||||||
# logic is:
|
|
||||||
# 1) use INVOKEAI_ROOT environment variable (no check for this being a valid directory)
|
|
||||||
# 2) use VIRTUAL_ENV environment variable, with a check for initfile being there
|
|
||||||
# 3) use ~/invokeai
|
|
||||||
|
|
||||||
if os.environ.get("INVOKEAI_ROOT"):
|
|
||||||
Globals.root = osp.abspath(os.environ.get("INVOKEAI_ROOT"))
|
|
||||||
elif (
|
|
||||||
os.environ.get("VIRTUAL_ENV")
|
|
||||||
and Path(os.environ.get("VIRTUAL_ENV"), "..", Globals.initfile).exists()
|
|
||||||
):
|
|
||||||
Globals.root = osp.abspath(osp.join(os.environ.get("VIRTUAL_ENV"), ".."))
|
|
||||||
else:
|
|
||||||
Globals.root = osp.abspath(osp.expanduser("~/invokeai"))
|
|
||||||
|
|
||||||
# Try loading patchmatch
|
|
||||||
Globals.try_patchmatch = True
|
|
||||||
|
|
||||||
# Use CPU even if GPU is available (main use case is for debugging MPS issues)
|
|
||||||
Globals.always_use_cpu = False
|
|
||||||
|
|
||||||
# Whether the internet is reachable for dynamic downloads
|
|
||||||
# The CLI will test connectivity at startup time.
|
|
||||||
Globals.internet_available = True
|
|
||||||
|
|
||||||
# Whether to disable xformers
|
|
||||||
Globals.disable_xformers = False
|
|
||||||
|
|
||||||
# Low-memory tradeoff for guidance calculations.
|
|
||||||
Globals.sequential_guidance = False
|
|
||||||
|
|
||||||
# whether we are forcing full precision
|
|
||||||
Globals.full_precision = False
|
|
||||||
|
|
||||||
# whether we should convert ckpt files into diffusers models on the fly
|
|
||||||
Globals.ckpt_convert = True
|
|
||||||
|
|
||||||
# logging tokenization everywhere
|
|
||||||
Globals.log_tokenization = False
|
|
||||||
|
|
||||||
|
|
||||||
def global_config_file() -> Path:
|
|
||||||
return Path(Globals.root, Globals.config_dir, Globals.models_file)
|
|
||||||
|
|
||||||
|
|
||||||
def global_config_dir() -> Path:
|
|
||||||
return Path(Globals.root, Globals.config_dir)
|
|
||||||
|
|
||||||
|
|
||||||
def global_models_dir() -> Path:
|
|
||||||
return Path(Globals.root, Globals.models_dir)
|
|
||||||
|
|
||||||
|
|
||||||
def global_autoscan_dir() -> Path:
|
|
||||||
return Path(Globals.root, Globals.autoscan_dir)
|
|
||||||
|
|
||||||
|
|
||||||
def global_converted_ckpts_dir() -> Path:
|
|
||||||
return Path(global_models_dir(), Globals.converted_ckpts_dir)
|
|
||||||
|
|
||||||
|
|
||||||
def global_set_root(root_dir: Union[str, Path]):
|
|
||||||
Globals.root = root_dir
|
|
||||||
|
|
||||||
|
|
||||||
def global_cache_dir(subdir: Union[str, Path] = "") -> Path:
|
|
||||||
"""
|
|
||||||
Returns Path to the model cache directory. If a subdirectory
|
|
||||||
is provided, it will be appended to the end of the path, allowing
|
|
||||||
for Hugging Face-style conventions. Currently, Hugging Face has
|
|
||||||
moved all models into the "hub" subfolder, so for any pretrained
|
|
||||||
HF model, use:
|
|
||||||
global_cache_dir('hub')
|
|
||||||
|
|
||||||
The legacy location for transformers used to be global_cache_dir('transformers')
|
|
||||||
and global_cache_dir('diffusers') for diffusers.
|
|
||||||
"""
|
|
||||||
home: str = os.getenv("HF_HOME")
|
|
||||||
|
|
||||||
if home is None:
|
|
||||||
home = os.getenv("XDG_CACHE_HOME")
|
|
||||||
|
|
||||||
if home is not None:
|
|
||||||
# Set `home` to $XDG_CACHE_HOME/huggingface, which is the default location mentioned in Hugging Face Hub Client Library.
|
|
||||||
# See: https://huggingface.co/docs/huggingface_hub/main/en/package_reference/environment_variables#xdgcachehome
|
|
||||||
home += os.sep + "huggingface"
|
|
||||||
|
|
||||||
if home is not None:
|
|
||||||
return Path(home, subdir)
|
|
||||||
else:
|
|
||||||
return Path(Globals.root, "models", subdir)
|
|
||||||
|
|
||||||
def copy_conf_to_globals(conf: Union[dict,BaseSettings]):
|
|
||||||
'''
|
|
||||||
Given a dict or dict-like object, copy its keys and
|
|
||||||
values into the Globals Namespace. This is a transitional
|
|
||||||
workaround until we remove Globals entirely.
|
|
||||||
'''
|
|
||||||
if isinstance(conf,BaseSettings):
|
|
||||||
conf = conf.dict()
|
|
||||||
for key in conf.keys():
|
|
||||||
if key is not None:
|
|
||||||
setattr(Globals,key,conf[key])
|
|
@ -6,7 +6,9 @@ be suppressed or deferred
|
|||||||
"""
|
"""
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import invokeai.backend.util.logging as logger
|
import invokeai.backend.util.logging as logger
|
||||||
from invokeai.backend.globals import Globals
|
from invokeai.app.services.config import InvokeAIAppConfig
|
||||||
|
|
||||||
|
config = InvokeAIAppConfig()
|
||||||
|
|
||||||
class PatchMatch:
|
class PatchMatch:
|
||||||
"""
|
"""
|
||||||
@ -23,7 +25,7 @@ class PatchMatch:
|
|||||||
def _load_patch_match(self):
|
def _load_patch_match(self):
|
||||||
if self.tried_load:
|
if self.tried_load:
|
||||||
return
|
return
|
||||||
if Globals.try_patchmatch:
|
if config.try_patchmatch:
|
||||||
from patchmatch import patch_match as pm
|
from patchmatch import patch_match as pm
|
||||||
|
|
||||||
if pm.patchmatch_available:
|
if pm.patchmatch_available:
|
||||||
|
@ -33,11 +33,11 @@ from PIL import Image, ImageOps
|
|||||||
from transformers import AutoProcessor, CLIPSegForImageSegmentation
|
from transformers import AutoProcessor, CLIPSegForImageSegmentation
|
||||||
|
|
||||||
import invokeai.backend.util.logging as logger
|
import invokeai.backend.util.logging as logger
|
||||||
from invokeai.backend.globals import global_cache_dir
|
from invokeai.app.services.config import InvokeAIAppConfig
|
||||||
|
|
||||||
CLIPSEG_MODEL = "CIDAS/clipseg-rd64-refined"
|
CLIPSEG_MODEL = "CIDAS/clipseg-rd64-refined"
|
||||||
CLIPSEG_SIZE = 352
|
CLIPSEG_SIZE = 352
|
||||||
|
config = InvokeAIAppConfig()
|
||||||
|
|
||||||
class SegmentedGrayscale(object):
|
class SegmentedGrayscale(object):
|
||||||
def __init__(self, image: Image, heatmap: torch.Tensor):
|
def __init__(self, image: Image, heatmap: torch.Tensor):
|
||||||
@ -88,10 +88,10 @@ class Txt2Mask(object):
|
|||||||
# BUG: we are not doing anything with the device option at this time
|
# BUG: we are not doing anything with the device option at this time
|
||||||
self.device = device
|
self.device = device
|
||||||
self.processor = AutoProcessor.from_pretrained(
|
self.processor = AutoProcessor.from_pretrained(
|
||||||
CLIPSEG_MODEL, cache_dir=global_cache_dir("hub")
|
CLIPSEG_MODEL, cache_dir=config.cache_dir
|
||||||
)
|
)
|
||||||
self.model = CLIPSegForImageSegmentation.from_pretrained(
|
self.model = CLIPSegForImageSegmentation.from_pretrained(
|
||||||
CLIPSEG_MODEL, cache_dir=global_cache_dir("hub")
|
CLIPSEG_MODEL, cache_dir=config.cache_dir
|
||||||
)
|
)
|
||||||
|
|
||||||
@torch.no_grad()
|
@torch.no_grad()
|
||||||
|
@ -26,7 +26,7 @@ import torch
|
|||||||
from safetensors.torch import load_file
|
from safetensors.torch import load_file
|
||||||
|
|
||||||
import invokeai.backend.util.logging as logger
|
import invokeai.backend.util.logging as logger
|
||||||
from invokeai.backend.globals import global_cache_dir, global_config_dir
|
from invokeai.app.services.config import InvokeAIAppConfig
|
||||||
|
|
||||||
from .model_manager import ModelManager, SDLegacyType
|
from .model_manager import ModelManager, SDLegacyType
|
||||||
|
|
||||||
@ -73,6 +73,7 @@ from transformers import (
|
|||||||
|
|
||||||
from ..stable_diffusion import StableDiffusionGeneratorPipeline
|
from ..stable_diffusion import StableDiffusionGeneratorPipeline
|
||||||
|
|
||||||
|
config = InvokeAIAppConfig()
|
||||||
|
|
||||||
def shave_segments(path, n_shave_prefix_segments=1):
|
def shave_segments(path, n_shave_prefix_segments=1):
|
||||||
"""
|
"""
|
||||||
@ -842,7 +843,7 @@ def convert_ldm_bert_checkpoint(checkpoint, config):
|
|||||||
|
|
||||||
def convert_ldm_clip_checkpoint(checkpoint):
|
def convert_ldm_clip_checkpoint(checkpoint):
|
||||||
text_model = CLIPTextModel.from_pretrained(
|
text_model = CLIPTextModel.from_pretrained(
|
||||||
"openai/clip-vit-large-patch14", cache_dir=global_cache_dir("hub")
|
"openai/clip-vit-large-patch14", cache_dir=config.cache_dir
|
||||||
)
|
)
|
||||||
|
|
||||||
keys = list(checkpoint.keys())
|
keys = list(checkpoint.keys())
|
||||||
@ -897,7 +898,7 @@ textenc_pattern = re.compile("|".join(protected.keys()))
|
|||||||
|
|
||||||
|
|
||||||
def convert_paint_by_example_checkpoint(checkpoint):
|
def convert_paint_by_example_checkpoint(checkpoint):
|
||||||
cache_dir = global_cache_dir("hub")
|
cache_dir = config.cache_dir
|
||||||
config = CLIPVisionConfig.from_pretrained(
|
config = CLIPVisionConfig.from_pretrained(
|
||||||
"openai/clip-vit-large-patch14", cache_dir=cache_dir
|
"openai/clip-vit-large-patch14", cache_dir=cache_dir
|
||||||
)
|
)
|
||||||
@ -969,7 +970,7 @@ def convert_paint_by_example_checkpoint(checkpoint):
|
|||||||
|
|
||||||
|
|
||||||
def convert_open_clip_checkpoint(checkpoint):
|
def convert_open_clip_checkpoint(checkpoint):
|
||||||
cache_dir = global_cache_dir("hub")
|
cache_dir = config.cache_dir
|
||||||
text_model = CLIPTextModel.from_pretrained(
|
text_model = CLIPTextModel.from_pretrained(
|
||||||
"stabilityai/stable-diffusion-2", subfolder="text_encoder", cache_dir=cache_dir
|
"stabilityai/stable-diffusion-2", subfolder="text_encoder", cache_dir=cache_dir
|
||||||
)
|
)
|
||||||
@ -1105,7 +1106,7 @@ def load_pipeline_from_original_stable_diffusion_ckpt(
|
|||||||
else:
|
else:
|
||||||
checkpoint = load_file(checkpoint_path)
|
checkpoint = load_file(checkpoint_path)
|
||||||
|
|
||||||
cache_dir = global_cache_dir("hub")
|
cache_dir = config.cache_dir
|
||||||
pipeline_class = (
|
pipeline_class = (
|
||||||
StableDiffusionGeneratorPipeline
|
StableDiffusionGeneratorPipeline
|
||||||
if return_generator_pipeline
|
if return_generator_pipeline
|
||||||
@ -1129,25 +1130,23 @@ def load_pipeline_from_original_stable_diffusion_ckpt(
|
|||||||
|
|
||||||
if model_type == SDLegacyType.V2_v:
|
if model_type == SDLegacyType.V2_v:
|
||||||
original_config_file = (
|
original_config_file = (
|
||||||
global_config_dir() / "stable-diffusion" / "v2-inference-v.yaml"
|
config.legacy_conf_path / "v2-inference-v.yaml"
|
||||||
)
|
)
|
||||||
if global_step == 110000:
|
if global_step == 110000:
|
||||||
# v2.1 needs to upcast attention
|
# v2.1 needs to upcast attention
|
||||||
upcast_attention = True
|
upcast_attention = True
|
||||||
elif model_type == SDLegacyType.V2_e:
|
elif model_type == SDLegacyType.V2_e:
|
||||||
original_config_file = (
|
original_config_file = (
|
||||||
global_config_dir() / "stable-diffusion" / "v2-inference.yaml"
|
config.legacy_conf_path / "v2-inference.yaml"
|
||||||
)
|
)
|
||||||
elif model_type == SDLegacyType.V1_INPAINT:
|
elif model_type == SDLegacyType.V1_INPAINT:
|
||||||
original_config_file = (
|
original_config_file = (
|
||||||
global_config_dir()
|
config.legacy_conf_path / "v1-inpainting-inference.yaml"
|
||||||
/ "stable-diffusion"
|
|
||||||
/ "v1-inpainting-inference.yaml"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
elif model_type == SDLegacyType.V1:
|
elif model_type == SDLegacyType.V1:
|
||||||
original_config_file = (
|
original_config_file = (
|
||||||
global_config_dir() / "stable-diffusion" / "v1-inference.yaml"
|
config.legacy_conf_path / "v1-inference.yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -1297,7 +1296,7 @@ def load_pipeline_from_original_stable_diffusion_ckpt(
|
|||||||
)
|
)
|
||||||
safety_checker = StableDiffusionSafetyChecker.from_pretrained(
|
safety_checker = StableDiffusionSafetyChecker.from_pretrained(
|
||||||
"CompVis/stable-diffusion-safety-checker",
|
"CompVis/stable-diffusion-safety-checker",
|
||||||
cache_dir=global_cache_dir("hub"),
|
cache_dir=config.cache_dir,
|
||||||
)
|
)
|
||||||
feature_extractor = AutoFeatureExtractor.from_pretrained(
|
feature_extractor = AutoFeatureExtractor.from_pretrained(
|
||||||
"CompVis/stable-diffusion-safety-checker", cache_dir=cache_dir
|
"CompVis/stable-diffusion-safety-checker", cache_dir=cache_dir
|
||||||
|
@ -36,8 +36,6 @@ from omegaconf import OmegaConf
|
|||||||
from omegaconf.dictconfig import DictConfig
|
from omegaconf.dictconfig import DictConfig
|
||||||
from picklescan.scanner import scan_file_path
|
from picklescan.scanner import scan_file_path
|
||||||
|
|
||||||
from invokeai.backend.globals import Globals, global_cache_dir
|
|
||||||
|
|
||||||
from transformers import (
|
from transformers import (
|
||||||
CLIPTextModel,
|
CLIPTextModel,
|
||||||
CLIPTokenizer,
|
CLIPTokenizer,
|
||||||
@ -49,9 +47,9 @@ from diffusers.pipelines.stable_diffusion.safety_checker import (
|
|||||||
from ..stable_diffusion import (
|
from ..stable_diffusion import (
|
||||||
StableDiffusionGeneratorPipeline,
|
StableDiffusionGeneratorPipeline,
|
||||||
)
|
)
|
||||||
|
from invokeai.app.services.config import InvokeAIAppConfig
|
||||||
from ..util import CUDA_DEVICE, ask_user, download_with_resume
|
from ..util import CUDA_DEVICE, ask_user, download_with_resume
|
||||||
|
|
||||||
|
|
||||||
class SDLegacyType(Enum):
|
class SDLegacyType(Enum):
|
||||||
V1 = auto()
|
V1 = auto()
|
||||||
V1_INPAINT = auto()
|
V1_INPAINT = auto()
|
||||||
@ -70,6 +68,7 @@ class SDModelComponent(Enum):
|
|||||||
feature_extractor="feature_extractor"
|
feature_extractor="feature_extractor"
|
||||||
|
|
||||||
DEFAULT_MAX_MODELS = 2
|
DEFAULT_MAX_MODELS = 2
|
||||||
|
config = InvokeAIAppConfig()
|
||||||
|
|
||||||
class ModelManager(object):
|
class ModelManager(object):
|
||||||
"""
|
"""
|
||||||
@ -292,7 +291,7 @@ class ModelManager(object):
|
|||||||
"""
|
"""
|
||||||
# if we are converting legacy files automatically, then
|
# if we are converting legacy files automatically, then
|
||||||
# there are no legacy ckpts!
|
# there are no legacy ckpts!
|
||||||
if Globals.ckpt_convert:
|
if config.ckpt_convert:
|
||||||
return False
|
return False
|
||||||
info = self.model_info(model_name)
|
info = self.model_info(model_name)
|
||||||
if "weights" in info and info["weights"].endswith((".ckpt", ".safetensors")):
|
if "weights" in info and info["weights"].endswith((".ckpt", ".safetensors")):
|
||||||
@ -502,13 +501,13 @@ class ModelManager(object):
|
|||||||
|
|
||||||
# TODO: scan weights maybe?
|
# TODO: scan weights maybe?
|
||||||
pipeline_args: dict[str, Any] = dict(
|
pipeline_args: dict[str, Any] = dict(
|
||||||
safety_checker=None, local_files_only=not Globals.internet_available
|
safety_checker=None, local_files_only=not config.internet_available
|
||||||
)
|
)
|
||||||
if "vae" in mconfig and mconfig["vae"] is not None:
|
if "vae" in mconfig and mconfig["vae"] is not None:
|
||||||
if vae := self._load_vae(mconfig["vae"]):
|
if vae := self._load_vae(mconfig["vae"]):
|
||||||
pipeline_args.update(vae=vae)
|
pipeline_args.update(vae=vae)
|
||||||
if not isinstance(name_or_path, Path):
|
if not isinstance(name_or_path, Path):
|
||||||
pipeline_args.update(cache_dir=global_cache_dir("hub"))
|
pipeline_args.update(cache_dir=config.cache_dir)
|
||||||
if using_fp16:
|
if using_fp16:
|
||||||
pipeline_args.update(torch_dtype=torch.float16)
|
pipeline_args.update(torch_dtype=torch.float16)
|
||||||
fp_args_list = [{"revision": "fp16"}, {}]
|
fp_args_list = [{"revision": "fp16"}, {}]
|
||||||
@ -561,9 +560,9 @@ class ModelManager(object):
|
|||||||
height = mconfig.height
|
height = mconfig.height
|
||||||
|
|
||||||
if not os.path.isabs(config):
|
if not os.path.isabs(config):
|
||||||
config = os.path.join(Globals.root, config)
|
config = os.path.join(config.root, config)
|
||||||
if not os.path.isabs(weights):
|
if not os.path.isabs(weights):
|
||||||
weights = os.path.normpath(os.path.join(Globals.root, weights))
|
weights = os.path.normpath(os.path.join(config.root, weights))
|
||||||
|
|
||||||
# Convert to diffusers and return a diffusers pipeline
|
# Convert to diffusers and return a diffusers pipeline
|
||||||
self.logger.info(f"Converting legacy checkpoint {model_name} into a diffusers model...")
|
self.logger.info(f"Converting legacy checkpoint {model_name} into a diffusers model...")
|
||||||
@ -581,7 +580,7 @@ class ModelManager(object):
|
|||||||
vae_path = (
|
vae_path = (
|
||||||
vae
|
vae
|
||||||
if os.path.isabs(vae)
|
if os.path.isabs(vae)
|
||||||
else os.path.normpath(os.path.join(Globals.root, vae))
|
else os.path.normpath(os.path.join(config.root, vae))
|
||||||
)
|
)
|
||||||
if self._has_cuda():
|
if self._has_cuda():
|
||||||
torch.cuda.empty_cache()
|
torch.cuda.empty_cache()
|
||||||
@ -616,7 +615,7 @@ class ModelManager(object):
|
|||||||
if "path" in mconfig and mconfig["path"] is not None:
|
if "path" in mconfig and mconfig["path"] is not None:
|
||||||
path = Path(mconfig["path"])
|
path = Path(mconfig["path"])
|
||||||
if not path.is_absolute():
|
if not path.is_absolute():
|
||||||
path = Path(Globals.root, path).resolve()
|
path = Path(config.root, path).resolve()
|
||||||
return path
|
return path
|
||||||
elif "repo_id" in mconfig:
|
elif "repo_id" in mconfig:
|
||||||
return mconfig["repo_id"]
|
return mconfig["repo_id"]
|
||||||
@ -864,25 +863,16 @@ class ModelManager(object):
|
|||||||
model_type = self.probe_model_type(checkpoint)
|
model_type = self.probe_model_type(checkpoint)
|
||||||
if model_type == SDLegacyType.V1:
|
if model_type == SDLegacyType.V1:
|
||||||
self.logger.debug("SD-v1 model detected")
|
self.logger.debug("SD-v1 model detected")
|
||||||
model_config_file = Path(
|
model_config_file = config.legacy_conf_path / "v1-inference.yaml"
|
||||||
Globals.root, "configs/stable-diffusion/v1-inference.yaml"
|
|
||||||
)
|
|
||||||
elif model_type == SDLegacyType.V1_INPAINT:
|
elif model_type == SDLegacyType.V1_INPAINT:
|
||||||
self.logger.debug("SD-v1 inpainting model detected")
|
self.logger.debug("SD-v1 inpainting model detected")
|
||||||
model_config_file = Path(
|
model_config_file = config.legacy_conf_path / "v1-inpainting-inference.yaml",
|
||||||
Globals.root,
|
|
||||||
"configs/stable-diffusion/v1-inpainting-inference.yaml",
|
|
||||||
)
|
|
||||||
elif model_type == SDLegacyType.V2_v:
|
elif model_type == SDLegacyType.V2_v:
|
||||||
self.logger.debug("SD-v2-v model detected")
|
self.logger.debug("SD-v2-v model detected")
|
||||||
model_config_file = Path(
|
model_config_file = config.legacy_conf_path / "v2-inference-v.yaml"
|
||||||
Globals.root, "configs/stable-diffusion/v2-inference-v.yaml"
|
|
||||||
)
|
|
||||||
elif model_type == SDLegacyType.V2_e:
|
elif model_type == SDLegacyType.V2_e:
|
||||||
self.logger.debug("SD-v2-e model detected")
|
self.logger.debug("SD-v2-e model detected")
|
||||||
model_config_file = Path(
|
model_config_file = config.legacy_conf_path / "v2-inference.yaml"
|
||||||
Globals.root, "configs/stable-diffusion/v2-inference.yaml"
|
|
||||||
)
|
|
||||||
elif model_type == SDLegacyType.V2:
|
elif model_type == SDLegacyType.V2:
|
||||||
self.logger.warning(
|
self.logger.warning(
|
||||||
f"{thing} is a V2 checkpoint file, but its parameterization cannot be determined. Please provide configuration file path."
|
f"{thing} is a V2 checkpoint file, but its parameterization cannot be determined. Please provide configuration file path."
|
||||||
@ -909,9 +899,7 @@ class ModelManager(object):
|
|||||||
self.logger.debug(f"Using VAE file {vae_path.name}")
|
self.logger.debug(f"Using VAE file {vae_path.name}")
|
||||||
vae = None if vae_path else dict(repo_id="stabilityai/sd-vae-ft-mse")
|
vae = None if vae_path else dict(repo_id="stabilityai/sd-vae-ft-mse")
|
||||||
|
|
||||||
diffuser_path = Path(
|
diffuser_path = config.root / "models/converted_ckpts" / model_path.stem
|
||||||
Globals.root, "models", Globals.converted_ckpts_dir, model_path.stem
|
|
||||||
)
|
|
||||||
model_name = self.convert_and_import(
|
model_name = self.convert_and_import(
|
||||||
model_path,
|
model_path,
|
||||||
diffusers_path=diffuser_path,
|
diffusers_path=diffuser_path,
|
||||||
@ -1044,9 +1032,7 @@ class ModelManager(object):
|
|||||||
"""
|
"""
|
||||||
yaml_str = OmegaConf.to_yaml(self.config)
|
yaml_str = OmegaConf.to_yaml(self.config)
|
||||||
if not os.path.isabs(config_file_path):
|
if not os.path.isabs(config_file_path):
|
||||||
config_file_path = os.path.normpath(
|
config_file_path = config.model_conf_path
|
||||||
os.path.join(Globals.root, config_file_path)
|
|
||||||
)
|
|
||||||
tmpfile = os.path.join(os.path.dirname(config_file_path), "new_config.tmp")
|
tmpfile = os.path.join(os.path.dirname(config_file_path), "new_config.tmp")
|
||||||
with open(tmpfile, "w", encoding="utf-8") as outfile:
|
with open(tmpfile, "w", encoding="utf-8") as outfile:
|
||||||
outfile.write(self.preamble())
|
outfile.write(self.preamble())
|
||||||
@ -1078,7 +1064,7 @@ class ModelManager(object):
|
|||||||
"""
|
"""
|
||||||
# Three transformer models to check: bert, clip and safety checker, and
|
# Three transformer models to check: bert, clip and safety checker, and
|
||||||
# the diffusers as well
|
# the diffusers as well
|
||||||
models_dir = Path(Globals.root, "models")
|
models_dir = config.root / "models"
|
||||||
legacy_locations = [
|
legacy_locations = [
|
||||||
Path(
|
Path(
|
||||||
models_dir,
|
models_dir,
|
||||||
@ -1090,8 +1076,8 @@ class ModelManager(object):
|
|||||||
"openai/clip-vit-large-patch14/models--openai--clip-vit-large-patch14",
|
"openai/clip-vit-large-patch14/models--openai--clip-vit-large-patch14",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
legacy_locations.extend(list(global_cache_dir("diffusers").glob("*")))
|
legacy_cache_dir = config.cache_dir / "../diffusers"
|
||||||
|
legacy_locations.extend(list(legacy_cache_dir.glob("*")))
|
||||||
legacy_layout = False
|
legacy_layout = False
|
||||||
for model in legacy_locations:
|
for model in legacy_locations:
|
||||||
legacy_layout = legacy_layout or model.exists()
|
legacy_layout = legacy_layout or model.exists()
|
||||||
@ -1113,7 +1099,7 @@ class ModelManager(object):
|
|||||||
|
|
||||||
# transformer files get moved into the hub directory
|
# transformer files get moved into the hub directory
|
||||||
if cls._is_huggingface_hub_directory_present():
|
if cls._is_huggingface_hub_directory_present():
|
||||||
hub = global_cache_dir("hub")
|
hub = config.cache_dir
|
||||||
else:
|
else:
|
||||||
hub = models_dir / "hub"
|
hub = models_dir / "hub"
|
||||||
|
|
||||||
@ -1152,12 +1138,12 @@ class ModelManager(object):
|
|||||||
if str(source).startswith(("http:", "https:", "ftp:")):
|
if str(source).startswith(("http:", "https:", "ftp:")):
|
||||||
dest_directory = Path(dest_directory)
|
dest_directory = Path(dest_directory)
|
||||||
if not dest_directory.is_absolute():
|
if not dest_directory.is_absolute():
|
||||||
dest_directory = Globals.root / dest_directory
|
dest_directory = config.root / dest_directory
|
||||||
dest_directory.mkdir(parents=True, exist_ok=True)
|
dest_directory.mkdir(parents=True, exist_ok=True)
|
||||||
resolved_path = download_with_resume(str(source), dest_directory)
|
resolved_path = download_with_resume(str(source), dest_directory)
|
||||||
else:
|
else:
|
||||||
if not os.path.isabs(source):
|
if not os.path.isabs(source):
|
||||||
source = os.path.join(Globals.root, source)
|
source = config.root / source
|
||||||
resolved_path = Path(source)
|
resolved_path = Path(source)
|
||||||
return resolved_path
|
return resolved_path
|
||||||
|
|
||||||
@ -1208,7 +1194,7 @@ class ModelManager(object):
|
|||||||
path = name_or_path
|
path = name_or_path
|
||||||
else:
|
else:
|
||||||
owner, repo = name_or_path.split("/")
|
owner, repo = name_or_path.split("/")
|
||||||
path = Path(global_cache_dir("hub") / f"models--{owner}--{repo}")
|
path = Path(config.cache_dir / f"models--{owner}--{repo}")
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
return None
|
return None
|
||||||
hashpath = path / "checksum.sha256"
|
hashpath = path / "checksum.sha256"
|
||||||
@ -1269,8 +1255,8 @@ class ModelManager(object):
|
|||||||
using_fp16 = self.precision == "float16"
|
using_fp16 = self.precision == "float16"
|
||||||
|
|
||||||
vae_args.update(
|
vae_args.update(
|
||||||
cache_dir=global_cache_dir("hub"),
|
cache_dir=config.cache_dir,
|
||||||
local_files_only=not Globals.internet_available,
|
local_files_only=not config.internet_available,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.logger.debug(f"Loading diffusers VAE from {name_or_path}")
|
self.logger.debug(f"Loading diffusers VAE from {name_or_path}")
|
||||||
@ -1308,7 +1294,7 @@ class ModelManager(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _delete_model_from_cache(cls,repo_id):
|
def _delete_model_from_cache(cls,repo_id):
|
||||||
cache_info = scan_cache_dir(global_cache_dir("hub"))
|
cache_info = scan_cache_dir(config.cache_dir)
|
||||||
|
|
||||||
# I'm sure there is a way to do this with comprehensions
|
# I'm sure there is a way to do this with comprehensions
|
||||||
# but the code quickly became incomprehensible!
|
# but the code quickly became incomprehensible!
|
||||||
@ -1327,7 +1313,7 @@ class ModelManager(object):
|
|||||||
def _abs_path(path: str | Path) -> Path:
|
def _abs_path(path: str | Path) -> Path:
|
||||||
if path is None or Path(path).is_absolute():
|
if path is None or Path(path).is_absolute():
|
||||||
return path
|
return path
|
||||||
return Path(Globals.root, path).resolve()
|
return Path(config.root, path).resolve()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _is_huggingface_hub_directory_present() -> bool:
|
def _is_huggingface_hub_directory_present() -> bool:
|
||||||
|
@ -19,11 +19,12 @@ from compel.prompt_parser import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
import invokeai.backend.util.logging as logger
|
import invokeai.backend.util.logging as logger
|
||||||
from invokeai.backend.globals import Globals
|
|
||||||
|
|
||||||
|
from invokeai.app.services.config import InvokeAIAppConfig
|
||||||
from ..stable_diffusion import InvokeAIDiffuserComponent
|
from ..stable_diffusion import InvokeAIDiffuserComponent
|
||||||
from ..util import torch_dtype
|
from ..util import torch_dtype
|
||||||
|
|
||||||
|
config = InvokeAIAppConfig()
|
||||||
|
|
||||||
def get_uc_and_c_and_ec(
|
def get_uc_and_c_and_ec(
|
||||||
prompt_string, model, log_tokens=False, skip_normalize_legacy_blend=False
|
prompt_string, model, log_tokens=False, skip_normalize_legacy_blend=False
|
||||||
@ -61,7 +62,7 @@ def get_uc_and_c_and_ec(
|
|||||||
negative_prompt_string
|
negative_prompt_string
|
||||||
)
|
)
|
||||||
|
|
||||||
if log_tokens or getattr(Globals, "log_tokenization", False):
|
if log_tokens or config.log_tokenization:
|
||||||
log_tokenization(positive_prompt, negative_prompt, tokenizer=tokenizer)
|
log_tokenization(positive_prompt, negative_prompt, tokenizer=tokenizer)
|
||||||
|
|
||||||
c, options = compel.build_conditioning_tensor_for_prompt_object(positive_prompt)
|
c, options = compel.build_conditioning_tensor_for_prompt_object(positive_prompt)
|
||||||
|
@ -6,7 +6,8 @@ import numpy as np
|
|||||||
import torch
|
import torch
|
||||||
|
|
||||||
import invokeai.backend.util.logging as logger
|
import invokeai.backend.util.logging as logger
|
||||||
from ..globals import Globals
|
from invokeai.app.services.config import InvokeAIAppConfig
|
||||||
|
config = InvokeAIAppConfig()
|
||||||
|
|
||||||
pretrained_model_url = (
|
pretrained_model_url = (
|
||||||
"https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/codeformer.pth"
|
"https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/codeformer.pth"
|
||||||
@ -18,7 +19,7 @@ class CodeFormerRestoration:
|
|||||||
self, codeformer_dir="models/codeformer", codeformer_model_path="codeformer.pth"
|
self, codeformer_dir="models/codeformer", codeformer_model_path="codeformer.pth"
|
||||||
) -> None:
|
) -> None:
|
||||||
if not os.path.isabs(codeformer_dir):
|
if not os.path.isabs(codeformer_dir):
|
||||||
codeformer_dir = os.path.join(Globals.root, codeformer_dir)
|
codeformer_dir = os.path.join(config.root, codeformer_dir)
|
||||||
|
|
||||||
self.model_path = os.path.join(codeformer_dir, codeformer_model_path)
|
self.model_path = os.path.join(codeformer_dir, codeformer_model_path)
|
||||||
self.codeformer_model_exists = os.path.isfile(self.model_path)
|
self.codeformer_model_exists = os.path.isfile(self.model_path)
|
||||||
@ -72,7 +73,7 @@ class CodeFormerRestoration:
|
|||||||
use_parse=True,
|
use_parse=True,
|
||||||
device=device,
|
device=device,
|
||||||
model_rootpath=os.path.join(
|
model_rootpath=os.path.join(
|
||||||
Globals.root, "models", "gfpgan", "weights"
|
config.root, "models", "gfpgan", "weights"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
face_helper.clean_all()
|
face_helper.clean_all()
|
||||||
|
@ -7,13 +7,14 @@ import torch
|
|||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
import invokeai.backend.util.logging as logger
|
import invokeai.backend.util.logging as logger
|
||||||
from invokeai.backend.globals import Globals
|
from invokeai.app.services.config import InvokeAIAppConfig
|
||||||
|
config = InvokeAIAppConfig()
|
||||||
|
|
||||||
class GFPGAN:
|
class GFPGAN:
|
||||||
def __init__(self, gfpgan_model_path="models/gfpgan/GFPGANv1.4.pth") -> None:
|
def __init__(self, gfpgan_model_path="models/gfpgan/GFPGANv1.4.pth") -> None:
|
||||||
if not os.path.isabs(gfpgan_model_path):
|
if not os.path.isabs(gfpgan_model_path):
|
||||||
gfpgan_model_path = os.path.abspath(
|
gfpgan_model_path = os.path.abspath(
|
||||||
os.path.join(Globals.root, gfpgan_model_path)
|
os.path.join(config.root, gfpgan_model_path)
|
||||||
)
|
)
|
||||||
self.model_path = gfpgan_model_path
|
self.model_path = gfpgan_model_path
|
||||||
self.gfpgan_model_exists = os.path.isfile(self.model_path)
|
self.gfpgan_model_exists = os.path.isfile(self.model_path)
|
||||||
@ -33,7 +34,7 @@ class GFPGAN:
|
|||||||
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
||||||
warnings.filterwarnings("ignore", category=UserWarning)
|
warnings.filterwarnings("ignore", category=UserWarning)
|
||||||
cwd = os.getcwd()
|
cwd = os.getcwd()
|
||||||
os.chdir(os.path.join(Globals.root, "models"))
|
os.chdir(os.path.join(config.root, "models"))
|
||||||
try:
|
try:
|
||||||
from gfpgan import GFPGANer
|
from gfpgan import GFPGANer
|
||||||
|
|
||||||
|
@ -15,9 +15,11 @@ from transformers import AutoFeatureExtractor
|
|||||||
|
|
||||||
import invokeai.assets.web as web_assets
|
import invokeai.assets.web as web_assets
|
||||||
import invokeai.backend.util.logging as logger
|
import invokeai.backend.util.logging as logger
|
||||||
from .globals import global_cache_dir
|
from invokeai.app.services.config import InvokeAIAppConfig
|
||||||
from .util import CPU_DEVICE
|
from .util import CPU_DEVICE
|
||||||
|
|
||||||
|
config = InvokeAIAppConfig()
|
||||||
|
|
||||||
class SafetyChecker(object):
|
class SafetyChecker(object):
|
||||||
CAUTION_IMG = "caution.png"
|
CAUTION_IMG = "caution.png"
|
||||||
|
|
||||||
@ -29,7 +31,7 @@ class SafetyChecker(object):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
safety_model_id = "CompVis/stable-diffusion-safety-checker"
|
safety_model_id = "CompVis/stable-diffusion-safety-checker"
|
||||||
safety_model_path = global_cache_dir("hub")
|
safety_model_path = config.cache_dir
|
||||||
self.safety_checker = StableDiffusionSafetyChecker.from_pretrained(
|
self.safety_checker = StableDiffusionSafetyChecker.from_pretrained(
|
||||||
safety_model_id,
|
safety_model_id,
|
||||||
local_files_only=True,
|
local_files_only=True,
|
||||||
|
@ -18,15 +18,15 @@ from huggingface_hub import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
import invokeai.backend.util.logging as logger
|
import invokeai.backend.util.logging as logger
|
||||||
from invokeai.backend.globals import Globals
|
from invokeai.app.services.config import InvokeAIAppConfig
|
||||||
|
config = InvokeAIAppConfig()
|
||||||
|
|
||||||
class HuggingFaceConceptsLibrary(object):
|
class HuggingFaceConceptsLibrary(object):
|
||||||
def __init__(self, root=None):
|
def __init__(self, root=None):
|
||||||
"""
|
"""
|
||||||
Initialize the Concepts object. May optionally pass a root directory.
|
Initialize the Concepts object. May optionally pass a root directory.
|
||||||
"""
|
"""
|
||||||
self.root = root or Globals.root
|
self.root = root or config.root
|
||||||
self.hf_api = HfApi()
|
self.hf_api = HfApi()
|
||||||
self.local_concepts = dict()
|
self.local_concepts = dict()
|
||||||
self.concept_list = None
|
self.concept_list = None
|
||||||
@ -58,7 +58,7 @@ class HuggingFaceConceptsLibrary(object):
|
|||||||
self.concept_list.extend(list(local_concepts_to_add))
|
self.concept_list.extend(list(local_concepts_to_add))
|
||||||
return self.concept_list
|
return self.concept_list
|
||||||
return self.concept_list
|
return self.concept_list
|
||||||
elif Globals.internet_available is True:
|
elif config.internet_available is True:
|
||||||
try:
|
try:
|
||||||
models = self.hf_api.list_models(
|
models = self.hf_api.list_models(
|
||||||
filter=ModelFilter(model_name="sd-concepts-library/")
|
filter=ModelFilter(model_name="sd-concepts-library/")
|
||||||
|
@ -33,8 +33,7 @@ from torchvision.transforms.functional import resize as tv_resize
|
|||||||
from transformers import CLIPFeatureExtractor, CLIPTextModel, CLIPTokenizer
|
from transformers import CLIPFeatureExtractor, CLIPTextModel, CLIPTokenizer
|
||||||
from typing_extensions import ParamSpec
|
from typing_extensions import ParamSpec
|
||||||
|
|
||||||
from invokeai.backend.globals import Globals
|
from invokeai.app.services.config import InvokeAIAppConfig
|
||||||
|
|
||||||
from ..util import CPU_DEVICE, normalize_device
|
from ..util import CPU_DEVICE, normalize_device
|
||||||
from .diffusion import (
|
from .diffusion import (
|
||||||
AttentionMapSaver,
|
AttentionMapSaver,
|
||||||
@ -44,6 +43,7 @@ from .diffusion import (
|
|||||||
from .offloading import FullyLoadedModelGroup, LazilyLoadedModelGroup, ModelGroup
|
from .offloading import FullyLoadedModelGroup, LazilyLoadedModelGroup, ModelGroup
|
||||||
from .textual_inversion_manager import TextualInversionManager
|
from .textual_inversion_manager import TextualInversionManager
|
||||||
|
|
||||||
|
config = InvokeAIAppConfig()
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PipelineIntermediateState:
|
class PipelineIntermediateState:
|
||||||
@ -351,7 +351,7 @@ class StableDiffusionGeneratorPipeline(StableDiffusionPipeline):
|
|||||||
if (
|
if (
|
||||||
torch.cuda.is_available()
|
torch.cuda.is_available()
|
||||||
and is_xformers_available()
|
and is_xformers_available()
|
||||||
and not Globals.disable_xformers
|
and not config.disable_xformers
|
||||||
):
|
):
|
||||||
self.enable_xformers_memory_efficient_attention()
|
self.enable_xformers_memory_efficient_attention()
|
||||||
else:
|
else:
|
||||||
|
@ -9,7 +9,7 @@ from diffusers.models.attention_processor import AttentionProcessor
|
|||||||
from typing_extensions import TypeAlias
|
from typing_extensions import TypeAlias
|
||||||
|
|
||||||
import invokeai.backend.util.logging as logger
|
import invokeai.backend.util.logging as logger
|
||||||
from invokeai.backend.globals import Globals
|
from invokeai.app.services.config import InvokeAIAppConfig
|
||||||
|
|
||||||
from .cross_attention_control import (
|
from .cross_attention_control import (
|
||||||
Arguments,
|
Arguments,
|
||||||
@ -31,6 +31,7 @@ ModelForwardCallback: TypeAlias = Union[
|
|||||||
Callable[[torch.Tensor, torch.Tensor, torch.Tensor], torch.Tensor],
|
Callable[[torch.Tensor, torch.Tensor, torch.Tensor], torch.Tensor],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
config = InvokeAIAppConfig()
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class PostprocessingSettings:
|
class PostprocessingSettings:
|
||||||
@ -77,7 +78,7 @@ class InvokeAIDiffuserComponent:
|
|||||||
self.is_running_diffusers = is_running_diffusers
|
self.is_running_diffusers = is_running_diffusers
|
||||||
self.model_forward_callback = model_forward_callback
|
self.model_forward_callback = model_forward_callback
|
||||||
self.cross_attention_control_context = None
|
self.cross_attention_control_context = None
|
||||||
self.sequential_guidance = Globals.sequential_guidance
|
self.sequential_guidance = config.sequential_guidance
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def custom_attention_context(
|
def custom_attention_context(
|
||||||
|
@ -4,17 +4,16 @@ from contextlib import nullcontext
|
|||||||
|
|
||||||
import torch
|
import torch
|
||||||
from torch import autocast
|
from torch import autocast
|
||||||
|
from invokeai.app.services.config import InvokeAIAppConfig
|
||||||
from invokeai.backend.globals import Globals
|
|
||||||
|
|
||||||
CPU_DEVICE = torch.device("cpu")
|
CPU_DEVICE = torch.device("cpu")
|
||||||
CUDA_DEVICE = torch.device("cuda")
|
CUDA_DEVICE = torch.device("cuda")
|
||||||
MPS_DEVICE = torch.device("mps")
|
MPS_DEVICE = torch.device("mps")
|
||||||
|
config = InvokeAIAppConfig()
|
||||||
|
|
||||||
def choose_torch_device() -> torch.device:
|
def choose_torch_device() -> torch.device:
|
||||||
"""Convenience routine for guessing which GPU device to run model on"""
|
"""Convenience routine for guessing which GPU device to run model on"""
|
||||||
if Globals.always_use_cpu:
|
if config.always_use_cpu:
|
||||||
return CPU_DEVICE
|
return CPU_DEVICE
|
||||||
if torch.cuda.is_available():
|
if torch.cuda.is_available():
|
||||||
return torch.device("cuda")
|
return torch.device("cuda")
|
||||||
@ -33,7 +32,7 @@ def choose_precision(device: torch.device) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def torch_dtype(device: torch.device) -> torch.dtype:
|
def torch_dtype(device: torch.device) -> torch.dtype:
|
||||||
if Globals.full_precision:
|
if config.full_precision:
|
||||||
return torch.float32
|
return torch.float32
|
||||||
if choose_precision(device) == "float16":
|
if choose_precision(device) == "float16":
|
||||||
return torch.float16
|
return torch.float16
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,497 +0,0 @@
|
|||||||
"""
|
|
||||||
Readline helper functions for invoke.py.
|
|
||||||
You may import the global singleton `completer` to get access to the
|
|
||||||
completer object itself. This is useful when you want to autocomplete
|
|
||||||
seeds:
|
|
||||||
|
|
||||||
from invokeai.frontend.CLI.readline import completer
|
|
||||||
completer.add_seed(18247566)
|
|
||||||
completer.add_seed(9281839)
|
|
||||||
"""
|
|
||||||
import atexit
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
|
|
||||||
from ...backend.args import Args
|
|
||||||
from ...backend.globals import Globals
|
|
||||||
from ...backend.stable_diffusion import HuggingFaceConceptsLibrary
|
|
||||||
|
|
||||||
# ---------------readline utilities---------------------
|
|
||||||
try:
|
|
||||||
import readline
|
|
||||||
|
|
||||||
readline_available = True
|
|
||||||
except (ImportError, ModuleNotFoundError) as e:
|
|
||||||
print(f"** An error occurred when loading the readline module: {str(e)}")
|
|
||||||
readline_available = False
|
|
||||||
|
|
||||||
IMG_EXTENSIONS = (".png", ".jpg", ".jpeg", ".PNG", ".JPG", ".JPEG", ".gif", ".GIF")
|
|
||||||
WEIGHT_EXTENSIONS = (".ckpt", ".vae", ".safetensors")
|
|
||||||
TEXT_EXTENSIONS = (".txt", ".TXT")
|
|
||||||
CONFIG_EXTENSIONS = (".yaml", ".yml")
|
|
||||||
COMMANDS = (
|
|
||||||
"--steps",
|
|
||||||
"-s",
|
|
||||||
"--seed",
|
|
||||||
"-S",
|
|
||||||
"--iterations",
|
|
||||||
"-n",
|
|
||||||
"--width",
|
|
||||||
"-W",
|
|
||||||
"--height",
|
|
||||||
"-H",
|
|
||||||
"--cfg_scale",
|
|
||||||
"-C",
|
|
||||||
"--threshold",
|
|
||||||
"--perlin",
|
|
||||||
"--grid",
|
|
||||||
"-g",
|
|
||||||
"--individual",
|
|
||||||
"-i",
|
|
||||||
"--save_intermediates",
|
|
||||||
"--init_img",
|
|
||||||
"-I",
|
|
||||||
"--init_mask",
|
|
||||||
"-M",
|
|
||||||
"--init_color",
|
|
||||||
"--strength",
|
|
||||||
"-f",
|
|
||||||
"--variants",
|
|
||||||
"-v",
|
|
||||||
"--outdir",
|
|
||||||
"-o",
|
|
||||||
"--sampler",
|
|
||||||
"-A",
|
|
||||||
"-m",
|
|
||||||
"--embedding_path",
|
|
||||||
"--device",
|
|
||||||
"--grid",
|
|
||||||
"-g",
|
|
||||||
"--facetool",
|
|
||||||
"-ft",
|
|
||||||
"--facetool_strength",
|
|
||||||
"-G",
|
|
||||||
"--codeformer_fidelity",
|
|
||||||
"-cf",
|
|
||||||
"--upscale",
|
|
||||||
"-U",
|
|
||||||
"-save_orig",
|
|
||||||
"--save_original",
|
|
||||||
"--log_tokenization",
|
|
||||||
"-t",
|
|
||||||
"--hires_fix",
|
|
||||||
"--inpaint_replace",
|
|
||||||
"-r",
|
|
||||||
"--png_compression",
|
|
||||||
"-z",
|
|
||||||
"--text_mask",
|
|
||||||
"-tm",
|
|
||||||
"--h_symmetry_time_pct",
|
|
||||||
"--v_symmetry_time_pct",
|
|
||||||
"!fix",
|
|
||||||
"!fetch",
|
|
||||||
"!replay",
|
|
||||||
"!history",
|
|
||||||
"!search",
|
|
||||||
"!clear",
|
|
||||||
"!models",
|
|
||||||
"!switch",
|
|
||||||
"!import_model",
|
|
||||||
"!optimize_model",
|
|
||||||
"!convert_model",
|
|
||||||
"!edit_model",
|
|
||||||
"!del_model",
|
|
||||||
"!mask",
|
|
||||||
"!triggers",
|
|
||||||
)
|
|
||||||
MODEL_COMMANDS = (
|
|
||||||
"!switch",
|
|
||||||
"!edit_model",
|
|
||||||
"!del_model",
|
|
||||||
)
|
|
||||||
CKPT_MODEL_COMMANDS = ("!optimize_model",)
|
|
||||||
WEIGHT_COMMANDS = (
|
|
||||||
"!import_model",
|
|
||||||
"!convert_model",
|
|
||||||
)
|
|
||||||
IMG_PATH_COMMANDS = ("--outdir[=\s]",)
|
|
||||||
TEXT_PATH_COMMANDS = ("!replay",)
|
|
||||||
IMG_FILE_COMMANDS = (
|
|
||||||
"!fix",
|
|
||||||
"!fetch",
|
|
||||||
"!mask",
|
|
||||||
"--init_img[=\s]",
|
|
||||||
"-I",
|
|
||||||
"--init_mask[=\s]",
|
|
||||||
"-M",
|
|
||||||
"--init_color[=\s]",
|
|
||||||
"--embedding_path[=\s]",
|
|
||||||
)
|
|
||||||
|
|
||||||
path_regexp = "(" + "|".join(IMG_PATH_COMMANDS + IMG_FILE_COMMANDS) + ")\s*\S*$"
|
|
||||||
weight_regexp = "(" + "|".join(WEIGHT_COMMANDS) + ")\s*\S*$"
|
|
||||||
text_regexp = "(" + "|".join(TEXT_PATH_COMMANDS) + ")\s*\S*$"
|
|
||||||
|
|
||||||
|
|
||||||
class Completer(object):
|
|
||||||
def __init__(self, options, models={}):
|
|
||||||
self.options = sorted(options)
|
|
||||||
self.models = models
|
|
||||||
self.seeds = set()
|
|
||||||
self.matches = list()
|
|
||||||
self.default_dir = None
|
|
||||||
self.linebuffer = None
|
|
||||||
self.auto_history_active = True
|
|
||||||
self.extensions = None
|
|
||||||
self.concepts = None
|
|
||||||
self.embedding_terms = set()
|
|
||||||
return
|
|
||||||
|
|
||||||
def complete(self, text, state):
|
|
||||||
"""
|
|
||||||
Completes invoke command line.
|
|
||||||
BUG: it doesn't correctly complete files that have spaces in the name.
|
|
||||||
"""
|
|
||||||
buffer = readline.get_line_buffer()
|
|
||||||
|
|
||||||
if state == 0:
|
|
||||||
# extensions defined, so go directly into path completion mode
|
|
||||||
if self.extensions is not None:
|
|
||||||
self.matches = self._path_completions(text, state, self.extensions)
|
|
||||||
|
|
||||||
# looking for an image file
|
|
||||||
elif re.search(path_regexp, buffer):
|
|
||||||
do_shortcut = re.search("^" + "|".join(IMG_FILE_COMMANDS), buffer)
|
|
||||||
self.matches = self._path_completions(
|
|
||||||
text, state, IMG_EXTENSIONS, shortcut_ok=do_shortcut
|
|
||||||
)
|
|
||||||
|
|
||||||
# looking for a seed
|
|
||||||
elif re.search("(-S\s*|--seed[=\s])\d*$", buffer):
|
|
||||||
self.matches = self._seed_completions(text, state)
|
|
||||||
|
|
||||||
# looking for an embedding concept
|
|
||||||
elif re.search("<[\w-]*$", buffer):
|
|
||||||
self.matches = self._concept_completions(text, state)
|
|
||||||
|
|
||||||
# looking for a model
|
|
||||||
elif re.match("^" + "|".join(MODEL_COMMANDS), buffer):
|
|
||||||
self.matches = self._model_completions(text, state)
|
|
||||||
|
|
||||||
# looking for a ckpt model
|
|
||||||
elif re.match("^" + "|".join(CKPT_MODEL_COMMANDS), buffer):
|
|
||||||
self.matches = self._model_completions(text, state, ckpt_only=True)
|
|
||||||
|
|
||||||
elif re.search(weight_regexp, buffer):
|
|
||||||
self.matches = self._path_completions(
|
|
||||||
text,
|
|
||||||
state,
|
|
||||||
WEIGHT_EXTENSIONS,
|
|
||||||
default_dir=Globals.root,
|
|
||||||
)
|
|
||||||
|
|
||||||
elif re.search(text_regexp, buffer):
|
|
||||||
self.matches = self._path_completions(text, state, TEXT_EXTENSIONS)
|
|
||||||
|
|
||||||
# This is the first time for this text, so build a match list.
|
|
||||||
elif text:
|
|
||||||
self.matches = [s for s in self.options if s and s.startswith(text)]
|
|
||||||
else:
|
|
||||||
self.matches = self.options[:]
|
|
||||||
|
|
||||||
# Return the state'th item from the match list,
|
|
||||||
# if we have that many.
|
|
||||||
try:
|
|
||||||
response = self.matches[state]
|
|
||||||
except IndexError:
|
|
||||||
response = None
|
|
||||||
return response
|
|
||||||
|
|
||||||
def complete_extensions(self, extensions: list):
|
|
||||||
"""
|
|
||||||
If called with a list of extensions, will force completer
|
|
||||||
to do file path completions.
|
|
||||||
"""
|
|
||||||
self.extensions = extensions
|
|
||||||
|
|
||||||
def add_history(self, line):
|
|
||||||
"""
|
|
||||||
Pass thru to readline
|
|
||||||
"""
|
|
||||||
if not self.auto_history_active:
|
|
||||||
readline.add_history(line)
|
|
||||||
|
|
||||||
def clear_history(self):
|
|
||||||
"""
|
|
||||||
Pass clear_history() thru to readline
|
|
||||||
"""
|
|
||||||
readline.clear_history()
|
|
||||||
|
|
||||||
def search_history(self, match: str):
|
|
||||||
"""
|
|
||||||
Like show_history() but only shows items that
|
|
||||||
contain the match string.
|
|
||||||
"""
|
|
||||||
self.show_history(match)
|
|
||||||
|
|
||||||
def remove_history_item(self, pos):
|
|
||||||
readline.remove_history_item(pos)
|
|
||||||
|
|
||||||
def add_seed(self, seed):
|
|
||||||
"""
|
|
||||||
Add a seed to the autocomplete list for display when -S is autocompleted.
|
|
||||||
"""
|
|
||||||
if seed is not None:
|
|
||||||
self.seeds.add(str(seed))
|
|
||||||
|
|
||||||
def set_default_dir(self, path):
|
|
||||||
self.default_dir = path
|
|
||||||
|
|
||||||
def set_options(self, options):
|
|
||||||
self.options = options
|
|
||||||
|
|
||||||
def get_line(self, index):
|
|
||||||
try:
|
|
||||||
line = self.get_history_item(index)
|
|
||||||
except IndexError:
|
|
||||||
return None
|
|
||||||
return line
|
|
||||||
|
|
||||||
def get_current_history_length(self):
|
|
||||||
return readline.get_current_history_length()
|
|
||||||
|
|
||||||
def get_history_item(self, index):
|
|
||||||
return readline.get_history_item(index)
|
|
||||||
|
|
||||||
def show_history(self, match=None):
|
|
||||||
"""
|
|
||||||
Print the session history using the pydoc pager
|
|
||||||
"""
|
|
||||||
import pydoc
|
|
||||||
|
|
||||||
lines = list()
|
|
||||||
h_len = self.get_current_history_length()
|
|
||||||
if h_len < 1:
|
|
||||||
print("<empty history>")
|
|
||||||
return
|
|
||||||
|
|
||||||
for i in range(0, h_len):
|
|
||||||
line = self.get_history_item(i + 1)
|
|
||||||
if match and match not in line:
|
|
||||||
continue
|
|
||||||
lines.append(f"[{i+1}] {line}")
|
|
||||||
pydoc.pager("\n".join(lines))
|
|
||||||
|
|
||||||
def set_line(self, line) -> None:
|
|
||||||
"""
|
|
||||||
Set the default string displayed in the next line of input.
|
|
||||||
"""
|
|
||||||
self.linebuffer = line
|
|
||||||
readline.redisplay()
|
|
||||||
|
|
||||||
def update_models(self, models: dict) -> None:
|
|
||||||
"""
|
|
||||||
update our list of models
|
|
||||||
"""
|
|
||||||
self.models = models
|
|
||||||
|
|
||||||
def _seed_completions(self, text, state):
|
|
||||||
m = re.search("(-S\s?|--seed[=\s]?)(\d*)", text)
|
|
||||||
if m:
|
|
||||||
switch = m.groups()[0]
|
|
||||||
partial = m.groups()[1]
|
|
||||||
else:
|
|
||||||
switch = ""
|
|
||||||
partial = text
|
|
||||||
|
|
||||||
matches = list()
|
|
||||||
for s in self.seeds:
|
|
||||||
if s.startswith(partial):
|
|
||||||
matches.append(switch + s)
|
|
||||||
matches.sort()
|
|
||||||
return matches
|
|
||||||
|
|
||||||
def add_embedding_terms(self, terms: list[str]):
|
|
||||||
self.embedding_terms = set(terms)
|
|
||||||
if self.concepts:
|
|
||||||
self.embedding_terms.update(set(self.concepts.list_concepts()))
|
|
||||||
|
|
||||||
def _concept_completions(self, text, state):
|
|
||||||
if self.concepts is None:
|
|
||||||
# cache Concepts() instance so we can check for updates in concepts_list during runtime.
|
|
||||||
self.concepts = HuggingFaceConceptsLibrary()
|
|
||||||
self.embedding_terms.update(set(self.concepts.list_concepts()))
|
|
||||||
else:
|
|
||||||
self.embedding_terms.update(set(self.concepts.list_concepts()))
|
|
||||||
|
|
||||||
partial = text[1:] # this removes the leading '<'
|
|
||||||
if len(partial) == 0:
|
|
||||||
return list(self.embedding_terms) # whole dump - think if user wants this!
|
|
||||||
|
|
||||||
matches = list()
|
|
||||||
for concept in self.embedding_terms:
|
|
||||||
if concept.startswith(partial):
|
|
||||||
matches.append(f"<{concept}>")
|
|
||||||
matches.sort()
|
|
||||||
return matches
|
|
||||||
|
|
||||||
def _model_completions(self, text, state, ckpt_only=False):
|
|
||||||
m = re.search("(!switch\s+)(\w*)", text)
|
|
||||||
if m:
|
|
||||||
switch = m.groups()[0]
|
|
||||||
partial = m.groups()[1]
|
|
||||||
else:
|
|
||||||
switch = ""
|
|
||||||
partial = text
|
|
||||||
matches = list()
|
|
||||||
for s in self.models:
|
|
||||||
format = self.models[s]["format"]
|
|
||||||
if format == "vae":
|
|
||||||
continue
|
|
||||||
if ckpt_only and format != "ckpt":
|
|
||||||
continue
|
|
||||||
if s.startswith(partial):
|
|
||||||
matches.append(switch + s)
|
|
||||||
matches.sort()
|
|
||||||
return matches
|
|
||||||
|
|
||||||
def _pre_input_hook(self):
|
|
||||||
if self.linebuffer:
|
|
||||||
readline.insert_text(self.linebuffer)
|
|
||||||
readline.redisplay()
|
|
||||||
self.linebuffer = None
|
|
||||||
|
|
||||||
def _path_completions(
|
|
||||||
self, text, state, extensions, shortcut_ok=True, default_dir: str = ""
|
|
||||||
):
|
|
||||||
# separate the switch from the partial path
|
|
||||||
match = re.search("^(-\w|--\w+=?)(.*)", text)
|
|
||||||
if match is None:
|
|
||||||
switch = None
|
|
||||||
partial_path = text
|
|
||||||
else:
|
|
||||||
switch, partial_path = match.groups()
|
|
||||||
|
|
||||||
partial_path = partial_path.lstrip()
|
|
||||||
|
|
||||||
matches = list()
|
|
||||||
path = os.path.expanduser(partial_path)
|
|
||||||
|
|
||||||
if os.path.isdir(path):
|
|
||||||
dir = path
|
|
||||||
elif os.path.dirname(path) != "":
|
|
||||||
dir = os.path.dirname(path)
|
|
||||||
else:
|
|
||||||
dir = default_dir if os.path.exists(default_dir) else ""
|
|
||||||
path = os.path.join(dir, path)
|
|
||||||
|
|
||||||
dir_list = os.listdir(dir or ".")
|
|
||||||
if shortcut_ok and os.path.exists(self.default_dir) and dir == "":
|
|
||||||
dir_list += os.listdir(self.default_dir)
|
|
||||||
|
|
||||||
for node in dir_list:
|
|
||||||
if node.startswith(".") and len(node) > 1:
|
|
||||||
continue
|
|
||||||
full_path = os.path.join(dir, node)
|
|
||||||
|
|
||||||
if not (node.endswith(extensions) or os.path.isdir(full_path)):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if path and not full_path.startswith(path):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if switch is None:
|
|
||||||
match_path = os.path.join(dir, node)
|
|
||||||
matches.append(
|
|
||||||
match_path + "/" if os.path.isdir(full_path) else match_path
|
|
||||||
)
|
|
||||||
elif os.path.isdir(full_path):
|
|
||||||
matches.append(
|
|
||||||
switch + os.path.join(os.path.dirname(full_path), node) + "/"
|
|
||||||
)
|
|
||||||
elif node.endswith(extensions):
|
|
||||||
matches.append(switch + os.path.join(os.path.dirname(full_path), node))
|
|
||||||
|
|
||||||
return matches
|
|
||||||
|
|
||||||
|
|
||||||
class DummyCompleter(Completer):
|
|
||||||
def __init__(self, options):
|
|
||||||
super().__init__(options)
|
|
||||||
self.history = list()
|
|
||||||
|
|
||||||
def add_history(self, line):
|
|
||||||
self.history.append(line)
|
|
||||||
|
|
||||||
def clear_history(self):
|
|
||||||
self.history = list()
|
|
||||||
|
|
||||||
def get_current_history_length(self):
|
|
||||||
return len(self.history)
|
|
||||||
|
|
||||||
def get_history_item(self, index):
|
|
||||||
return self.history[index - 1]
|
|
||||||
|
|
||||||
def remove_history_item(self, index):
|
|
||||||
return self.history.pop(index - 1)
|
|
||||||
|
|
||||||
def set_line(self, line):
|
|
||||||
print(f"# {line}")
|
|
||||||
|
|
||||||
|
|
||||||
def generic_completer(commands: list) -> Completer:
|
|
||||||
if readline_available:
|
|
||||||
completer = Completer(commands, [])
|
|
||||||
readline.set_completer(completer.complete)
|
|
||||||
readline.set_pre_input_hook(completer._pre_input_hook)
|
|
||||||
readline.set_completer_delims(" ")
|
|
||||||
readline.parse_and_bind("tab: complete")
|
|
||||||
readline.parse_and_bind("set print-completions-horizontally off")
|
|
||||||
readline.parse_and_bind("set page-completions on")
|
|
||||||
readline.parse_and_bind("set skip-completed-text on")
|
|
||||||
readline.parse_and_bind("set show-all-if-ambiguous on")
|
|
||||||
else:
|
|
||||||
completer = DummyCompleter(commands)
|
|
||||||
return completer
|
|
||||||
|
|
||||||
|
|
||||||
def get_completer(opt: Args, models=[]) -> Completer:
|
|
||||||
if readline_available:
|
|
||||||
completer = Completer(COMMANDS, models)
|
|
||||||
|
|
||||||
readline.set_completer(completer.complete)
|
|
||||||
# pyreadline3 does not have a set_auto_history() method
|
|
||||||
try:
|
|
||||||
readline.set_auto_history(False)
|
|
||||||
completer.auto_history_active = False
|
|
||||||
except:
|
|
||||||
completer.auto_history_active = True
|
|
||||||
readline.set_pre_input_hook(completer._pre_input_hook)
|
|
||||||
readline.set_completer_delims(" ")
|
|
||||||
readline.parse_and_bind("tab: complete")
|
|
||||||
readline.parse_and_bind("set print-completions-horizontally off")
|
|
||||||
readline.parse_and_bind("set page-completions on")
|
|
||||||
readline.parse_and_bind("set skip-completed-text on")
|
|
||||||
readline.parse_and_bind("set show-all-if-ambiguous on")
|
|
||||||
|
|
||||||
outdir = os.path.expanduser(opt.outdir)
|
|
||||||
if os.path.isabs(outdir):
|
|
||||||
histfile = os.path.join(outdir, ".invoke_history")
|
|
||||||
else:
|
|
||||||
histfile = os.path.join(Globals.root, outdir, ".invoke_history")
|
|
||||||
try:
|
|
||||||
readline.read_history_file(histfile)
|
|
||||||
readline.set_history_length(1000)
|
|
||||||
except FileNotFoundError:
|
|
||||||
pass
|
|
||||||
except OSError: # file likely corrupted
|
|
||||||
newname = f"{histfile}.old"
|
|
||||||
print(
|
|
||||||
f"## Your history file {histfile} couldn't be loaded and may be corrupted. Renaming it to {newname}"
|
|
||||||
)
|
|
||||||
os.replace(histfile, newname)
|
|
||||||
atexit.register(readline.write_history_file, histfile)
|
|
||||||
|
|
||||||
else:
|
|
||||||
completer = DummyCompleter(COMMANDS)
|
|
||||||
return completer
|
|
@ -1,30 +0,0 @@
|
|||||||
'''
|
|
||||||
This is a modularized version of the sd-metadata.py script,
|
|
||||||
which retrieves and prints the metadata from a series of generated png files.
|
|
||||||
'''
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
from invokeai.backend.image_util import retrieve_metadata
|
|
||||||
|
|
||||||
|
|
||||||
def print_metadata():
|
|
||||||
if len(sys.argv) < 2:
|
|
||||||
print("Usage: file2prompt.py <file1.png> <file2.png> <file3.png>...")
|
|
||||||
print("This script opens up the indicated invoke.py-generated PNG file(s) and prints out their metadata.")
|
|
||||||
exit(-1)
|
|
||||||
|
|
||||||
filenames = sys.argv[1:]
|
|
||||||
for f in filenames:
|
|
||||||
try:
|
|
||||||
metadata = retrieve_metadata(f)
|
|
||||||
print(f'{f}:\n',json.dumps(metadata['sd-metadata'], indent=4))
|
|
||||||
except FileNotFoundError:
|
|
||||||
sys.stderr.write(f'{f} not found\n')
|
|
||||||
continue
|
|
||||||
except PermissionError:
|
|
||||||
sys.stderr.write(f'{f} could not be opened due to inadequate permissions\n')
|
|
||||||
continue
|
|
||||||
|
|
||||||
if __name__== '__main__':
|
|
||||||
print_metadata()
|
|
||||||
|
|
@ -104,7 +104,6 @@ dependencies = [
|
|||||||
"textual_inversion.py" = "invokeai.frontend.training:invokeai_textual_inversion"
|
"textual_inversion.py" = "invokeai.frontend.training:invokeai_textual_inversion"
|
||||||
|
|
||||||
# modern entrypoints
|
# modern entrypoints
|
||||||
"invokeai" = "invokeai.frontend.CLI:invokeai_command_line_interface"
|
|
||||||
"invokeai-configure" = "invokeai.frontend.install:invokeai_configure"
|
"invokeai-configure" = "invokeai.frontend.install:invokeai_configure"
|
||||||
"invokeai-merge" = "invokeai.frontend.merge:invokeai_merge_diffusers"
|
"invokeai-merge" = "invokeai.frontend.merge:invokeai_merge_diffusers"
|
||||||
"invokeai-ti" = "invokeai.frontend.training:invokeai_textual_inversion"
|
"invokeai-ti" = "invokeai.frontend.training:invokeai_textual_inversion"
|
||||||
|
Loading…
Reference in New Issue
Block a user