Files
eniewold-ComfyUI-Subworkflow/workflow_node.py
T

67 lines
2.0 KiB
Python

"""
FunctionWorkflow: loads an inner workflow (UI or API format), infers its I/O from
FunctionInput / FunctionOutput nodes, and executes it as a subgraph expansion.
"""
from .workflow_utils import (
list_workflow_files,
load_workflow,
get_workflow_io,
build_expansion,
MAX_SLOTS,
PLACEHOLDER,
)
# A string subclass whose __ne__ always returns False so that ComfyUI's
# type-matching considers it equal to any other type string.
class _AnyType(str):
def __eq__(self, _):
return True
def __ne__(self, _):
return False
_ANY = _AnyType("*")
class FunctionWorkflow:
"""
Selects a saved API-format workflow, exposes its FunctionInput nodes as
inputs and its FunctionOutput nodes as outputs, then executes the inner
workflow as a transparent subgraph when the outer workflow runs.
"""
@classmethod
def INPUT_TYPES(cls):
workflows = list_workflow_files()
return {
"required": {
"workflow": (workflows, {}),
},
}
# Fixed upper bound; the JS extension hides the unused tail slots.
RETURN_TYPES = (_ANY,) * MAX_SLOTS
RETURN_NAMES = tuple(f"out_{i}" for i in range(MAX_SLOTS))
FUNCTION = "execute"
CATEGORY = "workflow_functions"
@classmethod
def VALIDATE_INPUTS(cls, input_types=None, **kwargs):
# Accept any incoming types for dynamic wff_in_* inputs.
return True
@classmethod
def execute(cls, workflow: str, **kwargs):
if not workflow or workflow.startswith("["):
raise ValueError("No workflow selected. Choose a workflow file from the dropdown.")
try:
data = load_workflow(workflow)
except FileNotFoundError:
raise ValueError(f"Workflow file not found: {workflow!r}")
output_refs, graph = build_expansion(data, kwargs)
while len(output_refs) < MAX_SLOTS:
output_refs.append(None)
return {"result": tuple(output_refs), "expand": graph.finalize()}