Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
5ede173
Support Combo outputs in a more sane way
Kosinkadink Dec 3, 2025
0b5d402
Remove test validate_inputs function on test node
Kosinkadink Dec 3, 2025
009e90a
Make curr_prefix be a list of strings instead of string for easier pa…
Kosinkadink Dec 3, 2025
f8ede95
Start to account for id prefixes from frontend, need to fix bug with …
Kosinkadink Dec 3, 2025
203a4e9
Merge branch 'master' into v3-improvements
Kosinkadink Dec 3, 2025
c4bbb1e
Ensure inputs/outputs/hidden are lists in schema finalize function, r…
Kosinkadink Dec 3, 2025
7f9bfbc
Add raw_link and extra_dict to all relevant Inputs
Kosinkadink Dec 3, 2025
639cf87
Merge branch 'master' into v3-improvements
Kosinkadink Dec 3, 2025
154bf49
Make nested DynamicCombos work properly with prefixed keys on latest …
Kosinkadink Dec 3, 2025
23cd5bb
Replace ... usage with a MISSING sentinel for clarity in nodes_logic.py
Kosinkadink Dec 4, 2025
3a40e08
Added CustomCombo node in backend to reflect frontend node
Kosinkadink Dec 4, 2025
7637be3
Merge branch 'master' into v3-improvements
Kosinkadink Dec 4, 2025
2b6106d
Prepare Autogrow's expand_schema_for_dynamic to work with upcoming fr…
Kosinkadink Dec 4, 2025
c2c4810
Prepare for look up table for dynamic input stuff
Kosinkadink Dec 4, 2025
60762e1
More progress towards dynamic input lookup function stuff
Kosinkadink Dec 4, 2025
bdb2de3
Merge branch 'master' into v3-improvements
Kosinkadink Dec 4, 2025
25b47b5
Finished converting _expand_schema_for_dynamic to be done via lookup …
Kosinkadink Dec 5, 2025
74858bc
Change order of functions
Kosinkadink Dec 5, 2025
5d31396
Removed some unneeded functions after dynamic refactor
Kosinkadink Dec 5, 2025
f0e0a19
Merge branch 'master' into v3-improvements
Kosinkadink Dec 5, 2025
ffdfcaf
Merge branch 'master' into v3-improvements
Kosinkadink Dec 6, 2025
a2a22ce
Merge branch 'v3-improvements' of https://github.com/comfyanonymous/C…
Kosinkadink Dec 6, 2025
0b78859
Make MatchType's output default displayname "MATCHTYPE"
Kosinkadink Dec 6, 2025
e2bc630
Merge branch 'master' into v3-improvements
Kosinkadink Dec 6, 2025
ebb466d
Merge branch 'v3-improvements' of https://github.com/comfyanonymous/C…
Kosinkadink Dec 6, 2025
43856bd
Merge remote-tracking branch 'origin/master' into v3-improvements
Kosinkadink Dec 9, 2025
ad1a0f9
Fix DynamicSlot get_all
Kosinkadink Dec 10, 2025
133184e
Merge branch 'master' into v3-improvements
Kosinkadink Dec 10, 2025
90f17a2
Merge branch 'master' into v3-improvements
Kosinkadink Dec 14, 2025
a4226db
Removed redundant code - dynamic stuff no longer happens in OOP way
Kosinkadink Dec 14, 2025
0d9364e
Natively support AnyType (*) without __ne__ hacks
Kosinkadink Dec 14, 2025
364b9b6
Remove stray code that made it in
Kosinkadink Dec 14, 2025
cb582ab
Remove expand_schema_for_dynamic left over on DynamicInput class
Kosinkadink Dec 14, 2025
308ae94
get_dynamic() on DynamicInput/Output was not doing anything anymore, …
Kosinkadink Dec 14, 2025
b1b1429
Make validate_inputs validate combo input correctly
Kosinkadink Dec 14, 2025
dd7c045
Temporarily comment out conversion to 'new' (9 month old) COMBO forma…
Kosinkadink Dec 16, 2025
f3c27d6
Merge branch 'master' into v3-improvements
Kosinkadink Dec 16, 2025
ba13d10
Remove refrences to resources feature scrapped from V3
Kosinkadink Dec 16, 2025
3b4927a
Expose DynamicCombo in public API
Kosinkadink Dec 16, 2025
c2b98d0
satisfy ruff after some code got commented out
Kosinkadink Dec 16, 2025
3c71a0d
Make missing input error prettier for dynamic types
Kosinkadink Dec 16, 2025
85a97cc
Created a Switch2 node as a side-by-side test, will likely go with Sw…
Kosinkadink Dec 16, 2025
6630cd8
Figured out Switch situation
Kosinkadink Dec 17, 2025
cb7d245
Merge branch 'master' into v3-improvements
Kosinkadink Dec 17, 2025
a3d1459
Pass in v3_data in IsChangedCache.get function's fingerprint_inputs, …
Kosinkadink Dec 17, 2025
d764601
Switch order of Switch and Soft Switch nodes in file
Kosinkadink Dec 17, 2025
2eca887
Temp test node for MatchType
Kosinkadink Dec 17, 2025
98cc896
Fix missing v3_data for v1 nodes in validation
Kosinkadink Dec 18, 2025
004b1b5
For now, remove chacking duplicate id's for dynamic types
Kosinkadink Dec 18, 2025
e517a6b
Add Resize Image/Mask node that thanks to MatchType+DynamicCombo is 1…
Kosinkadink Dec 18, 2025
fc93133
Merge branch 'master' into v3-improvements
Kosinkadink Dec 18, 2025
0958579
Made DynamicCombo references in DCTestNode use public interface
Kosinkadink Dec 18, 2025
e8d4074
Add an AnyTypeTestNode
Kosinkadink Dec 18, 2025
bef3356
Merge branch 'master' into v3-improvements
Kosinkadink Dec 18, 2025
dd4786b
Make lazy status for specific inputs on DynamicInputs work by having …
Kosinkadink Dec 19, 2025
b4bc854
Comment out test logic nodes
Kosinkadink Dec 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion comfy_api/latest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from ._util import VideoCodec, VideoContainer, VideoComponents, MESH, VOXEL
from . import _io_public as io
from . import _ui_public as ui
# from comfy_api.latest._resources import _RESOURCES as resources #noqa: F401
from comfy_execution.utils import get_executing_context
from comfy_execution.progress import get_progress_state, PreviewImageTuple
from PIL import Image
Expand Down
357 changes: 200 additions & 157 deletions comfy_api/latest/_io.py

