- Add `optional` kwarg to DynamicSlot.Input (default True). When False,
declaring a when=None Option is rejected because the unconnected branch
is unreachable.
- Always publish `forceInput=True` on the slot itself. slotType may
include widget-capable types (INT/STRING/etc.) but a DynamicSlot is
meant to look like a connection point, never a widget.
Amp-Thread-ID: https://ampcode.com/threads/T-019e8568-f382-743d-a97f-0de3ff29d501
Co-authored-by: Amp <amp@ampcode.com>
Grouping AnyType with concrete types conflates the known-type case with
the unresolvable-wildcard case under one branch and muddies slotType.
AnyType must stand alone as when=io.AnyType so the two states stay
distinct in both dispatch and the serialized schema.
Amp-Thread-ID: https://ampcode.com/threads/T-019e8568-f382-743d-a97f-0de3ff29d501
Co-authored-by: Amp <amp@ampcode.com>
Bug fixes:
- execution.py validate_inputs() now calls _select_option for DynamicSlot
links instead of validating against slotType. Old code accepted any
concrete upstream type whenever AnyType was enumerated (slotType
contained '*', which validate_node_input treats as accept-anything).
Verified end-to-end: LATENT into an IMAGE+AnyType slot is now rejected.
- Thread live_input_types through get_input_data so custom V3
validate_inputs() sees the same DynamicSlot branch that finalization
picked, instead of re-finalizing without resolver context.
- DynamicSlot.Option._when_types is now an ordered tuple (preserves
author declaration order); _slot_io_type/slotType ordering was
previously nondeterministic via frozenset iteration.
Design:
- DynamicSlot.Input.get_all() now returns [self] + children, matching
Autogrow / DynamicCombo so consumers like PriceBadge work uniformly.
- Enforce per-option type uniqueness in DynamicSlot.Input: each io_type
may appear in at most one option's 'when', and at most one option may
declare when=None. Removes the ambiguous first-match-on-overlap case
for single concrete types; ordering still matters when upstream is a
multi-type union.
- Reject non-Option entries in options=[...] explicitly.
Polish:
- Trim verbose DynamicSlot docstrings and inline comments.
Amp-Thread-ID: https://ampcode.com/threads/T-019e8568-f382-743d-a97f-0de3ff29d501
Co-authored-by: Amp <amp@ampcode.com>
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>
The public `comfy_api.latest.io` re-export does not include DynamicSlot,
so the isinstance chain in _effective_io_type raised AttributeError the
first time it ran against any V3 input that wasn't an Autogrow. End-to-end
test caught this with a MatchType chain feeding a probe node.
Amp-Thread-ID: https://ampcode.com/threads/T-019e8568-f382-743d-a97f-0de3ff29d501
Co-authored-by: Amp <amp@ampcode.com>
register_dynamic_input_func is a private helper (underscore module, not
in __all__, not re-exported). Its only callers are the three core
setup_dynamic_input_funcs() registrations, all of which were updated
together. No third party can be relying on the old 5-arg signature, so
the inspect.signature shim and accompanying backward-compat tests are
over-engineered.
Amp-Thread-ID: https://ampcode.com/threads/T-019e8568-f382-743d-a97f-0de3ff29d501
Co-authored-by: Amp <amp@ampcode.com>
* Add _parse_link helper validating both node_id (str) and slot_idx (int,
rejecting bool) so malformed API JSON (e.g. ['n1', '0']) degrades to
AnyType instead of crashing with TypeError.
* Add slot_idx type guards in resolve_output_type and is_output_list.
* Extract _get_class_def_for_node helper to dedupe node/class lookup
across resolve_output_type, is_output_list, get_declared_slot_io_type.
* register_dynamic_input_func now detects 5-argument legacy callables
via inspect.signature and silently wraps them; preserves backward
compatibility for any custom node that registered its own dynamic
input expansion against the pre-live_input_types signature.
* Tests: malformed link (str slot idx, wrong arity), bad slot type
directly to resolve_output_type, non-string class_type. Tests for
the legacy 5-arg shim and the modern 6-arg passthrough, including
callables with uninspectable signatures.
Amp-Thread-ID: https://ampcode.com/threads/T-019e8568-f382-743d-a97f-0de3ff29d501
Co-authored-by: Amp <amp@ampcode.com>
Resolves the concrete io_type of any output/input slot in a prompt by
walking the graph, so API-submitted workflows (no frontend) and the
execution engine agree on resolved types even when MatchType chains are
involved.
* New comfy_execution/type_resolver.py: TypeResolver class with output
resolution (incl. MatchType template walking, cycle detection, depth
cap, AnyType fallback + one-shot warning), input resolution (links and
literals), is_output_list / is_input_list helpers, effective slot
io_type peeling for dynamic wrappers (Autogrow -> wrapped element
type, DynamicSlot -> underlying slot type), and bulk
compute_live_input_types.
* DynamicPrompt now lazily exposes get_type_resolver() and invalidates
the resolver cache on add_ephemeral_node.
* get_finalized_class_inputs / parse_class_inputs / DYNAMIC_INPUT_LOOKUP
callable signature accept an optional live_input_types dict. Existing
Autogrow/DynamicSlot/DynamicCombo expansions accept and ignore it;
future per-type dynamic inputs use it as their discriminator.
* validate_inputs and get_input_data both build live_input_types via
the resolver and pass it through; validate_inputs also uses the
resolver to determine received_type for linked inputs so MatchType
chains in API workflows validate correctly.
* validate_prompt builds one TypeResolver and shares it across all
output-node validations to avoid re-walking chains.
* tests-unit/execution_test/test_type_resolver.py covers V1 static
return types, V1 wildcard warning behavior, MatchType resolution
including first-wins, cycle termination, chain walking, input
resolution, Autogrow peeling, list info, and cache invalidation.
Amp-Thread-ID: https://ampcode.com/threads/T-019e8568-f382-743d-a97f-0de3ff29d501
Co-authored-by: Amp <amp@ampcode.com>
* mm: re-instantate smart memory for VRAM
* mm: restore non-dynamic smart memory
By popular demand. We aren't quite ready for the deprecation as non
dynamic enabled GPUs and some high-vram custom model loader setups
prefer the old full hands on.
* memory_management: Add direct to read GPU mode
Make destination optional (or make it optionally GPU) and use aimdo
to file_read direct to GPU.
* ops: Remove stream pin buffers and use aimdo reads
This consumed too much RAM and its better to just take the hit on
the CPU syncing back the stream on a short ring buffer. Aimdo
implements this so just rip the stream pin buffer from comfy.
* model_management: all active pin registration movement
Its better to just let the active model load past the pin limit as
pins and let the pins move around. The saves the HDD and SATA
people disk traffic while only costing a few GPU syncs.
* utils: use aimdo file handle
This opens on windows with more favourable flags
* mp: only count the model proper for loaded_ram and vram
Exclude live loras from the numbers to avoid the case where the reported
loaded memory exceeds the size of the model.
This causes me confusion in the Kijai visualizer when it looked fully
loaded but was hitting disk due to this accounding disrepency.
* utils: add bit reverse utility
useful for max scattering something ordered.
* pinned_memory: Implement offload balancing
Use a max scatter alogorithm to prioritize pins of the same size such
that when doing a little bit of offloading it gets scattered, allowing
the prefetcher to more evenly swollow the offload.
* comfy-aimdo 0.4.7
Aimdo 0.4.7 implement VRAM buffer exhaustion predection to avoid
early speculative load of weights that definately wont fix once the
inference gets further in.
* model-prefetch: consolidate pin ensures on the sync point
This could happen mid prefetch block, cause a sync of the entire
block and lose overlap. Get ahead of the problem with a free down
at the natural compute stream sync point.
* mm: Put a 2GB min on the pin ceiling
This is reasonably bad if it starts causing swap pressure, moreso than
during normal ram-cache proceedings. Clamp it.
* add --fast-disk
* Move dataset/text nodes to text category
* Rename category utils into utilities
* Rename category api node into partner
* Move categories conditioning, latent, sampling, model_patches, training, etc. under model category
* Dispatch partner nodes in to 3d, audio, image, text, video categories
* Move PreviewAny node to utilities category