feature: dynamic switch based on Execution Model Inversion.

This commit is contained in:
Dr.Lt.Data
2024-08-16 11:53:02 +09:00
parent 94f30ef317
commit 06b428ec8c
8 changed files with 66 additions and 31 deletions
+3 -5
View File
@@ -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}`.
+1 -1
View File
@@ -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);
}
}
+1 -1
View File
@@ -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
+8
View File
@@ -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
+11 -8
View File
@@ -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
+11 -4
View File
@@ -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,)
+30 -11
View File
@@ -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):
+1 -1
View File
@@ -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"]