mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2025-07-28 00:36:32 +00:00
Merge pull request #8951 from comfyanonymous/v3-definition-wip
V3 update - refactor names and node structure
This commit is contained in:
commit
f8b7170103
@ -1,6 +1,10 @@
|
|||||||
class ComfyNodeInternal:
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
class ComfyNodeInternal(ABC):
|
||||||
"""Class that all V3-based APIs inherit from for ComfyNode.
|
"""Class that all V3-based APIs inherit from for ComfyNode.
|
||||||
|
|
||||||
This is intended to only be referenced within execution.py, as it has to handle all V3 APIs going forward."""
|
This is intended to only be referenced within execution.py, as it has to handle all V3 APIs going forward."""
|
||||||
...
|
@classmethod
|
||||||
|
@abstractmethod
|
||||||
|
def GET_NODE_INFO_V1(cls):
|
||||||
|
...
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
from typing import Callable, Optional
|
from typing import Callable, Optional
|
||||||
|
|
||||||
|
|
||||||
def first_real_override(cls: type, name: str, *, base: type) -> Optional[Callable]:
|
def first_real_override(cls: type, name: str, *, base: type=None) -> Optional[Callable]:
|
||||||
"""Return the *callable* override of `name` visible on `cls`, or None if every
|
"""Return the *callable* override of `name` visible on `cls`, or None if every
|
||||||
implementation up to (and including) `base` is the placeholder defined on `base`.
|
implementation up to (and including) `base` is the placeholder defined on `base`.
|
||||||
|
|
||||||
|
If base is not provided, it will assume cls has a GET_BASE_CLASS
|
||||||
"""
|
"""
|
||||||
|
if base is None:
|
||||||
|
if not hasattr(cls, "GET_BASE_CLASS"):
|
||||||
|
raise ValueError("base is required if cls does not have a GET_BASE_CLASS; is this a valid ComfyNode subclass?")
|
||||||
|
base = cls.GET_BASE_CLASS()
|
||||||
base_attr = getattr(base, name, None)
|
base_attr = getattr(base, name, None)
|
||||||
if base_attr is None:
|
if base_attr is None:
|
||||||
return None
|
return None
|
||||||
|
@ -6,11 +6,12 @@ from collections import Counter
|
|||||||
from dataclasses import asdict, dataclass
|
from dataclasses import asdict, dataclass
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Any, Callable, Literal, TypedDict, TypeVar
|
from typing import Any, Callable, Literal, TypedDict, TypeVar
|
||||||
|
from comfy_api.v3.helpers import first_real_override
|
||||||
|
|
||||||
# used for type hinting
|
# used for type hinting
|
||||||
import torch
|
import torch
|
||||||
from spandrel import ImageModelDescriptor
|
from spandrel import ImageModelDescriptor
|
||||||
from typing_extensions import NotRequired
|
from typing_extensions import NotRequired, final
|
||||||
|
|
||||||
from comfy.clip_vision import ClipVisionModel
|
from comfy.clip_vision import ClipVisionModel
|
||||||
from comfy.clip_vision import Output as ClipVisionOutput_
|
from comfy.clip_vision import Output as ClipVisionOutput_
|
||||||
@ -189,14 +190,15 @@ class InputV3(IO_V3):
|
|||||||
self.lazy = lazy
|
self.lazy = lazy
|
||||||
self.extra_dict = extra_dict if extra_dict is not None else {}
|
self.extra_dict = extra_dict if extra_dict is not None else {}
|
||||||
|
|
||||||
def as_dict_V1(self):
|
def as_dict(self):
|
||||||
return prune_dict({
|
return prune_dict({
|
||||||
"display_name": self.display_name,
|
"display_name": self.display_name,
|
||||||
|
"optional": self.optional,
|
||||||
"tooltip": self.tooltip,
|
"tooltip": self.tooltip,
|
||||||
"lazy": self.lazy,
|
"lazy": self.lazy,
|
||||||
}) | prune_dict(self.extra_dict)
|
}) | prune_dict(self.extra_dict)
|
||||||
|
|
||||||
def get_io_type_V1(self):
|
def get_io_type(self):
|
||||||
return self.io_type
|
return self.io_type
|
||||||
|
|
||||||
class WidgetInputV3(InputV3):
|
class WidgetInputV3(InputV3):
|
||||||
@ -205,23 +207,23 @@ class WidgetInputV3(InputV3):
|
|||||||
'''
|
'''
|
||||||
def __init__(self, id: str, display_name: str=None, optional=False, tooltip: str=None, lazy: bool=None,
|
def __init__(self, id: str, display_name: str=None, optional=False, tooltip: str=None, lazy: bool=None,
|
||||||
default: Any=None,
|
default: Any=None,
|
||||||
socketless: bool=None, widgetType: str=None, force_input: bool=None, extra_dict=None):
|
socketless: bool=None, widget_type: str=None, force_input: bool=None, extra_dict=None):
|
||||||
super().__init__(id, display_name, optional, tooltip, lazy, extra_dict)
|
super().__init__(id, display_name, optional, tooltip, lazy, extra_dict)
|
||||||
self.default = default
|
self.default = default
|
||||||
self.socketless = socketless
|
self.socketless = socketless
|
||||||
self.widgetType = widgetType
|
self.widget_type = widget_type
|
||||||
self.force_input = force_input
|
self.force_input = force_input
|
||||||
|
|
||||||
def as_dict_V1(self):
|
def as_dict(self):
|
||||||
return super().as_dict_V1() | prune_dict({
|
return super().as_dict() | prune_dict({
|
||||||
"default": self.default,
|
"default": self.default,
|
||||||
"socketless": self.socketless,
|
"socketless": self.socketless,
|
||||||
"widgetType": self.widgetType,
|
"widgetType": self.widget_type,
|
||||||
"forceInput": self.force_input,
|
"forceInput": self.force_input,
|
||||||
})
|
})
|
||||||
|
|
||||||
def get_io_type_V1(self):
|
def get_io_type(self):
|
||||||
return self.widgetType if self.widgetType is not None else super().get_io_type_V1()
|
return self.widget_type if self.widget_type is not None else super().get_io_type()
|
||||||
|
|
||||||
|
|
||||||
class OutputV3(IO_V3):
|
class OutputV3(IO_V3):
|
||||||
@ -232,13 +234,16 @@ class OutputV3(IO_V3):
|
|||||||
self.tooltip = tooltip
|
self.tooltip = tooltip
|
||||||
self.is_output_list = is_output_list
|
self.is_output_list = is_output_list
|
||||||
|
|
||||||
def as_dict_V3(self):
|
def as_dict(self):
|
||||||
return prune_dict({
|
return prune_dict({
|
||||||
"display_name": self.display_name,
|
"display_name": self.display_name,
|
||||||
"tooltip": self.tooltip,
|
"tooltip": self.tooltip,
|
||||||
"is_output_list": self.is_output_list,
|
"is_output_list": self.is_output_list,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
def get_io_type(self):
|
||||||
|
return self.io_type
|
||||||
|
|
||||||
|
|
||||||
class ComfyTypeI(ComfyType):
|
class ComfyTypeI(ComfyType):
|
||||||
'''ComfyType subclass that only has a default Input class - intended for types that only have Inputs.'''
|
'''ComfyType subclass that only has a default Input class - intended for types that only have Inputs.'''
|
||||||
@ -326,8 +331,8 @@ class Boolean(ComfyTypeIO):
|
|||||||
self.label_off = label_off
|
self.label_off = label_off
|
||||||
self.default: bool
|
self.default: bool
|
||||||
|
|
||||||
def as_dict_V1(self):
|
def as_dict(self):
|
||||||
return super().as_dict_V1() | prune_dict({
|
return super().as_dict() | prune_dict({
|
||||||
"label_on": self.label_on,
|
"label_on": self.label_on,
|
||||||
"label_off": self.label_off,
|
"label_off": self.label_off,
|
||||||
})
|
})
|
||||||
@ -349,8 +354,8 @@ class Int(ComfyTypeIO):
|
|||||||
self.display_mode = display_mode
|
self.display_mode = display_mode
|
||||||
self.default: int
|
self.default: int
|
||||||
|
|
||||||
def as_dict_V1(self):
|
def as_dict(self):
|
||||||
return super().as_dict_V1() | prune_dict({
|
return super().as_dict() | prune_dict({
|
||||||
"min": self.min,
|
"min": self.min,
|
||||||
"max": self.max,
|
"max": self.max,
|
||||||
"step": self.step,
|
"step": self.step,
|
||||||
@ -375,8 +380,8 @@ class Float(ComfyTypeIO):
|
|||||||
self.display_mode = display_mode
|
self.display_mode = display_mode
|
||||||
self.default: float
|
self.default: float
|
||||||
|
|
||||||
def as_dict_V1(self):
|
def as_dict(self):
|
||||||
return super().as_dict_V1() | prune_dict({
|
return super().as_dict() | prune_dict({
|
||||||
"min": self.min,
|
"min": self.min,
|
||||||
"max": self.max,
|
"max": self.max,
|
||||||
"step": self.step,
|
"step": self.step,
|
||||||
@ -399,8 +404,8 @@ class String(ComfyTypeIO):
|
|||||||
self.dynamic_prompts = dynamic_prompts
|
self.dynamic_prompts = dynamic_prompts
|
||||||
self.default: str
|
self.default: str
|
||||||
|
|
||||||
def as_dict_V1(self):
|
def as_dict(self):
|
||||||
return super().as_dict_V1() | prune_dict({
|
return super().as_dict() | prune_dict({
|
||||||
"multiline": self.multiline,
|
"multiline": self.multiline,
|
||||||
"placeholder": self.placeholder,
|
"placeholder": self.placeholder,
|
||||||
"dynamicPrompts": self.dynamic_prompts,
|
"dynamicPrompts": self.dynamic_prompts,
|
||||||
@ -426,8 +431,8 @@ class Combo(ComfyTypeI):
|
|||||||
self.remote = remote
|
self.remote = remote
|
||||||
self.default: str
|
self.default: str
|
||||||
|
|
||||||
def as_dict_V1(self):
|
def as_dict(self):
|
||||||
return super().as_dict_V1() | prune_dict({
|
return super().as_dict() | prune_dict({
|
||||||
"multiselect": self.multiselect,
|
"multiselect": self.multiselect,
|
||||||
"options": self.options,
|
"options": self.options,
|
||||||
"control_after_generate": self.control_after_generate,
|
"control_after_generate": self.control_after_generate,
|
||||||
@ -445,15 +450,15 @@ class MultiCombo(ComfyTypeI):
|
|||||||
class Input(Combo.Input):
|
class Input(Combo.Input):
|
||||||
def __init__(self, id: str, options: list[str], display_name: str=None, optional=False, tooltip: str=None, lazy: bool=None,
|
def __init__(self, id: str, options: list[str], display_name: str=None, optional=False, tooltip: str=None, lazy: bool=None,
|
||||||
default: list[str]=None, placeholder: str=None, chip: bool=None, control_after_generate: bool=None,
|
default: list[str]=None, placeholder: str=None, chip: bool=None, control_after_generate: bool=None,
|
||||||
socketless: bool=None, widgetType: str=None):
|
socketless: bool=None):
|
||||||
super().__init__(id, options, display_name, optional, tooltip, lazy, default, control_after_generate, socketless, widgetType)
|
super().__init__(id, options, display_name, optional, tooltip, lazy, default, control_after_generate, socketless=socketless)
|
||||||
self.multiselect = True
|
self.multiselect = True
|
||||||
self.placeholder = placeholder
|
self.placeholder = placeholder
|
||||||
self.chip = chip
|
self.chip = chip
|
||||||
self.default: list[str]
|
self.default: list[str]
|
||||||
|
|
||||||
def as_dict_V1(self):
|
def as_dict(self):
|
||||||
to_return = super().as_dict_V1() | prune_dict({
|
to_return = super().as_dict() | prune_dict({
|
||||||
"multi_select": self.multiselect,
|
"multi_select": self.multiselect,
|
||||||
"placeholder": self.placeholder,
|
"placeholder": self.placeholder,
|
||||||
"chip": self.chip,
|
"chip": self.chip,
|
||||||
@ -768,9 +773,9 @@ class MultiType:
|
|||||||
display_name = id.display_name if id.display_name is not None else display_name
|
display_name = id.display_name if id.display_name is not None else display_name
|
||||||
lazy = id.lazy if id.lazy is not None else lazy
|
lazy = id.lazy if id.lazy is not None else lazy
|
||||||
id = id.id
|
id = id.id
|
||||||
# if is a widget input, make sure widgetType is set appropriately
|
# if is a widget input, make sure widget_type is set appropriately
|
||||||
if isinstance(self.input_override, WidgetInputV3):
|
if isinstance(self.input_override, WidgetInputV3):
|
||||||
self.input_override.widgetType = self.input_override.get_io_type_V1()
|
self.input_override.widget_type = self.input_override.get_io_type()
|
||||||
super().__init__(id, display_name, optional, tooltip, lazy, extra_dict)
|
super().__init__(id, display_name, optional, tooltip, lazy, extra_dict)
|
||||||
self._io_types = types
|
self._io_types = types
|
||||||
|
|
||||||
@ -787,18 +792,18 @@ class MultiType:
|
|||||||
io_types.append(x)
|
io_types.append(x)
|
||||||
return io_types
|
return io_types
|
||||||
|
|
||||||
def get_io_type_V1(self):
|
def get_io_type(self):
|
||||||
# ensure types are unique and order is preserved
|
# ensure types are unique and order is preserved
|
||||||
str_types = [x.io_type for x in self.io_types]
|
str_types = [x.io_type for x in self.io_types]
|
||||||
if self.input_override is not None:
|
if self.input_override is not None:
|
||||||
str_types.insert(0, self.input_override.get_io_type_V1())
|
str_types.insert(0, self.input_override.get_io_type())
|
||||||
return ",".join(list(dict.fromkeys(str_types)))
|
return ",".join(list(dict.fromkeys(str_types)))
|
||||||
|
|
||||||
def as_dict_V1(self):
|
def as_dict(self):
|
||||||
if self.input_override is not None:
|
if self.input_override is not None:
|
||||||
return self.input_override.as_dict_V1() | super().as_dict_V1()
|
return self.input_override.as_dict() | super().as_dict()
|
||||||
else:
|
else:
|
||||||
return super().as_dict_V1()
|
return super().as_dict()
|
||||||
|
|
||||||
class DynamicInput(InputV3, ABC):
|
class DynamicInput(InputV3, ABC):
|
||||||
'''
|
'''
|
||||||
@ -890,8 +895,8 @@ class MatchType(ComfyTypeIO):
|
|||||||
def get_dynamic(self) -> list[InputV3]:
|
def get_dynamic(self) -> list[InputV3]:
|
||||||
return [self]
|
return [self]
|
||||||
|
|
||||||
def as_dict_V1(self):
|
def as_dict(self):
|
||||||
return super().as_dict_V1() | prune_dict({
|
return super().as_dict() | prune_dict({
|
||||||
"template": self.template.as_dict(),
|
"template": self.template.as_dict(),
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -904,8 +909,8 @@ class MatchType(ComfyTypeIO):
|
|||||||
def get_dynamic(self) -> list[OutputV3]:
|
def get_dynamic(self) -> list[OutputV3]:
|
||||||
return [self]
|
return [self]
|
||||||
|
|
||||||
def as_dict_V3(self):
|
def as_dict(self):
|
||||||
return super().as_dict_V3() | prune_dict({
|
return super().as_dict() | prune_dict({
|
||||||
"template": self.template.as_dict(),
|
"template": self.template.as_dict(),
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -980,6 +985,19 @@ class NodeInfoV1:
|
|||||||
experimental: bool=None
|
experimental: bool=None
|
||||||
api_node: bool=None
|
api_node: bool=None
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class NodeInfoV3:
|
||||||
|
input: dict=None
|
||||||
|
output: dict=None
|
||||||
|
hidden: list[str]=None
|
||||||
|
name: str=None
|
||||||
|
display_name: str=None
|
||||||
|
description: str=None
|
||||||
|
category: str=None
|
||||||
|
output_node: bool=None
|
||||||
|
deprecated: bool=None
|
||||||
|
experimental: bool=None
|
||||||
|
api_node: bool=None
|
||||||
|
|
||||||
def as_pruned_dict(dataclass_obj):
|
def as_pruned_dict(dataclass_obj):
|
||||||
'''Return dict of dataclass object with pruned None values.'''
|
'''Return dict of dataclass object with pruned None values.'''
|
||||||
@ -1082,6 +1100,84 @@ class SchemaV3:
|
|||||||
if output.id is None:
|
if output.id is None:
|
||||||
output.id = f"_{i}_{output.io_type}_"
|
output.id = f"_{i}_{output.io_type}_"
|
||||||
|
|
||||||
|
def get_v1_info(self, cls) -> NodeInfoV1:
|
||||||
|
# get V1 inputs
|
||||||
|
input = {
|
||||||
|
"required": {}
|
||||||
|
}
|
||||||
|
if self.inputs:
|
||||||
|
for i in self.inputs:
|
||||||
|
if isinstance(i, DynamicInput):
|
||||||
|
dynamic_inputs = i.get_dynamic()
|
||||||
|
for d in dynamic_inputs:
|
||||||
|
add_to_dict_v1(d, input)
|
||||||
|
else:
|
||||||
|
add_to_dict_v1(i, input)
|
||||||
|
if self.hidden:
|
||||||
|
for hidden in self.hidden:
|
||||||
|
input.setdefault("hidden", {})[hidden.name] = (hidden.value,)
|
||||||
|
# create separate lists from output fields
|
||||||
|
output = []
|
||||||
|
output_is_list = []
|
||||||
|
output_name = []
|
||||||
|
output_tooltips = []
|
||||||
|
if self.outputs:
|
||||||
|
for o in self.outputs:
|
||||||
|
output.append(o.io_type)
|
||||||
|
output_is_list.append(o.is_output_list)
|
||||||
|
output_name.append(o.display_name if o.display_name else o.io_type)
|
||||||
|
output_tooltips.append(o.tooltip if o.tooltip else None)
|
||||||
|
|
||||||
|
info = NodeInfoV1(
|
||||||
|
input=input,
|
||||||
|
input_order={key: list(value.keys()) for (key, value) in input.items()},
|
||||||
|
output=output,
|
||||||
|
output_is_list=output_is_list,
|
||||||
|
output_name=output_name,
|
||||||
|
output_tooltips=output_tooltips,
|
||||||
|
name=self.node_id,
|
||||||
|
display_name=self.display_name,
|
||||||
|
category=self.category,
|
||||||
|
description=self.description,
|
||||||
|
output_node=self.is_output_node,
|
||||||
|
deprecated=self.is_deprecated,
|
||||||
|
experimental=self.is_experimental,
|
||||||
|
api_node=self.is_api_node,
|
||||||
|
python_module=getattr(cls, "RELATIVE_PYTHON_MODULE", "nodes")
|
||||||
|
)
|
||||||
|
return info
|
||||||
|
|
||||||
|
|
||||||
|
def get_v3_info(self, cls) -> NodeInfoV3:
|
||||||
|
input_dict = {}
|
||||||
|
output_dict = {}
|
||||||
|
hidden_list = []
|
||||||
|
# TODO: make sure dynamic types will be handled correctly
|
||||||
|
if self.inputs:
|
||||||
|
for input in self.inputs:
|
||||||
|
add_to_dict_v3(input, input_dict)
|
||||||
|
if self.outputs:
|
||||||
|
for output in self.outputs:
|
||||||
|
add_to_dict_v3(output, output_dict)
|
||||||
|
if self.hidden:
|
||||||
|
for hidden in self.hidden:
|
||||||
|
hidden_list.append(hidden.value)
|
||||||
|
|
||||||
|
info = NodeInfoV3(
|
||||||
|
input=input_dict,
|
||||||
|
output=output_dict,
|
||||||
|
hidden=hidden_list,
|
||||||
|
name=self.node_id,
|
||||||
|
display_name=self.display_name,
|
||||||
|
description=self.description,
|
||||||
|
category=self.category,
|
||||||
|
output_node=self.is_output_node,
|
||||||
|
deprecated=self.is_deprecated,
|
||||||
|
experimental=self.is_experimental,
|
||||||
|
api_node=self.is_api_node,
|
||||||
|
python_module=getattr(cls, "RELATIVE_PYTHON_MODULE", "nodes")
|
||||||
|
)
|
||||||
|
return info
|
||||||
|
|
||||||
class Serializer:
|
class Serializer:
|
||||||
def __init_subclass__(cls, io_type: str, **kwargs):
|
def __init_subclass__(cls, io_type: str, **kwargs):
|
||||||
@ -1140,11 +1236,18 @@ def lock_class(cls):
|
|||||||
|
|
||||||
def add_to_dict_v1(i: InputV3, input: dict):
|
def add_to_dict_v1(i: InputV3, input: dict):
|
||||||
key = "optional" if i.optional else "required"
|
key = "optional" if i.optional else "required"
|
||||||
input.setdefault(key, {})[i.id] = (i.get_io_type_V1(), i.as_dict_V1())
|
as_dict = i.as_dict()
|
||||||
|
# for v1, we don't want to include the optional key
|
||||||
|
as_dict.pop("optional", None)
|
||||||
|
input.setdefault(key, {})[i.id] = (i.get_io_type(), as_dict)
|
||||||
|
|
||||||
|
def add_to_dict_v3(io: InputV3 | OutputV3, d: dict):
|
||||||
|
d[io.id] = (io.get_io_type(), io.as_dict())
|
||||||
|
|
||||||
|
|
||||||
class ComfyNodeV3(ComfyNodeInternal):
|
|
||||||
"""Common base class for all V3 nodes."""
|
class _ComfyNodeBaseInternal(ComfyNodeInternal):
|
||||||
|
"""Common base class for storing internal methods and properties; DO NOT USE for defining nodes."""
|
||||||
|
|
||||||
RELATIVE_PYTHON_MODULE = None
|
RELATIVE_PYTHON_MODULE = None
|
||||||
SCHEMA = None
|
SCHEMA = None
|
||||||
@ -1163,6 +1266,7 @@ class ComfyNodeV3(ComfyNodeInternal):
|
|||||||
@classmethod
|
@classmethod
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def execute(cls, **kwargs) -> NodeOutput:
|
def execute(cls, **kwargs) -> NodeOutput:
|
||||||
|
"""Override this function with one that performs node's actions."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -1191,28 +1295,28 @@ class ComfyNodeV3(ComfyNodeInternal):
|
|||||||
"""
|
"""
|
||||||
return [name for name in kwargs if kwargs[name] is None]
|
return [name for name in kwargs if kwargs[name] is None]
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def GET_SERIALIZERS(cls) -> list[Serializer]:
|
|
||||||
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
|
||||||
self.__class__.VALIDATE_CLASS()
|
self.__class__.VALIDATE_CLASS()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def GET_SERIALIZERS(cls) -> list[Serializer]:
|
||||||
|
return []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def GET_BASE_CLASS(cls):
|
||||||
|
return _ComfyNodeBaseInternal
|
||||||
|
|
||||||
|
@final
|
||||||
@classmethod
|
@classmethod
|
||||||
def VALIDATE_CLASS(cls):
|
def VALIDATE_CLASS(cls):
|
||||||
if not callable(cls.define_schema):
|
if first_real_override(cls, "define_schema") is None:
|
||||||
raise Exception(f"No define_schema function was defined for node class {cls.__name__}.")
|
raise Exception(f"No define_schema function was defined for node class {cls.__name__}.")
|
||||||
if not callable(cls.execute):
|
if first_real_override(cls, "execute") is None:
|
||||||
raise Exception(f"No execute function was defined for node class {cls.__name__}.")
|
raise Exception(f"No execute function was defined for node class {cls.__name__}.")
|
||||||
|
|
||||||
|
@final
|
||||||
@classmethod
|
@classmethod
|
||||||
def EXECUTE_NORMALIZED(cls, *args, **kwargs) -> NodeOutput:
|
def EXECUTE_NORMALIZED(cls, *args, **kwargs) -> NodeOutput:
|
||||||
to_return = cls.execute(*args, **kwargs)
|
to_return = cls.execute(*args, **kwargs)
|
||||||
@ -1229,6 +1333,7 @@ class ComfyNodeV3(ComfyNodeInternal):
|
|||||||
else:
|
else:
|
||||||
raise Exception(f"Invalid return type from node: {type(to_return)}")
|
raise Exception(f"Invalid return type from node: {type(to_return)}")
|
||||||
|
|
||||||
|
@final
|
||||||
@classmethod
|
@classmethod
|
||||||
def PREPARE_CLASS_CLONE(cls, hidden_inputs: dict) -> type[ComfyNodeV3]:
|
def PREPARE_CLASS_CLONE(cls, hidden_inputs: dict) -> type[ComfyNodeV3]:
|
||||||
"""Creates clone of real node class to prevent monkey-patching."""
|
"""Creates clone of real node class to prevent monkey-patching."""
|
||||||
@ -1238,10 +1343,24 @@ class ComfyNodeV3(ComfyNodeInternal):
|
|||||||
type_clone.hidden = HiddenHolder.from_dict(hidden_inputs)
|
type_clone.hidden = HiddenHolder.from_dict(hidden_inputs)
|
||||||
return type_clone
|
return type_clone
|
||||||
|
|
||||||
|
@final
|
||||||
|
@classmethod
|
||||||
|
def GET_NODE_INFO_V3(cls) -> dict[str, Any]:
|
||||||
|
schema = cls.GET_SCHEMA()
|
||||||
|
info = schema.get_v3_info(cls)
|
||||||
|
return asdict(info)
|
||||||
#############################################
|
#############################################
|
||||||
# V1 Backwards Compatibility code
|
# V1 Backwards Compatibility code
|
||||||
#--------------------------------------------
|
#--------------------------------------------
|
||||||
|
@final
|
||||||
|
@classmethod
|
||||||
|
def GET_NODE_INFO_V1(cls) -> dict[str, Any]:
|
||||||
|
schema = cls.GET_SCHEMA()
|
||||||
|
info = schema.get_v1_info(cls)
|
||||||
|
return asdict(info)
|
||||||
|
|
||||||
_DESCRIPTION = None
|
_DESCRIPTION = None
|
||||||
|
@final
|
||||||
@classproperty
|
@classproperty
|
||||||
def DESCRIPTION(cls): # noqa
|
def DESCRIPTION(cls): # noqa
|
||||||
if cls._DESCRIPTION is None:
|
if cls._DESCRIPTION is None:
|
||||||
@ -1249,6 +1368,7 @@ class ComfyNodeV3(ComfyNodeInternal):
|
|||||||
return cls._DESCRIPTION
|
return cls._DESCRIPTION
|
||||||
|
|
||||||
_CATEGORY = None
|
_CATEGORY = None
|
||||||
|
@final
|
||||||
@classproperty
|
@classproperty
|
||||||
def CATEGORY(cls): # noqa
|
def CATEGORY(cls): # noqa
|
||||||
if cls._CATEGORY is None:
|
if cls._CATEGORY is None:
|
||||||
@ -1256,6 +1376,7 @@ class ComfyNodeV3(ComfyNodeInternal):
|
|||||||
return cls._CATEGORY
|
return cls._CATEGORY
|
||||||
|
|
||||||
_EXPERIMENTAL = None
|
_EXPERIMENTAL = None
|
||||||
|
@final
|
||||||
@classproperty
|
@classproperty
|
||||||
def EXPERIMENTAL(cls): # noqa
|
def EXPERIMENTAL(cls): # noqa
|
||||||
if cls._EXPERIMENTAL is None:
|
if cls._EXPERIMENTAL is None:
|
||||||
@ -1263,6 +1384,7 @@ class ComfyNodeV3(ComfyNodeInternal):
|
|||||||
return cls._EXPERIMENTAL
|
return cls._EXPERIMENTAL
|
||||||
|
|
||||||
_DEPRECATED = None
|
_DEPRECATED = None
|
||||||
|
@final
|
||||||
@classproperty
|
@classproperty
|
||||||
def DEPRECATED(cls): # noqa
|
def DEPRECATED(cls): # noqa
|
||||||
if cls._DEPRECATED is None:
|
if cls._DEPRECATED is None:
|
||||||
@ -1270,6 +1392,7 @@ class ComfyNodeV3(ComfyNodeInternal):
|
|||||||
return cls._DEPRECATED
|
return cls._DEPRECATED
|
||||||
|
|
||||||
_API_NODE = None
|
_API_NODE = None
|
||||||
|
@final
|
||||||
@classproperty
|
@classproperty
|
||||||
def API_NODE(cls): # noqa
|
def API_NODE(cls): # noqa
|
||||||
if cls._API_NODE is None:
|
if cls._API_NODE is None:
|
||||||
@ -1277,6 +1400,7 @@ class ComfyNodeV3(ComfyNodeInternal):
|
|||||||
return cls._API_NODE
|
return cls._API_NODE
|
||||||
|
|
||||||
_OUTPUT_NODE = None
|
_OUTPUT_NODE = None
|
||||||
|
@final
|
||||||
@classproperty
|
@classproperty
|
||||||
def OUTPUT_NODE(cls): # noqa
|
def OUTPUT_NODE(cls): # noqa
|
||||||
if cls._OUTPUT_NODE is None:
|
if cls._OUTPUT_NODE is None:
|
||||||
@ -1284,6 +1408,7 @@ class ComfyNodeV3(ComfyNodeInternal):
|
|||||||
return cls._OUTPUT_NODE
|
return cls._OUTPUT_NODE
|
||||||
|
|
||||||
_INPUT_IS_LIST = None
|
_INPUT_IS_LIST = None
|
||||||
|
@final
|
||||||
@classproperty
|
@classproperty
|
||||||
def INPUT_IS_LIST(cls): # noqa
|
def INPUT_IS_LIST(cls): # noqa
|
||||||
if cls._INPUT_IS_LIST is None:
|
if cls._INPUT_IS_LIST is None:
|
||||||
@ -1291,6 +1416,7 @@ class ComfyNodeV3(ComfyNodeInternal):
|
|||||||
return cls._INPUT_IS_LIST
|
return cls._INPUT_IS_LIST
|
||||||
_OUTPUT_IS_LIST = None
|
_OUTPUT_IS_LIST = None
|
||||||
|
|
||||||
|
@final
|
||||||
@classproperty
|
@classproperty
|
||||||
def OUTPUT_IS_LIST(cls): # noqa
|
def OUTPUT_IS_LIST(cls): # noqa
|
||||||
if cls._OUTPUT_IS_LIST is None:
|
if cls._OUTPUT_IS_LIST is None:
|
||||||
@ -1298,6 +1424,7 @@ class ComfyNodeV3(ComfyNodeInternal):
|
|||||||
return cls._OUTPUT_IS_LIST
|
return cls._OUTPUT_IS_LIST
|
||||||
|
|
||||||
_RETURN_TYPES = None
|
_RETURN_TYPES = None
|
||||||
|
@final
|
||||||
@classproperty
|
@classproperty
|
||||||
def RETURN_TYPES(cls): # noqa
|
def RETURN_TYPES(cls): # noqa
|
||||||
if cls._RETURN_TYPES is None:
|
if cls._RETURN_TYPES is None:
|
||||||
@ -1305,6 +1432,7 @@ class ComfyNodeV3(ComfyNodeInternal):
|
|||||||
return cls._RETURN_TYPES
|
return cls._RETURN_TYPES
|
||||||
|
|
||||||
_RETURN_NAMES = None
|
_RETURN_NAMES = None
|
||||||
|
@final
|
||||||
@classproperty
|
@classproperty
|
||||||
def RETURN_NAMES(cls): # noqa
|
def RETURN_NAMES(cls): # noqa
|
||||||
if cls._RETURN_NAMES is None:
|
if cls._RETURN_NAMES is None:
|
||||||
@ -1312,6 +1440,7 @@ class ComfyNodeV3(ComfyNodeInternal):
|
|||||||
return cls._RETURN_NAMES
|
return cls._RETURN_NAMES
|
||||||
|
|
||||||
_OUTPUT_TOOLTIPS = None
|
_OUTPUT_TOOLTIPS = None
|
||||||
|
@final
|
||||||
@classproperty
|
@classproperty
|
||||||
def OUTPUT_TOOLTIPS(cls): # noqa
|
def OUTPUT_TOOLTIPS(cls): # noqa
|
||||||
if cls._OUTPUT_TOOLTIPS is None:
|
if cls._OUTPUT_TOOLTIPS is None:
|
||||||
@ -1319,6 +1448,7 @@ class ComfyNodeV3(ComfyNodeInternal):
|
|||||||
return cls._OUTPUT_TOOLTIPS
|
return cls._OUTPUT_TOOLTIPS
|
||||||
|
|
||||||
_NOT_IDEMPOTENT = None
|
_NOT_IDEMPOTENT = None
|
||||||
|
@final
|
||||||
@classproperty
|
@classproperty
|
||||||
def NOT_IDEMPOTENT(cls): # noqa
|
def NOT_IDEMPOTENT(cls): # noqa
|
||||||
if cls._NOT_IDEMPOTENT is None:
|
if cls._NOT_IDEMPOTENT is None:
|
||||||
@ -1327,28 +1457,19 @@ class ComfyNodeV3(ComfyNodeInternal):
|
|||||||
|
|
||||||
FUNCTION = "EXECUTE_NORMALIZED"
|
FUNCTION = "EXECUTE_NORMALIZED"
|
||||||
|
|
||||||
|
@final
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(cls, include_hidden=True, return_schema=False) -> dict[str, dict] | tuple[dict[str, dict], SchemaV3]:
|
def INPUT_TYPES(cls, include_hidden=True, return_schema=False) -> dict[str, dict] | tuple[dict[str, dict], SchemaV3]:
|
||||||
schema = cls.FINALIZE_SCHEMA()
|
schema = cls.FINALIZE_SCHEMA()
|
||||||
# for V1, make inputs be a dict with potential keys {required, optional, hidden}
|
info = schema.get_v1_info(cls)
|
||||||
input = {
|
input = info.input
|
||||||
"required": {}
|
if not include_hidden:
|
||||||
}
|
input.pop("hidden", None)
|
||||||
if schema.inputs:
|
|
||||||
for i in schema.inputs:
|
|
||||||
if isinstance(i, DynamicInput):
|
|
||||||
dynamic_inputs = i.get_dynamic()
|
|
||||||
for d in dynamic_inputs:
|
|
||||||
add_to_dict_v1(d, input)
|
|
||||||
else:
|
|
||||||
add_to_dict_v1(i, input)
|
|
||||||
if schema.hidden and include_hidden:
|
|
||||||
for hidden in schema.hidden:
|
|
||||||
input.setdefault("hidden", {})[hidden.name] = (hidden.value,)
|
|
||||||
if return_schema:
|
if return_schema:
|
||||||
return input, schema
|
return input, schema
|
||||||
return input
|
return input
|
||||||
|
|
||||||
|
@final
|
||||||
@classmethod
|
@classmethod
|
||||||
def FINALIZE_SCHEMA(cls):
|
def FINALIZE_SCHEMA(cls):
|
||||||
"""Call define_schema and finalize it."""
|
"""Call define_schema and finalize it."""
|
||||||
@ -1356,6 +1477,7 @@ class ComfyNodeV3(ComfyNodeInternal):
|
|||||||
schema.finalize()
|
schema.finalize()
|
||||||
return schema
|
return schema
|
||||||
|
|
||||||
|
@final
|
||||||
@classmethod
|
@classmethod
|
||||||
def GET_SCHEMA(cls) -> SchemaV3:
|
def GET_SCHEMA(cls) -> SchemaV3:
|
||||||
"""Validate node class, finalize schema, validate schema, and set expected class properties."""
|
"""Validate node class, finalize schema, validate schema, and set expected class properties."""
|
||||||
@ -1397,47 +1519,58 @@ class ComfyNodeV3(ComfyNodeInternal):
|
|||||||
cls._OUTPUT_TOOLTIPS = output_tooltips
|
cls._OUTPUT_TOOLTIPS = output_tooltips
|
||||||
cls.SCHEMA = schema
|
cls.SCHEMA = schema
|
||||||
return schema
|
return schema
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def GET_NODE_INFO_V1(cls) -> dict[str, Any]:
|
|
||||||
schema = cls.GET_SCHEMA()
|
|
||||||
# get V1 inputs
|
|
||||||
input = cls.INPUT_TYPES()
|
|
||||||
|
|
||||||
# create separate lists from output fields
|
|
||||||
output = []
|
|
||||||
output_is_list = []
|
|
||||||
output_name = []
|
|
||||||
output_tooltips = []
|
|
||||||
if schema.outputs:
|
|
||||||
for o in schema.outputs:
|
|
||||||
output.append(o.io_type)
|
|
||||||
output_is_list.append(o.is_output_list)
|
|
||||||
output_name.append(o.display_name if o.display_name else o.io_type)
|
|
||||||
output_tooltips.append(o.tooltip if o.tooltip else None)
|
|
||||||
|
|
||||||
info = NodeInfoV1(
|
|
||||||
input=input,
|
|
||||||
input_order={key: list(value.keys()) for (key, value) in input.items()},
|
|
||||||
output=output,
|
|
||||||
output_is_list=output_is_list,
|
|
||||||
output_name=output_name,
|
|
||||||
output_tooltips=output_tooltips,
|
|
||||||
name=schema.node_id,
|
|
||||||
display_name=schema.display_name,
|
|
||||||
category=schema.category,
|
|
||||||
description=schema.description,
|
|
||||||
output_node=schema.is_output_node,
|
|
||||||
deprecated=schema.is_deprecated,
|
|
||||||
experimental=schema.is_experimental,
|
|
||||||
api_node=schema.is_api_node,
|
|
||||||
python_module=getattr(cls, "RELATIVE_PYTHON_MODULE", "nodes")
|
|
||||||
)
|
|
||||||
return asdict(info)
|
|
||||||
#--------------------------------------------
|
#--------------------------------------------
|
||||||
#############################################
|
#############################################
|
||||||
|
|
||||||
|
|
||||||
|
class ComfyNodeV3(_ComfyNodeBaseInternal):
|
||||||
|
"""Common base class for all V3 nodes."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@abstractmethod
|
||||||
|
def define_schema(cls) -> SchemaV3:
|
||||||
|
"""Override this function with one that returns a SchemaV3 instance."""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@abstractmethod
|
||||||
|
def execute(cls, **kwargs) -> NodeOutput:
|
||||||
|
"""Override this function with one that performs node's actions."""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate_inputs(cls, **kwargs) -> bool:
|
||||||
|
"""Optionally, define this function to validate inputs; equivalent to V1's VALIDATE_INPUTS."""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fingerprint_inputs(cls, **kwargs) -> Any:
|
||||||
|
"""Optionally, define this function to fingerprint inputs; equivalent to V1's IS_CHANGED."""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@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
|
||||||
|
"""
|
||||||
|
return [name for name in kwargs if kwargs[name] is None]
|
||||||
|
|
||||||
|
@final
|
||||||
|
@classmethod
|
||||||
|
def GET_BASE_CLASS(cls):
|
||||||
|
"""DO NOT override this class. Will break things in execution.py."""
|
||||||
|
return ComfyNodeV3
|
||||||
|
|
||||||
|
|
||||||
class NodeOutput:
|
class NodeOutput:
|
||||||
'''
|
'''
|
||||||
Standardized output of a node; can pass in any number of args and/or a UIOutput into 'ui' kwarg.
|
Standardized output of a node; can pass in any number of args and/or a UIOutput into 'ui' kwarg.
|
||||||
@ -1479,57 +1612,4 @@ class _UIOutput(ABC):
|
|||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def as_dict(self) -> dict:
|
def as_dict(self) -> dict:
|
||||||
... # TODO: finish
|
...
|
||||||
|
|
||||||
class TestNode(ComfyNodeV3):
|
|
||||||
@classmethod
|
|
||||||
def define_schema(cls):
|
|
||||||
return SchemaV3(
|
|
||||||
node_id="TestNode_v3",
|
|
||||||
display_name="Test Node (V3)",
|
|
||||||
category="v3_test",
|
|
||||||
inputs=[Int.Input("my_int"),
|
|
||||||
#AutoGrowDynamicInput("growing", Image.Input),
|
|
||||||
Mask.Input("thing"),
|
|
||||||
],
|
|
||||||
outputs=[Image.Output("image_output")],
|
|
||||||
hidden=[Hidden.api_key_comfy_org, Hidden.auth_token_comfy_org, Hidden.unique_id]
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def execute(cls, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# if __name__ == "__main__":
|
|
||||||
# print("hello there")
|
|
||||||
# inputs: list[InputV3] = [
|
|
||||||
# Int.Input("tessfes", widgetType=String.io_type),
|
|
||||||
# Int.Input("my_int"),
|
|
||||||
# Custom("XYZ").Input("xyz"),
|
|
||||||
# Custom("MODEL_M").Input("model1"),
|
|
||||||
# Image.Input("my_image"),
|
|
||||||
# Float.Input("my_float"),
|
|
||||||
# MultiType.Input("my_inputs", [String, Custom("MODEL_M"), Custom("XYZ")]),
|
|
||||||
# ]
|
|
||||||
# Custom("XYZ").Input()
|
|
||||||
# outputs: list[OutputV3] = [
|
|
||||||
# Image.Output("image"),
|
|
||||||
# Custom("XYZ").Output("xyz"),
|
|
||||||
# ]
|
|
||||||
#
|
|
||||||
# for c in inputs:
|
|
||||||
# if isinstance(c, MultiType):
|
|
||||||
# print(f"{c}, {type(c)}, {type(c).io_type}, {c.id}, {[x.io_type for x in c.io_types]}")
|
|
||||||
# print(c.get_io_type_V1())
|
|
||||||
# else:
|
|
||||||
# print(f"{c}, {type(c)}, {type(c).io_type}, {c.id}")
|
|
||||||
#
|
|
||||||
# for c in outputs:
|
|
||||||
# print(f"{c}, {type(c)}, {type(c).io_type}, {c.id}")
|
|
||||||
#
|
|
||||||
# zz = TestNode()
|
|
||||||
# print(zz.GET_NODE_INFO_V1())
|
|
||||||
#
|
|
||||||
# # aa = NodeInfoV1()
|
|
||||||
# # print(asdict(aa))
|
|
||||||
# # print(as_pruned_dict(aa))
|
|
||||||
|
15
execution.py
15
execution.py
@ -28,6 +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.internal import ComfyNodeInternal
|
||||||
from comfy_api.v3 import io, helpers
|
from comfy_api.v3 import io, helpers
|
||||||
|
|
||||||
|
|
||||||
@ -54,7 +55,7 @@ class IsChangedCache:
|
|||||||
class_def = nodes.NODE_CLASS_MAPPINGS[class_type]
|
class_def = nodes.NODE_CLASS_MAPPINGS[class_type]
|
||||||
has_is_changed = False
|
has_is_changed = False
|
||||||
is_changed_name = None
|
is_changed_name = None
|
||||||
if issubclass(class_def, io.ComfyNodeV3) and helpers.first_real_override(class_def, "fingerprint_inputs", base=io.ComfyNodeV3) is not None:
|
if issubclass(class_def, ComfyNodeInternal) and helpers.first_real_override(class_def, "fingerprint_inputs") is not None:
|
||||||
has_is_changed = True
|
has_is_changed = True
|
||||||
is_changed_name = "fingerprint_inputs"
|
is_changed_name = "fingerprint_inputs"
|
||||||
elif hasattr(class_def, "IS_CHANGED"):
|
elif hasattr(class_def, "IS_CHANGED"):
|
||||||
@ -127,7 +128,7 @@ class CacheSet:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def get_input_data(inputs, class_def, unique_id, outputs=None, dynprompt=None, extra_data={}):
|
def get_input_data(inputs, class_def, unique_id, outputs=None, dynprompt=None, extra_data={}):
|
||||||
is_v3 = issubclass(class_def, io.ComfyNodeV3)
|
is_v3 = issubclass(class_def, ComfyNodeInternal)
|
||||||
if is_v3:
|
if is_v3:
|
||||||
valid_inputs, schema = class_def.INPUT_TYPES(include_hidden=False, return_schema=True)
|
valid_inputs, schema = class_def.INPUT_TYPES(include_hidden=False, return_schema=True)
|
||||||
else:
|
else:
|
||||||
@ -224,7 +225,7 @@ 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, io.ComfyNodeV3) or (io.is_class(obj) and issubclass(obj, io.ComfyNodeV3)):
|
if isinstance(obj, ComfyNodeInternal) or (io.is_class(obj) and issubclass(obj, ComfyNodeInternal)):
|
||||||
# if is just a class, then assign no resources or state, just create clone
|
# if is just a class, then assign no resources or state, just create clone
|
||||||
if io.is_class(obj):
|
if io.is_class(obj):
|
||||||
type_obj = obj
|
type_obj = obj
|
||||||
@ -411,8 +412,8 @@ 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 issubclass(class_def, io.ComfyNodeV3):
|
if issubclass(class_def, ComfyNodeInternal):
|
||||||
lazy_status_present = helpers.first_real_override(class_def, "check_lazy_status", base=io.ComfyNodeV3) is not None
|
lazy_status_present = helpers.first_real_override(class_def, "check_lazy_status") is not None
|
||||||
else:
|
else:
|
||||||
lazy_status_present = getattr(obj, "check_lazy_status", None) is not None
|
lazy_status_present = getattr(obj, "check_lazy_status", None) is not None
|
||||||
if lazy_status_present:
|
if lazy_status_present:
|
||||||
@ -674,9 +675,9 @@ def validate_inputs(prompt, item, validated):
|
|||||||
|
|
||||||
validate_function_inputs = []
|
validate_function_inputs = []
|
||||||
validate_has_kwargs = False
|
validate_has_kwargs = False
|
||||||
if issubclass(obj_class, io.ComfyNodeV3):
|
if issubclass(obj_class, ComfyNodeInternal):
|
||||||
validate_function_name = "validate_inputs"
|
validate_function_name = "validate_inputs"
|
||||||
validate_function = helpers.first_real_override(obj_class, validate_function_name, base=io.ComfyNodeV3)
|
validate_function = helpers.first_real_override(obj_class, validate_function_name)
|
||||||
else:
|
else:
|
||||||
validate_function_name = "VALIDATE_INPUTS"
|
validate_function_name = "VALIDATE_INPUTS"
|
||||||
validate_function = getattr(obj_class, validate_function_name, None)
|
validate_function = getattr(obj_class, validate_function_name, None)
|
||||||
|
@ -29,7 +29,7 @@ import comfy.model_management
|
|||||||
import node_helpers
|
import node_helpers
|
||||||
from comfyui_version import __version__
|
from comfyui_version import __version__
|
||||||
from app.frontend_management import FrontendManager
|
from app.frontend_management import FrontendManager
|
||||||
from comfy_api.v3.io import ComfyNodeV3
|
from comfy_api.internal import ComfyNodeInternal
|
||||||
|
|
||||||
from app.user_manager import UserManager
|
from app.user_manager import UserManager
|
||||||
from app.model_manager import ModelFileManager
|
from app.model_manager import ModelFileManager
|
||||||
@ -555,7 +555,7 @@ class PromptServer():
|
|||||||
|
|
||||||
def node_info(node_class):
|
def node_info(node_class):
|
||||||
obj_class = nodes.NODE_CLASS_MAPPINGS[node_class]
|
obj_class = nodes.NODE_CLASS_MAPPINGS[node_class]
|
||||||
if issubclass(obj_class, ComfyNodeV3):
|
if issubclass(obj_class, ComfyNodeInternal):
|
||||||
return obj_class.GET_NODE_INFO_V1()
|
return obj_class.GET_NODE_INFO_V1()
|
||||||
info = {}
|
info = {}
|
||||||
info['input'] = obj_class.INPUT_TYPES()
|
info['input'] = obj_class.INPUT_TYPES()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user