Skip to content

Commit 6794416

Browse files
author
KoleT
committed
Refactor nodes.py to simplify LoraStackerTagsQuery implementation, remove Example node, and add find packages configuration in pyproject.toml.
1 parent 052e93f commit 6794416

File tree

4 files changed

+1209
-163
lines changed

4 files changed

+1209
-163
lines changed

__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66
__email__ = "kole.tackney@gmail.com"
77
__version__ = "1.0.1"
88

9-
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', 'CC_VERSION']
9+
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
1010

pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "lora-stacker-civitai-tags"
7-
version = "1.0.0"
7+
version = "1.0.1"
88
description = "Fetches CivitAI trainedWords (trigger tags) for a LoRA stack and returns them as a comma-separated string."
99
authors = [
1010
{name = "Lorastack Tag autoloader", email = "kole.tackney@gmail.com"}
@@ -43,6 +43,9 @@ Repository = "https://github.com/KoleTackney/lora-stacker-civitai-tags"
4343

4444
includes = []
4545

46+
[tool.setuptools.packages.find]
47+
where = ["src"]
48+
4649
[tool.setuptools.package-data]
4750
"*" = ["*.*"]
4851

src/my_custom_nodepack/nodes.py

Lines changed: 64 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,8 @@
1-
from inspect import cleandoc
21
import hashlib
32
import json
43
import os
54
import requests
6-
75
import folder_paths
8-
class Example:
9-
"""
10-
A example node
11-
12-
Class methods
13-
-------------
14-
INPUT_TYPES (dict):
15-
Tell the main program input parameters of nodes.
16-
IS_CHANGED:
17-
optional method to control when the node is re executed.
18-
19-
Attributes
20-
----------
21-
RETURN_TYPES (`tuple`):
22-
The type of each element in the output tulple.
23-
RETURN_NAMES (`tuple`):
24-
Optional: The name of each output in the output tulple.
25-
FUNCTION (`str`):
26-
The name of the entry-point method. For example, if `FUNCTION = "execute"` then it will run Example().execute()
27-
OUTPUT_NODE ([`bool`]):
28-
If this node is an output node that outputs a result/image from the graph. The SaveImage node is an example.
29-
The backend iterates on these output nodes and tries to execute all their parents if their parent graph is properly connected.
30-
Assumed to be False if not present.
31-
CATEGORY (`str`):
32-
The category the node should appear in the UI.
33-
execute(s) -> tuple || None:
34-
The entry point method. The name of this method must be the same as the value of property `FUNCTION`.
35-
For example, if `FUNCTION = "execute"` then this method's name must be `execute`, if `FUNCTION = "foo"` then it must be `foo`.
36-
"""
37-
def __init__(self):
38-
pass
39-
40-
@classmethod
41-
def INPUT_TYPES(s):
42-
"""
43-
Return a dictionary which contains config for all input fields.
44-
Some types (string): "MODEL", "VAE", "CLIP", "CONDITIONING", "LATENT", "IMAGE", "INT", "STRING", "FLOAT".
45-
Input types "INT", "STRING" or "FLOAT" are special values for fields on the node.
46-
The type can be a list for selection.
47-
48-
Returns: `dict`:
49-
- Key input_fields_group (`string`): Can be either required, hidden or optional. A node class must have property `required`
50-
- Value input_fields (`dict`): Contains input fields config:
51-
* Key field_name (`string`): Name of a entry-point method's argument
52-
* Value field_config (`tuple`):
53-
+ First value is a string indicate the type of field or a list for selection.
54-
+ Secound value is a config for type "INT", "STRING" or "FLOAT".
55-
"""
56-
return {
57-
"required": {
58-
"image": ("Image", { "tooltip": "This is an image"}),
59-
"int_field": ("INT", {
60-
"default": 0,
61-
"min": 0, #Minimum value
62-
"max": 4096, #Maximum value
63-
"step": 64, #Slider's step
64-
"display": "number" # Cosmetic only: display as "number" or "slider"
65-
}),
66-
"float_field": ("FLOAT", {
67-
"default": 1.0,
68-
"min": 0.0,
69-
"max": 10.0,
70-
"step": 0.01,
71-
"round": 0.001, #The value represeting the precision to round to, will be set to the step value by default. Can be set to False to disable rounding.
72-
"display": "number"}),
73-
"print_to_screen": (["enable", "disable"],),
74-
"string_field": ("STRING", {
75-
"multiline": False, #True if you want the field to look like the one on the ClipTextEncode node
76-
"default": "Hello World!"
77-
}),
78-
},
79-
}
80-
81-
RETURN_TYPES = ("IMAGE",)
82-
#RETURN_NAMES = ("image_output_name",)
83-
DESCRIPTION = cleandoc(__doc__)
84-
FUNCTION = "test"
85-
86-
#OUTPUT_NODE = False
87-
#OUTPUT_TOOLTIPS = ("",) # Tooltips for the output node
88-
89-
CATEGORY = "Example"
90-
91-
def test(self, image, string_field, int_field, float_field, print_to_screen):
92-
if print_to_screen == "enable":
93-
print(f"""Your input contains:
94-
string_field aka input text: {string_field}
95-
int_field: {int_field}
96-
float_field: {float_field}
97-
""")
98-
#do some processing on the image, in this example I just invert it
99-
image = 1.0 - image
100-
return (image,)
101-
102-
"""
103-
The node will always be re executed if any of the inputs change but
104-
this method can be used to force the node to execute again even when the inputs don't change.
105-
You can make this node return a number or a string. This value will be compared to the one returned the last time the node was
106-
executed, if it is different the node will be executed again.
107-
This method is used in the core repo for the LoadImage node where they return the image hash as a string, if the image hash
108-
changes between executions the LoadImage node is executed again.
109-
"""
110-
#@classmethod
111-
#def IS_CHANGED(s, image, string_field, int_field, float_field, print_to_screen):
112-
# return ""
113-
1146

1157
def _load_json_from_file(file_path):
1168
try:
@@ -150,6 +42,61 @@ def _calculate_sha256(file_path):
15042
return sha256_hash.hexdigest()
15143

15244

45+
def query_lora_stack_tags(
46+
lora_stack,
47+
query_tags,
48+
print_tags,
49+
force_fetch,
50+
separator,
51+
opt_prompt=None,
52+
):
53+
if not lora_stack:
54+
output_tags = opt_prompt or ""
55+
return lora_stack, output_tags
56+
57+
json_tags_path = os.path.join(os.path.dirname(__file__), "loras_tags.json")
58+
lora_tags = _load_json_from_file(json_tags_path) or {}
59+
60+
all_tags = []
61+
for lora_name, _, _ in lora_stack:
62+
if lora_name == "None":
63+
continue
64+
65+
cached_tags = lora_tags.get(lora_name)
66+
if cached_tags is not None and cached_tags:
67+
all_tags.extend(cached_tags)
68+
continue
69+
if cached_tags is not None and not cached_tags and not (query_tags or force_fetch):
70+
continue
71+
72+
lora_path = folder_paths.get_full_path("loras", lora_name)
73+
if not lora_path:
74+
continue
75+
76+
if query_tags or force_fetch:
77+
print("calculating lora hash")
78+
lora_sha256 = _calculate_sha256(lora_path)
79+
print("requesting infos")
80+
model_info = _get_model_version_info(lora_sha256)
81+
if model_info is not None and "trainedWords" in model_info:
82+
print("tags found!")
83+
lora_tags[lora_name] = model_info["trainedWords"]
84+
_save_dict_to_json(lora_tags, json_tags_path)
85+
all_tags.extend(model_info["trainedWords"])
86+
if print_tags:
87+
print("trainedWords:", ", ".join(model_info["trainedWords"]))
88+
else:
89+
print("No information found.")
90+
lora_tags[lora_name] = []
91+
_save_dict_to_json(lora_tags, json_tags_path)
92+
93+
output_tags = separator.join(all_tags)
94+
if opt_prompt:
95+
output_tags = f"{opt_prompt}{separator}{output_tags}" if output_tags else opt_prompt
96+
97+
return lora_stack, output_tags
98+
99+
153100
class LoraStackerTagsQuery:
154101
"""
155102
Fetch CivitAI trainedWords for a LoRA stack and return a CSV string.
@@ -184,62 +131,18 @@ def query_lora_stack_tags(
184131
separator,
185132
opt_prompt=None,
186133
):
187-
if not lora_stack:
188-
output_tags = opt_prompt or ""
189-
return (lora_stack, output_tags)
190-
191-
json_tags_path = os.path.join(os.path.dirname(__file__), "loras_tags.json")
192-
lora_tags = _load_json_from_file(json_tags_path) or {}
193-
194-
all_tags = []
195-
for lora_name, _, _ in lora_stack:
196-
if lora_name == "None":
197-
continue
198-
199-
cached_tags = lora_tags.get(lora_name)
200-
if cached_tags is not None and cached_tags:
201-
all_tags.extend(cached_tags)
202-
continue
203-
if cached_tags is not None and not cached_tags and not (query_tags or force_fetch):
204-
continue
205-
206-
lora_path = folder_paths.get_full_path("loras", lora_name)
207-
if not lora_path:
208-
continue
209-
210-
if query_tags or force_fetch:
211-
print("calculating lora hash")
212-
lora_sha256 = _calculate_sha256(lora_path)
213-
print("requesting infos")
214-
model_info = _get_model_version_info(lora_sha256)
215-
if model_info is not None and "trainedWords" in model_info:
216-
print("tags found!")
217-
lora_tags[lora_name] = model_info["trainedWords"]
218-
_save_dict_to_json(lora_tags, json_tags_path)
219-
all_tags.extend(model_info["trainedWords"])
220-
if print_tags:
221-
print("trainedWords:", ", ".join(model_info["trainedWords"]))
222-
else:
223-
print("No informations found.")
224-
lora_tags[lora_name] = []
225-
_save_dict_to_json(lora_tags, json_tags_path)
226-
227-
output_tags = separator.join(all_tags)
228-
if opt_prompt:
229-
output_tags = f"{opt_prompt}{separator}{output_tags}" if output_tags else opt_prompt
230-
231-
return (lora_stack, output_tags)
232-
233-
234-
# A dictionary that contains all nodes you want to export with their names
235-
# NOTE: names should be globally unique
134+
return query_lora_stack_tags(
135+
lora_stack, query_tags, print_tags, force_fetch, separator, opt_prompt
136+
)
137+
138+
139+
# A dictionary that contains all nodes you want to export with their names
140+
# NOTE: names should be globally unique
236141
NODE_CLASS_MAPPINGS = {
237-
"Example": Example,
238142
"LoraStackerTagsQuery": LoraStackerTagsQuery,
239143
}
240-
241-
# A dictionary that contains the friendly/humanly readable titles for the nodes
144+
145+
# A dictionary that contains the friendly/humanly readable titles for the nodes
242146
NODE_DISPLAY_NAME_MAPPINGS = {
243-
"Example": "Example Node",
244147
"LoraStackerTagsQuery": "LoRA Stacker Tags (CivitAI)",
245148
}

0 commit comments

Comments
 (0)