Added support for adding weight hooks that aren't registered on the ModelPatcher at sampling time

This commit is contained in:
kosinkadink1@gmail.com 2024-09-17 06:22:41 +09:00
parent f5c899f42a
commit 4b472ba44c
5 changed files with 374 additions and 67 deletions

View File

@ -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:

View File

@ -16,7 +16,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
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 = {}

View File

@ -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)

View File

@ -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

View File

@ -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,