mirror of
https://git.datalinker.icu/comfyanonymous/ComfyUI
synced 2025-12-09 22:14:34 +08:00
* Added output_matchtypes to generated json for v3, initial backend support for MatchType, created nodes_logic.py and added SwitchNode * Fixed providing list of allowed_types * Add workaround in validation.py for V3 Combo outputs not working as Combo inputs * Make match type receive_type pass validation * Also add MatchType check to input_type in validation - will likely trigger when connecting to non-lazy stuff * Make sure this PR only has MatchType stuff * Initial work on DynamicCombo * Add get_dynamic function, not yet filled out correctly * Mark Switch node as Beta * Make sure other unfinished dynamic types are not accidentally used * Send DynamicCombo.Option inputs in the same format as normal v1 inputs * add dynamic combo test node * Support validation of inputs and outputs * Add missing input params to DynamicCombo.Input * Add get_all function to inputs for id validation purposes * Fix imports for v3 returning everything when doing io/ui/IO/UI instead of what is in __all__ of _io.py and _ui.py * Modifying behavior of get_dynamic in V3 + serialization so can be used in execution code * Fix v3 schema validation code after changes * Refactor hidden_values for v3 in execution.py to be more general v3_data, add helper functions for dynamic behavior, preparing for restructuring dynamic type into object (not finished yet) * Add nesting of inputs on DynamicCombo during execution * Work with latest frontend commits * Fix cringe arrows * frontend will no longer namespace dynamic inputs widgets so reflect that in code, refactor build_nested_inputs * Prepare Autogrow support for the love of the game * satisfy ruff * Create test nodes for Autogrow to collab with frontend development * Add nested combo to DCTestNode * Remove array support from build_nested_inputs, properly handle missing expected values * Make execution.validate_inputs properly validate required dynamic inputs, renamed dynamic_data to dynamic_paths for clarity * MatchType does not need any DynamicInput/Output features on backend; will increase compatibility with dynamic types * Probably need this for ruff check * Change MatchType to have template be the first and only required param; output id's do nothing right now, so no need * Fix merge regression with LatentUpscaleModel type not being put in __all__ for _io.py, fix invalid type hint for validate_inputs * Make Switch node inputs optional, disallow both inputs from being missing, and still work properly with lazy; when one input is missing, use the other no matter what the switch is set to * Satisfy ruff * Move MatchType code above the types that inherit from DynamicInput * Add DynamicSlot type, awaiting frontend support * Make curr_prefix creation happen in Autogrow, move curr_prefix in DynamicCombo to only be created if input exists in live_inputs * I was confused, fixing accidentally redundant curr_prefix addition in Autogrow * Make sure Autogrow inputs are force_input = True when WidgetInput, fix runtime validation by removing original input from expected inputs, fix min/max bounds, change test nodes slightly * Remove unnecessary id usage in Autogrow test node outputs * Commented out Switch node + test nodes * Remove commented out code from Autogrow * Make TemplatePrefix max more clear, allow max == 1 * Replace all dict[str] with dict[str, Any] * Renamed add_to_dict_live_inputs to expand_schema_for_dynamic * Fixed typo in DynamicSlot input code * note about live_inputs not being present soon in get_v1_info (internal function anyway) * For now, hide DynamicCombo and Autogrow from public interface * Removed comment
156 lines
5.5 KiB
Python
156 lines
5.5 KiB
Python
from typing import TypedDict
|
|
from typing_extensions import override
|
|
from comfy_api.latest import ComfyExtension, io
|
|
from comfy_api.latest import _io
|
|
|
|
|
|
|
|
class SwitchNode(io.ComfyNode):
|
|
@classmethod
|
|
def define_schema(cls):
|
|
template = io.MatchType.Template("switch")
|
|
return io.Schema(
|
|
node_id="ComfySwitchNode",
|
|
display_name="Switch",
|
|
category="logic",
|
|
is_experimental=True,
|
|
inputs=[
|
|
io.Boolean.Input("switch"),
|
|
io.MatchType.Input("on_false", template=template, lazy=True, optional=True),
|
|
io.MatchType.Input("on_true", template=template, lazy=True, optional=True),
|
|
],
|
|
outputs=[
|
|
io.MatchType.Output(template=template, display_name="output"),
|
|
],
|
|
)
|
|
|
|
@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.
|
|
# 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 ...:
|
|
return ["on_true"]
|
|
if on_true is ...:
|
|
return ["on_false"]
|
|
# Normal lazy switch operation
|
|
if switch and on_true is None:
|
|
return ["on_true"]
|
|
if not switch and on_false is None:
|
|
return ["on_false"]
|
|
|
|
@classmethod
|
|
def validate_inputs(cls, switch, on_false=..., on_true=...):
|
|
# 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 ...:
|
|
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 ...:
|
|
return io.NodeOutput(on_false)
|
|
if on_false is ...:
|
|
return io.NodeOutput(on_true)
|
|
return io.NodeOutput(on_true if switch else on_false)
|
|
|
|
|
|
class DCTestNode(io.ComfyNode):
|
|
class DCValues(TypedDict):
|
|
combo: str
|
|
string: str
|
|
integer: int
|
|
image: io.Image.Type
|
|
subcombo: dict[str]
|
|
|
|
@classmethod
|
|
def define_schema(cls):
|
|
return io.Schema(
|
|
node_id="DCTestNode",
|
|
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)]),
|
|
])
|
|
])]
|
|
)],
|
|
outputs=[io.AnyType.Output()],
|
|
)
|
|
|
|
@classmethod
|
|
def execute(cls, combo: DCValues) -> io.NodeOutput:
|
|
combo_val = combo["combo"]
|
|
if combo_val == "option1":
|
|
return io.NodeOutput(combo["string"])
|
|
elif combo_val == "option2":
|
|
return io.NodeOutput(combo["integer"])
|
|
elif combo_val == "option3":
|
|
return io.NodeOutput(combo["image"])
|
|
elif combo_val == "option4":
|
|
return io.NodeOutput(f"{combo['subcombo']}")
|
|
else:
|
|
raise ValueError(f"Invalid combo: {combo_val}")
|
|
|
|
|
|
class AutogrowNamesTestNode(io.ComfyNode):
|
|
@classmethod
|
|
def define_schema(cls):
|
|
template = _io.Autogrow.TemplateNames(input=io.Float.Input("float"), names=["a", "b", "c"])
|
|
return io.Schema(
|
|
node_id="AutogrowNamesTestNode",
|
|
display_name="AutogrowNamesTest",
|
|
category="logic",
|
|
inputs=[
|
|
_io.Autogrow.Input("autogrow", template=template)
|
|
],
|
|
outputs=[io.String.Output()],
|
|
)
|
|
|
|
@classmethod
|
|
def execute(cls, autogrow: _io.Autogrow.Type) -> io.NodeOutput:
|
|
vals = list(autogrow.values())
|
|
combined = ",".join([str(x) for x in vals])
|
|
return io.NodeOutput(combined)
|
|
|
|
class AutogrowPrefixTestNode(io.ComfyNode):
|
|
@classmethod
|
|
def define_schema(cls):
|
|
template = _io.Autogrow.TemplatePrefix(input=io.Float.Input("float"), prefix="float", min=1, max=10)
|
|
return io.Schema(
|
|
node_id="AutogrowPrefixTestNode",
|
|
display_name="AutogrowPrefixTest",
|
|
category="logic",
|
|
inputs=[
|
|
_io.Autogrow.Input("autogrow", template=template)
|
|
],
|
|
outputs=[io.String.Output()],
|
|
)
|
|
|
|
@classmethod
|
|
def execute(cls, autogrow: _io.Autogrow.Type) -> io.NodeOutput:
|
|
vals = list(autogrow.values())
|
|
combined = ",".join([str(x) for x in vals])
|
|
return io.NodeOutput(combined)
|
|
|
|
class LogicExtension(ComfyExtension):
|
|
@override
|
|
async def get_node_list(self) -> list[type[io.ComfyNode]]:
|
|
return [
|
|
# SwitchNode,
|
|
# DCTestNode,
|
|
# AutogrowNamesTestNode,
|
|
# AutogrowPrefixTestNode,
|
|
]
|
|
|
|
async def comfy_entrypoint() -> LogicExtension:
|
|
return LogicExtension()
|