From e344c61837bee95fb5e521d86a2eac3da1dd8759 Mon Sep 17 00:00:00 2001 From: CHENTIANLE <141470236+IIs-fanta@users.noreply.github.com> Date: Mon, 15 Sep 2025 17:59:41 +0800 Subject: [PATCH 1/3] Add files via upload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新了节点配置,Power Lora Loader节点现在可以输出格式化的Lora信息文本。 The node configuration has been updated, and the Power Lora Loader node can now output formatted Lora information text. --- README.md | 8 +++ power_lora_loader.py | 115 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 power_lora_loader.py diff --git a/README.md b/README.md index 5c0f55e8..31bec7dd 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,14 @@ Note, you can right-click on a bunch of the rgthree-comfy nodes and select `🛟 > - from the properties, change the `Show Strengths` to choose between showing a single, simple > strength value (which will be used for both model and clip), or a more advanced view with > both model and clip strengths being modifiable. +> - Now includes an `lora_info_text` output port that provides a formatted string listing all enabled Loras with their filenames and strengths. +> Example output format: +> ``` +> FLUX_test1.safetensors: 0.83 +> FLUX_test2.safetensors: 0.46 +> FLUX_test3.safetensors: 0.74 +> FLUX_test4-000016.safetensors: 0.48 +> ``` > diff --git a/power_lora_loader.py b/power_lora_loader.py new file mode 100644 index 00000000..07d0e704 --- /dev/null +++ b/power_lora_loader.py @@ -0,0 +1,115 @@ +import asyncio +import folder_paths + +from typing import Union + +from nodes import LoraLoader +from .constants import get_category, get_name +from .power_prompt_utils import get_lora_by_filename +from .utils import FlexibleOptionalInputType, any_type +from .server.utils_info import get_model_info +from .log import log_node_warn + +NODE_NAME = get_name('Power Lora Loader') + + +class RgthreePowerLoraLoader: + """ The Power Lora Loader is a powerful, flexible node to add multiple loras to a model/clip.""" + + NAME = NODE_NAME + CATEGORY = get_category() + + @classmethod + def INPUT_TYPES(cls): # pylint: disable = invalid-name, missing-function-docstring + return { + "required": { + }, + # Since we will pass any number of loras in from the UI, this needs to always allow an + "optional": FlexibleOptionalInputType(type=any_type, data={ + "model": ("MODEL",), + "clip": ("CLIP",), + }), + "hidden": {}, + } + + RETURN_TYPES = ("MODEL", "CLIP", "STRING") + RETURN_NAMES = ("MODEL", "CLIP", "lora_info_text") + FUNCTION = "load_loras" + + def load_loras(self, model=None, clip=None, **kwargs): + """Loops over the provided loras in kwargs and applies valid ones.""" + lora_info_text = "" + lora_lines = [] + + for key, value in kwargs.items(): + key = key.upper() + if key.startswith('LORA_') and 'on' in value and 'lora' in value and 'strength' in value: + strength_model = value['strength'] + # If we just passed one strength value, then use it for both, if we passed a strengthTwo + # as well, then our `strength` will be for the model, and `strengthTwo` for clip. + strength_clip = value['strengthTwo'] if 'strengthTwo' in value else None + if clip is None: + if strength_clip is not None and strength_clip != 0: + log_node_warn(NODE_NAME, 'Recieved clip strength eventhough no clip supplied!') + strength_clip = 0 + else: + strength_clip = strength_clip if strength_clip is not None else strength_model + if value['on'] and (strength_model != 0 or strength_clip != 0): + lora = get_lora_by_filename(value['lora'], log_node=self.NAME) + if model is not None and lora is not None: + model, clip = LoraLoader().load_lora(model, clip, lora, strength_model, strength_clip) + + # 收集Lora信息用于输出文本 + lora_filename = value['lora'] + # 对于大多数情况,使用模型强度作为显示强度 + display_strength = strength_model + lora_lines.append(f"{lora_filename}: {display_strength:.2f}") + + # 将收集的Lora信息格式化为文本 + if lora_lines: + lora_info_text = "\n".join(lora_lines) + + return (model, clip, lora_info_text) + + @classmethod + def get_enabled_loras_from_prompt_node(cls, + prompt_node: dict) -> list[dict[str, Union[str, float]]]: + """Gets enabled loras of a node within a server prompt.""" + result = [] + for name, lora in prompt_node['inputs'].items(): + if name.startswith('lora_') and lora['on']: + lora_file = get_lora_by_filename(lora['lora'], log_node=cls.NAME) + if lora_file is not None: # Add the same safety check + lora_dict = { + 'name': lora['lora'], + 'strength': lora['strength'], + 'path': folder_paths.get_full_path("loras", lora_file) + } + if 'strengthTwo' in lora: + lora_dict['strength_clip'] = lora['strengthTwo'] + result.append(lora_dict) + return result + + @classmethod + def get_enabled_triggers_from_prompt_node(cls, prompt_node: dict, max_each: int = 1): + """Gets trigger words up to the max for enabled loras of a node within a server prompt.""" + loras = [l['name'] for l in cls.get_enabled_loras_from_prompt_node(prompt_node)] + trained_words = [] + for lora in loras: + info = asyncio.run(get_model_info(lora, 'loras')) + if not info or not info.keys(): + log_node_warn( + NODE_NAME, + f'No info found for lora {lora} when grabbing triggers. Have you generated an info file' + ' from the Power Lora Loader "Show Info" dialog?' + ) + continue + if 'trainedWords' not in info or not info['trainedWords']: + log_node_warn( + NODE_NAME, + f'No trained words for lora {lora} when grabbing triggers. Have you fetched data from' + 'civitai or manually added words?' + ) + continue + trained_words += [w for wi in info['trainedWords'][:max_each] if (wi and (w := wi['word']))] + return trained_words From 9c211a9989ceb22ff09e3da091eb1e5cccff9221 Mon Sep 17 00:00:00 2001 From: CHENTIANLE <141470236+IIs-fanta@users.noreply.github.com> Date: Mon, 15 Sep 2025 18:01:29 +0800 Subject: [PATCH 2/3] Add files via upload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新了节点配置,Power Lora Loader节点现在可以输出格式化的Lora信息文本。 The node configuration has been updated, and the Power Lora Loader node can now output formatted Lora information text. --- py/power_lora_loader.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/py/power_lora_loader.py b/py/power_lora_loader.py index 591862c7..07d0e704 100644 --- a/py/power_lora_loader.py +++ b/py/power_lora_loader.py @@ -32,12 +32,15 @@ def INPUT_TYPES(cls): # pylint: disable = invalid-name, missing-function-docstr "hidden": {}, } - RETURN_TYPES = ("MODEL", "CLIP") - RETURN_NAMES = ("MODEL", "CLIP") + RETURN_TYPES = ("MODEL", "CLIP", "STRING") + RETURN_NAMES = ("MODEL", "CLIP", "lora_info_text") FUNCTION = "load_loras" def load_loras(self, model=None, clip=None, **kwargs): """Loops over the provided loras in kwargs and applies valid ones.""" + lora_info_text = "" + lora_lines = [] + for key, value in kwargs.items(): key = key.upper() if key.startswith('LORA_') and 'on' in value and 'lora' in value and 'strength' in value: @@ -55,8 +58,18 @@ def load_loras(self, model=None, clip=None, **kwargs): lora = get_lora_by_filename(value['lora'], log_node=self.NAME) if model is not None and lora is not None: model, clip = LoraLoader().load_lora(model, clip, lora, strength_model, strength_clip) - - return (model, clip) + + # 收集Lora信息用于输出文本 + lora_filename = value['lora'] + # 对于大多数情况,使用模型强度作为显示强度 + display_strength = strength_model + lora_lines.append(f"{lora_filename}: {display_strength:.2f}") + + # 将收集的Lora信息格式化为文本 + if lora_lines: + lora_info_text = "\n".join(lora_lines) + + return (model, clip, lora_info_text) @classmethod def get_enabled_loras_from_prompt_node(cls, From c231948b45133e137272fec09ead828d5fd4b025 Mon Sep 17 00:00:00 2001 From: CHENTIANLE <141470236+IIs-fanta@users.noreply.github.com> Date: Mon, 15 Sep 2025 18:04:35 +0800 Subject: [PATCH 3/3] Delete power_lora_loader.py --- power_lora_loader.py | 115 ------------------------------------------- 1 file changed, 115 deletions(-) delete mode 100644 power_lora_loader.py diff --git a/power_lora_loader.py b/power_lora_loader.py deleted file mode 100644 index 07d0e704..00000000 --- a/power_lora_loader.py +++ /dev/null @@ -1,115 +0,0 @@ -import asyncio -import folder_paths - -from typing import Union - -from nodes import LoraLoader -from .constants import get_category, get_name -from .power_prompt_utils import get_lora_by_filename -from .utils import FlexibleOptionalInputType, any_type -from .server.utils_info import get_model_info -from .log import log_node_warn - -NODE_NAME = get_name('Power Lora Loader') - - -class RgthreePowerLoraLoader: - """ The Power Lora Loader is a powerful, flexible node to add multiple loras to a model/clip.""" - - NAME = NODE_NAME - CATEGORY = get_category() - - @classmethod - def INPUT_TYPES(cls): # pylint: disable = invalid-name, missing-function-docstring - return { - "required": { - }, - # Since we will pass any number of loras in from the UI, this needs to always allow an - "optional": FlexibleOptionalInputType(type=any_type, data={ - "model": ("MODEL",), - "clip": ("CLIP",), - }), - "hidden": {}, - } - - RETURN_TYPES = ("MODEL", "CLIP", "STRING") - RETURN_NAMES = ("MODEL", "CLIP", "lora_info_text") - FUNCTION = "load_loras" - - def load_loras(self, model=None, clip=None, **kwargs): - """Loops over the provided loras in kwargs and applies valid ones.""" - lora_info_text = "" - lora_lines = [] - - for key, value in kwargs.items(): - key = key.upper() - if key.startswith('LORA_') and 'on' in value and 'lora' in value and 'strength' in value: - strength_model = value['strength'] - # If we just passed one strength value, then use it for both, if we passed a strengthTwo - # as well, then our `strength` will be for the model, and `strengthTwo` for clip. - strength_clip = value['strengthTwo'] if 'strengthTwo' in value else None - if clip is None: - if strength_clip is not None and strength_clip != 0: - log_node_warn(NODE_NAME, 'Recieved clip strength eventhough no clip supplied!') - strength_clip = 0 - else: - strength_clip = strength_clip if strength_clip is not None else strength_model - if value['on'] and (strength_model != 0 or strength_clip != 0): - lora = get_lora_by_filename(value['lora'], log_node=self.NAME) - if model is not None and lora is not None: - model, clip = LoraLoader().load_lora(model, clip, lora, strength_model, strength_clip) - - # 收集Lora信息用于输出文本 - lora_filename = value['lora'] - # 对于大多数情况,使用模型强度作为显示强度 - display_strength = strength_model - lora_lines.append(f"{lora_filename}: {display_strength:.2f}") - - # 将收集的Lora信息格式化为文本 - if lora_lines: - lora_info_text = "\n".join(lora_lines) - - return (model, clip, lora_info_text) - - @classmethod - def get_enabled_loras_from_prompt_node(cls, - prompt_node: dict) -> list[dict[str, Union[str, float]]]: - """Gets enabled loras of a node within a server prompt.""" - result = [] - for name, lora in prompt_node['inputs'].items(): - if name.startswith('lora_') and lora['on']: - lora_file = get_lora_by_filename(lora['lora'], log_node=cls.NAME) - if lora_file is not None: # Add the same safety check - lora_dict = { - 'name': lora['lora'], - 'strength': lora['strength'], - 'path': folder_paths.get_full_path("loras", lora_file) - } - if 'strengthTwo' in lora: - lora_dict['strength_clip'] = lora['strengthTwo'] - result.append(lora_dict) - return result - - @classmethod - def get_enabled_triggers_from_prompt_node(cls, prompt_node: dict, max_each: int = 1): - """Gets trigger words up to the max for enabled loras of a node within a server prompt.""" - loras = [l['name'] for l in cls.get_enabled_loras_from_prompt_node(prompt_node)] - trained_words = [] - for lora in loras: - info = asyncio.run(get_model_info(lora, 'loras')) - if not info or not info.keys(): - log_node_warn( - NODE_NAME, - f'No info found for lora {lora} when grabbing triggers. Have you generated an info file' - ' from the Power Lora Loader "Show Info" dialog?' - ) - continue - if 'trainedWords' not in info or not info['trainedWords']: - log_node_warn( - NODE_NAME, - f'No trained words for lora {lora} when grabbing triggers. Have you fetched data from' - 'civitai or manually added words?' - ) - continue - trained_words += [w for wi in info['trainedWords'][:max_each] if (wi and (w := wi['word']))] - return trained_words