From 06b428ec8cf9b09b69cde52fc21d7a8cd83b52b0 Mon Sep 17 00:00:00 2001 From: "Dr.Lt.Data" Date: Fri, 16 Aug 2024 11:53:02 +0900 Subject: [PATCH] feature: dynamic switch based on Execution Model Inversion. --- README.md | 8 +++---- js/impact-pack.js | 2 +- modules/impact/config.py | 2 +- modules/impact/core.py | 8 +++++++ modules/impact/impact_server.py | 19 ++++++++------- modules/impact/logics.py | 15 ++++++++---- modules/impact/util_nodes.py | 41 ++++++++++++++++++++++++--------- pyproject.toml | 2 +- 8 files changed, 66 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index e126b8e..c372fc6 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ This custom node helps to conveniently enhance images through Detector, Detailer ## NOTICE +* V7.0: Supports Switch based on Execution Model Inversion. * V6.0: Supports FLUX.1 model in Impact KSampler, Detailers, PreviewBridgeLatent * V5.0: It is no longer compatible with versions of ComfyUI before 2024.04.08. * V4.87.4: Update to a version of ComfyUI after 2024.04.08 for proper functionality. @@ -193,11 +194,8 @@ This custom node helps to conveniently enhance images through Detector, Detailer ### Switch nodes * `Switch (image,mask)`, `Switch (latent)`, `Switch (SEGS)` - Among multiple inputs, it selects the input designated by the selector and outputs it. The first input must be provided, while the others are optional. However, if the input specified by the selector is not connected, an error may occur. * `Switch (Any)` - This is a Switch node that takes an arbitrary number of inputs and produces a single output. Its type is determined when connected to any node, and connecting inputs increases the available slots for connections. - * `Inversed Switch (Any)` - In contrast to `Switch (Any)`, it takes a single input and outputs one of many. Due to ComfyUI's functional limitations, the value of `select` must be determined at the time of queuing a prompt, and while it can serve as a `Primitive Node` or `ImpactInt`, it cannot function properly when connected through other nodes. - * Guide - * When the `Switch (Any)` and `Inversed Switch (Any)` selects are transformed into primitives, it's important to be cautious because the select range is not appropriately constrained, potentially leading to unintended behavior. - * `Switch (image,mask)`, `Switch (latent)`, `Switch (SEGS)`, `Switch (Any)` supports `sel_mode` param. The `sel_mode` sets the moment at which the `select` parameter is determined. `select_on_prompt` determines the `select` at the time of queuing the prompt, while `select_on_execution` determines it during the execution of the workflow. While `select_on_execution` offers more flexibility, it can potentially trigger workflow execution errors due to running nodes that may be impossible to execute within the limitations of ComfyUI. `select_on_prompt` bypasses this constraint by treating any inputs not selected as if they were disconnected. However, please note that when using `select_on_prompt`, the `select` can only be used with widgets or `Primitive Nodes` determined at the queue prompt. - * There is an issue when connecting the built-in reroute node with the switch's input/output slots. it can lead to forced disconnections during workflow loading. Therefore, it is advisable not to use reroute for making connections in such cases. However, there are no issues when using the reroute node in Pythongossss. + * `Inversed Switch (Any)` - In contrast to `Switch (Any)`, it takes a single input and outputs one of many. + * NOTE: See this [tutorial](https://github.com/ltdrdata/ComfyUI-extension-tutorials/blob/Main/ComfyUI-Impact-Pack/tutorial/switch.md) ### [Wildcards](http://github.com/ltdrdata/ComfyUI-extension-tutorials/blob/Main/ComfyUI-Impact-Pack/tutorial/ImpactWildcard.md) nodes * These are nodes that supports syntax in the form of `__wildcard-name__` and dynamic prompt syntax like `{a|b|c}`. diff --git a/js/impact-pack.js b/js/impact-pack.js index 233a333..5dbc547 100644 --- a/js/impact-pack.js +++ b/js/impact-pack.js @@ -507,7 +507,7 @@ app.registerExtension({ !stackTrace.includes('LGraphNode.connect') && // for mouse device !stackTrace.includes('loadGraphData') && this.inputs[index].name != 'select') { - this.removeInput(index); + this.removeInput(index); } } diff --git a/modules/impact/config.py b/modules/impact/config.py index c1f95f5..9a5939f 100644 --- a/modules/impact/config.py +++ b/modules/impact/config.py @@ -1,7 +1,7 @@ import configparser import os -version_code = [6, 2, 1] +version_code = [7, 0] version = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '') dependency_version = 22 diff --git a/modules/impact/core.py b/modules/impact/core.py index d60e6c0..cfd3ab7 100644 --- a/modules/impact/core.py +++ b/modules/impact/core.py @@ -45,6 +45,14 @@ current_prompt = None SCHEDULERS = comfy.samplers.KSampler.SCHEDULERS + ['AYS SDXL', 'AYS SD1', 'AYS SVD', 'GITS[coeff=1.2]'] +def is_execution_model_version_supported(): + try: + import comfy_execution + return True + except: + return False + + def set_previewbridge_image(node_id, file, item): global pb_id_cnt diff --git a/modules/impact/impact_server.py b/modules/impact/impact_server.py index bc2d51f..61bd59b 100644 --- a/modules/impact/impact_server.py +++ b/modules/impact/impact_server.py @@ -353,13 +353,16 @@ def onprompt_for_switch(json_data): cls = v['class_type'] if cls == 'ImpactInversedSwitch': - select_input = v['inputs']['select'] - if isinstance(select_input, list) and len(select_input) == 2: - input_node = json_data['prompt'][select_input[0]] - if input_node['class_type'] == 'ImpactInt' and 'inputs' in input_node and 'value' in input_node['inputs']: - inversed_switch_info[k] = input_node['inputs']['value'] - else: - inversed_switch_info[k] = select_input + if 'sel_mode' in v['inputs'] and v['inputs']['sel_mode'] and 'select' in v['inputs']: + select_input = v['inputs']['select'] + if isinstance(select_input, list) and len(select_input) == 2: + input_node = json_data['prompt'][select_input[0]] + if input_node['class_type'] == 'ImpactInt' and 'inputs' in input_node and 'value' in input_node['inputs']: + inversed_switch_info[k] = input_node['inputs']['value'] + else: + print(f"\n##### ##### #####\n[WARN] {cls}: For the 'select' operation, only 'select_index' of the 'ImpactInversedSwitch', which is not an input, or 'ImpactInt' and 'Primitive' are allowed as inputs if 'select_on_prompt' is selected.\n##### ##### #####\n") + else: + inversed_switch_info[k] = select_input elif cls in ['ImpactSwitch', 'LatentSwitch', 'SEGSSwitch', 'ImpactMakeImageList']: if 'sel_mode' in v['inputs'] and v['inputs']['sel_mode'] and 'select' in v['inputs']: @@ -372,7 +375,7 @@ def onprompt_for_switch(json_data): if isinstance(input_node['inputs']['select'], int): onprompt_switch_info[k] = input_node['inputs']['select'] else: - print(f"\n##### ##### #####\n[WARN] {cls}: For the 'select' operation, only 'select_index' of the 'ImpactSwitch', which is not an input, or 'ImpactInt' and 'Primitive' are allowed as inputs.\n##### ##### #####\n") + print(f"\n##### ##### #####\n[WARN] {cls}: For the 'select' operation, only 'select_index' of the 'ImpactSwitch', which is not an input, or 'ImpactInt' and 'Primitive' are allowed as inputs if 'select_on_prompt' is selected.\n##### ##### #####\n") else: onprompt_switch_info[k] = select_input diff --git a/modules/impact/logics.py b/modules/impact/logics.py index 8398707..60cb087 100644 --- a/modules/impact/logics.py +++ b/modules/impact/logics.py @@ -86,11 +86,18 @@ class ImpactConditionalBranch: class ImpactConditionalBranchSelMode: @classmethod def INPUT_TYPES(cls): - return { - "required": { + if not core.is_execution_model_version_supported(): + required_inputs = { "cond": ("BOOLEAN",), "sel_mode": ("BOOLEAN", {"default": True, "label_on": "select_on_prompt", "label_off": "select_on_execution"}), - }, + } + else: + required_inputs = { + "cond": ("BOOLEAN",), + } + + return { + "required": required_inputs, "optional": { "tt_value": (any_typ,), "ff_value": (any_typ,), @@ -102,7 +109,7 @@ class ImpactConditionalBranchSelMode: RETURN_TYPES = (any_typ, ) - def doit(self, cond, sel_mode, tt_value=None, ff_value=None): + def doit(self, cond, tt_value=None, ff_value=None, **kwargs): print(f'tt={tt_value is None}\nff={ff_value is None}') if cond: return (tt_value,) diff --git a/modules/impact/util_nodes.py b/modules/impact/util_nodes.py index 9bd45e2..925514f 100644 --- a/modules/impact/util_nodes.py +++ b/modules/impact/util_nodes.py @@ -6,29 +6,47 @@ import comfy import sys import nodes import re +import impact.core as core from server import PromptServer +import inspect class GeneralSwitch: @classmethod def INPUT_TYPES(s): - return {"required": { + dyn_inputs = {"input1": (any_typ, {"lazy": True}), } + if core.is_execution_model_version_supported(): + stack = inspect.stack() + if stack[2].function == 'get_input_info' and stack[3].function == 'add_node': + for x in range(2, 200): + dyn_inputs[f"input{x}"] = (any_typ, {"lazy": True}) + + inputs = {"required": { "select": ("INT", {"default": 1, "min": 1, "max": 999999, "step": 1}), - "sel_mode": ("BOOLEAN", {"default": True, "label_on": "select_on_prompt", "label_off": "select_on_execution", "forceInput": False}), - }, - "optional": { - "input1": (any_typ,), + "sel_mode": ("BOOLEAN", {"default": False, "label_on": "select_on_prompt", "label_off": "select_on_execution", "forceInput": False}), }, + "optional": dyn_inputs, "hidden": {"unique_id": "UNIQUE_ID", "extra_pnginfo": "EXTRA_PNGINFO"} } + return inputs + RETURN_TYPES = (any_typ, "STRING", "INT") RETURN_NAMES = ("selected_value", "selected_label", "selected_index") FUNCTION = "doit" CATEGORY = "ImpactPack/Util" - def doit(self, *args, **kwargs): + def check_lazy_status(self, *args, **kwargs): + selected_index = int(kwargs['select']) + input_name = f"input{selected_index}" + + print(f"SELECTED: {input_name}") + + return [input_name] + + @staticmethod + def doit(*args, **kwargs): selected_index = int(kwargs['select']) input_name = f"input{selected_index}" @@ -50,11 +68,10 @@ class GeneralSwitch: print(f"[Impact-Pack] The switch node does not guarantee proper functioning in API mode.") if input_name in kwargs: - return (kwargs[input_name], selected_label, selected_index) + return kwargs[input_name], selected_label, selected_index else: print(f"ImpactSwitch: invalid select index (ignored)") - return (None, "", selected_index) - + return None, "", selected_index class LatentSwitch: @classmethod @@ -129,7 +146,9 @@ class GeneralInversedSwitch: "select": ("INT", {"default": 1, "min": 1, "max": 999999, "step": 1}), "input": (any_typ,), }, - "hidden": {"unique_id": "UNIQUE_ID"}, + "optional": { + "sel_mode": ("BOOLEAN", {"default": False, "label_on": "select_on_prompt", "label_off": "select_on_execution", "forceInput": False}), + }, } RETURN_TYPES = ByPassTypeTuple((any_typ, )) @@ -137,7 +156,7 @@ class GeneralInversedSwitch: CATEGORY = "ImpactPack/Util" - def doit(self, select, input, unique_id): + def doit(self, select, input, **kwargs): res = [] for i in range(0, select): diff --git a/pyproject.toml b/pyproject.toml index 9e183d7..3887c62 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui-impact-pack" description = "This extension offers various detector nodes and detailer nodes that allow you to configure a workflow that automatically enhances facial details. And provide iterative upscaler." -version = "6.2.1" +version = "7.0" license = { file = "LICENSE.txt" } dependencies = ["segment-anything", "scikit-image", "piexif", "transformers", "opencv-python-headless", "GitPython", "scipy>=1.11.4"]