Large diffs are not rendered by default.

72 changes: 0 additions & 72 deletions comfy_api/latest/_resources.py

This file was deleted.

5 changes: 5 additions & 0 deletions comfy_execution/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ def get_input_info(
extra_info = input_info[1]
else:
extra_info = {}
# if input_type is a list, it is a Combo defined in outdated format; convert it.
# NOTE: uncomment this when we are confident old format going away won't cause too much trouble.
# if isinstance(input_type, list):
# extra_info["options"] = input_type
# input_type = IO.Combo.io_type
return input_type, input_category, extra_info

class TopologicalSort:
Expand Down
14 changes: 14 additions & 0 deletions comfy_execution/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,24 @@ def validate_node_input(
"""
# If the types are exactly the same, we can return immediately
# Use pre-union behaviour: inverse of `__ne__`
# NOTE: this lets legacy '*' Any types work that override the __ne__ method of the str class.
if not received_type != input_type:
return True

# If one of the types is '*', we can return True immediately; this is the 'Any' type.
if received_type == IO.AnyType.io_type or input_type == IO.AnyType.io_type:
return True

# If the received type or input_type is a MatchType, we can return True immediately;
# validation for this is handled by the frontend
if received_type == IO.MatchType.io_type or input_type == IO.MatchType.io_type:
return True

# This accounts for some custom nodes that output lists of options as the type;
# if we ever want to break them on purpose, this can be removed
if isinstance(received_type, list) and input_type == IO.Combo.io_type:
return True

# Not equal, and not strings
if not isinstance(received_type, str) or not isinstance(input_type, str):
return False
Expand All @@ -37,6 +47,10 @@ def validate_node_input(
received_types = set(t.strip() for t in received_type.split(","))
input_types = set(t.strip() for t in input_type.split(","))

# If any of the types is '*', we can return True immediately; this is the 'Any' type.
if IO.AnyType.io_type in received_types or IO.AnyType.io_type in input_types:
return True

if strict:
# In strict mode, all received types must be in the input types
return received_types.issubset(input_types)
Expand Down
149 changes: 131 additions & 18 deletions comfy_extras/nodes_logic.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from __future__ import annotations
from typing import TypedDict
from typing_extensions import override
from comfy_api.latest import ComfyExtension, io
from comfy_api.latest import _io

# sentinel for missing inputs
MISSING = object()


class SwitchNode(io.ComfyNode):
Expand All @@ -14,6 +17,59 @@ def define_schema(cls):
display_name="Switch",
category="logic",
is_experimental=True,
inputs=[
io.Boolean.Input("switch"),
io.MatchType.Input("on_false", template=template, lazy=True),
io.MatchType.Input("on_true", template=template, lazy=True),
],
outputs=[
io.MatchType.Output(template=template, display_name="output"),
],
)

@classmethod
def check_lazy_status(cls, switch, on_false=None, on_true=None):
if switch and on_true is None:
return ["on_true"]
if not switch and on_false is None:
return ["on_false"]

@classmethod
def execute(cls, switch, on_true, on_false) -> io.NodeOutput:
return io.NodeOutput(on_true if switch else on_false)


class MatchTypeTestNode(io.ComfyNode):
@classmethod
def define_schema(cls):
template = io.MatchType.Template("switch", [io.Image, io.Mask, io.Latent])
return io.Schema(
node_id="MatchTypeTestNode",
display_name="MatchTypeTest",
category="logic",
is_experimental=True,
inputs=[
io.MatchType.Input("input", template=template),
],
outputs=[
io.MatchType.Output(template=template, display_name="output"),
],
)

@classmethod
def execute(cls, input) -> io.NodeOutput:
return io.NodeOutput(input)


class SoftSwitchNode(io.ComfyNode):
@classmethod
def define_schema(cls):
template = io.MatchType.Template("switch")
return io.Schema(
node_id="ComfySoftSwitchNode",
display_name="Soft Switch",
category="logic",
is_experimental=True,
inputs=[
io.Boolean.Input("switch"),
io.MatchType.Input("on_false", template=template, lazy=True, optional=True),
Expand All @@ -25,14 +81,14 @@ def define_schema(cls):
)

@classmethod
def check_lazy_status(cls, switch, on_false=..., on_true=...):
# We use ... instead of None, as None is passed for connected-but-unevaluated inputs.
def check_lazy_status(cls, switch, on_false=MISSING, on_true=MISSING):
# We use MISSING instead of None, as None is passed for connected-but-unevaluated inputs.
# This trick allows us to ignore the value of the switch and still be able to run execute().

# One of the inputs may be missing, in which case we need to evaluate the other input
if on_false is ...:
if on_false is MISSING:
return ["on_true"]
if on_true is ...:
if on_true is MISSING:
return ["on_false"]
# Normal lazy switch operation
if switch and on_true is None:
Expand All @@ -41,22 +97,43 @@ def check_lazy_status(cls, switch, on_false=..., on_true=...):
return ["on_false"]

@classmethod
def validate_inputs(cls, switch, on_false=..., on_true=...):
def validate_inputs(cls, switch, on_false=MISSING, on_true=MISSING):
# This check happens before check_lazy_status(), so we can eliminate the case where
# both inputs are missing.
if on_false is ... and on_true is ...:
if on_false is MISSING and on_true is MISSING:
return "At least one of on_false or on_true must be connected to Switch node"
return True

@classmethod
def execute(cls, switch, on_true=..., on_false=...) -> io.NodeOutput:
if on_true is ...:
def execute(cls, switch, on_true=MISSING, on_false=MISSING) -> io.NodeOutput:
if on_true is MISSING:
return io.NodeOutput(on_false)
if on_false is ...:
if on_false is MISSING:
return io.NodeOutput(on_true)
return io.NodeOutput(on_true if switch else on_false)


class CustomComboNode(io.ComfyNode):
"""
Frontend node that allows user to write their own options for a combo.
This is here to make sure the node has a backend-representation to avoid some annoyances.
"""
@classmethod
def define_schema(cls):
return io.Schema(
node_id="CustomCombo",
display_name="Custom Combo",
category="utils",
is_experimental=True,
inputs=[io.Combo.Input("choice", options=[])],
outputs=[io.String.Output()]
)

@classmethod
def execute(cls, choice: io.Combo.Type) -> io.NodeOutput:
return io.NodeOutput(choice)


class DCTestNode(io.ComfyNode):
class DCValues(TypedDict):
combo: str
Expand All @@ -72,14 +149,14 @@ def define_schema(cls):
display_name="DCTest",
category="logic",
is_output_node=True,
inputs=[_io.DynamicCombo.Input("combo", options=[
_io.DynamicCombo.Option("option1", [io.String.Input("string")]),
_io.DynamicCombo.Option("option2", [io.Int.Input("integer")]),
_io.DynamicCombo.Option("option3", [io.Image.Input("image")]),
_io.DynamicCombo.Option("option4", [
_io.DynamicCombo.Input("subcombo", options=[
_io.DynamicCombo.Option("opt1", [io.Float.Input("float_x"), io.Float.Input("float_y")]),
_io.DynamicCombo.Option("opt2", [io.Mask.Input("mask1", optional=True)]),
inputs=[io.DynamicCombo.Input("combo", options=[
io.DynamicCombo.Option("option1", [io.String.Input("string")]),
io.DynamicCombo.Option("option2", [io.Int.Input("integer")]),
io.DynamicCombo.Option("option3", [io.Image.Input("image")]),
io.DynamicCombo.Option("option4", [
io.DynamicCombo.Input("subcombo", options=[
io.DynamicCombo.Option("opt1", [io.Float.Input("float_x"), io.Float.Input("float_y")]),
io.DynamicCombo.Option("opt2", [io.Mask.Input("mask1", optional=True)]),
])
])]
)],
Expand Down Expand Up @@ -141,14 +218,50 @@ def execute(cls, autogrow: _io.Autogrow.Type) -> io.NodeOutput:
combined = ",".join([str(x) for x in vals])
return io.NodeOutput(combined)

class ComboOutputTestNode(io.ComfyNode):
@classmethod
def define_schema(cls):
return io.Schema(
node_id="ComboOptionTestNode",
display_name="ComboOptionTest",
category="logic",
inputs=[io.Combo.Input("combo", options=["option1", "option2", "option3"]),
io.Combo.Input("combo2", options=["option4", "option5", "option6"])],
outputs=[io.Combo.Output(), io.Combo.Output()],
)

@classmethod
def execute(cls, combo: io.Combo.Type, combo2: io.Combo.Type) -> io.NodeOutput:
return io.NodeOutput(combo, combo2)

class AnyTypeTestNode(io.ComfyNode):
@classmethod
def define_schema(cls):
return io.Schema(
node_id="AnyNodeTestNode",
display_name="AnyNodeTest",
category="logic",
inputs=[io.AnyType.Input("any")],
outputs=[io.AnyType.Output()],
)

@classmethod
def execute(cls, any: io.AnyType.Type) -> io.NodeOutput:
return io.NodeOutput(any)

class LogicExtension(ComfyExtension):
@override
async def get_node_list(self) -> list[type[io.ComfyNode]]:
return [
# SwitchNode,
SwitchNode,
# SoftSwitchNode,
# CustomComboNode,
# DCTestNode,
# AutogrowNamesTestNode,
# AutogrowPrefixTestNode,
# ComboOutputTestNode,
# MatchTypeTestNode,
# AnyTypeTestNode,
]

async def comfy_entrypoint() -> LogicExtension:
Expand Down
Loading