Allow autogrow to work with all inputs being optional

This commit is contained in:
Jedrzej Kosinski 2026-01-19 15:24:05 -08:00
parent 12256ff5af
commit 3adb63a098
2 changed files with 21 additions and 13 deletions

View File

@ -1030,8 +1030,8 @@ class Autogrow(ComfyTypeI):
# account for the edge case that all inputs are optional and no values are received # account for the edge case that all inputs are optional and no values are received
if not new_dict_added_to: if not new_dict_added_to:
finalized_prefix = finalize_prefix(curr_prefix) finalized_prefix = finalize_prefix(curr_prefix)
out_dict["dynamic_paths"][finalized_prefix] = None out_dict["dynamic_paths"][finalized_prefix] = finalized_prefix
out_dict["dynamic_paths_empty_dict"][finalized_prefix] = finalized_prefix out_dict["dynamic_paths_default_value"][finalized_prefix] = DynamicPathsDefaultValue.EMPTY_DICT
parse_class_inputs(out_dict, live_inputs, new_dict, curr_prefix) parse_class_inputs(out_dict, live_inputs, new_dict, curr_prefix)
@comfytype(io_type="COMFY_DYNAMICCOMBO_V3") @comfytype(io_type="COMFY_DYNAMICCOMBO_V3")
@ -1169,6 +1169,8 @@ class V3Data(TypedDict):
'Dictionary where the keys are the hidden input ids and the values are the values of the hidden inputs.' 'Dictionary where the keys are the hidden input ids and the values are the values of the hidden inputs.'
dynamic_paths: dict[str, Any] dynamic_paths: dict[str, Any]
'Dictionary where the keys are the input ids and the values dictate how to turn the inputs into a nested dictionary.' 'Dictionary where the keys are the input ids and the values dictate how to turn the inputs into a nested dictionary.'
dynamic_paths_default_value: dict[str, Any]
'Dictionary where the keys are the input ids and the values are a string from DynamicPathsDefaultValue for the inputs if value is None.'
create_dynamic_tuple: bool create_dynamic_tuple: bool
'When True, the value of the dynamic input will be in the format (value, path_key).' 'When True, the value of the dynamic input will be in the format (value, path_key).'
@ -1522,7 +1524,7 @@ def get_finalized_class_inputs(d: dict[str, Any], live_inputs: dict[str, Any], i
"required": {}, "required": {},
"optional": {}, "optional": {},
"dynamic_paths": {}, "dynamic_paths": {},
"dynamic_paths_empty_dict": {}, "dynamic_paths_default_value": {},
} }
d = d.copy() d = d.copy()
# ignore hidden for parsing # ignore hidden for parsing
@ -1535,9 +1537,9 @@ def get_finalized_class_inputs(d: dict[str, Any], live_inputs: dict[str, Any], i
if dynamic_paths is not None and len(dynamic_paths) > 0: if dynamic_paths is not None and len(dynamic_paths) > 0:
v3_data["dynamic_paths"] = dynamic_paths v3_data["dynamic_paths"] = dynamic_paths
# this list is used for autogrow, in the case all inputs are optional and no values are passed # this list is used for autogrow, in the case all inputs are optional and no values are passed
dynamic_paths_empty_dict = out_dict.pop("dynamic_paths_empty_dict", None) dynamic_paths_default_value = out_dict.pop("dynamic_paths_default_value", None)
if dynamic_paths_empty_dict is not None and len(dynamic_paths_empty_dict) > 0: if dynamic_paths_default_value is not None and len(dynamic_paths_default_value) > 0:
v3_data["dynamic_paths_empty_dict"] = dynamic_paths_empty_dict v3_data["dynamic_paths_default_value"] = dynamic_paths_default_value
return out_dict, hidden, v3_data return out_dict, hidden, v3_data
def parse_class_inputs(out_dict: dict[str, Any], live_inputs: dict[str, Any], curr_dict: dict[str, Any], curr_prefix: list[str] | None=None) -> None: def parse_class_inputs(out_dict: dict[str, Any], live_inputs: dict[str, Any], curr_dict: dict[str, Any], curr_prefix: list[str] | None=None) -> None:
@ -1574,14 +1576,15 @@ def add_to_dict_v1(i: Input, d: dict):
def add_to_dict_v3(io: Input | Output, d: dict): def add_to_dict_v3(io: Input | Output, d: dict):
d[io.id] = (io.get_io_type(), io.as_dict()) d[io.id] = (io.get_io_type(), io.as_dict())
class DynamicPathsDefaultValue:
EMPTY_DICT = "empty_dict"
def build_nested_inputs(values: dict[str, Any], v3_data: V3Data): def build_nested_inputs(values: dict[str, Any], v3_data: V3Data):
paths = v3_data.get("dynamic_paths", None) paths = v3_data.get("dynamic_paths", None)
empty_dict = v3_data.get("dynamic_paths_empty_dict", None) default_value_dict = v3_data.get("dynamic_paths_default_value", {})
if paths is None and empty_dict is None: if paths is None:
return values return values
values = values.copy() values = values.copy()
if empty_dict is not None and len(empty_dict) > 0:
...
result = {} result = {}
@ -1596,6 +1599,11 @@ def build_nested_inputs(values: dict[str, Any], v3_data: V3Data):
if is_last: if is_last:
value = values.pop(key, None) value = values.pop(key, None)
if value is None:
# see if a default value was provided for this key
default_option = default_value_dict.get(key, None)
if default_option == DynamicPathsDefaultValue.EMPTY_DICT:
value = {}
if create_tuple: if create_tuple:
value = (value, key) value = (value, key)
current[p] = value current[p] = value

View File

@ -186,7 +186,7 @@ class AutogrowNamesTestNode(io.ComfyNode):
class AutogrowPrefixTestNode(io.ComfyNode): class AutogrowPrefixTestNode(io.ComfyNode):
@classmethod @classmethod
def define_schema(cls): def define_schema(cls):
template = _io.Autogrow.TemplatePrefix(input=io.Float.Input("float"), prefix="float", min=1, max=10) template = _io.Autogrow.TemplatePrefix(input=io.Float.Input("float", optional=True), prefix="float", min=1, max=10)
return io.Schema( return io.Schema(
node_id="AutogrowPrefixTestNode", node_id="AutogrowPrefixTestNode",
display_name="AutogrowPrefixTest", display_name="AutogrowPrefixTest",
@ -257,9 +257,9 @@ class LogicExtension(ComfyExtension):
CustomComboNode, CustomComboNode,
# SoftSwitchNode, # SoftSwitchNode,
# ConvertStringToComboNode, # ConvertStringToComboNode,
# DCTestNode, DCTestNode,
# AutogrowNamesTestNode, # AutogrowNamesTestNode,
# AutogrowPrefixTestNode, AutogrowPrefixTestNode,
# ComboOutputTestNode, # ComboOutputTestNode,
# InvertBooleanNode, # InvertBooleanNode,
] ]