diff --git a/comfy/hooks.py b/comfy/hooks.py index 8dd9e3aab..570628668 100644 --- a/comfy/hooks.py +++ b/comfy/hooks.py @@ -1,5 +1,6 @@ -from typing import TYPE_CHECKING, List, Dict, Tuple +from typing import TYPE_CHECKING, List, Dict, Tuple, Callable import enum +import math import torch import numpy as np @@ -15,41 +16,23 @@ class EnumHookMode(enum.Enum): MinVram = "minvram" MaxSpeed = "maxspeed" -class InterpolationMethod: - LINEAR = "linear" - EASE_IN = "ease_in" - EASE_OUT = "ease_out" - EASE_IN_OUT = "ease_in_out" +class EnumHookType(enum.Enum): + Weight = "weight" + Patch = "patch" - _LIST = [LINEAR, EASE_IN, EASE_OUT, EASE_IN_OUT] +class EnumWeightTarget(enum.Enum): + Model = "model" + Clip = "clip" - @classmethod - def get_weights(cls, num_from: float, num_to: float, length: int, method: str, reverse=False): - diff = num_to - num_from - if method == cls.LINEAR: - weights = torch.linspace(num_from, num_to, length) - elif method == cls.EASE_IN: - index = torch.linspace(0, 1, length) - weights = diff * np.power(index, 2) + num_from - elif method == cls.EASE_OUT: - index = torch.linspace(0, 1, length) - weights = diff * (1 - np.power(1 - index, 2)) + num_from - elif method == cls.EASE_IN_OUT: - index = torch.linspace(0, 1, length) - weights = diff * ((1 - np.cos(index * np.pi)) / 2) + num_from - else: - raise ValueError(f"Unrecognized interpolation method '{method}'.") - if reverse: - weights = weights.flip(dims=(0,)) - return weights - -class HookRef: +class _HookRef: pass class Hook: - def __init__(self): - self.hook_ref = HookRef() - self.hook_keyframe = HookKeyframeGroup() + def __init__(self, hook_type: EnumHookType=None, hook_ref: _HookRef=None, + hook_keyframe: 'HookKeyframeGroup'=None): + self.hook_type = hook_type + self.hook_ref = hook_ref if hook_ref else _HookRef() + self.hook_keyframe = hook_keyframe if hook_keyframe else HookKeyframeGroup() @property def strength(self): @@ -62,18 +45,84 @@ class Hook: def reset(self): self.hook_keyframe.reset() - def clone(self): - c = Hook() + def clone(self, subtype: Callable=None): + if subtype is None: + subtype = type(self) + c: Hook = subtype() + c.hook_type = self.hook_type c.hook_ref = self.hook_ref c.hook_keyframe = self.hook_keyframe return c - + def __eq__(self, other: 'Hook'): return self.__class__ == other.__class__ and self.hook_ref == other.hook_ref def __hash__(self): return hash(self.hook_ref) + +class WeightHook(Hook): + def __init__(self, strength_model=1.0, strength_clip=1.0): + super().__init__(hook_type=EnumHookType.Weight) + self.weights: Dict = None + self.weights_clip: Dict = None + self.need_weight_init = True + self._strength_model = strength_model + self._strength_clip = strength_clip + + @property + def strength_model(self): + return self._strength_model * self.strength + + @property + def strength_clip(self): + return self._strength_clip * self.strength + + def add_hook_patches(self, model: 'ModelPatcher', target: EnumWeightTarget): + weights = None + if target == EnumWeightTarget.Model: + strength = self._strength_model + else: + strength = self._strength_clip + + if self.need_weight_init: + key_map = {} + if target == EnumWeightTarget.Model: + key_map = comfy.lora.model_lora_keys_unet(model.model, key_map) + else: + key_map = comfy.lora.model_lora_keys_clip(model.model, key_map) + weights = comfy.lora.load_lora(self.weights, key_map) + else: + if target == EnumWeightTarget.Model: + weights = self.weights + else: + weights = self.weights_clip + k = model.add_hook_patches(hook=self, patches=weights, strength_patch=strength) + # TODO: add logs about any keys that were not applied + + def clone(self, subtype: Callable=None): + if subtype is None: + subtype = type(self) + c: WeightHook = super().clone(subtype) + c.weights = self.weights + c.weights_clip = self.weights_clip + c.need_weight_init = self.need_weight_init + c._strength_model = self._strength_model + c._strength_clip = self._strength_clip + return c + +class PatchHook(Hook): + def __init__(self): + super().__init__(hook_type=EnumHookType.Patch) + self.patches: Dict = None + + def clone(self, subtype: Callable=None): + if subtype is None: + subtype = type(self) + c: PatchHook = super().clone(type(self)) + c.patches = self.patches + return c + class HookGroup: def __init__(self): self.hooks: List[Hook] = [] @@ -121,6 +170,7 @@ class HookGroup: final_hook = final_hook.clone_and_combine(hook) return final_hook + class HookKeyframe: def __init__(self, strength: float, start_percent=0.0, guarantee_steps=1): self.strength = strength @@ -216,6 +266,35 @@ class HookKeyframeGroup: # return True if keyframe changed, False if no change return prev_index != self._current_index + +class InterpolationMethod: + LINEAR = "linear" + EASE_IN = "ease_in" + EASE_OUT = "ease_out" + EASE_IN_OUT = "ease_in_out" + + _LIST = [LINEAR, EASE_IN, EASE_OUT, EASE_IN_OUT] + + @classmethod + def get_weights(cls, num_from: float, num_to: float, length: int, method: str, reverse=False): + diff = num_to - num_from + if method == cls.LINEAR: + weights = torch.linspace(num_from, num_to, length) + elif method == cls.EASE_IN: + index = torch.linspace(0, 1, length) + weights = diff * np.power(index, 2) + num_from + elif method == cls.EASE_OUT: + index = torch.linspace(0, 1, length) + weights = diff * (1 - np.power(1 - index, 2)) + num_from + elif method == cls.EASE_IN_OUT: + index = torch.linspace(0, 1, length) + weights = diff * ((1 - np.cos(index * np.pi)) / 2) + num_from + else: + raise ValueError(f"Unrecognized interpolation method '{method}'.") + if reverse: + weights = weights.flip(dims=(0,)) + return weights + def get_sorted_list_via_attr(objects: List, attr: str) -> List: if not objects: return objects @@ -239,15 +318,68 @@ def get_sorted_list_via_attr(objects: List, attr: str) -> List: sorted_list.extend(object_list) return sorted_list +def create_hook_lora(lora: Dict[str, torch.Tensor], strength_model: float, strength_clip: float): + hook_group = HookGroup() + hook = WeightHook(strength_model=strength_model, strength_clip=strength_clip) + hook_group.add(hook) + hook.weights = lora + hook.need_weight_init = True + return hook_group + +def create_hook_model_as_lora(model: 'ModelPatcher', clip: 'CLIP', + model_loaded: 'ModelPatcher', clip_loaded: 'CLIP', + strength_model: float, strength_clip: float): + hook_group = HookGroup() + hook = WeightHook(strength_model=strength_model, strength_clip=strength_clip) + hook_group.add(hook) + if model is not None and model_loaded is not None: + expected_model_keys = set(model_loaded.model.state_dict().keys()) + patches_model: Dict[str, torch.Tensor] = model_loaded.model.state_dict() + # do not include ANY model_sampling components of the model that should act as a patch + for key in list(patches_model.keys()): + if key.startswith("model_sampling"): + expected_model_keys.discard(key) + patches_model.pop(key, None) + weights_model, k = model.get_weight_diffs(patches_model) + else: + weights_model = {} + k = () + + if clip is not None and clip_loaded is not None: + expected_clip_keys = clip_loaded.patcher.model.state_dict().copy() + patches_clip: Dict[str, torch.Tensor] = clip_loaded.cond_stage_model.state_dict() + weights_clip, k1 = clip.patcher.get_weight_diffs(patches_clip) + else: + weights_clip = {} + k1 = () + + k = set(k) + k1 = set(k1) + if model is not None and model_loaded is not None: + for key in expected_model_keys: + if key not in k: + print(f"MODEL-AS-LORA NOT LOADED {key}") + if clip is not None and clip_loaded is not None: + for key in expected_clip_keys: + if key not in k1: + print(f"CLIP-AS-LORA NOT LOADED {key}") + + hook.weights = weights_model + hook.weights_clip = weights_clip + hook.need_weight_init = False + return hook_group def load_hook_lora_for_models(model: 'ModelPatcher', clip: 'CLIP', lora: Dict[str, torch.Tensor], - hook: Hook, strength_model: float, strength_clip: float): + strength_model: float, strength_clip: float): key_map = {} if model is not None: key_map = comfy.lora.model_lora_keys_unet(model.model, key_map) if clip is not None: key_map = comfy.lora.model_lora_keys_clip(clip.cond_stage_model, key_map) + hook_group = HookGroup() + hook = WeightHook() + hook_group.add(hook) loaded: Dict[str] = comfy.lora.load_lora(lora, key_map) if model is not None: new_modelpatcher = model.clone() @@ -267,11 +399,14 @@ def load_hook_lora_for_models(model: 'ModelPatcher', clip: 'CLIP', lora: Dict[st for x in loaded: if (x not in k) and (x not in k1): print(f"NOT LOADED {x}") - return (new_modelpatcher, new_clip) + return (new_modelpatcher, new_clip, hook_group) def load_hook_model_as_lora_for_models(model: 'ModelPatcher', clip: 'CLIP', model_loaded: 'ModelPatcher', clip_loaded: 'CLIP', - hook: Hook, strength_model: float, strength_clip: float): + strength_model: float, strength_clip: float): + hook_group = HookGroup() + hook = WeightHook() + hook_group.add(hook) if model is not None and model_loaded is not None: new_modelpatcher = model.clone() expected_model_keys = set(model_loaded.model.state_dict().keys()) @@ -307,7 +442,7 @@ def load_hook_model_as_lora_for_models(model: 'ModelPatcher', clip: 'CLIP', if key not in k1: print(f"CLIP-AS-LORA NOT LOADED {key}") - return (new_modelpatcher, new_clip) + return (new_modelpatcher, new_clip, hook_group) def set_hooks_for_conditioning(cond, hooks: HookGroup): if hooks is None: diff --git a/comfy/model_patcher.py b/comfy/model_patcher.py index e39d6abb6..808601c72 100644 --- a/comfy/model_patcher.py +++ b/comfy/model_patcher.py @@ -16,7 +16,7 @@ along with this program. If not, see . """ -from typing import Dict, List, Tuple, Optional +from typing import Dict, List, Tuple, Optional, Callable import torch import copy import inspect @@ -113,7 +113,8 @@ class ModelPatcher: self.weight_inplace_update = weight_inplace_update self.patches_uuid = uuid.uuid4() - self.hook_patches: Dict[comfy.hooks.HookRef] = {} + self.hook_patches: Dict[comfy.hooks._HookRef] = {} + self.hook_patches_backup: Dict[comfy.hooks._HookRef] = {} self.hook_backup: Dict[str, Tuple[torch.Tensor, torch.device]] = {} self.cached_hook_patches: Dict[comfy.hooks.HookGroup, Dict[str, torch.Tensor]] = {} self.current_hooks: Optional[comfy.hooks.HookGroup] = None @@ -155,10 +156,8 @@ class ModelPatcher: n.object_patches_backup = self.object_patches_backup # hooks - for hook_ref in self.hook_patches: - n.hook_patches[hook_ref] = {} - for k in self.hook_patches[hook_ref]: - n.hook_patches[hook_ref][k] = self.hook_patches[hook_ref][k][:] + n.hook_patches = self.create_hook_patches_clone(self.hook_patches) + n.hook_patches_backup = self.create_hook_patches_clone(self.hook_patches_backup) # TODO: do we really need to clone cached_hook_patches/current_hooks? for group in self.cached_hook_patches: n.cached_hook_patches[group] = {} @@ -170,6 +169,15 @@ class ModelPatcher: n.hook_mode = self.hook_mode return n + @staticmethod + def create_hook_patches_clone(orig_hook_patches): + new_hook_patches = {} + for hook_ref in orig_hook_patches: + new_hook_patches[hook_ref] = {} + for k in orig_hook_patches[hook_ref]: + new_hook_patches[hook_ref][k] = orig_hook_patches[hook_ref][k][:] + return new_hook_patches + def is_clone(self, other): if hasattr(other, 'model') and self.model is other.model: return True @@ -570,6 +578,12 @@ class ModelPatcher: def clean(self): self.clean_hooks() + self.restore_hook_patches() + + def restore_hook_patches(self): + if len(self.hook_patches_backup) > 0: + self.hook_patches = self.hook_patches_backup + self.hook_patches_backup = {} def set_hook_mode(self, hook_mode: comfy.hooks.EnumHookMode): self.hook_mode = hook_mode @@ -591,8 +605,22 @@ class ModelPatcher: if cached_group.contains(hook): self.cached_hook_patches.pop(cached_group) - def add_hook_patches(self, hook: comfy.hooks.Hook, patches, strength_patch=1.0, strength_model=1.0, is_diff=False): + def register_all_hook_patches(self, hooks_dict: Dict[comfy.hooks.Hook, None], target: comfy.hooks.EnumWeightTarget): + self.restore_hook_patches() + weight_hooks_to_register: List[comfy.hooks.WeightHook] = [] + for hook in hooks_dict: + if hook.hook_type == comfy.hooks.EnumHookType.Weight: + if hook.hook_ref not in self.hook_patches: + weight_hooks_to_register.append(hook) + if len(weight_hooks_to_register) > 0: + self.hook_patches_backup = self.create_hook_patches_clone(self.hook_patches) + for hook in weight_hooks_to_register: + hook.add_hook_patches(self, target) + + def add_hook_patches(self, hook: comfy.hooks.WeightHook, patches, strength_patch=1.0, strength_model=1.0, is_diff=False): # NOTE: this mirrors behavior of add_patches func + if is_diff: + comfy.model_management.unload_model_clones(self) current_hook_patches: Dict[str,List] = self.hook_patches.get(hook.hook_ref, {}) p = set() model_sd = self.model.state_dict() @@ -613,7 +641,6 @@ class ModelPatcher: if is_diff: # take difference between desired weight and existing weight to get diff # TODO: try to implement diff via strength_path/strength_model diff - comfy.model_management.unload_model_clones(self) model_dtype = comfy.utils.get_attr(self.model, key).dtype if model_dtype in [torch.float8_e5m2, torch.float8_e4m3fn]: diff_weight = (patches[k].to(torch.float32)-comfy.utils.get_attr(self.model, key).to(torch.float32)).to(model_dtype) @@ -628,6 +655,22 @@ class ModelPatcher: self.patches_uuid = uuid.uuid4() return list(p) + def get_weight_diffs(self, patches): + comfy.model_management.unload_model_clones(self) + weights: Dict[str, Tuple] = {} + p = set() + model_sd = self.model.state_dict() + for k in patches: + if k in model_sd: + p.add(k) + model_dtype = comfy.utils.get_attr(self.model, k).dtype + if model_dtype in [torch.float8_e5m2, torch.float8_e4m3fn]: + diff_weight = (patches[k].to(torch.float32)-comfy.utils.get_attr(self.model, k).to(torch.float32)).to(model_dtype) + else: + diff_weight = patches[k]-comfy.utils.get_attr(self.model, k) + weights[k] = (diff_weight,) + return weights, p + def get_combined_hook_patches(self, hooks: comfy.hooks.HookGroup): # combined_patches will contain weights of all relevant hooks, per key combined_patches = {} diff --git a/comfy/sampler_helpers.py b/comfy/sampler_helpers.py index 25a0f5ccb..492148473 100644 --- a/comfy/sampler_helpers.py +++ b/comfy/sampler_helpers.py @@ -1,6 +1,7 @@ import torch import comfy.model_management import comfy.conds +import comfy.hooks def prepare_mask(noise_mask, shape, device): """ensures noise mask is of proper dimensions""" @@ -77,3 +78,13 @@ def cleanup_models(conds, models): control_cleanup += get_models_from_cond(conds[k], "control") cleanup_additional_models(set(control_cleanup)) + +def prepare_model_patcher(model, conds): + # check for hooks in conds - if not registered, see if can be applied + hooks = {} + for k in conds: + for cond in conds[k]: + if 'hooks' in cond: + for hook in cond['hooks'].hooks: + hooks[hook] = None + model.register_all_hook_patches(hooks, comfy.hooks.EnumWeightTarget.Model) diff --git a/comfy/samplers.py b/comfy/samplers.py index 63a8562ef..537acd523 100644 --- a/comfy/samplers.py +++ b/comfy/samplers.py @@ -779,9 +779,12 @@ class CFGGuider: latent_image = latent_image.to(device) sigmas = sigmas.to(device) - output = self.inner_sample(noise, latent_image, device, sampler, sigmas, denoise_mask, callback, disable_pbar, seed) + try: + comfy.sampler_helpers.prepare_model_patcher(self.model_patcher, self.conds) + output = self.inner_sample(noise, latent_image, device, sampler, sigmas, denoise_mask, callback, disable_pbar, seed) + finally: + self.model_patcher.clean() - self.model_patcher.clean() comfy.sampler_helpers.cleanup_models(self.conds, self.loaded_models) del self.inner_model del self.conds diff --git a/comfy_extras/nodes_hooks.py b/comfy_extras/nodes_hooks.py index d803c0092..db219c25c 100644 --- a/comfy_extras/nodes_hooks.py +++ b/comfy_extras/nodes_hooks.py @@ -8,6 +8,7 @@ if TYPE_CHECKING: import comfy.hooks import comfy.sd +import comfy.utils import folder_paths ########################################### @@ -39,7 +40,7 @@ class PairConditioningSetProperties: def set_properties(self, positive_NEW, negative_NEW, strength: float, set_cond_area: str, - opt_mask: torch.Tensor=None, opt_hooks: comfy.hooks.Hook=None, opt_timesteps: Tuple=None): + opt_mask: torch.Tensor=None, opt_hooks: comfy.hooks.HookGroup=None, opt_timesteps: Tuple=None): final_positive, final_negative = comfy.hooks.set_mask_conds(conds=[positive_NEW, negative_NEW], strength=strength, set_cond_area=set_cond_area, opt_mask=opt_mask, opt_hooks=opt_hooks, opt_timestep_range=opt_timesteps) @@ -70,10 +71,10 @@ class ConditioningSetProperties: def set_properties(self, cond_NEW, strength: float, set_cond_area: str, - opt_mask: torch.Tensor=None, opt_hooks: comfy.hooks.Hook=None, opt_timesteps: Tuple=None): + opt_mask: torch.Tensor=None, opt_hooks: comfy.hooks.HookGroup=None, opt_timesteps: Tuple=None): (final_cond,) = comfy.hooks.set_mask_conds(conds=[cond_NEW], - strength=strength, set_cond_area=set_cond_area, - opt_mask=opt_mask, opt_hooks=opt_hooks, opt_timestep_range=opt_timesteps) + strength=strength, set_cond_area=set_cond_area, + opt_mask=opt_mask, opt_hooks=opt_hooks, opt_timestep_range=opt_timesteps) return (final_cond,) class PairConditioningCombine: @@ -198,6 +199,124 @@ class ConditioningTimestepsRange: ########################################### +########################################### +# Create Hooks +#------------------------------------------ +class CreateHookLora: + NodeId = 'CreateHookLora' + NodeName = 'Create Hook LoRA' + def __init__(self): + self.loaded_lora = None + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "lora_name": (folder_paths.get_filename_list("loras"), ), + "strength_model": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}), + "strength_clip": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}), + } + } + + RETURN_TYPES = ("HOOKS",) + CATEGORY = "advanced/hooks/create" + FUNCTION = "create_hook" + + def create_hook(self, lora_name: str, strength_model: float, strength_clip: float): + if strength_model == 0 and strength_clip == 0: + return (None,) + + lora_path = folder_paths.get_full_path("loras", lora_name) + lora = None + if self.loaded_lora is not None: + if self.loaded_lora[0] == lora_path: + lora = self.loaded_lora[1] + else: + temp = self.loaded_lora + self.loaded_lora = None + del temp + + if lora is None: + lora = comfy.utils.load_torch_file(lora_path, safe_load=True) + self.loaded_lora = (lora_path, lora) + + hooks = comfy.hooks.create_hook_lora(lora=lora, strength_model=strength_model, strength_clip=strength_clip) + return (hooks,) + +class CreateHookLoraModelOnly(CreateHookLora): + NodeId = 'CreateHookLoraModelOnly' + NodeName = 'Create Hook LoRA (MO)' + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "lora_name": (folder_paths.get_filename_list("loras"), ), + "strength_model": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}), + } + } + + RETURN_TYPES = ("HOOKS",) + CATEGORY = "advanced/hooks/create" + FUNCTION = "create_hook_model_only" + + def create_hook_model_only(self, lora_name: str, strength_model: float): + return self.create_hook(lora_name=lora_name, strength_model=strength_model, strength_clip=0) + +class CreateHookModelAsLora: + NodeId = 'CreateHookModelAsLora' + NodeName = 'Create Hook Model as LoRA' + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "model": ("MODEL",), + "clip": ("CLIP",), + "ckpt_name": (folder_paths.get_filename_list("checkpoints"), ), + "strength_model": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}), + "strength_clip": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}), + } + } + + RETURN_TYPES = ("HOOKS",) + CATEGORY = "advanced/hooks/create" + FUNCTION = "create_hook" + + def create_hook(self, model: 'ModelPatcher', clip: 'CLIP', ckpt_name: str, + strength_model: float, strength_clip: float): + ckpt_path = folder_paths.get_full_path("checkpoints", ckpt_name) + out = comfy.sd.load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=True, embedding_directory=folder_paths.get_folder_paths("embeddings")) + model_loaded = out[0] + clip_loaded = out[1] + + hooks = comfy.hooks.create_hook_model_as_lora(model=model, clip=clip, + model_loaded=model_loaded, clip_loaded=clip_loaded, + strength_model=strength_model, strength_clip=strength_clip) + return (hooks,) + +class CreateHookModelAsLoraModelOnly: + NodeId = 'CreateHookModelAsLoraModelOnly' + NodeName = 'Create Hook Model as LoRA (MO)' + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "model": ("MODEL",), + "ckpt_name": (folder_paths.get_filename_list("checkpoints"), ), + "strength_model": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}), + } + } + + RETURN_TYPES = ("HOOKS",) + CATEGORY = "advanced/hooks/create" + FUNCTION = "create_hook_model_only" + + def create_hook_model_only(self, model: 'ModelPatcher', ckpt_name: str, strength_model: float): + return CreateHookModelAsLora.create_hook(self, model=model, clip=None, ckpt_name=ckpt_name, + strength_model=strength_model, strength_clip=0) +#------------------------------------------ +########################################### + + ########################################### # Register Hooks #------------------------------------------ @@ -242,12 +361,9 @@ class RegisterHookLora: lora = comfy.utils.load_torch_file(lora_path, safe_load=True) self.loaded_lora = (lora_path, lora) - hook = comfy.hooks.Hook() - hook_group = comfy.hooks.HookGroup() - hook_group.add(hook) - model_lora, clip_lora = comfy.hooks.load_hook_lora_for_models(model=model, clip=clip, lora=lora, hook=hook, + model_lora, clip_lora, hooks = comfy.hooks.load_hook_lora_for_models(model=model, clip=clip, lora=lora, strength_model=strength_model, strength_clip=strength_clip) - return (model_lora, clip_lora, hook_group) + return (model_lora, clip_lora, hooks) class RegisterHookLoraModelOnly(RegisterHookLora): NodeId = 'RegisterHookLoraModelOnly' @@ -257,10 +373,8 @@ class RegisterHookLoraModelOnly(RegisterHookLora): return { "required": { "model": ("MODEL",), - "clip": ("CLIP",), "lora_name": (folder_paths.get_filename_list("loras"), ), "strength_model": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}), - "strength_clip": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}), } } @@ -299,14 +413,10 @@ class RegisterHookModelAsLora: model_loaded = out[0] clip_loaded = out[1] - hook = comfy.hooks.Hook() - hook_group = comfy.hooks.HookGroup() - hook_group.add(hook) - model_lora, clip_lora = comfy.hooks.load_hook_model_as_lora_for_models(model=model, clip=clip, - model_loaded=model_loaded, clip_loaded=clip_loaded, - hook=hook, - strength_model=strength_model, strength_clip=strength_clip) - return (model_lora, clip_lora, hook_group) + model_lora, clip_lora, hooks = comfy.hooks.load_hook_model_as_lora_for_models(model=model, clip=clip, + model_loaded=model_loaded, clip_loaded=clip_loaded, + strength_model=strength_model, strength_clip=strength_clip) + return (model_lora, clip_lora, hooks) class RegisterHookModelAsLoraModelOnly: NodeId = 'RegisterHookModelAsLoraModelOnly' @@ -551,6 +661,11 @@ class CombineHooksEight: ########################################### node_list = [ + # Create + CreateHookLora, + CreateHookLoraModelOnly, + CreateHookModelAsLora, + CreateHookModelAsLoraModelOnly, # Register RegisterHookLora, RegisterHookLoraModelOnly,