forked from rgthree/rgthree-comfy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path__init__.py
274 lines (228 loc) · 11.2 KB
/
__init__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
"""
@author: rgthree
@title: Comfy Nodes
@nickname: rgthree
@description: A bunch of nodes I created that I also find useful.
"""
import glob
import json
import os
import shutil
import re
from .config import RGTHREE_CONFIG
from .py.rgthree_server import *
from .py.log import log_welcome
from .py.context import RgthreeContext
from .py.context_switch import RgthreeContextSwitch
from .py.context_switch_big import RgthreeContextSwitchBig
from .py.display_any import RgthreeDisplayAny, RgthreeDisplayInt
from .py.lora_stack import RgthreeLoraLoaderStack
from .py.seed import RgthreeSeed
from .py.sdxl_empty_latent_image import RgthreeSDXLEmptyLatentImage
from .py.power_prompt import RgthreePowerPrompt
from .py.power_prompt_simple import RgthreePowerPromptSimple
from .py.image_inset_crop import RgthreeImageInsetCrop
from .py.context_big import RgthreeBigContext
from .py.ksampler_config import RgthreeKSamplerConfig
from .py.sdxl_power_prompt_postive import RgthreeSDXLPowerPromptPositive
from .py.sdxl_power_prompt_simple import RgthreeSDXLPowerPromptSimple
from .py.any_switch import RgthreeAnySwitch
from .py.context_merge import RgthreeContextMerge
from .py.context_merge_big import RgthreeContextMergeBig
from .py.image_comparer import RgthreeImageComparer
NODE_CLASS_MAPPINGS = {
RgthreeBigContext.NAME: RgthreeBigContext,
RgthreeContext.NAME: RgthreeContext,
RgthreeContextSwitch.NAME: RgthreeContextSwitch,
RgthreeContextSwitchBig.NAME: RgthreeContextSwitchBig,
RgthreeDisplayInt.NAME: RgthreeDisplayInt,
RgthreeDisplayAny.NAME: RgthreeDisplayAny,
RgthreeLoraLoaderStack.NAME: RgthreeLoraLoaderStack,
RgthreeSeed.NAME: RgthreeSeed,
RgthreeImageInsetCrop.NAME: RgthreeImageInsetCrop,
RgthreePowerPrompt.NAME: RgthreePowerPrompt,
RgthreePowerPromptSimple.NAME: RgthreePowerPromptSimple,
RgthreeKSamplerConfig.NAME: RgthreeKSamplerConfig,
RgthreeSDXLEmptyLatentImage.NAME: RgthreeSDXLEmptyLatentImage,
RgthreeSDXLPowerPromptPositive.NAME: RgthreeSDXLPowerPromptPositive,
RgthreeSDXLPowerPromptSimple.NAME: RgthreeSDXLPowerPromptSimple,
RgthreeAnySwitch.NAME: RgthreeAnySwitch,
RgthreeContextMerge.NAME: RgthreeContextMerge,
RgthreeContextMergeBig.NAME: RgthreeContextMergeBig,
RgthreeImageComparer.NAME: RgthreeImageComparer,
}
# WEB_DIRECTORY is the comfyui nodes directory that ComfyUI will link and auto-load.
WEB_DIRECTORY = "./web/comfyui"
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
DIR_WEB = os.path.abspath(f'{THIS_DIR}/{WEB_DIRECTORY}')
DIR_PY = os.path.abspath(f'{THIS_DIR}/py')
# remove old directories
OLD_DIRS = [
os.path.abspath(f'{THIS_DIR}/../../web/extensions/rgthree'),
os.path.abspath(f'{THIS_DIR}/../../web/extensions/rgthree-comfy'),
]
for old_dir in OLD_DIRS:
if os.path.exists(old_dir):
shutil.rmtree(old_dir)
def extend_config(default_config, user_config):
cfg = {}
for key, value in default_config.items():
if key not in user_config:
cfg[key] = value
elif isinstance(value, dict):
cfg[key] = extend_config(value, user_config[key])
else:
cfg[key] = user_config[key] if key in user_config else value
return cfg
with open(os.path.join(DIR_WEB, 'rgthree_config.js'), 'w', encoding = 'UTF-8') as file:
file.write('export const rgthreeConfig = ' + json.dumps(RGTHREE_CONFIG))
# shutil.copy(os.path.join(THIS_DIR, 'rgthree_config.json'), os.path.join(DIR_WEB, 'rgthree_config.js'))
NOT_NODES = ['constants', 'log', 'utils', 'rgthree']
__all__ = ['NODE_CLASS_MAPPINGS', 'WEB_DIRECTORY']
nodes = []
for file in glob.glob(os.path.join(DIR_PY, '*.py')) + glob.glob(os.path.join(DIR_WEB, 'js', '*.js')):
name = os.path.splitext(file)[0]
if name not in nodes and name not in NOT_NODES and not name.startswith(
'_') and not name.startswith('base') and not 'utils' in name:
nodes.append(name)
log_welcome(num_nodes=len(nodes))
# Alright, I don't like doing this, but until https://github.com/comfyanonymous/ComfyUI/issues/1502
# and/or https://github.com/comfyanonymous/ComfyUI/pull/1503 is pulled into ComfyUI, we need a way
# to optimize the recursion that happens on prompt eval. This is particularly important for
# rgthree nodes because workflows can contain many context nodes, but the problem would exist for
# other nodes' (like "pipe" nodes, efficieny nodes). With `Context Big` nodes being
# introduced, the number of input recursion that happens in these methods is exponential with a
# saving of 1000's of percentage points over.
if 'patch_recursive_execution' in RGTHREE_CONFIG and RGTHREE_CONFIG['patch_recursive_execution']:
import execution
msg = "\33[32m[rgthree] Optimizing ComfyUI recursive execution. \33[33mIf queueing and/or re-queueing seems "
msg += "broken, change \"patch_recursive_execution\" to false in rgthree_config.json \33[0m"
print(msg)
class RgthreePatchRecursiveExecute_Set_patch_recursive_execution_to_false_if_not_working:
"""A fake 'list' that the caller for recursive_will_execute expects but we override such that
`len(inst)` will return the count number, and `inst[-1]` will return the unique_id. Since that
all the caller cares about, we can save several minutes and many MB of ram by simply counting
numbers instead of concatenating a list of millions (only to count it). However the caller
expects such a list, so we fake it with this.
This mimics the enhancement from https://github.com/rgthree/ComfyUI/commit/50b3fb1 but without
modifying the execution.py
"""
def __init__(self, unique_id):
self.unique_id = unique_id
self.count = 0
def add(self, value):
self.count += value
def __getitem__(self, key):
"""Returns the `unique_id` with '-1' since that's what the caller expects."""
if key == -1:
return self.unique_id
# This one would future proof the proposed changes, in that case "0" is the count
if key == 0:
return self.count
else:
return -1
def __len__(self):
"""Returns the "count" of the "list" as if we were building up a list instea of just
incrementing `count`.
"""
return self.count
# The following (hopefully) future proofs if https://github.com/rgthree/ComfyUI/commit/50b3fb1
# goes in, which changes from using `len` on a list, to sort directly (and, thus "<" and ">").
def __gt__(self, other):
return self.count > other
def __lt__(self, other):
return self.count < other
def __str__(self):
return str((self.count, self.unique_id,))
execution.rgthree_cache_recursive_output_delete_if_changed_output = {}
execution.rgthree_cache_recursive_will_execute = {}
def rgthree_execute(self, *args, **kwargs):
# When we execute, we'll reset our global cache here.
execution.rgthree_cache_recursive_output_delete_if_changed_output = {}
execution.rgthree_cache_recursive_will_execute = {}
return self.old_execute(*args, **kwargs)
def rgthree_recursive_will_execute(prompt, outputs, current_item, *args, **kwargs):
unique_id = current_item
inputs = prompt[unique_id]['inputs']
will_execute = RgthreePatchRecursiveExecute_Set_patch_recursive_execution_to_false_if_not_working(unique_id)
if unique_id in outputs:
return will_execute
will_execute.add(1)
for x in inputs:
input_data = inputs[x]
if isinstance(input_data, list):
input_unique_id = input_data[0]
output_index = input_data[1]
node_output_cache_key = f'{input_unique_id}.{output_index}'
will_execute_value = None
# If this node's output has already been recursively evaluated, then we can reuse.
if node_output_cache_key in execution.rgthree_cache_recursive_will_execute:
will_execute_value = execution.rgthree_cache_recursive_will_execute[node_output_cache_key]
elif input_unique_id not in outputs:
will_execute_value = execution.recursive_will_execute(prompt, outputs, input_unique_id, *args, **kwargs)
execution.rgthree_cache_recursive_will_execute[node_output_cache_key] = will_execute_value
if will_execute_value is not None:
will_execute.add(len(will_execute_value))
return will_execute
def rgthree_recursive_output_delete_if_changed(prompt, old_prompt, outputs, current_item, *args, **kwargs):
unique_id = current_item
inputs = prompt[unique_id]['inputs']
class_type = prompt[unique_id]['class_type']
class_def = execution.nodes.NODE_CLASS_MAPPINGS[class_type]
is_changed_old = ''
is_changed = ''
to_delete = False
if hasattr(class_def, 'IS_CHANGED'):
if unique_id in old_prompt and 'is_changed' in old_prompt[unique_id]:
is_changed_old = old_prompt[unique_id]['is_changed']
if 'is_changed' not in prompt[unique_id]:
input_data_all = execution.get_input_data(inputs, class_def, unique_id, outputs)
if input_data_all is not None:
try:
#is_changed = class_def.IS_CHANGED(**input_data_all)
is_changed = execution.map_node_over_list(class_def, input_data_all, "IS_CHANGED")
prompt[unique_id]['is_changed'] = is_changed
except:
to_delete = True
else:
is_changed = prompt[unique_id]['is_changed']
if unique_id not in outputs:
return True
if not to_delete:
if is_changed != is_changed_old:
to_delete = True
elif unique_id not in old_prompt:
to_delete = True
elif inputs == old_prompt[unique_id]['inputs']:
for x in inputs:
input_data = inputs[x]
if isinstance(input_data, list):
input_unique_id = input_data[0]
output_index = input_data[1]
node_output_cache_key = f'{input_unique_id}.{output_index}'
# If this node's output has already been recursively evaluated, then we can stop.
if node_output_cache_key in execution.rgthree_cache_recursive_output_delete_if_changed_output:
to_delete = execution.rgthree_cache_recursive_output_delete_if_changed_output[
node_output_cache_key]
elif input_unique_id in outputs:
to_delete = execution.recursive_output_delete_if_changed(prompt, old_prompt, outputs,
input_unique_id, *args, **kwargs)
execution.rgthree_cache_recursive_output_delete_if_changed_output[
node_output_cache_key] = to_delete
else:
to_delete = True
if to_delete:
break
else:
to_delete = True
if to_delete:
d = outputs.pop(unique_id)
del d
return to_delete
execution.PromptExecutor.old_execute = execution.PromptExecutor.execute
execution.PromptExecutor.execute = rgthree_execute
execution.old_recursive_output_delete_if_changed = execution.recursive_output_delete_if_changed
execution.recursive_output_delete_if_changed = rgthree_recursive_output_delete_if_changed
execution.old_recursive_will_execute = execution.recursive_will_execute
execution.recursive_will_execute = rgthree_recursive_will_execute
print()