mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2025-07-27 16:26:39 +00:00
Support validate_inputs for v3 replacing VALIDATE_INPUTS, support check_lazy_mix for v3, prep for renaming IS_CHANGED to fingerprint_inputs, reorder some class methods
This commit is contained in:
parent
936bf6b60f
commit
82e6eeab75
@ -1025,12 +1025,6 @@ class ComfyNodeV3:
|
|||||||
resources: Resources = None
|
resources: Resources = None
|
||||||
hidden: HiddenHolder = None
|
hidden: HiddenHolder = None
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def GET_NODE_INFO_V3(cls) -> dict[str, Any]:
|
|
||||||
schema = cls.GET_SCHEMA()
|
|
||||||
# TODO: finish
|
|
||||||
return None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def DEFINE_SCHEMA(cls) -> SchemaV3:
|
def DEFINE_SCHEMA(cls) -> SchemaV3:
|
||||||
@ -1046,10 +1040,46 @@ class ComfyNodeV3:
|
|||||||
pass
|
pass
|
||||||
execute = None
|
execute = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate_inputs(cls, **kwargs) -> bool:
|
||||||
|
"""Optionally, define this function to validate inputs; equivalnet to V1's VALIDATE_INPUTS."""
|
||||||
|
pass
|
||||||
|
validate_inputs = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fingerprint_inputs(cls, **kwargs) -> Any:
|
||||||
|
"""Optionally, define this function to fingerprint inputs; equivalent to V1's IS_CHANGED."""
|
||||||
|
pass
|
||||||
|
fingerprint_inputs = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def check_lazy_status(cls, **kwargs) -> list[str]:
|
||||||
|
"""Optionally, define this function to return a list of input names that should be evaluated.
|
||||||
|
|
||||||
|
This basic mixin impl. requires all inputs.
|
||||||
|
|
||||||
|
:kwargs: All node inputs will be included here. If the input is ``None``, it should be assumed that it has not yet been evaluated. \
|
||||||
|
When using ``INPUT_IS_LIST = True``, unevaluated will instead be ``(None,)``.
|
||||||
|
|
||||||
|
Params should match the nodes execution ``FUNCTION`` (self, and all inputs by name).
|
||||||
|
Will be executed repeatedly until it returns an empty list, or all requested items were already evaluated (and sent as params).
|
||||||
|
|
||||||
|
Comfy Docs: https://docs.comfy.org/custom-nodes/backend/lazy_evaluation#defining-check-lazy-status
|
||||||
|
"""
|
||||||
|
need = [name for name in kwargs if kwargs[name] is None]
|
||||||
|
return need
|
||||||
|
check_lazy_status = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def GET_SERIALIZERS(cls) -> list[Serializer]:
|
def GET_SERIALIZERS(cls) -> list[Serializer]:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def GET_NODE_INFO_V3(cls) -> dict[str, Any]:
|
||||||
|
schema = cls.GET_SCHEMA()
|
||||||
|
# TODO: finish
|
||||||
|
return None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.local_state: NodeStateLocal = None
|
self.local_state: NodeStateLocal = None
|
||||||
self.local_resources: ResourcesLocal = None
|
self.local_resources: ResourcesLocal = None
|
||||||
|
@ -73,7 +73,7 @@ class V3TestNode(io.ComfyNodeV3):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def VALIDATE_INPUTS(cls, image: io.Image.Type, some_int: int, combo: io.Combo.Type, combo2: io.MultiCombo.Type, xyz: XYZ.Type=None, mask: io.Mask.Type=None, **kwargs):
|
def validate_inputs(cls, image: io.Image.Type, some_int: int, combo: io.Combo.Type, combo2: io.MultiCombo.Type, xyz: XYZ.Type=None, mask: io.Mask.Type=None, **kwargs):
|
||||||
if some_int < 0:
|
if some_int < 0:
|
||||||
raise Exception("some_int must be greater than 0")
|
raise Exception("some_int must be greater than 0")
|
||||||
if combo == "c":
|
if combo == "c":
|
||||||
@ -172,6 +172,15 @@ class NInputsTest(io.ComfyNodeV3):
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate_inputs(cls, nmock, nmock2):
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def check_lazy_status(cls, **kwargs) -> list[str]:
|
||||||
|
need = [name for name in kwargs if kwargs[name] is None]
|
||||||
|
return need
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def execute(cls, nmock, nmock2):
|
def execute(cls, nmock, nmock2):
|
||||||
first_image = nmock[0]
|
first_image = nmock[0]
|
||||||
|
31
execution.py
31
execution.py
@ -28,7 +28,7 @@ from comfy_execution.graph import (
|
|||||||
)
|
)
|
||||||
from comfy_execution.graph_utils import GraphBuilder, is_link
|
from comfy_execution.graph_utils import GraphBuilder, is_link
|
||||||
from comfy_execution.validation import validate_node_input
|
from comfy_execution.validation import validate_node_input
|
||||||
from comfy_api.v3.io import NodeOutput, ComfyNodeV3, Hidden, NodeStateLocal, ResourcesLocal, AutogrowDynamic
|
from comfy_api.v3.io import NodeOutput, ComfyNodeV3, Hidden, NodeStateLocal, ResourcesLocal, AutogrowDynamic, is_class
|
||||||
|
|
||||||
|
|
||||||
class ExecutionResult(Enum):
|
class ExecutionResult(Enum):
|
||||||
@ -216,7 +216,15 @@ def _map_node_over_list(obj, input_data_all, func, allow_interrupt=False, execut
|
|||||||
if pre_execute_cb is not None and index is not None:
|
if pre_execute_cb is not None and index is not None:
|
||||||
pre_execute_cb(index)
|
pre_execute_cb(index)
|
||||||
# V3
|
# V3
|
||||||
if isinstance(obj, ComfyNodeV3):
|
if isinstance(obj, ComfyNodeV3) or (is_class(obj) and issubclass(obj, ComfyNodeV3)):
|
||||||
|
# if is just a class, then assign no resources or state, just create clone
|
||||||
|
if is_class(obj):
|
||||||
|
type_obj = obj
|
||||||
|
obj.VALIDATE_CLASS()
|
||||||
|
class_clone = obj.prepare_class_clone(hidden_inputs)
|
||||||
|
# otherwise, use class instance to populate/reuse some fields
|
||||||
|
else:
|
||||||
|
type_obj = type(obj)
|
||||||
type(obj).VALIDATE_CLASS()
|
type(obj).VALIDATE_CLASS()
|
||||||
class_clone = type(obj).prepare_class_clone(hidden_inputs)
|
class_clone = type(obj).prepare_class_clone(hidden_inputs)
|
||||||
# NOTE: this is a mock of state management; for local, just stores NodeStateLocal on node instance
|
# NOTE: this is a mock of state management; for local, just stores NodeStateLocal on node instance
|
||||||
@ -239,7 +247,7 @@ def _map_node_over_list(obj, input_data_all, func, allow_interrupt=False, execut
|
|||||||
dynamic_list.append(real_inputs.pop(d.id, None))
|
dynamic_list.append(real_inputs.pop(d.id, None))
|
||||||
dynamic_list = [x for x in dynamic_list if x is not None]
|
dynamic_list = [x for x in dynamic_list if x is not None]
|
||||||
inputs = {**real_inputs, add_key: dynamic_list}
|
inputs = {**real_inputs, add_key: dynamic_list}
|
||||||
results.append(getattr(type(obj), func).__func__(class_clone, **inputs))
|
results.append(getattr(type_obj, func).__func__(class_clone, **inputs))
|
||||||
# V1
|
# V1
|
||||||
else:
|
else:
|
||||||
results.append(getattr(obj, func)(**inputs))
|
results.append(getattr(obj, func)(**inputs))
|
||||||
@ -392,7 +400,7 @@ def execute(server, dynprompt, caches, current_item, extra_data, executed, promp
|
|||||||
obj = class_def()
|
obj = class_def()
|
||||||
caches.objects.set(unique_id, obj)
|
caches.objects.set(unique_id, obj)
|
||||||
|
|
||||||
if hasattr(obj, "check_lazy_status"):
|
if getattr(obj, "check_lazy_status", None) is not None:
|
||||||
required_inputs = _map_node_over_list(obj, input_data_all, "check_lazy_status", allow_interrupt=True, hidden_inputs=hidden_inputs)
|
required_inputs = _map_node_over_list(obj, input_data_all, "check_lazy_status", allow_interrupt=True, hidden_inputs=hidden_inputs)
|
||||||
required_inputs = set(sum([r for r in required_inputs if isinstance(r,list)], []))
|
required_inputs = set(sum([r for r in required_inputs if isinstance(r,list)], []))
|
||||||
required_inputs = [x for x in required_inputs if isinstance(x,str) and (
|
required_inputs = [x for x in required_inputs if isinstance(x,str) and (
|
||||||
@ -651,8 +659,16 @@ def validate_inputs(prompt, item, validated):
|
|||||||
|
|
||||||
validate_function_inputs = []
|
validate_function_inputs = []
|
||||||
validate_has_kwargs = False
|
validate_has_kwargs = False
|
||||||
if hasattr(obj_class, "VALIDATE_INPUTS"):
|
validate_function_name = None
|
||||||
argspec = inspect.getfullargspec(obj_class.VALIDATE_INPUTS)
|
validate_function = None
|
||||||
|
if issubclass(obj_class, ComfyNodeV3):
|
||||||
|
validate_function_name = "validate_inputs"
|
||||||
|
validate_function = getattr(obj_class, validate_function_name, None)
|
||||||
|
else:
|
||||||
|
validate_function_name = "VALIDATE_INPUTS"
|
||||||
|
validate_function = getattr(obj_class, validate_function_name, None)
|
||||||
|
if validate_function is not None:
|
||||||
|
argspec = inspect.getfullargspec(validate_function)
|
||||||
validate_function_inputs = argspec.args
|
validate_function_inputs = argspec.args
|
||||||
validate_has_kwargs = argspec.varkw is not None
|
validate_has_kwargs = argspec.varkw is not None
|
||||||
received_types = {}
|
received_types = {}
|
||||||
@ -835,8 +851,7 @@ def validate_inputs(prompt, item, validated):
|
|||||||
if 'input_types' in validate_function_inputs:
|
if 'input_types' in validate_function_inputs:
|
||||||
input_filtered['input_types'] = [received_types]
|
input_filtered['input_types'] = [received_types]
|
||||||
|
|
||||||
#ret = obj_class.VALIDATE_INPUTS(**input_filtered)
|
ret = _map_node_over_list(obj_class, input_filtered, validate_function_name, hidden_inputs=hidden_inputs)
|
||||||
ret = _map_node_over_list(obj_class, input_filtered, "VALIDATE_INPUTS", hidden_inputs=hidden_inputs)
|
|
||||||
for x in input_filtered:
|
for x in input_filtered:
|
||||||
for i, r in enumerate(ret):
|
for i, r in enumerate(ret):
|
||||||
if r is not True and not isinstance(r, ExecutionBlocker):
|
if r is not True and not isinstance(r, ExecutionBlocker):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user