fix(lora): detect LoraManager in ComfyUI's extra custom_nodes folders

Auto-detection looked only in ComfyUI's main custom_nodes folder (#144).
It now also searches every custom_nodes folder ComfyUI registers through
folder_paths, main folder first, skipping folders that cannot be read.
This commit is contained in:
Vito Sansevero
2026-10-25 06:42:08 -07:00
parent a905cc2316
commit 82f7fc8a6f
2 changed files with 92 additions and 12 deletions
+34 -11
View File
@@ -82,23 +82,46 @@ def detect_lora_manager(custom_path: str = "") -> Optional[str]:
if p.is_dir() and _looks_like_lora_manager(p):
return str(p.resolve())
# 2. Auto-detect via custom_nodes (case-insensitive scan)
# 2. Auto-detect in every custom_nodes folder (case-insensitive scan):
# ComfyUI's own first, then the extra ones it registered (#144).
folders = []
root = find_comfyui_root()
if root:
custom_nodes = root / "custom_nodes"
if custom_nodes.is_dir():
for entry in custom_nodes.iterdir():
if (
entry.is_dir()
and "lora" in entry.name.lower()
and "manager" in entry.name.lower()
and _looks_like_lora_manager(entry)
):
return str(entry.resolve())
folders.append(root / "custom_nodes")
folders.extend(custom_nodes_directories())
seen = set()
for custom_nodes in folders:
key = os.path.normcase(os.path.abspath(str(custom_nodes)))
if key in seen:
continue
seen.add(key)
found = _lora_manager_in(Path(custom_nodes))
if found:
return found
return None
def _lora_manager_in(custom_nodes: Path) -> Optional[str]:
"""The LoraManager install directly under ``custom_nodes``, or None."""
try:
entries = sorted(custom_nodes.iterdir()) if custom_nodes.is_dir() else []
except OSError:
return None
for entry in entries:
try:
if (
entry.is_dir()
and "lora" in entry.name.lower()
and "manager" in entry.name.lower()
and _looks_like_lora_manager(entry)
):
return str(entry.resolve())
except OSError:
continue
return None
def _looks_like_lora_manager(path: Path) -> bool:
"""Heuristic: does this directory look like a LoraManager install?"""
# Must have __init__.py (ComfyUI extension) or README.md
+58 -1
View File
@@ -817,9 +817,66 @@ class TestDetectLoraManager(unittest.TestCase):
self.assertTrue(lora_utils._looks_like_lora_manager(lm))
def test_nothing_found(self):
with patch.object(lora_utils, "find_comfyui_root", return_value=None):
with (
patch.object(lora_utils, "find_comfyui_root", return_value=None),
patch.object(lora_utils, "custom_nodes_directories", return_value=[]),
):
self.assertIsNone(lora_utils.detect_lora_manager())
def test_auto_detects_in_an_extra_custom_nodes_folder(self):
# #144: ComfyUI's main custom_nodes has no LoraManager; an extra
# custom_nodes path registered through folder_paths (extra model
# paths, --base-directory) has it.
root = _make_comfy_root(self.tmp)
extra = self.tmp / "shared_nodes"
extra.mkdir()
lm = _make_lora_manager(extra, "ComfyUI-Lora-Manager")
fake = types.SimpleNamespace(
base_path=str(root),
get_folder_paths=lambda name: (
[str(root / "custom_nodes"), str(extra)]
if name == "custom_nodes"
else []
),
)
with (
patch.dict(sys.modules, {"folder_paths": fake}),
patch.object(lora_utils, "find_comfyui_root", return_value=root),
):
self.assertEqual(lora_utils.detect_lora_manager(), str(lm.resolve()))
def test_main_custom_nodes_wins_over_an_extra_folder(self):
root = _make_comfy_root(self.tmp)
main = _make_lora_manager(root / "custom_nodes")
extra = self.tmp / "shared_nodes"
extra.mkdir()
_make_lora_manager(extra)
with (
patch.object(lora_utils, "find_comfyui_root", return_value=root),
patch.object(
lora_utils,
"custom_nodes_directories",
return_value=[(root / "custom_nodes").resolve(), extra.resolve()],
),
):
self.assertEqual(lora_utils.detect_lora_manager(), str(main.resolve()))
def test_an_unreadable_extra_folder_is_skipped(self):
root = _make_comfy_root(self.tmp)
missing = self.tmp / "gone"
extra = self.tmp / "shared_nodes"
extra.mkdir()
lm = _make_lora_manager(extra)
with (
patch.object(lora_utils, "find_comfyui_root", return_value=root),
patch.object(
lora_utils,
"custom_nodes_directories",
return_value=[missing, extra.resolve()],
),
):
self.assertEqual(lora_utils.detect_lora_manager(), str(lm.resolve()))
class TestFindLoraDirectories(unittest.TestCase):