DynamicOutputs: tighter integration with DynamicCombo / DynamicSlot via FromInput

Lets dynamic-input nodes co-declare per-option outputs so the option list is a
single source of truth for both inputs and outputs.

* DynamicCombo.Option / DynamicSlot.Option gain an optional outputs=[...] list.
* DynamicOutputs.FromInput(input_id) is a positional placeholder in outputs[]
  that resolves to the referenced input's active-option outputs at finalize
  time. DynamicCombo selects by literal value; DynamicSlot selects by the
  upstream slot's resolved type (or when=None when unlinked).
* get_finalized_class_outputs gains schema_inputs / live_input_types and the
  TypeResolver / execute() compute live_input_types only when at least one
  FromInput → DynamicSlot is present.
* Schema.validate(): FromInput must reference an existing DynamicCombo /
  DynamicSlot input, each input may be referenced at most once, and option
  output ids stay globally unique.
* V1 info synthesizes the dynamic_outputs entry as kind='by_key' for combos
  and kind='by_slot' for slots, inlining the option outputs.

Tests: combo + slot FromInput finalization, FromInput validation (missing /
duplicate / id collision), and TypeResolver end-to-end picks for both kinds.

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 21:10:17 -07:00
parent 188065c011
commit cca4119fdf
5 changed files with 575 additions and 51 deletions

View File

@@ -503,8 +503,25 @@ async def execute(server, dynprompt, caches, current_item, extra_data, executed,
finalized_outputs = None
if issubclass(class_def, _ComfyNodeInternal):
schema = class_def.GET_SCHEMA()
if any(isinstance(o, _io.DynamicOutputs.ByKey) for o in schema.outputs):
finalized_outputs = _io.get_finalized_class_outputs(schema.outputs, inputs)
has_dynamic = any(
isinstance(o, (_io.DynamicOutputs.ByKey, _io.DynamicOutputs.FromInput))
for o in schema.outputs
)
if has_dynamic:
# live_input_types is only needed for FromInput → DynamicSlot finalization
needs_live_types = any(
isinstance(o, _io.DynamicOutputs.FromInput)
and any(isinstance(i, _io.DynamicSlot.Input) and i.id == o.input_id for i in schema.inputs)
for o in schema.outputs
)
live_input_types = None
if needs_live_types and hasattr(dynprompt, "get_type_resolver"):
live_input_types = dynprompt.get_type_resolver().compute_live_input_types(unique_id)
finalized_outputs = _io.get_finalized_class_outputs(
schema.outputs, inputs,
schema_inputs=schema.inputs,
live_input_types=live_input_types,
)
input_data_all = None
try: