From 1c8a6268ea0c12173b1f600276acbe9797a61ddf Mon Sep 17 00:00:00 2001
From: TSC <112517630+LucianoCirino@users.noreply.github.com>
Date: Wed, 6 Sep 2023 09:23:57 -0500
Subject: [PATCH 01/14] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 42ec53e..bb46036 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
Efficiency Nodes for ComfyUI
=======
### A collection of ComfyUI custom nodes to help streamline workflows and reduce total node count.
-## [Direct Download Link](https://github.com/LucianoCirino/efficiency-nodes-comfyui/releases/download/v1.92/efficiency-nodes-comfyui-.v192.7z)
+## [Direct Download Link](https://github.com/LucianoCirino/efficiency-nodes-comfyui/releases/download/v1.92/efficiency-nodes-comfyui-v192.7z)
Efficient Loader
From 8cb207b539d4456c248a251908e1e27ba95496f0 Mon Sep 17 00:00:00 2001
From: TSC <112517630+LucianoCirino@users.noreply.github.com>
Date: Thu, 7 Sep 2023 20:41:20 -0500
Subject: [PATCH 02/14] ControlNet Fixes (XY Input+StackApplications)
1) Fixed issue where Control Net Stacks would not get applied
2) Fixed issue where XY Input Control Net was not coded whatsoever.
---
efficiency_nodes.py | 141 ++++++++++++++++++++++++--------------------
1 file changed, 78 insertions(+), 63 deletions(-)
diff --git a/efficiency_nodes.py b/efficiency_nodes.py
index 5635025..887edec 100644
--- a/efficiency_nodes.py
+++ b/efficiency_nodes.py
@@ -28,7 +28,8 @@ font_path = os.path.join(my_dir, 'arial.ttf')
# Append comfy_dir to sys.path & import files
sys.path.append(comfy_dir)
-from nodes import LatentUpscaleBy, KSampler, KSamplerAdvanced, VAEDecode, VAEDecodeTiled, CLIPSetLastLayer, CLIPTextEncode, ControlNetApplyAdvanced
+from nodes import LatentUpscaleBy, KSampler, KSamplerAdvanced, VAEDecode, VAEDecodeTiled, VAEEncode, VAEEncodeTiled, \
+ ImageScaleBy, CLIPSetLastLayer, CLIPTextEncode, ControlNetLoader, ControlNetApply, ControlNetApplyAdvanced
from comfy_extras.nodes_clip_sdxl import CLIPTextEncodeSDXL, CLIPTextEncodeSDXLRefiner
import comfy.samplers
import comfy.sd
@@ -370,10 +371,11 @@ class TSC_Apply_ControlNet_Stack:
def apply_cnet_stack(self, positive, negative, cnet_stack):
for control_net_tuple in cnet_stack:
control_net, image, strength, start_percent, end_percent = control_net_tuple
- controlnet_conditioning = ControlNetApplyAdvanced().apply_controlnet(positive, negative,
- control_net, image, strength,
- start_percent, end_percent)
- return controlnet_conditioning
+ controlnet_conditioning = ControlNetApplyAdvanced().apply_controlnet(positive, negative, control_net, image,
+ strength, start_percent, end_percent)
+ positive, negative = controlnet_conditioning[0], controlnet_conditioning[1]
+
+ return (positive, negative, )
########################################################################################################################
# TSC KSampler (Efficient)
@@ -388,7 +390,7 @@ class TSC_KSampler:
@classmethod
def INPUT_TYPES(cls):
return {"required":
- {"sampler_state": (["Sample", "Hold", "Script"], ),
+ {"sampler_state": (["Sample", "Script", "Hold"], ),
"model": ("MODEL",),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
@@ -529,36 +531,23 @@ class TSC_KSampler:
def vae_decode_latent(vae, samples, vae_decode):
return VAEDecodeTiled().decode(vae,samples,512)[0] if "tiled" in vae_decode else VAEDecode().decode(vae,samples)[0]
+ def vae_encode_image(vae, pixels, vae_decode):
+ return VAEEncodeTiled().encode(vae,pixels,512)[0] if "tiled" in vae_decode else VAEEncode().encode(vae,pixels)[0]
+
# ---------------------------------------------------------------------------------------------------------------
def sample_latent_image(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image,
denoise, sampler_type, add_noise, start_at_step, end_at_step, return_with_leftover_noise,
- refiner_model=None, refiner_positive=None, refiner_negative=None):
-
- if keys_exist_in_script("tile"):
- tile_width, tile_height, tiling_strategy, blenderneko_tiled_nodes = script["tile"]
- TSampler = blenderneko_tiled_nodes.TiledKSampler
- TSamplerAdvanced = blenderneko_tiled_nodes.TiledKSamplerAdvanced
+ refiner_model, refiner_positive, refiner_negative, vae, vae_decode, sampler_state):
# Sample the latent_image(s) using the Comfy KSampler nodes
if sampler_type == "regular":
- if keys_exist_in_script("tile"):
- samples = TSampler().sample(model, seed, tile_width, tile_height, tiling_strategy, steps, cfg,
- sampler_name, scheduler, positive, negative,
- latent_image, denoise=denoise)[0]
- else:
- samples = KSampler().sample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative,
- latent_image, denoise=denoise)[0]
+ samples = KSampler().sample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative,
+ latent_image, denoise=denoise)[0]
elif sampler_type == "advanced":
- if keys_exist_in_script("tile"):
- samples = TSamplerAdvanced().sample(model, add_noise, seed, tile_width, tile_height, tiling_strategy,
- steps, cfg, sampler_name, scheduler,
- positive, negative, latent_image, start_at_step, end_at_step,
- return_with_leftover_noise, "disabled", denoise=1.0)[0]
- else:
- samples = KSamplerAdvanced().sample(model, add_noise, seed, steps, cfg, sampler_name, scheduler,
- positive, negative, latent_image, start_at_step, end_at_step,
- return_with_leftover_noise, denoise=1.0)[0]
+ samples = KSamplerAdvanced().sample(model, add_noise, seed, steps, cfg, sampler_name, scheduler,
+ positive, negative, latent_image, start_at_step, end_at_step,
+ return_with_leftover_noise, denoise=1.0)[0]
elif sampler_type == "sdxl":
# Disable refiner if refine_at_step is -1
@@ -579,24 +568,35 @@ class TSC_KSampler:
samples, end_at_step, steps,
return_with_leftover_noise, denoise=1.0)[0]
- # Check if "hiresfix" exists in the script after main sampling has taken place
- if keys_exist_in_script("hiresfix"):
+ if sampler_state == "Script":
- # Unpack the tuple from the script's "hiresfix" key
- latent_upscale_method, upscale_by, hires_steps, hires_denoise, iterations, upscale_function = script["hiresfix"]
-
- # Iterate for the given number of iterations
- for _ in range(iterations):
- upscaled_latent_image = upscale_function().upscale(samples, latent_upscale_method, upscale_by)[0]
- # Use the regular KSampler for each iteration
- if False: #if keys_exist_in_script("tile"): # Disabled for HiResFix
- samples = TSampler().sample(model, seed, tile_width, tile_height, tiling_strategy, steps, cfg,
- sampler_name, scheduler,
- positive, negative, upscaled_latent_image, denoise=hires_denoise)[0]
- else:
+ # Check if "hiresfix" exists in the script after main sampling has taken place
+ if keys_exist_in_script("hiresfix"):
+ # Unpack the tuple from the script's "hiresfix" key
+ latent_upscale_method, upscale_by, hires_steps, hires_denoise, iterations, upscale_function = script["hiresfix"]
+ # Iterate for the given number of iterations
+ for _ in range(iterations):
+ upscaled_latent_image = upscale_function().upscale(samples, latent_upscale_method, upscale_by)[0]
samples = KSampler().sample(model, seed, hires_steps, cfg, sampler_name, scheduler,
- positive, negative, upscaled_latent_image, denoise=hires_denoise)[0]
+ positive, negative, upscaled_latent_image, denoise=hires_denoise)[0]
+ # Check if "tile" exists in the script after main sampling has taken place
+ if keys_exist_in_script("tile"):
+ # Unpack the tuple from the script's "tile" key
+ upscale_by, tile_controlnet, tile_size, tiling_strategy, tiling_steps, tiled_denoise,\
+ blenderneko_tiled_nodes = script["tile"]
+ # VAE Decode samples
+ image = vae_decode_latent(vae, samples, vae_decode)
+ # Upscale image
+ upscaled_image = ImageScaleBy().upscale(image, "nearest-exact", upscale_by)[0]
+ upscaled_latent = vae_encode_image(vae, upscaled_image, vae_decode)
+ # Apply Control Net using upscaled_image and loaded control_net
+ positive = ControlNetApply().apply_controlnet(positive, tile_controlnet, upscaled_image, 1)[0]
+ # Sample latent
+ TSampler = blenderneko_tiled_nodes.TiledKSampler
+ samples = TSampler().sample(model, seed-1, tile_size, tile_size, tiling_strategy, tiling_steps, cfg,
+ sampler_name, scheduler, positive, negative, upscaled_latent,
+ denoise=tiled_denoise)[0]
return samples
# ---------------------------------------------------------------------------------------------------------------
@@ -640,8 +640,9 @@ class TSC_KSampler:
send_command_to_frontend(startListening=True, maxCount=steps-1, sendBlob=False)
samples = sample_latent_image(model, seed, steps, cfg, sampler_name, scheduler, positive, negative,
- latent_image, denoise, sampler_type, add_noise, start_at_step, end_at_step,
- return_with_leftover_noise, refiner_model, refiner_positive, refiner_negative)
+ latent_image, denoise, sampler_type, add_noise, start_at_step, end_at_step,
+ return_with_leftover_noise, refiner_model, refiner_positive, refiner_negative,
+ vae, vae_decode, sampler_state)
# Cache samples in the 'last_helds' dictionary "latent"
update_value_by_id("latent", my_unique_id, samples)
@@ -1421,7 +1422,8 @@ class TSC_KSampler:
samples = sample_latent_image(model, seed, steps, cfg, sampler_name, scheduler, positive, negative,
latent_image, denoise, sampler_type, add_noise, start_at_step, end_at_step,
- return_with_leftover_noise, refiner_model, refiner_positive, refiner_negative)
+ return_with_leftover_noise, refiner_model, refiner_positive, refiner_negative,
+ vae, vae_decode, sampler_state)
# Add the latent tensor to the tensors list
latent_list.append(samples)
@@ -1491,7 +1493,6 @@ class TSC_KSampler:
ckpt_name, clip_skip, refiner_name, refiner_clip_skip, positive_prompt,
negative_prompt, ascore, lora_stack, cnet_stack, X_label, len(X_value))
-
if X_type != "Nothing" and Y_type == "Nothing":
if X_type == "XY_Capsule":
model, clip, refiner_model, refiner_clip = \
@@ -3309,7 +3310,7 @@ class TSC_XYplot_Control_Net_End:
# =======================================================================================================================
# TSC XY Plot: Control Net
-class TSC_XYplot_Control_Net:
+class TSC_XYplot_Control_Net(TSC_XYplot_Control_Net_Strength, TSC_XYplot_Control_Net_Start, TSC_XYplot_Control_Net_End):
parameters = ["strength", "start_percent", "end_percent"]
@classmethod
def INPUT_TYPES(cls):
@@ -3326,7 +3327,7 @@ class TSC_XYplot_Control_Net:
"first_end_percent": ("FLOAT", {"default": 0.0, "min": 0.00, "max": 1.0, "step": 0.01}),
"last_end_percent": ("FLOAT", {"default": 1.0, "min": 0.00, "max": 1.0, "step": 0.01}),
"strength": ("FLOAT", {"default": 1.0, "min": 0.00, "max": 10.0, "step": 0.01}),
- "start_percent": ("FLOAT", {"default": 1.0, "min": 0.00, "max": 1.0, "step": 0.01}),
+ "start_percent": ("FLOAT", {"default": 0.0, "min": 0.00, "max": 1.0, "step": 0.01}),
"end_percent": ("FLOAT", {"default": 1.0, "min": 0.00, "max": 1.0, "step": 0.01}),
},
"optional": {"cnet_stack": ("CONTROL_NET_STACK",)},
@@ -3338,10 +3339,17 @@ class TSC_XYplot_Control_Net:
CATEGORY = "Efficiency Nodes/XY Inputs"
def xy_value(self, control_net, image, target_parameter, batch_count, first_strength, last_strength, first_start_percent,
- last_start_percent, first_end_percent, last_end_percent, strength, start_percent, cnet_stack=None):
+ last_start_percent, first_end_percent, last_end_percent, strength, start_percent, end_percent, cnet_stack=None):
-
- return ((xy_type, xy_value),)
+ if target_parameter == "strength":
+ return TSC_XYplot_Control_Net_Strength.xy_value(self, control_net, image, batch_count, first_strength,
+ last_strength, start_percent, end_percent, cnet_stack=cnet_stack)
+ elif target_parameter == "start_percent":
+ return TSC_XYplot_Control_Net_Start.xy_value(self, control_net, image, batch_count, first_start_percent,
+ last_start_percent, strength, end_percent, cnet_stack=cnet_stack)
+ elif target_parameter == "end_percent":
+ return TSC_XYplot_Control_Net_End.xy_value(self, control_net, image, batch_count, first_end_percent,
+ last_end_percent, strength, start_percent, cnet_stack=cnet_stack)
#=======================================================================================================================
# TSC XY Plot: Control Net Plot
@@ -4007,8 +4015,8 @@ NODE_CLASS_MAPPINGS = {
"XY Input: LoRA Stacks": TSC_XYplot_LoRA_Stacks,
"XY Input: Control Net": TSC_XYplot_Control_Net,
"XY Input: Control Net Plot": TSC_XYplot_Control_Net_Plot,
- "XY Input: Manual XY Entry": TSC_XYplot_Manual_XY_Entry, # DISABLED, NEEDS UPDATE
- "Manual XY Entry Info": TSC_XYplot_Manual_XY_Entry_Info, # DISABLED, NEEDS UPDATE
+ "XY Input: Manual XY Entry": TSC_XYplot_Manual_XY_Entry,
+ "Manual XY Entry Info": TSC_XYplot_Manual_XY_Entry_Info,
"Join XY Inputs of Same Type": TSC_XYplot_JoinInputs,
"Image Overlay": TSC_ImageOverlay
}
@@ -4107,7 +4115,7 @@ class TSC_HighRes_Fix:
NODE_CLASS_MAPPINGS.update({"HighRes-Fix Script": TSC_HighRes_Fix})
########################################################################################################################
-'''# FUTURE
+'''
# Tiled Sampling KSamplers (https://github.com/BlenderNeko/ComfyUI_TiledKSampler)
blenderneko_tiled_ksampler_path = os.path.join(custom_nodes_dir, "ComfyUI_TiledKSampler")
if os.path.exists(blenderneko_tiled_ksampler_path):
@@ -4117,14 +4125,20 @@ if os.path.exists(blenderneko_tiled_ksampler_path):
blenderneko_tiled_nodes = import_module("ComfyUI_TiledKSampler.nodes")
print(f"\r{message('Efficiency Nodes:')} {printout}{success('Success!')}")
- # TSC Tiled Sampling
- class TSC_Tiled_Sampling:
-
+ # TSC Tiled Upscaler
+ class TSC_Tiled_Upscaler:
@classmethod
def INPUT_TYPES(cls):
- return {"required": {"tile_width": ("INT", {"default": 512, "min": 256, "max": MAX_RESOLUTION, "step": 64}),
- "tile_height": ("INT", {"default": 512, "min": 256, "max": MAX_RESOLUTION, "step": 64}),
+ # Split the list based on the keyword "tile"
+ cnet_tile_filenames = [name for name in folder_paths.get_filename_list("controlnet") if "tile" in name]
+ cnet_other_filenames = [name for name in folder_paths.get_filename_list("controlnet") if "tile" not in name]
+
+ return {"required": {"tile_controlnet": (cnet_tile_filenames + cnet_other_filenames,),
+ "upscale_by": ("FLOAT", {"default": 1.25, "min": 0.01, "max": 8.0, "step": 0.25}),
+ "tile_size": ("INT", {"default": 512, "min": 256, "max": MAX_RESOLUTION, "step": 64}),
"tiling_strategy": (["random", "random strict", "padded", 'simple', 'none'],),
+ "tiling_steps": ("INT", {"default": 30, "min": 1, "max": 10000}),
+ "denoise": ("FLOAT", {"default": .56, "min": 0.0, "max": 1.0, "step": 0.01}),
},
"optional": {"script": ("SCRIPT",)}}
@@ -4132,13 +4146,14 @@ if os.path.exists(blenderneko_tiled_ksampler_path):
FUNCTION = "tiled_sampling"
CATEGORY = "Efficiency Nodes/Scripts"
- def tiled_sampling(self, tile_width, tile_height, tiling_strategy, script=None):
+ def tiled_sampling(self, upscale_by, tile_controlnet, tile_size, tiling_strategy, tiling_steps, denoise, script=None):
if tiling_strategy != 'none':
script = script or {}
- script["tile"] = (tile_width, tile_height, tiling_strategy, blenderneko_tiled_nodes)
+ script["tile"] = (upscale_by, ControlNetLoader().load_controlnet(tile_controlnet)[0],
+ tile_size, tiling_strategy, tiling_steps, denoise, blenderneko_tiled_nodes)
return (script,)
- NODE_CLASS_MAPPINGS.update({"Tiled Sampling Script": TSC_Tiled_Sampling})
+ NODE_CLASS_MAPPINGS.update({"Tiled Upscaler Script": TSC_Tiled_Upscaler})
except ImportError:
print(f"\r{message('Efficiency Nodes:')} {printout}{error('Failed!')}")
From 8ba2fe2777b05cd1bd68ef79dca7bebc087674c2 Mon Sep 17 00:00:00 2001
From: TSC <112517630+LucianoCirino@users.noreply.github.com>
Date: Thu, 7 Sep 2023 22:54:47 -0500
Subject: [PATCH 03/14] Improve Compatability with Nested Nodes
---
js/appearance.js | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/js/appearance.js b/js/appearance.js
index cb15850..5ea8599 100644
--- a/js/appearance.js
+++ b/js/appearance.js
@@ -63,6 +63,7 @@ let colorKeys = Object.keys(COLOR_THEMES).filter(key => key !== "none");
shuffleArray(colorKeys); // Shuffle the color themes initially
function setNodeColors(node, theme) {
+ if (!theme) {return;}
node.shape = "box";
if(theme.nodeColor && theme.nodeBgColor) {
node.color = theme.nodeColor;
@@ -79,9 +80,10 @@ const ext = {
let colorKey = NODE_COLORS[title];
if (colorKey === "random") {
- if (colorKeys.length === 0) {
- colorKeys = Object.values(COLOR_THEMES).filter(theme => theme.nodeColor && theme.nodeBgColor);
- shuffleArray(colorKeys); // Reshuffle when out of colors
+ // Check for a valid color key before popping
+ if (colorKeys.length === 0 || !COLOR_THEMES[colorKeys[colorKeys.length - 1]]) {
+ colorKeys = Object.keys(COLOR_THEMES).filter(key => key !== "none");
+ shuffleArray(colorKeys);
}
colorKey = colorKeys.pop();
}
From efe4656caa0c6a8ab9048644303b4d64e8c0ad20 Mon Sep 17 00:00:00 2001
From: TSC <112517630+LucianoCirino@users.noreply.github.com>
Date: Sat, 9 Sep 2023 11:49:23 -0500
Subject: [PATCH 04/14] Change how Web Extensions are Loaded
This change comes from changes made to ComfyUI from commmit https://github.com/comfyanonymous/ComfyUI/pull/1273
---
__init__.py | 6 +++---
tsc_utils.py | 29 ++++++-----------------------
2 files changed, 9 insertions(+), 26 deletions(-)
diff --git a/__init__.py b/__init__.py
index d8edad8..806f466 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1,3 +1,3 @@
-from .efficiency_nodes import NODE_CLASS_MAPPINGS
-
-__all__ = ['NODE_CLASS_MAPPINGS']
+from .efficiency_nodes import NODE_CLASS_MAPPINGS
+WEB_DIRECTORY = "js"
+__all__ = ['NODE_CLASS_MAPPINGS']
diff --git a/tsc_utils.py b/tsc_utils.py
index 7d62f10..74e364f 100644
--- a/tsc_utils.py
+++ b/tsc_utils.py
@@ -521,33 +521,16 @@ def packages(python_exe=None, versions=False):
install_packages(my_dir)
#-----------------------------------------------------------------------------------------------------------------------
-# Auto install efficiency nodes web extensions '\js\' to 'ComfyUI\web\extensions'
+# Delete efficiency nodes web extensions from 'ComfyUI\web\extensions'.
+# PR from https://github.com/comfyanonymous/ComfyUI/pull/1273 now allows defining web extensions through a dir path in
import shutil
-# Source and destination directories
-source_dir = os.path.join(my_dir, 'js')
+# Destination directory
destination_dir = os.path.join(comfy_dir, 'web', 'extensions', 'efficiency-nodes-comfyui')
-# Create the destination directory if it doesn't exist
-os.makedirs(destination_dir, exist_ok=True)
-
-# Get a list of all .js files in the source directory
-source_files = [f for f in os.listdir(source_dir) if f.endswith('.js')]
-
-# Clear files in the destination directory that aren't in the source directory
-for file_name in os.listdir(destination_dir):
- if file_name not in source_files and file_name.endswith('.js'):
- file_path = os.path.join(destination_dir, file_name)
- os.unlink(file_path)
-
-# Iterate over all files in the source directory for copying
-for file_name in source_files:
- # Full paths for source and destination
- source_path = os.path.join(source_dir, file_name)
- destination_path = os.path.join(destination_dir, file_name)
-
- # Directly copy the file (this will overwrite if the file already exists)
- shutil.copy2(source_path, destination_path)
+# Check if the directory exists and delete it
+if os.path.exists(destination_dir):
+ shutil.rmtree(destination_dir)
#-----------------------------------------------------------------------------------------------------------------------
# Establish a websocket connection to communicate with "efficiency-nodes.js" under:
From 8429544192a1ec65aaea5a760895c4283fed661d Mon Sep 17 00:00:00 2001
From: TSC <112517630+LucianoCirino@users.noreply.github.com>
Date: Sat, 9 Sep 2023 11:51:12 -0500
Subject: [PATCH 05/14] Update tsc_utils.py
---
tsc_utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tsc_utils.py b/tsc_utils.py
index 74e364f..5eec0f4 100644
--- a/tsc_utils.py
+++ b/tsc_utils.py
@@ -522,7 +522,7 @@ install_packages(my_dir)
#-----------------------------------------------------------------------------------------------------------------------
# Delete efficiency nodes web extensions from 'ComfyUI\web\extensions'.
-# PR from https://github.com/comfyanonymous/ComfyUI/pull/1273 now allows defining web extensions through a dir path in
+# Pull https://github.com/comfyanonymous/ComfyUI/pull/1273 now allows defining web extensions through a dir path in init
import shutil
# Destination directory
From c8f21356e8983fa984e54b1f8c923ba26e586b15 Mon Sep 17 00:00:00 2001
From: TSC <112517630+LucianoCirino@users.noreply.github.com>
Date: Sat, 9 Sep 2023 13:19:21 -0500
Subject: [PATCH 06/14] Generalize Auto-Package Installation Error Handling
---
tsc_utils.py | 45 +++++++++++++++++++++++++--------------------
1 file changed, 25 insertions(+), 20 deletions(-)
diff --git a/tsc_utils.py b/tsc_utils.py
index 5eec0f4..e9e422a 100644
--- a/tsc_utils.py
+++ b/tsc_utils.py
@@ -489,33 +489,38 @@ def install_packages(my_dir):
with open(os.path.join(my_dir, 'requirements.txt'), 'r') as f:
required_packages = [line.strip() for line in f if line.strip()]
- installed_packages = packages(embedded_python_exe if use_embedded else None, versions=False)
- for pkg in required_packages:
- if pkg not in installed_packages:
- print(f"\033[32mEfficiency Nodes:\033[0m Installing required package '{pkg}'...", end='', flush=True)
- try:
+ try:
+ installed_packages = packages(embedded_python_exe if use_embedded else None, versions=False)
+
+ for pkg in required_packages:
+ if pkg not in installed_packages:
+ printout = f"Installing required package '{pkg}'..."
+ print(f"{message('Efficiency Nodes:')} {printout}", end='', flush=True)
if use_embedded: # Targeted installation
subprocess.check_call(['pip', 'install', pkg, '--target=' + target_dir, '--no-warn-script-location',
- '--disable-pip-version-check'], stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
+ '--disable-pip-version-check'], stdout=subprocess.DEVNULL,
+ stderr=subprocess.PIPE)
else: # Untargeted installation
subprocess.check_call(['pip', 'install', pkg], stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
- print(f"\r\033[32mEfficiency Nodes:\033[0m Installing required package '{pkg}'... Installed!", flush=True)
+ print(f"\r{message('Efficiency Nodes:')} {printout}{success('Installed!')}", flush=True)
- except subprocess.CalledProcessError as e: # Failed installation
- base_message = f"\r\033[31mEfficiency Nodes Error:\033[0m Failed to install python package '{pkg}'. "
- if e.stderr:
- error_message = e.stderr.decode()
- print(base_message + f"Error message: {error_message}")
- else:
- print(base_message + "\nPlease check your permissions, network connectivity, or try a manual installation.")
+ except Exception as e: # This catches all exceptions derived from the base Exception class
+ print_general_error_message()
def packages(python_exe=None, versions=False):
- # Get packages of the active or embedded Python environment
- if python_exe:
- return [(r.decode().split('==')[0] if not versions else r.decode()) for r in
- subprocess.check_output([python_exe, '-m', 'pip', 'freeze']).split()]
- else:
- return [(r.split('==')[0] if not versions else r) for r in subprocess.getoutput('pip freeze').splitlines()]
+ try:
+ if python_exe:
+ return [(r.decode().split('==')[0] if not versions else r.decode()) for r in
+ subprocess.check_output([python_exe, '-m', 'pip', 'freeze']).split()]
+ else:
+ return [(r.split('==')[0] if not versions else r) for r in subprocess.getoutput('pip freeze').splitlines()]
+ except subprocess.CalledProcessError as e:
+ raise e # re-raise the error to handle it outside
+
+def print_general_error_message():
+ print(
+ f"\r{message('Efficiency Nodes:')} An unexpected error occurred during the package installation process. {error('Failed!')}")
+ print(warning("Please try manually installing the required packages from the requirements.txt file."))
# Install missing packages
install_packages(my_dir)
From 7a0adcc33528637829816b9532f0cf8caf709f13 Mon Sep 17 00:00:00 2001
From: TSC <112517630+LucianoCirino@users.noreply.github.com>
Date: Sat, 9 Sep 2023 19:57:52 -0500
Subject: [PATCH 07/14] Fixed ClipSkip Issues (Loader & XY Plot)
Efficient Loader "Clip" output was not getting its "Clip Set Last Layer" settings applied. XY Plot was also not properly applying Clip Skips.
---
efficiency_nodes.py | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/efficiency_nodes.py b/efficiency_nodes.py
index 887edec..e2eb81c 100644
--- a/efficiency_nodes.py
+++ b/efficiency_nodes.py
@@ -82,11 +82,11 @@ def encode_prompts(positive_prompt, negative_prompt, clip, clip_skip, refiner_cl
empty_latent_height, negative_prompt)[0]
# Return results based on return_type
if return_type == "base":
- return positive_encoded, negative_encoded
+ return positive_encoded, negative_encoded, clip
elif return_type == "refiner":
- return refiner_positive_encoded, refiner_negative_encoded
+ return refiner_positive_encoded, refiner_negative_encoded, refiner_clip
elif return_type == "both":
- return positive_encoded, negative_encoded, refiner_positive_encoded, refiner_negative_encoded
+ return positive_encoded, negative_encoded, clip, refiner_positive_encoded, refiner_negative_encoded, refiner_clip
########################################################################################################################
# TSC Efficient Loader
@@ -163,7 +163,7 @@ class TSC_EfficientLoader:
clip_skip = clip_skip[0] if loader_type == "sdxl" else clip_skip
# Encode prompt based on loader_type
- positive_encoded, negative_encoded, refiner_positive_encoded, refiner_negative_encoded = \
+ positive_encoded, negative_encoded, clip, refiner_positive_encoded, refiner_negative_encoded, refiner_clip = \
encode_prompts(positive, negative, clip, clip_skip, refiner_clip, refiner_clip_skip, ascore,
loader_type == "sdxl", empty_latent_width, empty_latent_height)
@@ -1376,7 +1376,7 @@ class TSC_KSampler:
# Encode base prompt
if encode == True:
- positive, negative = \
+ positive, negative, clip = \
encode_prompts(positive_prompt, negative_prompt, clip, clip_skip, refiner_clip,
refiner_clip_skip, ascore, sampler_type == "sdxl", empty_latent_width,
empty_latent_height, return_type="base")
@@ -1386,7 +1386,7 @@ class TSC_KSampler:
positive, negative = controlnet_conditioning[0], controlnet_conditioning[1]
if encode_refiner == True:
- refiner_positive, refiner_negative = \
+ refiner_positive, refiner_negative, refiner_clip = \
encode_prompts(positive_prompt, negative_prompt, clip, clip_skip, refiner_clip,
refiner_clip_skip, ascore, sampler_type == "sdxl", empty_latent_width,
empty_latent_height, return_type="refiner")
From 359ff0f7888100119cc31065c16d688d48e34ca9 Mon Sep 17 00:00:00 2001
From: TSC <112517630+LucianoCirino@users.noreply.github.com>
Date: Sun, 10 Sep 2023 14:09:51 -0500
Subject: [PATCH 08/14] Use sys.executable for Pip Calls
This PR addresses an issue where the code could potentially use a global version of pip not associated with the running Python interpreter, leading to errors such as "module pip not found".
---
tsc_utils.py | 23 ++++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/tsc_utils.py b/tsc_utils.py
index e9e422a..94baacd 100644
--- a/tsc_utils.py
+++ b/tsc_utils.py
@@ -472,10 +472,11 @@ def global_preview_method():
#-----------------------------------------------------------------------------------------------------------------------
# Auto install Efficiency Nodes Python package dependencies
import subprocess
-# Note: Auto installer install packages inside the requirements.txt.
-# It first trys ComfyUI's python_embedded folder if python.exe exists inside ...\ComfyUI_windows_portable\python_embeded.
-# If no python.exe is found, it attempts a general global pip install of packages.
-# On an error, an user is directed to attempt manually installing the packages themselves.
+# Note: This auto-installer installs packages listed in the requirements.txt.
+# It first checks if python.exe exists inside the ...\ComfyUI_windows_portable\python_embeded directory.
+# If python.exe is found in that location, it will use this embedded Python version for the installation.
+# Otherwise, it uses the Python interpreter that's currently executing the script (via sys.executable) to attempt a general pip install of the packages.
+# If any errors occur during installation, the user is directed to manually install the required packages.
def install_packages(my_dir):
# Compute path to the target site-packages
@@ -496,12 +497,15 @@ def install_packages(my_dir):
if pkg not in installed_packages:
printout = f"Installing required package '{pkg}'..."
print(f"{message('Efficiency Nodes:')} {printout}", end='', flush=True)
+
if use_embedded: # Targeted installation
- subprocess.check_call(['pip', 'install', pkg, '--target=' + target_dir, '--no-warn-script-location',
- '--disable-pip-version-check'], stdout=subprocess.DEVNULL,
- stderr=subprocess.PIPE)
+ subprocess.check_call([embedded_python_exe, '-m', 'pip', 'install', pkg, '--target=' + target_dir,
+ '--no-warn-script-location', '--disable-pip-version-check'],
+ stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
else: # Untargeted installation
- subprocess.check_call(['pip', 'install', pkg], stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
+ subprocess.check_call([sys.executable, "-m", "pip", 'install', pkg],
+ stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
+
print(f"\r{message('Efficiency Nodes:')} {printout}{success('Installed!')}", flush=True)
except Exception as e: # This catches all exceptions derived from the base Exception class
@@ -513,7 +517,8 @@ def packages(python_exe=None, versions=False):
return [(r.decode().split('==')[0] if not versions else r.decode()) for r in
subprocess.check_output([python_exe, '-m', 'pip', 'freeze']).split()]
else:
- return [(r.split('==')[0] if not versions else r) for r in subprocess.getoutput('pip freeze').splitlines()]
+ return [(r.split('==')[0] if not versions else r) for r in
+ subprocess.getoutput([sys.executable, "-m", "pip", "freeze"]).splitlines()]
except subprocess.CalledProcessError as e:
raise e # re-raise the error to handle it outside
From 1479ea36a4a1e459318b5a42444aefcacc9112f6 Mon Sep 17 00:00:00 2001
From: TSC <112517630+LucianoCirino@users.noreply.github.com>
Date: Sun, 10 Sep 2023 17:35:08 -0500
Subject: [PATCH 09/14] updated when to use embedded
---
tsc_utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tsc_utils.py b/tsc_utils.py
index 94baacd..ef80f7a 100644
--- a/tsc_utils.py
+++ b/tsc_utils.py
@@ -484,7 +484,7 @@ def install_packages(my_dir):
embedded_python_exe = os.path.abspath(os.path.join(my_dir, '..', '..', '..', 'python_embeded', 'python.exe'))
# If embedded_python_exe exists, target the installations. Otherwise, go untargeted.
- use_embedded = os.path.exists(embedded_python_exe)
+ use_embedded = os.path.exists(embedded_python_exe) and embedded_python_exe == sys.executable
# Load packages from requirements.txt
with open(os.path.join(my_dir, 'requirements.txt'), 'r') as f:
From 9634646b40c5b997e36f32ee20eed92d35cb8192 Mon Sep 17 00:00:00 2001
From: TSC <112517630+LucianoCirino@users.noreply.github.com>
Date: Mon, 11 Sep 2023 18:17:34 -0500
Subject: [PATCH 10/14] Improve Auto Package Installation
- Added a timeout of 7 seconds to the package installation process using subprocess.check_call. If any package installation exceeds this duration, it's terminated, and a timeout message is displayed.
- Improved error handling during package installation. If a package fails to install, the specific reason is now displayed to the user.
Split the error message into two lines for better readability: one line indicates the failure, and the next line provides the specific error message.
- Instead of relying solely on pip freeze to determine installed packages, added a function to check if a package is installed by attempting to import it. This provides a more direct and reliable way to verify if a package is available for use.
- Removed the use of subprocess.getoutput which was causing hangs for some users and replaced it with subprocess.run.
Streamlined the logic to determine whether to use the embedded Python executable or the system's Python for installations.
---
tsc_utils.py | 56 +++++++++++++++++++++++-----------------------------
1 file changed, 25 insertions(+), 31 deletions(-)
diff --git a/tsc_utils.py b/tsc_utils.py
index ef80f7a..87d8777 100644
--- a/tsc_utils.py
+++ b/tsc_utils.py
@@ -472,11 +472,20 @@ def global_preview_method():
#-----------------------------------------------------------------------------------------------------------------------
# Auto install Efficiency Nodes Python package dependencies
import subprocess
-# Note: This auto-installer installs packages listed in the requirements.txt.
+# Note: This auto-installer attempts to import packages listed in the requirements.txt.
+# If the import fails, indicating the package isn't installed, the installer proceeds to install the package.
# It first checks if python.exe exists inside the ...\ComfyUI_windows_portable\python_embeded directory.
# If python.exe is found in that location, it will use this embedded Python version for the installation.
-# Otherwise, it uses the Python interpreter that's currently executing the script (via sys.executable) to attempt a general pip install of the packages.
-# If any errors occur during installation, the user is directed to manually install the required packages.
+# Otherwise, it uses the Python interpreter that's currently executing the script (via sys.executable)
+# to attempt a general pip install of the packages. If any errors occur during installation, an error message is
+# printed with the reason for the failure, and the user is directed to manually install the required packages.
+
+def is_package_installed(pkg_name):
+ try:
+ __import__(pkg_name)
+ return True
+ except ImportError:
+ return False
def install_packages(my_dir):
# Compute path to the target site-packages
@@ -490,41 +499,26 @@ def install_packages(my_dir):
with open(os.path.join(my_dir, 'requirements.txt'), 'r') as f:
required_packages = [line.strip() for line in f if line.strip()]
- try:
- installed_packages = packages(embedded_python_exe if use_embedded else None, versions=False)
-
- for pkg in required_packages:
- if pkg not in installed_packages:
- printout = f"Installing required package '{pkg}'..."
- print(f"{message('Efficiency Nodes:')} {printout}", end='', flush=True)
+ for pkg in required_packages:
+ if not is_package_installed(pkg):
+ printout = f"Installing required package '{pkg}'..."
+ print(f"{message('Efficiency Nodes:')} {printout}", end='', flush=True)
+ try:
if use_embedded: # Targeted installation
subprocess.check_call([embedded_python_exe, '-m', 'pip', 'install', pkg, '--target=' + target_dir,
'--no-warn-script-location', '--disable-pip-version-check'],
- stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
+ stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, timeout=7)
else: # Untargeted installation
subprocess.check_call([sys.executable, "-m", "pip", 'install', pkg],
- stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
-
- print(f"\r{message('Efficiency Nodes:')} {printout}{success('Installed!')}", flush=True)
-
- except Exception as e: # This catches all exceptions derived from the base Exception class
- print_general_error_message()
-
-def packages(python_exe=None, versions=False):
- try:
- if python_exe:
- return [(r.decode().split('==')[0] if not versions else r.decode()) for r in
- subprocess.check_output([python_exe, '-m', 'pip', 'freeze']).split()]
- else:
- return [(r.split('==')[0] if not versions else r) for r in
- subprocess.getoutput([sys.executable, "-m", "pip", "freeze"]).splitlines()]
- except subprocess.CalledProcessError as e:
- raise e # re-raise the error to handle it outside
-
+ stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, timeout=7)
+ print(f"\r{message('Efficiency Nodes:')} {printout}{success(' Installed!')}", flush=True)
+ except Exception as e:
+ print(f"\r{message('Efficiency Nodes:')} {printout}{error(' Failed!')}", flush=True)
+ print(f"{warning(str(e))}")
+
def print_general_error_message():
- print(
- f"\r{message('Efficiency Nodes:')} An unexpected error occurred during the package installation process. {error('Failed!')}")
+ print(f"{message('Efficiency Nodes:')} An unexpected error occurred during the package installation process. {error('Failed!')}")
print(warning("Please try manually installing the required packages from the requirements.txt file."))
# Install missing packages
From 74b4cfa101e2a3c39dcb7a7e35f9652546b7c6aa Mon Sep 17 00:00:00 2001
From: "Dr.Lt.Data"
Date: Wed, 13 Sep 2023 13:43:30 +0900
Subject: [PATCH 11/14] fix: XY Plot accumulation bug fix about XY Capsule
---
efficiency_nodes.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/efficiency_nodes.py b/efficiency_nodes.py
index e2eb81c..00d39b3 100644
--- a/efficiency_nodes.py
+++ b/efficiency_nodes.py
@@ -1518,10 +1518,11 @@ class TSC_KSampler:
elif X_type != "Nothing" and Y_type != "Nothing":
for Y_index, Y in enumerate(Y_value):
-
- if Y_type == "XY_Capsule" and X_type == "XY_Capsule":
+ if Y_type == "XY_Capsule" or X_type == "XY_Capsule":
model, clip, refiner_model, refiner_clip = \
clone_or_none(original_model, original_clip, original_refiner_model, original_refiner_clip)
+
+ if Y_type == "XY_Capsule" and X_type == "XY_Capsule":
Y.set_x_capsule(X)
# Define Y parameters and generate labels
From 820fe464bd3e18b7cd58190bf1b0b7adc951a7c7 Mon Sep 17 00:00:00 2001
From: Edgar
Date: Mon, 18 Sep 2023 16:31:07 +0200
Subject: [PATCH 12/14] Fixed start_at_step missing underscores
---
efficiency_nodes.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/efficiency_nodes.py b/efficiency_nodes.py
index 00d39b3..881060b 100644
--- a/efficiency_nodes.py
+++ b/efficiency_nodes.py
@@ -2513,7 +2513,7 @@ class TSC_XYplot_Steps:
xy_type = "Steps"
xy_first = first_step
xy_last = last_step
- elif target_parameter == "start at step":
+ elif target_parameter == "start_at_step":
xy_type = "StartStep"
xy_first = first_start_step
xy_last = last_start_step
From 7756c44639d50b3e217c6e22a3db79d46ec2e680 Mon Sep 17 00:00:00 2001
From: TSC <112517630+LucianoCirino@users.noreply.github.com>
Date: Mon, 18 Sep 2023 21:17:12 -0500
Subject: [PATCH 13/14] XY Input LoRA Stacks Fix
Fixed issue where LoRA Stacks where not properly being swapped between generations
---
efficiency_nodes.py | 39 ++++++++++++++++++++++++---------------
1 file changed, 24 insertions(+), 15 deletions(-)
diff --git a/efficiency_nodes.py b/efficiency_nodes.py
index 881060b..be5d20b 100644
--- a/efficiency_nodes.py
+++ b/efficiency_nodes.py
@@ -852,7 +852,7 @@ class TSC_KSampler:
else (os.path.basename(v[0]), v[1]) if v[2] is None
else (os.path.basename(v[0]),) + v[1:] for v in value]
- elif type_ == "LoRA" and isinstance(value, list):
+ elif (type_ == "LoRA" or type_ == "LoRA Stacks") and isinstance(value, list):
# Return only the first Tuple of each inner array
return [[(os.path.basename(v[0][0]),) + v[0][1:], "..."] if len(v) > 1
else [(os.path.basename(v[0][0]),) + v[0][1:]] for v in value]
@@ -953,6 +953,7 @@ class TSC_KSampler:
"Checkpoint",
"Refiner",
"LoRA",
+ "LoRA Stacks",
"VAE",
]
conditioners = {
@@ -997,6 +998,9 @@ class TSC_KSampler:
# Create a list of tuples with types and values
type_value_pairs = [(X_type, X_value.copy()), (Y_type, Y_value.copy())]
+ # Replace "LoRA Stacks" with "LoRA"
+ type_value_pairs = [('LoRA' if t == 'LoRA Stacks' else t, v) for t, v in type_value_pairs]
+
# Iterate over type-value pairs
for t, v in type_value_pairs:
if t in dict_map:
@@ -1202,7 +1206,7 @@ class TSC_KSampler:
text = f"RefClipSkip ({refiner_clip_skip[0]})"
elif "LoRA" in var_type:
- if not lora_stack:
+ if not lora_stack or var_type == "LoRA Stacks":
lora_stack = var.copy()
else:
# Updating the first tuple of lora_stack
@@ -1212,7 +1216,7 @@ class TSC_KSampler:
lora_name, lora_model_wt, lora_clip_wt = lora_stack[0]
lora_filename = os.path.splitext(os.path.basename(lora_name))[0]
- if var_type == "LoRA":
+ if var_type == "LoRA" or var_type == "LoRA Stacks":
if len(lora_stack) == 1:
lora_model_wt = format(float(lora_model_wt), ".2f").rstrip('0').rstrip('.')
lora_clip_wt = format(float(lora_clip_wt), ".2f").rstrip('0').rstrip('.')
@@ -1335,7 +1339,7 @@ class TSC_KSampler:
# Note: Index is held at 0 when Y_type == "Nothing"
# Load Checkpoint if required. If Y_type is LoRA, required models will be loaded by load_lora func.
- if (X_type == "Checkpoint" and index == 0 and Y_type != "LoRA"):
+ if (X_type == "Checkpoint" and index == 0 and Y_type not in ("LoRA", "LoRA Stacks")):
if lora_stack is None:
model, clip, _ = load_checkpoint(ckpt_name, xyplot_id, cache=cache[1])
else: # Load Efficient Loader LoRA
@@ -1344,11 +1348,11 @@ class TSC_KSampler:
encode = True
# Load LoRA if required
- elif (X_type == "LoRA" and index == 0):
+ elif (X_type in ("LoRA", "LoRA Stacks") and index == 0):
# Don't cache Checkpoints
model, clip = load_lora(lora_stack, ckpt_name, xyplot_id, cache=cache[2])
encode = True
- elif Y_type == "LoRA": # X_type must be Checkpoint, so cache those as defined
+ elif Y_type in ("LoRA", "LoRA Stacks"): # X_type must be Checkpoint, so cache those as defined
model, clip = load_lora(lora_stack, ckpt_name, xyplot_id,
cache=None, ckpt_cache=cache[1])
encode = True
@@ -1668,7 +1672,7 @@ class TSC_KSampler:
lora_name = lora_wt = lora_model_str = lora_clip_str = None
# Check for all possible LoRA types
- lora_types = ["LoRA", "LoRA Batch", "LoRA Wt", "LoRA MStr", "LoRA CStr"]
+ lora_types = ["LoRA", "LoRA Stacks", "LoRA Batch", "LoRA Wt", "LoRA MStr", "LoRA CStr"]
if X_type not in lora_types and Y_type not in lora_types:
if lora_stack:
@@ -1681,7 +1685,7 @@ class TSC_KSampler:
else:
if X_type in lora_types:
value = get_lora_sublist_name(X_type, X_value)
- if X_type == "LoRA":
+ if X_type in ("LoRA", "LoRA Stacks"):
lora_name = value
lora_model_str = None
lora_clip_str = None
@@ -1703,7 +1707,7 @@ class TSC_KSampler:
if Y_type in lora_types:
value = get_lora_sublist_name(Y_type, Y_value)
- if Y_type == "LoRA":
+ if Y_type in ("LoRA", "LoRA Stacks"):
lora_name = value
lora_model_str = None
lora_clip_str = None
@@ -1726,13 +1730,13 @@ class TSC_KSampler:
return lora_name, lora_wt, lora_model_str, lora_clip_str
def get_lora_sublist_name(lora_type, lora_value):
- if lora_type == "LoRA" or lora_type == "LoRA Batch":
+ if lora_type in ("LoRA", "LoRA Batch", "LoRA Stacks"):
formatted_sublists = []
for sublist in lora_value:
formatted_entries = []
for x in sublist:
base_name = os.path.splitext(os.path.basename(str(x[0])))[0]
- formatted_str = f"{base_name}({round(x[1], 3)},{round(x[2], 3)})" if lora_type == "LoRA" else f"{base_name}"
+ formatted_str = f"{base_name}({round(x[1], 3)},{round(x[2], 3)})" if lora_type in ("LoRA", "LoRA Stacks") else f"{base_name}"
formatted_entries.append(formatted_str)
formatted_sublists.append(f"{', '.join(formatted_entries)}")
return "\n ".join(formatted_sublists)
@@ -2375,7 +2379,7 @@ class TSC_XYplot:
# Check that dependencies are connected for specific plot types
encode_types = {
"Checkpoint", "Refiner",
- "LoRA", "LoRA Batch", "LoRA Wt", "LoRA MStr", "LoRA CStr",
+ "LoRA", "LoRA Stacks", "LoRA Batch", "LoRA Wt", "LoRA MStr", "LoRA CStr",
"Positive Prompt S/R", "Negative Prompt S/R",
"AScore+", "AScore-",
"Clip Skip", "Clip Skip (Refiner)",
@@ -2391,8 +2395,13 @@ class TSC_XYplot:
# Check if both X_type and Y_type are special lora_types
lora_types = {"LoRA Batch", "LoRA Wt", "LoRA MStr", "LoRA CStr"}
if (X_type in lora_types and Y_type not in lora_types) or (Y_type in lora_types and X_type not in lora_types):
- print(
- f"{error('XY Plot Error:')} Both X and Y must be connected to use the 'LoRA Plot' node.")
+ print(f"{error('XY Plot Error:')} Both X and Y must be connected to use the 'LoRA Plot' node.")
+ return (None,)
+
+ # Do not allow LoRA and LoRA Stacks
+ lora_types = {"LoRA", "LoRA Stacks"}
+ if (X_type in lora_types and Y_type in lora_types):
+ print(f"{error('XY Plot Error:')} X and Y input types must be different.")
return (None,)
# Clean Schedulers from Sampler data (if other type is Scheduler)
@@ -3139,7 +3148,7 @@ class TSC_XYplot_LoRA_Stacks:
CATEGORY = "Efficiency Nodes/XY Inputs"
def xy_value(self, node_state, lora_stack_1=None, lora_stack_2=None, lora_stack_3=None, lora_stack_4=None, lora_stack_5=None):
- xy_type = "LoRA"
+ xy_type = "LoRA Stacks"
xy_value = [stack for stack in [lora_stack_1, lora_stack_2, lora_stack_3, lora_stack_4, lora_stack_5] if stack is not None]
if not xy_value or not any(xy_value) or node_state == "Disabled":
return (None,)
From 43d265faf4e364d0a53a24a2deb183c23cf61e16 Mon Sep 17 00:00:00 2001
From: TSC <112517630+LucianoCirino@users.noreply.github.com>
Date: Mon, 18 Sep 2023 21:28:10 -0500
Subject: [PATCH 14/14] Few more I missed
---
efficiency_nodes.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/efficiency_nodes.py b/efficiency_nodes.py
index be5d20b..0ef9eaf 100644
--- a/efficiency_nodes.py
+++ b/efficiency_nodes.py
@@ -1043,7 +1043,7 @@ class TSC_KSampler:
elif X_type == "Refiner":
ckpt_dict = []
lora_dict = []
- elif X_type == "LoRA":
+ elif X_type in ("LoRA", "LoRA Stacks"):
ckpt_dict = []
refn_dict = []
@@ -1572,7 +1572,7 @@ class TSC_KSampler:
clear_cache_by_exception(xyplot_id, lora_dict=[], refn_dict=[])
elif X_type == "Refiner":
clear_cache_by_exception(xyplot_id, ckpt_dict=[], lora_dict=[])
- elif X_type == "LoRA":
+ elif X_type in ("LoRA", "LoRA Stacks"):
clear_cache_by_exception(xyplot_id, ckpt_dict=[], refn_dict=[])
# __________________________________________________________________________________________________________