mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-02-08 01:00:03 +00:00
Added hidden and state to passed-in clone of node class
This commit is contained in:
@@ -549,7 +549,40 @@ class ComboDynamicInput(DynamicInput, io_type="COMFY_COMBODYNAMIC_V3"):
|
||||
AutoGrowDynamicInput(id="dynamic", template_input=ImageInput(id="image"))
|
||||
|
||||
|
||||
class Hidden(str, Enum):
|
||||
class Hidden:
|
||||
def __init__(self, unique_id: str, prompt: Any,
|
||||
extra_pnginfo: Any, dynprompt: Any,
|
||||
auth_token_comfy_org: str, api_key_comfy_org: str, **kwargs):
|
||||
self.unique_id = unique_id
|
||||
"""UNIQUE_ID is the unique identifier of the node, and matches the id property of the node on the client side. It is commonly used in client-server communications (see messages)."""
|
||||
self.prompt = prompt
|
||||
"""PROMPT is the complete prompt sent by the client to the server. See the prompt object for a full description."""
|
||||
self.extra_pnginfo = extra_pnginfo
|
||||
"""EXTRA_PNGINFO is a dictionary that will be copied into the metadata of any .png files saved. Custom nodes can store additional information in this dictionary for saving (or as a way to communicate with a downstream node)."""
|
||||
self.dynprompt = dynprompt
|
||||
"""DYNPROMPT is an instance of comfy_execution.graph.DynamicPrompt. It differs from PROMPT in that it may mutate during the course of execution in response to Node Expansion."""
|
||||
self.auth_token_comfy_org = auth_token_comfy_org
|
||||
"""AUTH_TOKEN_COMFY_ORG is a token acquired from signing into a ComfyOrg account on frontend."""
|
||||
self.api_key_comfy_org = api_key_comfy_org
|
||||
"""API_KEY_COMFY_ORG is an API Key generated by ComfyOrg that allows skipping signing into a ComfyOrg account on frontend."""
|
||||
|
||||
def __getattr__(self, key: str):
|
||||
'''If hidden variable not found, return None.'''
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, d: dict):
|
||||
return cls(
|
||||
unique_id=d.get(HiddenEnum.unique_id),
|
||||
prompt=d.get(HiddenEnum.prompt),
|
||||
extra_pnginfo=d.get(HiddenEnum.extra_pnginfo),
|
||||
dynprompt=d.get(HiddenEnum.dynprompt),
|
||||
auth_token_comfy_org=d.get(HiddenEnum.auth_token_comfy_org),
|
||||
api_key_comfy_org=d.get(HiddenEnum.api_key_comfy_org),
|
||||
)
|
||||
|
||||
|
||||
class HiddenEnum(str, Enum):
|
||||
'''
|
||||
Enumerator for requesting hidden variables in nodes.
|
||||
'''
|
||||
@@ -607,7 +640,7 @@ class SchemaV3:
|
||||
"""The category of the node, as per the "Add Node" menu."""
|
||||
inputs: list[InputV3]=None
|
||||
outputs: list[OutputV3]=None
|
||||
hidden: list[Hidden]=None
|
||||
hidden: list[HiddenEnum]=None
|
||||
description: str=""
|
||||
"""Node description, shown as a tooltip when hovering over the node."""
|
||||
is_input_list: bool = False
|
||||
@@ -772,7 +805,7 @@ class ComfyNodeV3(ABC):
|
||||
raise Exception(f"No execute function was defined for node class {cls.__name__}.")
|
||||
|
||||
@classmethod
|
||||
def prepare_class_clone(cls) -> type[ComfyNodeV3]:
|
||||
def prepare_class_clone(cls, hidden_inputs: dict, *args, **kwargs) -> type[ComfyNodeV3]:
|
||||
"""Creates clone of real node class to prevent monkey-patching."""
|
||||
c_type: type[ComfyNodeV3] = cls if is_class(cls) else type(cls)
|
||||
type_clone: type[ComfyNodeV3] = type(f"CLEAN_{c_type.__name__}", c_type.__bases__, {})
|
||||
@@ -1063,7 +1096,7 @@ class TestNode(ComfyNodeV3):
|
||||
MaskInput("thing"),
|
||||
],
|
||||
outputs=[ImageOutput("image_output")],
|
||||
hidden=[Hidden.api_key_comfy_org, Hidden.auth_token_comfy_org, Hidden.unique_id]
|
||||
hidden=[HiddenEnum.api_key_comfy_org, HiddenEnum.auth_token_comfy_org, HiddenEnum.unique_id]
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -213,6 +213,29 @@ class ComfyTypeIO(ComfyType):
|
||||
...
|
||||
|
||||
|
||||
class NodeState:
|
||||
def __init__(self, node_id: str):
|
||||
self.node_id = node_id
|
||||
|
||||
|
||||
class NodeStateLocal(NodeState):
|
||||
def __init__(self, node_id: str):
|
||||
super().__init__(node_id)
|
||||
self.local_state = {}
|
||||
|
||||
def __getattr__(self, key: str):
|
||||
local_state = type(self).__getattribute__(self, "local_state")
|
||||
if key in local_state:
|
||||
return local_state[key]
|
||||
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{key}'")
|
||||
|
||||
def __setattr__(self, key: str, value: Any):
|
||||
if key in ['node_id', 'local_state']:
|
||||
super().__setattr__(key, value)
|
||||
else:
|
||||
self.local_state[key] = value
|
||||
|
||||
|
||||
@comfytype(io_type=IO.BOOLEAN)
|
||||
class Boolean:
|
||||
Type = bool
|
||||
@@ -528,7 +551,39 @@ class ComboDynamicInput(DynamicInput):
|
||||
AutoGrowDynamicInput(id="dynamic", template_input=Image.Input(id="image"))
|
||||
|
||||
|
||||
class Hidden(str, Enum):
|
||||
class Hidden:
|
||||
def __init__(self, unique_id: str, prompt: Any,
|
||||
extra_pnginfo: Any, dynprompt: Any,
|
||||
auth_token_comfy_org: str, api_key_comfy_org: str, **kwargs):
|
||||
self.unique_id = unique_id
|
||||
"""UNIQUE_ID is the unique identifier of the node, and matches the id property of the node on the client side. It is commonly used in client-server communications (see messages)."""
|
||||
self.prompt = prompt
|
||||
"""PROMPT is the complete prompt sent by the client to the server. See the prompt object for a full description."""
|
||||
self.extra_pnginfo = extra_pnginfo
|
||||
"""EXTRA_PNGINFO is a dictionary that will be copied into the metadata of any .png files saved. Custom nodes can store additional information in this dictionary for saving (or as a way to communicate with a downstream node)."""
|
||||
self.dynprompt = dynprompt
|
||||
"""DYNPROMPT is an instance of comfy_execution.graph.DynamicPrompt. It differs from PROMPT in that it may mutate during the course of execution in response to Node Expansion."""
|
||||
self.auth_token_comfy_org = auth_token_comfy_org
|
||||
"""AUTH_TOKEN_COMFY_ORG is a token acquired from signing into a ComfyOrg account on frontend."""
|
||||
self.api_key_comfy_org = api_key_comfy_org
|
||||
"""API_KEY_COMFY_ORG is an API Key generated by ComfyOrg that allows skipping signing into a ComfyOrg account on frontend."""
|
||||
|
||||
def __getattr__(self, key: str):
|
||||
'''If hidden variable not found, return None.'''
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, d: dict):
|
||||
return cls(
|
||||
unique_id=d.get(HiddenEnum.unique_id),
|
||||
prompt=d.get(HiddenEnum.prompt),
|
||||
extra_pnginfo=d.get(HiddenEnum.extra_pnginfo),
|
||||
dynprompt=d.get(HiddenEnum.dynprompt),
|
||||
auth_token_comfy_org=d.get(HiddenEnum.auth_token_comfy_org),
|
||||
api_key_comfy_org=d.get(HiddenEnum.api_key_comfy_org),
|
||||
)
|
||||
|
||||
class HiddenEnum(str, Enum):
|
||||
'''
|
||||
Enumerator for requesting hidden variables in nodes.
|
||||
'''
|
||||
@@ -585,7 +640,7 @@ class SchemaV3:
|
||||
"""The category of the node, as per the "Add Node" menu."""
|
||||
inputs: list[InputV3]=None
|
||||
outputs: list[OutputV3]=None
|
||||
hidden: list[Hidden]=None
|
||||
hidden: list[HiddenEnum]=None
|
||||
description: str=""
|
||||
"""Node description, shown as a tooltip when hovering over the node."""
|
||||
is_input_list: bool = False
|
||||
@@ -691,16 +746,6 @@ class Serializer:
|
||||
pass
|
||||
|
||||
|
||||
def prepare_class_clone(c: ComfyNodeV3 | type[ComfyNodeV3]) -> type[ComfyNodeV3]:
|
||||
"""Creates clone of real node class to prevent monkey-patching."""
|
||||
c_type: type[ComfyNodeV3] = c if is_class(c) else type(c)
|
||||
type_clone: type[ComfyNodeV3] = type(f"CLEAN_{c_type.__name__}", c_type.__bases__, {})
|
||||
# TODO: what parameters should be carried over?
|
||||
type_clone.SCHEMA = c_type.SCHEMA
|
||||
# TODO: add anything we would want to expose inside node's execute function
|
||||
return type_clone
|
||||
|
||||
|
||||
class classproperty(object):
|
||||
def __init__(self, f):
|
||||
self.f = f
|
||||
@@ -713,6 +758,10 @@ class ComfyNodeV3(BASE_CV3):
|
||||
|
||||
RELATIVE_PYTHON_MODULE = None
|
||||
SCHEMA = None
|
||||
|
||||
# filled in during execution
|
||||
state: NodeState = None
|
||||
hidden: Hidden = None
|
||||
|
||||
@classmethod
|
||||
def GET_NODE_INFO_V3(cls) -> dict[str, Any]:
|
||||
@@ -740,6 +789,7 @@ class ComfyNodeV3(BASE_CV3):
|
||||
return []
|
||||
|
||||
def __init__(self):
|
||||
self.local_state: NodeStateLocal = None
|
||||
self.__class__.VALIDATE_CLASS()
|
||||
|
||||
@classmethod
|
||||
@@ -750,12 +800,13 @@ class ComfyNodeV3(BASE_CV3):
|
||||
raise Exception(f"No execute function was defined for node class {cls.__name__}.")
|
||||
|
||||
@classmethod
|
||||
def prepare_class_clone(cls) -> type[ComfyNodeV3]:
|
||||
def prepare_class_clone(cls, hidden_inputs: dict, *args, **kwargs) -> type[ComfyNodeV3]:
|
||||
"""Creates clone of real node class to prevent monkey-patching."""
|
||||
c_type: type[ComfyNodeV3] = cls if is_class(cls) else type(cls)
|
||||
type_clone: type[ComfyNodeV3] = type(f"CLEAN_{c_type.__name__}", c_type.__bases__, {})
|
||||
# TODO: what parameters should be carried over?
|
||||
type_clone.SCHEMA = c_type.SCHEMA
|
||||
type_clone.hidden = Hidden.from_dict(hidden_inputs)
|
||||
# TODO: add anything we would want to expose inside node's execute function
|
||||
return type_clone
|
||||
|
||||
@@ -1040,7 +1091,7 @@ class TestNode(ComfyNodeV3):
|
||||
Mask.Input("thing"),
|
||||
],
|
||||
outputs=[Image.Output("image_output")],
|
||||
hidden=[Hidden.api_key_comfy_org, Hidden.auth_token_comfy_org, Hidden.unique_id]
|
||||
hidden=[HiddenEnum.api_key_comfy_org, HiddenEnum.auth_token_comfy_org, HiddenEnum.unique_id]
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
Reference in New Issue
Block a user