diff --git a/README.en.md b/README.en.md
index c4adbf4..8382b84 100644
--- a/README.en.md
+++ b/README.en.md
@@ -29,7 +29,14 @@ After installing the node package, the UI interface will be automatically switch
## Changelog
-**v1.0.6 (2024-02-16)**
+**v1.0.7 (2024-02-18)**
+
+- Added `easy cascadeLoader` - stable cascade Loader
+- Added `easy preSamplingCascade` - stable cascade kSampler for stage-c
+
+[SC Example](https://github.com/yolain/ComfyUI-Easy-Use?tab=readme-ov-file#StableCascade)
+
+**v1.0.6**
- Added `easy XYInputs: Checkpoint`
- Added `easy XYInputs: Lora`
@@ -209,6 +216,9 @@ Disclaimer: Opened source was not easy. I have a lot of respect for the contribu
+### StableCascade
+
+
## Credits
diff --git a/README.md b/README.md
index 3c578e5..ba62fc2 100644
--- a/README.md
+++ b/README.md
@@ -37,6 +37,15 @@
## 更新日志
+**v1.0.7 (2024-02-18)**
+
+- 增加 `easy cascadeLoader` - stable cascade 加载器
+- 增加 `easy preSamplingCascade` - stabled cascade stage C采样
+
+[SC示例](https://github.com/yolain/ComfyUI-Easy-Use?tab=readme-ov-file#StableCascade)
+目前还未支持Controlnet
+
+
**v1.0.6 (2024-02-16)**
- 增加 `easy XYInputs: Checkpoint`
@@ -217,6 +226,10 @@
+### StableCascade
+
+
+
## Credits
[ComfyUI](https://github.com/comfyanonymous/ComfyUI) - 功能强大且模块化的Stable Diffusion GUI
diff --git a/__init__.py b/__init__.py
index efe2bab..0661d80 100644
--- a/__init__.py
+++ b/__init__.py
@@ -87,4 +87,4 @@ WEB_DIRECTORY = "./web"
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', "WEB_DIRECTORY"]
-print('\033[34mComfy-Easy-Use (v1.0.6): \033[92mLoaded\033[0m')
\ No newline at end of file
+print('\033[34mComfy-Easy-Use (v1.0.7): \033[92mLoaded\033[0m')
\ No newline at end of file
diff --git a/py/easyNodes.py b/py/easyNodes.py
index 1126fbf..8b98dd3 100644
--- a/py/easyNodes.py
+++ b/py/easyNodes.py
@@ -28,7 +28,7 @@ from typing import Dict, List, Optional, Tuple, Union, Any
from .adv_encode import advanced_encode, advanced_encode_XL
from server import PromptServer
-from nodes import VAELoader, MAX_RESOLUTION, RepeatLatentBatch, NODE_CLASS_MAPPINGS as ALL_NODE_CLASS_MAPPINGS, ConditioningSetMask, ConditioningConcat, PreviewImage, SaveImage
+from nodes import VAELoader, MAX_RESOLUTION, RepeatLatentBatch, NODE_CLASS_MAPPINGS as ALL_NODE_CLASS_MAPPINGS, ConditioningSetMask, ConditioningConcat, PreviewImage, SaveImage, common_ksampler
from comfy_extras.nodes_mask import LatentCompositeMasked
from .config import BASE_RESOLUTIONS, RESOURCES_DIR, INPAINT_DIR, FOOOCUS_STYLES_DIR, FOOOCUS_INPAINT_HEAD, FOOOCUS_INPAINT_PATCH
from .log import log_node_info, log_node_error, log_node_warn, log_node_success
@@ -39,6 +39,7 @@ class easyLoader:
def __init__(self):
self.loaded_objects = {
"ckpt": defaultdict(tuple), # {ckpt_name: (model, ...)}
+ "unet": defaultdict(tuple),
"clip": defaultdict(tuple),
"clip_vision": defaultdict(tuple),
"bvae": defaultdict(tuple),
@@ -92,6 +93,8 @@ class easyLoader:
def update_loaded_objects(self, prompt):
desired_ckpt_names = set()
+ desired_unet_names = set()
+ desired_clip_names = set()
desired_vae_names = set()
desired_lora_names = set()
desired_lora_settings = set()
@@ -111,6 +114,12 @@ class easyLoader:
desired_ckpt_names.add(self.get_input_value(entry, "ckpt_name"))
desired_vae_names.add(self.get_input_value(entry, "vae_name"))
+ elif class_type == "easy cascadeLoader":
+ desired_unet_names.add(self.get_input_value(entry, "stage_c"))
+ desired_unet_names.add(self.get_input_value(entry, "stage_b"))
+ desired_clip_names.add(self.get_input_value(entry, "clip_name"))
+ desired_vae_names.add(self.get_input_value(entry, "stage_a"))
+
elif class_type == "easy XYInputs: ModelMergeBlocks":
desired_ckpt_names.add(self.get_input_value(entry, "ckpt_name_1"))
desired_ckpt_names.add(self.get_input_value(entry, "ckpt_name_2"))
@@ -118,9 +127,16 @@ class easyLoader:
if vae_use != 'Use Model 1' and vae_use != 'Use Model 2':
desired_vae_names.add(vae_use)
- object_types = ["ckpt", "clip", "bvae", "vae", "lora"]
+ object_types = ["ckpt", "unet", "clip", "bvae", "vae", "lora"]
for object_type in object_types:
- desired_names = desired_ckpt_names if object_type in ["ckpt", "clip", "bvae"] else desired_vae_names if object_type == "vae" else desired_lora_names
+ if object_type == 'unet':
+ desired_names = desired_unet_names
+ elif object_type in ["ckpt", "bvae"]:
+ desired_names = desired_ckpt_names
+ elif object_type == "vae":
+ desired_names = desired_vae_names
+ else:
+ desired_names = desired_lora_names
self.clear_unused_objects(desired_names, object_type)
def add_to_cache(self, obj_type, key, value):
@@ -225,6 +241,30 @@ class easyLoader:
return loaded_vae
+ def load_unet(self, unet_name):
+ if unet_name in self.loaded_objects["unet"]:
+ return self.loaded_objects["unet"][unet_name][0]
+
+ unet_path = folder_paths.get_full_path("unet", unet_name)
+ model = comfy.sd.load_unet(unet_path)
+ self.add_to_cache("unet", unet_name, model)
+ self.eviction_based_on_memory()
+
+ return model
+
+ def load_clip(self, clip_name, type='stable_diffusion'):
+ if type == 'stable_diffusion':
+ clip_type = comfy.sd.CLIPType.STABLE_DIFFUSION
+ else:
+ clip_type = comfy.sd.CLIPType.STABLE_CASCADE
+ clip_path = folder_paths.get_full_path("clip", clip_name)
+ load_clip = comfy.sd.load_clip(ckpt_paths=[clip_path],
+ embedding_directory=folder_paths.get_folder_paths("embeddings"), clip_type=clip_type)
+ self.add_to_cache("clip", clip_name, load_clip)
+ self.eviction_based_on_memory()
+
+ return load_clip
+
def load_lora(self, lora_name, model, clip, strength_model, strength_clip):
model_hash = str(model)[44:-1]
clip_hash = str(clip)[25:-1]
@@ -2062,6 +2102,177 @@ class comfyLoader:
my_unique_id
)
+
+# stable Cascade
+class cascadeLoader:
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(s):
+ resolution_strings = [f"{width} x {height}" for width, height in BASE_RESOLUTIONS]
+
+ return {"required": {
+ "stage_c": (folder_paths.get_filename_list("unet"),),
+ "stage_b": (folder_paths.get_filename_list("unet"),),
+ "stage_a": (folder_paths.get_filename_list("vae"),),
+ "clip_name": (["None"] + folder_paths.get_filename_list("clip"),),
+
+ "resolution": (resolution_strings, {"default": "1024 x 1024"}),
+ "empty_latent_width": ("INT", {"default": 1024, "min": 16, "max": MAX_RESOLUTION, "step": 8}),
+ "empty_latent_height": ("INT", {"default": 1024, "min": 16, "max": MAX_RESOLUTION, "step": 8}),
+ "compression": ("INT", {"default": 42, "min": 32, "max": 64, "step": 1}),
+
+ "positive": ("STRING", {"default": "Positive", "multiline": True}),
+ "negative": ("STRING", {"default": "", "multiline": True}),
+
+ "batch_size": ("INT", {"default": 1, "min": 1, "max": 64}),
+ },
+ "optional": {},
+ "hidden": {"prompt": "PROMPT", "my_unique_id": "UNIQUE_ID"}
+ }
+
+ RETURN_TYPES = ("PIPE_LINE", "MODEL", "MODEL", "VAE")
+ RETURN_NAMES = ("pipe", "model_c", "model_b", "vae")
+
+ FUNCTION = "adv_pipeloader"
+ CATEGORY = "EasyUse/Loaders"
+
+ def adv_pipeloader(self, stage_c, stage_b, stage_a, clip_name,
+ resolution, empty_latent_width, empty_latent_height, compression,
+ positive, negative, batch_size, prompt=None,
+ my_unique_id=None):
+
+
+ vae: VAE | None = None
+ model_c: ModelPatcher | None = None
+ model_b: ModelPatcher | None = None
+ clip: CLIP | None = None
+ can_load_lora = True
+ pipe_lora_stack = []
+
+ # resolution
+ if resolution != "自定义 x 自定义":
+ try:
+ width, height = map(int, resolution.split(' x '))
+ empty_latent_width = width
+ empty_latent_height = height
+ except ValueError:
+ raise ValueError("Invalid base_resolution format.")
+
+ # Create Empty Latent
+ c_latent = torch.zeros([batch_size, 16, empty_latent_height // compression, empty_latent_width // compression])
+ b_latent = torch.zeros([batch_size, 4, empty_latent_height // 4, empty_latent_width // 4])
+
+ samples = ({"samples": c_latent},{"samples": b_latent})
+
+ # Clean models from loaded_objects
+ easyCache.update_loaded_objects(prompt)
+
+ # Load unet
+ model_c = easyCache.load_unet(stage_c)
+ model_b = easyCache.load_unet(stage_b)
+ model = (model_c, model_b)
+
+ # Load clip
+ clip = easyCache.load_clip(clip_name, "stable_cascade")
+
+ # clipped = clip.clone()
+ # if clip_skip != 0 and can_load_lora:
+ # clipped.clip_layer(clip_skip)
+
+ # Load vae
+ vae = easyCache.load_vae(stage_a)
+
+ # 判断是否连接 styles selector
+ is_positive_linked_styles_selector = False
+ inputs_positive_values = prompt[my_unique_id]['inputs']['positive'] if "positive" in prompt[my_unique_id][
+ 'inputs'] else None
+ if type(inputs_positive_values) == list and inputs_positive_values != 'undefined' and inputs_positive_values[0]:
+ is_positive_linked_styles_selector = True if prompt[inputs_positive_values[0]] and \
+ prompt[inputs_positive_values[0]][
+ 'class_type'] == 'easy stylesSelector' else False
+ is_negative_linked_styles_selector = False
+ inputs_negative_values = prompt[my_unique_id]['inputs']['negative'] if "negative" in prompt[my_unique_id][
+ 'inputs'] else None
+ if type(inputs_negative_values) == list and inputs_negative_values != 'undefined' and inputs_negative_values[0]:
+ is_negative_linked_styles_selector = True if prompt[inputs_negative_values[0]] and \
+ prompt[inputs_negative_values[0]][
+ 'class_type'] == 'easy stylesSelector' else False
+
+ log_node_warn("正在处理提示词...")
+ positive_seed = find_wildcards_seed(my_unique_id, positive, prompt)
+ model_c, clip, positive, positive_decode, show_positive_prompt, pipe_lora_stack = process_with_loras(positive,
+ model_c, clip,
+ "Positive",
+ positive_seed,
+ can_load_lora,
+ pipe_lora_stack)
+ positive_wildcard_prompt = positive_decode if show_positive_prompt or is_positive_linked_styles_selector else ""
+ negative_seed = find_wildcards_seed(my_unique_id, negative, prompt)
+ model_c, clip, negative, negative_decode, show_negative_prompt, pipe_lora_stack = process_with_loras(negative,
+ model_c, clip,
+ "Negative",
+ negative_seed,
+ can_load_lora,
+ pipe_lora_stack)
+ negative_wildcard_prompt = negative_decode if show_negative_prompt or is_negative_linked_styles_selector else ""
+
+ tokens = clip.tokenize(positive)
+ cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True)
+ positive_embeddings_final = [[cond, {"pooled_output": pooled}]]
+
+ tokens = clip.tokenize(negative)
+ cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True)
+ negative_embeddings_final = [[cond, {"pooled_output": pooled}]]
+
+ image = easySampler.pil2tensor(Image.new('RGB', (1, 1), (0, 0, 0)))
+
+ log_node_warn("处理结束...")
+ pipe = {
+ "model": model,
+ "positive": positive_embeddings_final,
+ "negative": negative_embeddings_final,
+ "vae": vae,
+ "clip": clip,
+
+ "samples": samples,
+ "images": image,
+ "seed": 0,
+
+ "loader_settings": {
+ "vae_name": stage_a,
+
+ "lora_stack": pipe_lora_stack,
+
+ "refiner_ckpt_name": None,
+ "refiner_vae_name": None,
+ "refiner_lora_name": None,
+ "refiner_lora_model_strength": None,
+ "refiner_lora_clip_strength": None,
+
+ "positive": positive,
+ "positive_l": None,
+ "positive_g": None,
+ "positive_token_normalization": 'none',
+ "positive_weight_interpretation": 'comfy',
+ "positive_balance": None,
+ "negative": negative,
+ "negative_l": None,
+ "negative_g": None,
+ "negative_token_normalization": 'none',
+ "negative_weight_interpretation": 'comfy',
+ "negative_balance": None,
+ "empty_latent_width": empty_latent_width,
+ "empty_latent_height": empty_latent_height,
+ "batch_size": batch_size,
+ "seed": 0,
+ "empty_samples": samples, }
+ }
+
+ return {"ui": {"positive": positive_wildcard_prompt, "negative": negative_wildcard_prompt},
+ "result": (pipe, model_c, model_b, vae)}
+
# Zero123简易加载器 (3D)
try:
from comfy_extras.nodes_stable3d import camera_embeddings
@@ -2088,7 +2299,8 @@ class zero123Loader:
"elevation": ("FLOAT", {"default": 0.0, "min": -180.0, "max": 180.0}),
"azimuth": ("FLOAT", {"default": 0.0, "min": -180.0, "max": 180.0}),
},
- "hidden": {"prompt": "PROMPT"}, "my_unique_id": "UNIQUE_ID"}
+ "hidden": {"prompt": "PROMPT", "my_unique_id": "UNIQUE_ID"}
+ }
RETURN_TYPES = ("PIPE_LINE", "MODEL", "VAE")
RETURN_NAMES = ("pipe", "model", "vae")
@@ -2274,7 +2486,6 @@ class svdLoader:
return (pipe, model, vae)
-
# lora
class loraStackLoader:
def __init__(self):
@@ -2853,6 +3064,122 @@ class sdTurboSettings:
return {"ui": {"value": [seed_num]}, "result": (new_pipe,)}
+
+# cascade采样器
+class cascadeSettings:
+
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {"required":
+ {"pipe": ("PIPE_LINE",),
+ "steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
+ "cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0}),
+ "sampler_name": (comfy.samplers.KSampler.SAMPLERS,),
+ "scheduler": (comfy.samplers.KSampler.SCHEDULERS,),
+ "denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
+ "seed_num": ("INT", {"default": 0, "min": 0, "max": 1125899906842624}),
+ },
+
+ "optional": {
+ # "image_to_latent": ("IMAGE",),
+ # "latent": ("LATENT",)
+ },
+ "hidden":
+ {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO", "my_unique_id": "UNIQUE_ID"},
+ }
+
+ RETURN_TYPES = ("PIPE_LINE",)
+ RETURN_NAMES = ("pipe",)
+ OUTPUT_NODE = True
+
+ FUNCTION = "settings"
+ CATEGORY = "EasyUse/PreSampling"
+
+ def settings(self, pipe, steps, cfg, sampler_name, scheduler, denoise, seed_num, prompt=None, extra_pnginfo=None, my_unique_id=None):
+ # 图生图转换
+ vae = pipe["vae"]
+ batch_size = pipe["loader_settings"]["batch_size"] if "batch_size" in pipe["loader_settings"] else 1
+ # if image_to_latent is not None:
+ # samples = {"samples": vae.encode(image_to_latent)}
+ # samples = RepeatLatentBatch().repeat(samples, batch_size)[0]
+ # images = image_to_latent
+ # elif latent is not None:
+ # samples = RepeatLatentBatch().repeat(latent, batch_size)[0]
+ # images = pipe["images"]
+ # else:
+ samples = pipe["samples"][0]
+ images = pipe["images"]
+
+ # Clean loaded_objects
+ easyCache.update_loaded_objects(prompt)
+ samp_model = pipe["model"][0]
+ samp_positive = pipe["positive"]
+ samp_negative = pipe["negative"]
+ samp_samples = samples
+ samp_vae = pipe["vae"]
+ samp_clip = pipe["clip"]
+
+ samp_seed = seed_num if seed_num is not None else pipe['seed']
+
+ steps = steps if steps is not None else pipe['loader_settings']['steps']
+ start_step = pipe['loader_settings']['start_step'] if 'start_step' in pipe['loader_settings'] else 0
+ last_step = pipe['loader_settings']['last_step'] if 'last_step' in pipe['loader_settings'] else 10000
+ cfg = cfg if cfg is not None else pipe['loader_settings']['cfg']
+ sampler_name = sampler_name if sampler_name is not None else pipe['loader_settings']['sampler_name']
+ scheduler = scheduler if scheduler is not None else pipe['loader_settings']['scheduler']
+ denoise = denoise if denoise is not None else pipe['loader_settings']['denoise']
+ # 推理初始时间
+ start_time = int(time.time() * 1000)
+ # 开始推理
+ samp_samples = sampler.common_ksampler(samp_model, samp_seed, steps, cfg, sampler_name, scheduler,
+ samp_positive, samp_negative, samp_samples, denoise=denoise,
+ preview_latent=False, start_step=start_step,
+ last_step=last_step, force_full_denoise=False,
+ disable_noise=False)
+ # 推理结束时间
+ end_time = int(time.time() * 1000)
+ stage_c = samp_samples["samples"]
+
+ # zero_out
+ c1 = []
+ for t in samp_positive:
+ d = t[1].copy()
+ if "pooled_output" in d:
+ d["pooled_output"] = torch.zeros_like(d["pooled_output"])
+ n = [torch.zeros_like(t[0]), d]
+ c1.append(n)
+ # stage_b_conditioning
+ c2 = []
+ for t in c1:
+ d = t[1].copy()
+ d['stable_cascade_prior'] = stage_c
+ n = [t[0], d]
+ c2.append(n)
+
+ new_pipe = {
+ "model": pipe['model'][1],
+ "positive": c2,
+ "negative": c1,
+ "vae": pipe['vae'],
+ "clip": pipe['clip'],
+
+ "samples": pipe["samples"][1],
+ "images": pipe["images"],
+ "seed": seed_num,
+
+ "loader_settings": {
+ **pipe["loader_settings"]
+ }
+ }
+
+ del pipe
+
+ return {"ui": {"value": [seed_num]}, "result": (new_pipe,)}
+
+
# 预采样设置(动态CFG)
from .dynthres_core import DynThresh
class dynamicCFGSettings:
@@ -3113,7 +3440,6 @@ class samplerFull:
samp_samples = sampler.common_ksampler(samp_model, samp_seed, steps, cfg, sampler_name, scheduler, samp_positive, samp_negative, samp_samples, denoise=denoise, preview_latent=preview_latent, start_step=start_step, last_step=last_step, force_full_denoise=force_full_denoise, disable_noise=disable_noise)
# 推理结束时间
end_time = int(time.time() * 1000)
- # 解码图片
latent = samp_samples["samples"]
# 解码图片
@@ -3626,6 +3952,7 @@ class samplerSDTurbo:
"result": sampler.get_output(new_pipe, )}
+
class unsampler:
@classmethod
def INPUT_TYPES(s):
@@ -5367,6 +5694,7 @@ NODE_CLASS_MAPPINGS = {
"easy fullLoader": fullLoader,
"easy a1111Loader": a1111Loader,
"easy comfyLoader": comfyLoader,
+ "easy cascadeLoader": cascadeLoader,
"easy zero123Loader": zero123Loader,
"easy svdLoader": svdLoader,
"easy loraStack": loraStackLoader,
@@ -5383,6 +5711,7 @@ NODE_CLASS_MAPPINGS = {
"easy preSamplingAdvanced": samplerSettingsAdvanced,
"easy preSamplingSdTurbo": sdTurboSettings,
"easy preSamplingDynamicCFG": dynamicCFGSettings,
+ "easy preSamplingCascade": cascadeSettings,
# kSampler k采样器
"easy kSampler": samplerSimple,
"easy fullkSampler": samplerFull,
@@ -5438,6 +5767,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"easy fullLoader": "EasyLoader (Full)",
"easy a1111Loader": "EasyLoader (A1111)",
"easy comfyLoader": "EasyLoader (Comfy)",
+ "easy cascadeLoader": "EasyLoader (Cascade)",
"easy zero123Loader": "EasyLoader (Zero123)",
"easy svdLoader": "EasyLoader (SVD)",
"easy loraStack": "EasyLoraStack",
@@ -5455,6 +5785,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"easy preSamplingAdvanced": "PreSampling (Advanced)",
"easy preSamplingSdTurbo": "PreSampling (SDTurbo)",
"easy preSamplingDynamicCFG": "PreSampling (DynamicCFG)",
+ "easy preSamplingCascade": "PreSampling (Cascade)",
# kSampler k采样器
"easy kSampler": "EasyKSampler",
"easy fullkSampler": "EasyKSampler (Full)",
diff --git a/py/server.py b/py/server.py
index 92ffec6..8a619b6 100644
--- a/py/server.py
+++ b/py/server.py
@@ -132,7 +132,7 @@ def prompt_seed_update(json_data):
if 'class_type' not in v:
continue
cls = v['class_type']
- if cls == "easy wildcards" or cls == "easy preSampling" or cls == "easy preSamplingAdvanced" or cls == "easy preSamplingSdTurbo" or cls == "easy preSamplingDynamicCFG" or cls == "easy fullkSampler" or cls == 'easy seed' or cls == "easy latentNoisy":
+ if cls == "easy wildcards" or cls == "easy preSampling" or cls == "easy preSamplingAdvanced" or cls == "easy preSamplingSdTurbo" or cls == "easy preSamplingDynamicCFG" or cls == "easy preSamplingCascade" or cls == "easy fullkSampler" or cls == 'easy seed' or cls == "easy latentNoisy":
extra_data = next((x for x in workflow["nodes"] if str(x["id"]) == k), None)
if extra_data is not None:
inputs = extra_data.get('inputs')
diff --git a/web/js/easy/easyDynamicWidgets.js b/web/js/easy/easyDynamicWidgets.js
index 7170f91..3348520 100644
--- a/web/js/easy/easyDynamicWidgets.js
+++ b/web/js/easy/easyDynamicWidgets.js
@@ -425,6 +425,7 @@ app.registerExtension({
case "easy fullLoader":
case "easy a1111Loader":
case "easy comfyLoader":
+ case "easy cascadeLoader":
case "easy svdLoader":
case "easy loraStack":
case "easy latentNoisy":
@@ -743,7 +744,7 @@ app.registerExtension({
};
}
- if (["easy seed", "easy latentNoisy", "easy wildcards", "easy preSampling", "easy preSamplingAdvanced", "easy preSamplingSdTurbo", "easy preSamplingDynamicCFG", "easy fullkSampler"].includes(nodeData.name)) {
+ if (["easy seed", "easy latentNoisy", "easy wildcards", "easy preSampling", "easy preSamplingAdvanced", "easy preSamplingSdTurbo", "easy preSamplingCascade", "easy preSamplingDynamicCFG", "easy fullkSampler"].includes(nodeData.name)) {
const onNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = async function () {
onNodeCreated ? onNodeCreated.apply(this, []) : undefined;