106 lines
3.9 KiB
Python
106 lines
3.9 KiB
Python
"""Backend utility nodes for Switchboard.
|
|
|
|
Unlike the Group/Node Controllers (which are client-side and only toggle node
|
|
*modes*), these are real executing nodes that move data at runtime:
|
|
|
|
- Value On Boolean: pick one of two numbers from a boolean.
|
|
- Boolean Switch: route one of two inputs through, based on a boolean,
|
|
evaluating ONLY the chosen branch (lazy) -- the unused
|
|
input's whole upstream chain is skipped.
|
|
|
|
Written against the V3 node schema (``comfy_api.latest``). The node ids and
|
|
socket names below are load-bearing: they're what saved workflows reference, so
|
|
they match the V1 versions these replaced exactly.
|
|
"""
|
|
|
|
from typing_extensions import override
|
|
|
|
from comfy_api.latest import ComfyExtension, io
|
|
|
|
CATEGORY = "🎛️ Switchboard"
|
|
|
|
|
|
class SwitchboardValueOnBoolean(io.ComfyNode):
|
|
"""Output one of two values depending on a boolean."""
|
|
|
|
@classmethod
|
|
def define_schema(cls) -> io.Schema:
|
|
return io.Schema(
|
|
node_id="SwitchboardValueOnBoolean",
|
|
display_name="Value on Boolean",
|
|
category=CATEGORY,
|
|
description="Pick value_if_true or value_if_false based on a boolean. "
|
|
"The chosen number is returned as FLOAT/INT/STRING; the boolean passes through.",
|
|
inputs=[
|
|
io.Boolean.Input("boolean", default=True),
|
|
io.Float.Input(
|
|
"value_if_true", default=1.0, min=-1.0e9, max=1.0e9, step=0.01
|
|
),
|
|
io.Float.Input(
|
|
"value_if_false", default=0.0, min=-1.0e9, max=1.0e9, step=0.01
|
|
),
|
|
],
|
|
outputs=[
|
|
io.Float.Output(display_name="float"),
|
|
io.Int.Output(display_name="int"),
|
|
io.String.Output(display_name="string"),
|
|
io.Boolean.Output(display_name="boolean"),
|
|
],
|
|
)
|
|
|
|
@classmethod
|
|
def execute(cls, boolean, value_if_true, value_if_false) -> io.NodeOutput:
|
|
value = value_if_true if boolean else value_if_false
|
|
return io.NodeOutput(float(value), int(round(value)), str(value), bool(boolean))
|
|
|
|
|
|
class SwitchboardBooleanSwitch(io.ComfyNode):
|
|
"""Route one of two inputs (any type) based on a boolean, lazily."""
|
|
|
|
@classmethod
|
|
def define_schema(cls) -> io.Schema:
|
|
return io.Schema(
|
|
node_id="SwitchboardBooleanSwitch",
|
|
display_name="Boolean Switch",
|
|
category=CATEGORY,
|
|
description="Pass on_true through when boolean is True, otherwise on_false. "
|
|
"Accepts any type, and only the selected branch is executed (lazy).",
|
|
inputs=[
|
|
# An input socket, not a widget toggle -- wire a BOOLEAN in.
|
|
io.Boolean.Input("boolean", force_input=True),
|
|
# Lazy: only the selected branch is ever evaluated.
|
|
io.AnyType.Input("on_true", optional=True, lazy=True),
|
|
io.AnyType.Input("on_false", optional=True, lazy=True),
|
|
],
|
|
outputs=[
|
|
io.AnyType.Output(display_name="output"),
|
|
],
|
|
)
|
|
|
|
@classmethod
|
|
def check_lazy_status(cls, boolean, on_true=None, on_false=None) -> list[str]:
|
|
# Tell ComfyUI which lazy input we actually need so the other branch's
|
|
# upstream graph is never executed.
|
|
if boolean and on_true is None:
|
|
return ["on_true"]
|
|
if not boolean and on_false is None:
|
|
return ["on_false"]
|
|
return []
|
|
|
|
@classmethod
|
|
def execute(cls, boolean, on_true=None, on_false=None) -> io.NodeOutput:
|
|
return io.NodeOutput(on_true if boolean else on_false)
|
|
|
|
|
|
class SwitchboardExtension(ComfyExtension):
|
|
@override
|
|
async def get_node_list(self) -> list[type[io.ComfyNode]]:
|
|
return [
|
|
SwitchboardValueOnBoolean,
|
|
SwitchboardBooleanSwitch,
|
|
]
|
|
|
|
|
|
async def comfy_entrypoint() -> SwitchboardExtension:
|
|
return SwitchboardExtension()
|