48 lines
1.7 KiB
Python
48 lines
1.7 KiB
Python
import os, json
|
|
import comfy.utils, comfy.sd, folder_paths
|
|
|
|
class MultiLoraLoader:
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"model": ("MODEL",),
|
|
"lora_stack": ("STRING", {"default": "[]", "multiline": False}),
|
|
},
|
|
"optional": {
|
|
"clip": ("CLIP",), # 可选:接入后支持含 text encoder 权重的 LoRA
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("MODEL", "CLIP", "STRING")
|
|
RETURN_NAMES = ("model", "clip", "triggers")
|
|
FUNCTION = "apply"
|
|
CATEGORY = "loaders"
|
|
SEARCH_ALIASES = ["multi lora", "lora stack", "workflow tools", "多LoRA", "LoRA堆叠", "工作流工具"]
|
|
|
|
def apply(self, model, lora_stack="[]", clip=None):
|
|
try:
|
|
stack = json.loads(lora_stack)
|
|
except Exception:
|
|
return (model, clip, "")
|
|
|
|
triggers = []
|
|
for entry in stack:
|
|
if not entry.get("enabled", False):
|
|
continue
|
|
lora_name = entry.get("lora", "None")
|
|
strength = float(entry.get("strength", 1.0))
|
|
if lora_name == "None" or strength == 0:
|
|
continue
|
|
path = folder_paths.get_full_path("loras", lora_name)
|
|
if path and os.path.exists(path):
|
|
lora = comfy.utils.load_torch_file(path)
|
|
# clip 为 None 时 load_lora_for_models 会自动跳过 CLIP 部分
|
|
model, clip = comfy.sd.load_lora_for_models(model, clip, lora, strength, strength)
|
|
trigger = entry.get("trigger", "").strip()
|
|
if trigger:
|
|
triggers.append(trigger)
|
|
|
|
trigger_out = ", " + ", ".join(triggers) + ", " if triggers else ""
|
|
|
|
return (model, clip, trigger_out) |