diff --git a/__init__.py b/__init__.py index a06e8c2..0b9aafa 100644 --- a/__init__.py +++ b/__init__.py @@ -21,6 +21,7 @@ if not __package__ or __package__ == "": sys.modules[package_name] = current_module if not hasattr(current_module, "__path__"): current_module.__path__ = [this_dir] +from .core import node_definitions as _node_definitions from .core.extension import ModelResolverExtension from .core.file_manager import ( FileManagerError, @@ -54,77 +55,29 @@ from .core.version import ( # Web directory for JavaScript interface WEB_DIRECTORY = "./web" -MODEL_RESOLVER_DEPENDENCY_NODE_TYPE = "ModelResolverDependency" -MODEL_RESOLVER_DEPENDENCY_NODE_DISPLAY_NAME = "Model Resolver Opener" -MODEL_RESOLVER_DEPENDENCY_NODE_CATEGORY = "Model Resolver/Workflow" +ComfyExtension = _node_definitions.ComfyExtension +io = _node_definitions.io +ModelResolverDependencyNode = _node_definitions.ModelResolverDependencyNode +MODEL_RESOLVER_DEPENDENCY_NODE_TYPE = ( + _node_definitions.MODEL_RESOLVER_DEPENDENCY_NODE_TYPE +) +MODEL_RESOLVER_DEPENDENCY_NODE_DISPLAY_NAME = ( + _node_definitions.MODEL_RESOLVER_DEPENDENCY_NODE_DISPLAY_NAME +) +MODEL_RESOLVER_DEPENDENCY_NODE_CATEGORY = ( + _node_definitions.MODEL_RESOLVER_DEPENDENCY_NODE_CATEGORY +) MODEL_RESOLVER_DEPENDENCY_NODE_DESCRIPTION = ( - "This passive node declares Model Resolver as a workflow dependency and opens " - "its tools. It does not process images, models, or prompts. To stop adding it " - "automatically, open Model Resolver, go to Options -> Defaults -> and disable " - "'Embed opener node'. We recommend keeping it in an unobtrusive part of the " - "canvas: it helps ComfyUI-Manager identify and offer Model Resolver on other " - "ComfyUI installations, making the workflow easier to use in the future." + _node_definitions.MODEL_RESOLVER_DEPENDENCY_NODE_DESCRIPTION ) -try: - from comfy_api.latest import ComfyExtension, io -except Exception: - ComfyExtension = None - io = None - - if ComfyExtension is not None and io is not None: - - class ModelResolverDependencyNode(io.ComfyNode): - """Canvas node that declares Model Resolver and opens its workflow tools.""" - - @classmethod - def define_schema(cls) -> io.Schema: - return io.Schema( - node_id=MODEL_RESOLVER_DEPENDENCY_NODE_TYPE, - display_name=MODEL_RESOLVER_DEPENDENCY_NODE_DISPLAY_NAME, - category=MODEL_RESOLVER_DEPENDENCY_NODE_CATEGORY, - description=MODEL_RESOLVER_DEPENDENCY_NODE_DESCRIPTION, - inputs=[], - outputs=[], - ) - - @classmethod - def execute(cls) -> io.NodeOutput: - return io.NodeOutput() - - class ModelResolverNodeExtension(ComfyExtension): - async def get_node_list(self) -> list[type[io.ComfyNode]]: - return [ModelResolverDependencyNode] - - async def comfy_entrypoint() -> ComfyExtension: - return ModelResolverNodeExtension() - + ModelResolverNodeExtension = _node_definitions.ModelResolverNodeExtension + comfy_entrypoint = _node_definitions.comfy_entrypoint __all__ = ["WEB_DIRECTORY", "comfy_entrypoint"] else: - - class ModelResolverDependencyNode: - """Legacy fallback for ComfyUI builds without the v3 custom-node API.""" - - DESCRIPTION = MODEL_RESOLVER_DEPENDENCY_NODE_DESCRIPTION - RETURN_TYPES = () - FUNCTION = "noop" - CATEGORY = MODEL_RESOLVER_DEPENDENCY_NODE_CATEGORY - - @classmethod - def INPUT_TYPES(cls): - return {"required": {}} - - def noop(self): - return () - - NODE_CLASS_MAPPINGS = { - MODEL_RESOLVER_DEPENDENCY_NODE_TYPE: ModelResolverDependencyNode, - } - NODE_DISPLAY_NAME_MAPPINGS = { - MODEL_RESOLVER_DEPENDENCY_NODE_TYPE: MODEL_RESOLVER_DEPENDENCY_NODE_DISPLAY_NAME, - } - + NODE_CLASS_MAPPINGS = _node_definitions.NODE_CLASS_MAPPINGS + NODE_DISPLAY_NAME_MAPPINGS = _node_definitions.NODE_DISPLAY_NAME_MAPPINGS __all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"] diff --git a/core/node_definitions.py b/core/node_definitions.py new file mode 100644 index 0000000..a7a1b79 --- /dev/null +++ b/core/node_definitions.py @@ -0,0 +1,74 @@ +"""ComfyUI node definitions and runtime compatibility exports.""" + +try: + from comfy_api.latest import ComfyExtension, io +except Exception: + ComfyExtension = None + io = None + +MODEL_RESOLVER_DEPENDENCY_NODE_TYPE = "ModelResolverDependency" +MODEL_RESOLVER_DEPENDENCY_NODE_DISPLAY_NAME = "Model Resolver Opener" +MODEL_RESOLVER_DEPENDENCY_NODE_CATEGORY = "Model Resolver/Workflow" +MODEL_RESOLVER_DEPENDENCY_NODE_DESCRIPTION = ( + "This passive node declares Model Resolver as a workflow dependency and opens " + "its tools. It does not process images, models, or prompts. To stop adding it " + "automatically, open Model Resolver, go to Options -> Defaults -> and disable " + "'Embed opener node'. We recommend keeping it in an unobtrusive part of the " + "canvas: it helps ComfyUI-Manager identify and offer Model Resolver on other " + "ComfyUI installations, making the workflow easier to use in the future." +) + + +if ComfyExtension is not None and io is not None: + + class ModelResolverDependencyNode(io.ComfyNode): + """Canvas node that declares Model Resolver and opens its workflow tools.""" + + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id=MODEL_RESOLVER_DEPENDENCY_NODE_TYPE, + display_name=MODEL_RESOLVER_DEPENDENCY_NODE_DISPLAY_NAME, + category=MODEL_RESOLVER_DEPENDENCY_NODE_CATEGORY, + description=MODEL_RESOLVER_DEPENDENCY_NODE_DESCRIPTION, + inputs=[], + outputs=[], + ) + + @classmethod + def execute(cls) -> io.NodeOutput: + return io.NodeOutput() + + class ModelResolverNodeExtension(ComfyExtension): + async def get_node_list(self) -> list[type[io.ComfyNode]]: + return [ModelResolverDependencyNode] + + async def comfy_entrypoint() -> ComfyExtension: + return ModelResolverNodeExtension() + + __all__ = ["comfy_entrypoint"] +else: + + class ModelResolverDependencyNode: + """Legacy fallback for ComfyUI builds without the v3 custom-node API.""" + + DESCRIPTION = MODEL_RESOLVER_DEPENDENCY_NODE_DESCRIPTION + RETURN_TYPES = () + FUNCTION = "noop" + CATEGORY = MODEL_RESOLVER_DEPENDENCY_NODE_CATEGORY + + @classmethod + def INPUT_TYPES(cls): + return {"required": {}} + + def noop(self): + return () + + NODE_CLASS_MAPPINGS = { + MODEL_RESOLVER_DEPENDENCY_NODE_TYPE: ModelResolverDependencyNode, + } + NODE_DISPLAY_NAME_MAPPINGS = { + MODEL_RESOLVER_DEPENDENCY_NODE_TYPE: MODEL_RESOLVER_DEPENDENCY_NODE_DISPLAY_NAME, + } + + __all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"] diff --git a/tests/test_node_definitions.py b/tests/test_node_definitions.py new file mode 100644 index 0000000..af68e96 --- /dev/null +++ b/tests/test_node_definitions.py @@ -0,0 +1,29 @@ +import importlib + + +def test_node_definitions_expose_stable_model_resolver_metadata(): + module = importlib.import_module("core.node_definitions") + + assert module.MODEL_RESOLVER_DEPENDENCY_NODE_TYPE == ( + "ModelResolverDependency" + ) + assert module.MODEL_RESOLVER_DEPENDENCY_NODE_CATEGORY == ( + "Model Resolver/Workflow" + ) + assert module.MODEL_RESOLVER_DEPENDENCY_NODE_DISPLAY_NAME == ( + "Model Resolver Opener" + ) + assert "ModelResolverDependencyNode" in dir(module) + + +def test_node_definitions_preserve_runtime_specific_exports(): + module = importlib.import_module("core.node_definitions") + + if module.ComfyExtension is not None and module.io is not None: + assert callable(module.comfy_entrypoint) + assert "comfy_entrypoint" in module.__all__ + else: + assert module.NODE_CLASS_MAPPINGS[module.MODEL_RESOLVER_DEPENDENCY_NODE_TYPE] + assert module.NODE_DISPLAY_NAME_MAPPINGS[ + module.MODEL_RESOLVER_DEPENDENCY_NODE_TYPE + ] == module.MODEL_RESOLVER_DEPENDENCY_NODE_DISPLAY_NAME