feat(nodes): warn on duplicate LoRA loads

This commit is contained in:
Will Miao
2026-10-31 11:42:14 +08:00
parent 91f955a277
commit 78c722c274
2 changed files with 99 additions and 0 deletions
+32
View File
@@ -103,7 +103,39 @@ def _prepare_lora(lora, model, name):
return remapped
def _warn_duplicate_entries(lora_entries: list[dict]) -> None:
"""Warn when the same LoRA file appears more than once in one load call.
Entries are still loaded exactly as provided; this only surfaces accidental
double loads such as a stack plus its own derived text syntax.
"""
entries_by_path: dict[str, list[dict]] = {}
for entry in lora_entries:
path = entry.get("absolute_path") or entry.get("input_path")
if not path:
continue
key = os.path.normcase(os.path.abspath(path))
entries_by_path.setdefault(key, []).append(entry)
for entries in entries_by_path.values():
if len(entries) < 2:
continue
strengths = ", ".join(
f"{entry['model_strength']}/{entry['clip_strength']}" for entry in entries
)
logger.warning(
"LoRA '%s' (%s) appears %d times in the same loader input "
"(model/clip strengths: %s). All entries will be loaded as provided; "
"if this is intentional, ignore this warning.",
entries[0]["name"],
entries[0].get("absolute_path") or entries[0].get("input_path"),
len(entries),
strengths,
)
def _apply_entries(model, clip, lora_entries, nunchaku_model_kind):
_warn_duplicate_entries(lora_entries)
loaded_loras = []
all_trigger_words = []
+67
View File
@@ -1,3 +1,5 @@
import logging
import os
import types
import pytest
@@ -178,6 +180,71 @@ def test_lora_text_loader_qwen_batches_text_and_stack(monkeypatch):
assert loaded_loras == "<lora:stack_qwen:0.6> <lora:text_qwen:1.2>"
def test_lora_text_loader_warns_on_duplicate_path_but_loads_both(monkeypatch, caplog):
loader = LoraTextLoaderLM()
model = _Model(object())
clip = object()
monkeypatch.setattr(
"py.nodes.lora_loader.get_lora_info_absolute",
lambda name: ("/abs/same.safetensors", ["same_trigger"]),
)
load_calls = []
def mock_load_torch_file(path, safe_load=True):
load_calls.append(path)
return {"path": path}
monkeypatch.setattr("comfy.utils.load_torch_file", mock_load_torch_file)
monkeypatch.setattr(
"comfy.sd.load_lora_for_models",
lambda model_arg, clip_arg, lora_arg, model_strength, clip_strength: (model_arg, clip_arg),
)
with caplog.at_level(logging.WARNING, logger="py.nodes.lora_loader"):
_, _, trigger_words, loaded_loras = loader.load_loras_from_text(
model,
"<lora:same:1.0>",
clip=clip,
lora_stack=[("same.safetensors", 1.0, 1.0)],
)
assert load_calls == ["/abs/same.safetensors", "/abs/same.safetensors"]
assert trigger_words == "same_trigger,, same_trigger"
assert loaded_loras == "<lora:same:1.0> <lora:same:1.0>"
assert "appears 2 times" in caplog.text
assert "/abs/same.safetensors" in caplog.text
def test_lora_loader_does_not_warn_for_same_basename_in_different_folders(monkeypatch, caplog):
loader = LoraLoaderLM()
model = _Model(object())
def resolve(name):
if os.path.isabs(name):
return name, []
return f"/abs/b/{name}.safetensors", []
monkeypatch.setattr("py.nodes.lora_loader.get_lora_info_absolute", resolve)
monkeypatch.setattr("comfy.utils.load_torch_file", lambda path, safe_load=True: {"path": path})
monkeypatch.setattr(
"comfy.sd.load_lora_for_models",
lambda model_arg, clip_arg, lora_arg, model_strength, clip_strength: (model_arg, clip_arg),
)
with caplog.at_level(logging.WARNING, logger="py.nodes.lora_loader"):
_, _, _, loaded_loras = loader.load_loras(
model,
"",
lora_stack=[("/abs/a/same.safetensors", 1.0, 1.0)],
loras={"__value__": [{"active": True, "name": "same", "strength": 1.0}]},
)
assert loaded_loras == "<lora:same:1.0> <lora:same:1.0>"
assert "appears 2 times" not in caplog.text
def test_lora_loader_qwen_model_raises_clear_error_when_helper_import_fails(monkeypatch):
qwen_model = _Model(type("NunchakuQwenImageTransformer2DModel", (), {})())
loader = LoraLoaderLM()