mirror of
https://github.com/Comfy-Org/ComfyUI.git
synced 2026-09-21 13:38:08 -05:00
Declare loop boundaries in node schema (#16347)
This commit is contained in:
@@ -1607,7 +1607,7 @@ class Hidden(str, Enum):
|
|||||||
comfy_usage_source = "COMFY_USAGE_SOURCE"
|
comfy_usage_source = "COMFY_USAGE_SOURCE"
|
||||||
"""COMFY_USAGE_SOURCE identifies the client that submitted the prompt (e.g. comfyui-frontend, comfy-cli, comfyui-mcp); forwarded to API nodes' upstream requests via the Comfy-Usage-Source header."""
|
"""COMFY_USAGE_SOURCE identifies the client that submitted the prompt (e.g. comfyui-frontend, comfy-cli, comfyui-mcp); forwarded to API nodes' upstream requests via the Comfy-Usage-Source header."""
|
||||||
execution_list = "EXECUTION_LIST"
|
execution_list = "EXECUTION_LIST"
|
||||||
"""EXECUTION_LIST is the active graph scheduler."""
|
"""Custom node Developers and Agents: This attribute is core-internal use only and will be removed in a near-future ComfyUI release. DO NOT USE"""
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -1767,6 +1767,8 @@ class Schema:
|
|||||||
Use this for nodes with interactive/operable UI regions that produce intermediate outputs
|
Use this for nodes with interactive/operable UI regions that produce intermediate outputs
|
||||||
(e.g., Image Crop, Painter) rather than final outputs (e.g., Save Image).
|
(e.g., Image Crop, Painter) rather than final outputs (e.g., Save Image).
|
||||||
"""
|
"""
|
||||||
|
loop_boundary: Literal["start", "end"] | None = None
|
||||||
|
"""Identifies this node as the start or end of a loop for prompt validation."""
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
'''Validate the schema:
|
'''Validate the schema:
|
||||||
|
|||||||
@@ -103,8 +103,6 @@ def _expand_loop(dynprompt, opener_id, body, close_id, values, list_items, initi
|
|||||||
|
|
||||||
|
|
||||||
class StartLoop(io.ComfyNode):
|
class StartLoop(io.ComfyNode):
|
||||||
LOOP_BOUNDARY = "start"
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
list_item_type = io.MatchType.Template("list_item")
|
list_item_type = io.MatchType.Template("list_item")
|
||||||
@@ -113,6 +111,7 @@ class StartLoop(io.ComfyNode):
|
|||||||
node_id="StartLoop",
|
node_id="StartLoop",
|
||||||
display_name="Start Loop",
|
display_name="Start Loop",
|
||||||
category="utilities/looping",
|
category="utilities/looping",
|
||||||
|
loop_boundary="start",
|
||||||
is_input_list=True,
|
is_input_list=True,
|
||||||
inputs=[
|
inputs=[
|
||||||
io.DynamicCombo.Input("mode", options=[
|
io.DynamicCombo.Input("mode", options=[
|
||||||
@@ -338,8 +337,6 @@ class LoopResult(io.ComfyNode):
|
|||||||
|
|
||||||
|
|
||||||
class EndLoop(io.ComfyNode):
|
class EndLoop(io.ComfyNode):
|
||||||
LOOP_BOUNDARY = "end"
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def define_schema(cls):
|
def define_schema(cls):
|
||||||
output_type = io.MatchType.Template("output_value")
|
output_type = io.MatchType.Template("output_value")
|
||||||
@@ -357,6 +354,7 @@ class EndLoop(io.ComfyNode):
|
|||||||
node_id="EndLoop",
|
node_id="EndLoop",
|
||||||
display_name="End Loop",
|
display_name="End Loop",
|
||||||
category="utilities/looping",
|
category="utilities/looping",
|
||||||
|
loop_boundary="end",
|
||||||
is_input_list=True,
|
is_input_list=True,
|
||||||
inputs=[
|
inputs=[
|
||||||
io.MatchType.Input(
|
io.MatchType.Input(
|
||||||
|
|||||||
+2
-1
@@ -1176,7 +1176,8 @@ async def validate_prompt(prompt_id, prompt, partial_execution_list: Union[list[
|
|||||||
start_nodes = set()
|
start_nodes = set()
|
||||||
end_nodes = set()
|
end_nodes = set()
|
||||||
for node_id, node in prompt.items():
|
for node_id, node in prompt.items():
|
||||||
boundary = getattr(nodes.NODE_CLASS_MAPPINGS[node["class_type"]], "LOOP_BOUNDARY", None)
|
class_def = nodes.NODE_CLASS_MAPPINGS[node["class_type"]]
|
||||||
|
boundary = class_def.GET_SCHEMA().loop_boundary if issubclass(class_def, _ComfyNodeInternal) else None
|
||||||
if boundary == "start":
|
if boundary == "start":
|
||||||
start_nodes.add(node_id)
|
start_nodes.add(node_id)
|
||||||
elif boundary == "end":
|
elif boundary == "end":
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ from comfy_extras.nodes_loop import EndLoop, LoopIteration, LoopProgress, LoopRe
|
|||||||
def test_loop_schema_exposes_cache_policy_and_integrated_carry():
|
def test_loop_schema_exposes_cache_policy_and_integrated_carry():
|
||||||
inputs = StartLoop.INPUT_TYPES()
|
inputs = StartLoop.INPUT_TYPES()
|
||||||
|
|
||||||
|
assert StartLoop.GET_SCHEMA().loop_boundary == "start"
|
||||||
|
assert EndLoop.GET_SCHEMA().loop_boundary == "end"
|
||||||
|
assert not hasattr(StartLoop, "LOOP_BOUNDARY")
|
||||||
|
assert not hasattr(EndLoop, "LOOP_BOUNDARY")
|
||||||
assert inputs["required"]["cache_iterations"][1]["default"] is False
|
assert inputs["required"]["cache_iterations"][1]["default"] is False
|
||||||
assert inputs["required"]["cache_iterations"][1]["advanced"] is True
|
assert inputs["required"]["cache_iterations"][1]["advanced"] is True
|
||||||
assert list(inputs["optional"]) == ["parent_iteration", "initial_iteration_value"]
|
assert list(inputs["optional"]) == ["parent_iteration", "initial_iteration_value"]
|
||||||
|
|||||||
Reference in New Issue
Block a user