Added Load LoRA Tag
This commit is contained in:
@@ -38,7 +38,7 @@ It will attempt to use symlinks and junctions to prevent having to copy files an
|
||||
|
||||
All nodes Level Pixel:
|
||||
|
||||
<img width="1173" alt="level-pixel-nodes" src="https://github.com/user-attachments/assets/60623f84-b02a-4749-9e2c-4ab60431b383">
|
||||
<img width="1171" alt="level-pixel-nodes_2" src="https://github.com/user-attachments/assets/63b42605-1720-4a10-a54d-d2950d3d013f">
|
||||
|
||||
## LLM nodes
|
||||
|
||||
@@ -60,6 +60,14 @@ The core functionality is taken from [ComfyUI_VLM_nodes](https://github.com/goka
|
||||
|
||||
A more improved version of rembg nodes for ComfyUI with an extended list of models.
|
||||
|
||||
To use on GPU, at least CUDA 12.4 (Pytorch cu124) is required, so I recommend upgrading to newer versions of ComfyUI and Pytorch.
|
||||
If GPU still doesn't work, run:
|
||||
|
||||
```
|
||||
pip uninstall rembg
|
||||
pip install rembg[gpu]
|
||||
```
|
||||
|
||||
The core functionality is taken from [RemBG nodes for ComfyUI](https://github.com/Loewen-Hob/rembg-comfyui-node-better) and belongs to its authors.
|
||||
|
||||
## Autotagger
|
||||
@@ -78,6 +86,12 @@ Nodes are very convenient because you can use them to remove unnecessary tags by
|
||||
|
||||
The core functionality is taken from [comfyui_tag_fillter](https://github.com/sugarkwork/comfyui_tag_fillter) and belongs to its authors.
|
||||
|
||||
## Load LoRA Tag
|
||||
|
||||
LoRA loader from text in the style of Automatic1111 and Forge WebUI. For this version of loader, text output for errors when loading LoRA has been added as widget on node.
|
||||
|
||||
The core functionality is taken from [comfyui_lora_tag_loader](https://github.com/badjeff/comfyui_lora_tag_loader) and belongs to its authors.
|
||||
|
||||
## Model Unloader nodes
|
||||
|
||||
A node that automatically unloads all checkpoints from memory. It must be added to a sequential chain of nodes in the workflow. There are three versions of this node: Hard (complete unloading of all checkpoints from memory, except for GGUF (not supported yet)), Middle (the same as Hard, but in the future I plan to add widgets with the ability to select a mode), Soft (without unloading checkpoints from memory, just soft cleaning of memory from garbage).
|
||||
@@ -121,12 +135,14 @@ VLM nodes for ComfyUI/[ComfyUI_VLM_nodes](https://github.com/gokayfem/ComfyUI_VL
|
||||
|
||||
Tag Filter nodes for ComfyUI/[comfyui_tag_fillter](https://github.com/sugarkwork/comfyui_tag_fillter) - Best tag filter by category nodes for ComfyUI.
|
||||
|
||||
Load LoRA Tag node for ComfyUI/[comfyui_lora_tag_loader](https://github.com/badjeff/comfyui_lora_tag_loader) - Thanks to the author for this great node for LoRAs!
|
||||
|
||||
RemBG nodes for ComfyUI/[rembg-comfyui-node](https://github.com/Loewen-Hob/rembg-comfyui-node-better) - RemBG nodes for ComfyUI.
|
||||
|
||||
RemBG software package/[rembg](https://github.com/danielgatis/rembg) - Best software to remove background for any object in the picture.
|
||||
|
||||
# License
|
||||
|
||||
Copyright (c) 2024-present Level Pixel
|
||||
Copyright (c) 2024-present [Level Pixel](https://github.com/LevelPixel)
|
||||
|
||||
Licensed under Apache License
|
||||
|
||||
@@ -39,6 +39,7 @@ node_list = [
|
||||
"io.image_loaders_LP",
|
||||
"io.image_outputs_LP",
|
||||
"io.text_outputs_LP",
|
||||
"io.lora_tag_loader_LP",
|
||||
"llm.llm_LP",
|
||||
"tags.tags_utils_LP",
|
||||
"text.text_utils_LP",
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
from pathlib import Path
|
||||
import folder_paths
|
||||
import re
|
||||
|
||||
# Import ComfyUI files
|
||||
import comfy.sd
|
||||
import comfy.utils
|
||||
|
||||
class LoraTagLoader:
|
||||
def __init__(self):
|
||||
self.loaded_lora = None
|
||||
self.tag_pattern = r"\<[0-9a-zA-Z\:\_\-\.\s\/\(\)\\\\]+\>"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": { "model": ("MODEL",),
|
||||
"clip": ("CLIP", ),
|
||||
"text": ("STRING", {"multiline": True}),
|
||||
}}
|
||||
RETURN_TYPES = ("MODEL", "CLIP", "STRING")
|
||||
RETURN_NAMES = ("MODEL", "CLIP", "STRING")
|
||||
FUNCTION = "load_lora"
|
||||
OUTPUT_NODE = False
|
||||
|
||||
CATEGORY = "LevelPixel/IO"
|
||||
|
||||
def load_lora(self, model, clip, text):
|
||||
# print(f"\nLoraTagLoader input text: { text }")
|
||||
|
||||
founds = re.findall(self.tag_pattern, text)
|
||||
# print(f"\nfoound lora tags: { founds }")
|
||||
|
||||
if len(founds) < 1:
|
||||
return (model, clip, text)
|
||||
|
||||
model_lora = model
|
||||
clip_lora = clip
|
||||
|
||||
log = []
|
||||
log.append("")
|
||||
|
||||
lora_files = folder_paths.get_filename_list("loras")
|
||||
for f in founds:
|
||||
tag = f[1:-1]
|
||||
pak = tag.split(":")
|
||||
type = pak[0]
|
||||
if type != 'lora':
|
||||
continue
|
||||
name = None
|
||||
if len(pak) > 1 and len(pak[1]) > 0:
|
||||
name = pak[1]
|
||||
else:
|
||||
continue
|
||||
wModel = wClip = 0
|
||||
try:
|
||||
if len(pak) > 2 and len(pak[2]) > 0:
|
||||
wModel = float(pak[2])
|
||||
wClip = wModel
|
||||
if len(pak) > 3 and len(pak[3]) > 0:
|
||||
wClip = float(pak[3])
|
||||
except ValueError:
|
||||
continue
|
||||
if name == None:
|
||||
continue
|
||||
lora_name = None
|
||||
for lora_file in lora_files:
|
||||
if Path(lora_file).name.startswith(name) or lora_file.startswith(name):
|
||||
lora_name = lora_file
|
||||
break
|
||||
if lora_name == None:
|
||||
log[0] = log[0] + f"NOT found LoRA '{name}' \n"
|
||||
print(f"bypassed lora tag: { (type, name, wModel, wClip) } >> { lora_name }")
|
||||
continue
|
||||
print(f"detected lora tag: { (type, name, wModel, wClip) } >> { lora_name }")
|
||||
|
||||
lora_path = folder_paths.get_full_path("loras", lora_name)
|
||||
lora = None
|
||||
if self.loaded_lora is not None:
|
||||
if self.loaded_lora[0] == lora_path:
|
||||
lora = self.loaded_lora[1]
|
||||
else:
|
||||
temp = self.loaded_lora
|
||||
self.loaded_lora = None
|
||||
del temp
|
||||
|
||||
if lora is None:
|
||||
lora = comfy.utils.load_torch_file(lora_path, safe_load=True)
|
||||
self.loaded_lora = (lora_path, lora)
|
||||
|
||||
model_lora, clip_lora = comfy.sd.load_lora_for_models(model_lora, clip_lora, lora, wModel, wClip)
|
||||
|
||||
plain_prompt = re.sub(self.tag_pattern, "", text)
|
||||
return {"ui": {"log": log}, "result": (model_lora, clip_lora, plain_prompt)}
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LoraTagLoader|LP": LoraTagLoader,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LoraTagLoader|LP": "Load LoRA Tag [LP]",
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "comfyui_level_pixel"
|
||||
description = "Various nodes of the Level Pixel company. Includes convenient advanced nodes for working with images from folders; counting files in a folder; cleaning memory; tag filters. Model Unloader, LLM Unloader (GGUF unloaders), Free memory, Tag Filters, Tag Category Filters, Tag Choice Parser, File counter, Image Loader From Path (with counters), Image Remove Background based on RemBG, Autotagger."
|
||||
version = "1.0.7"
|
||||
version = "1.0.8"
|
||||
license = { file = "LICENSE" }
|
||||
dependencies = ["torch>=2.0.1", "torchvision>=0.15.2", "numpy", "matplotlib", "scikit-build-core>=0.10.7", "rembg>=2.0.59", "onnxruntime-gpu>=1.18.0", "onnxruntime>=1.20.0"]
|
||||
|
||||
|
||||
+1
-1
@@ -5,5 +5,5 @@ numpy
|
||||
matplotlib
|
||||
scikit-build-core>=0.10.7
|
||||
rembg>=2.0.59
|
||||
onnxruntime-gpu>=1.18.0
|
||||
onnxruntime-gpu>=1.20.0
|
||||
onnxruntime>=1.20.0
|
||||
@@ -80,7 +80,7 @@ app.registerExtension({
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
levelpixel.addStatusTagHandler(nodeType);
|
||||
|
||||
if (nodeData.name === "Autotagger|LevelPixel") {
|
||||
if (nodeData.name === "Autotagger|LP") {
|
||||
const onExecuted = nodeType.prototype.onExecuted;
|
||||
nodeType.prototype.onExecuted = function (message) {
|
||||
const r = onExecuted?.apply?.(this, arguments);
|
||||
@@ -139,3 +139,69 @@ app.registerExtension({
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
app.registerExtension({
|
||||
name: "levelpixel.LoraTagLoader",
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
levelpixel.addStatusTagHandler(nodeType);
|
||||
|
||||
if (nodeData.name === "LoraTagLoader|LP") {
|
||||
const onExecuted = nodeType.prototype.onExecuted;
|
||||
nodeType.prototype.onExecuted = function (message) {
|
||||
const r = onExecuted?.apply?.(this, arguments);
|
||||
|
||||
const pos = this.widgets.findIndex((w) => w.name === "log");
|
||||
if (pos !== -1) {
|
||||
for (let i = pos; i < this.widgets.length; i++) {
|
||||
this.widgets[i].onRemove?.();
|
||||
}
|
||||
this.widgets.length = pos;
|
||||
}
|
||||
|
||||
for (const list of message.log) {
|
||||
const w = ComfyWidgets["STRING"](this, "log", ["STRING", { multiline: true }], app).widget;
|
||||
w.inputEl.readOnly = true;
|
||||
w.inputEl.style.opacity = 0.6;
|
||||
w.value = list;
|
||||
}
|
||||
|
||||
this.onResize?.(this.size);
|
||||
|
||||
return r;
|
||||
};
|
||||
} else {
|
||||
const getExtraMenuOptions = nodeType.prototype.getExtraMenuOptions;
|
||||
nodeType.prototype.getExtraMenuOptions = function (_, options) {
|
||||
const r = getExtraMenuOptions?.apply?.(this, arguments);
|
||||
let img;
|
||||
if (this.imageIndex != null) {
|
||||
// An image is selected so select that
|
||||
img = this.imgs[this.imageIndex];
|
||||
} else if (this.overIndex != null) {
|
||||
// No image is selected but one is hovered
|
||||
img = this.imgs[this.overIndex];
|
||||
}
|
||||
if (img) {
|
||||
let pos = options.findIndex((o) => o.content === "Save Image");
|
||||
if (pos === -1) {
|
||||
pos = 0;
|
||||
} else {
|
||||
pos++;
|
||||
}
|
||||
options.splice(pos, 0, {
|
||||
content: "LoraTagLoader",
|
||||
callback: async () => {
|
||||
let src = img.src;
|
||||
src = src.replace("/view?", `/levelpixel/loratagloader/tag?node=${this.id}&clientId=${api.clientId}&`);
|
||||
const res = await (await fetch(src)).json();
|
||||
alert(res);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return r;
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user