DynamicSlot: typed option dispatch driven by TypeResolver

Replaces the old connected/unconnected fixed-child DynamicSlot with a
type-keyed option list. Each Option declares a 'when' condition (None,
io.AnyType, a single ComfyType, a list, or a MultiType.Input) and the
child inputs revealed when that condition matches the slot's resolved
upstream type.

Selection happens at schema-finalization time using live_input_types
computed by TypeResolver, so API-only workflows (no frontend) get the
same expansion the UI would.

- _io.py: redesign DynamicSlot.Input / Option; auto-derive slotType as
  the union of all non-None when sets; expose it via as_dict so the
  frontend knows what types are accepted; the class io_type stays
  COMFY_DYNAMICSLOT_V3 as the parse-time dispatch tag.
- type_resolver.py: return the auto-derived _slot_io_type for
  DynamicSlot.Input; document the AnyType (*) limitation.
- execution.py: validate links into a DynamicSlot against slotType,
  not the dispatch tag COMFY_DYNAMICSLOT_V3.
- tests: new test_dynamic_slot.py + regression coverage in
  test_type_resolver.py.

Amp-Thread-ID: https://ampcode.com/threads/T-019e8568-f382-743d-a97f-0de3ff29d501
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Jedrzej Kosinski
2026-06-01 17:56:32 -07:00
parent 0e4a15b7fb
commit d91c1d8d48
5 changed files with 455 additions and 34 deletions

View File

@@ -358,6 +358,35 @@ def test_effective_slot_type_on_v3_plain_input(fake_nodes_module, TypeResolver):
assert r.compute_live_input_types("n") == {"flag": "BOOLEAN"}
def test_effective_slot_type_peels_dynamic_slot(fake_nodes_module, TypeResolver):
"""A DynamicSlot input reports its auto-derived slotType (union of `when` types)."""
from comfy_api.latest import _io as io
class DSNode(io.ComfyNode):
@classmethod
def define_schema(cls):
return io.Schema(
node_id="DSNode",
inputs=[
io.DynamicSlot.Input("slot", options=[
io.DynamicSlot.Option(when=io.Image, inputs=[]),
io.DynamicSlot.Option(when=io.Latent, inputs=[]),
]),
],
outputs=[io.String.Output()],
)
@classmethod
def execute(cls, **kwargs):
return io.NodeOutput("")
DSNode.GET_SCHEMA()
fake_nodes_module["DSNode"] = DSNode
prompt = {"n": {"class_type": "DSNode", "inputs": {}}}
r = TypeResolver(prompt)
assert r.get_declared_slot_io_type("n", "slot") == "IMAGE,LATENT"
def test_compute_live_input_types_mixes_links_and_literals(fake_nodes_module, TypeResolver):
fake_nodes_module["Src"] = _v1_node(("MODEL",))
fake_nodes_module["Sink"] = _v1_node(