Compare commits
4
Commits
v2.0.0-rc.9
...
v2.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b08c7a90e | ||
|
|
76142c4b7e | ||
|
|
1d84fdaf9e | ||
|
|
1c50ae5297 |
@@ -12,10 +12,10 @@ test_graph:
|
||||
PYTHONPATH=../../ python -m prompt_control.test_graph
|
||||
|
||||
test_encode:
|
||||
PYTHONPATH=../../ python -m prompt_control.test_encode
|
||||
PYTHONPATH=../../ python -m prompt_control.test_encode --verbose
|
||||
|
||||
test_encode_both:
|
||||
TEST_TE="clip_l t5" PYTHONPATH=../../ python -m prompt_control.test_encode
|
||||
TEST_TE="clip_l t5" PYTHONPATH=../../ python -m prompt_control.test_encode --verbose
|
||||
|
||||
test_heavy: test_graph test_encode_both
|
||||
|
||||
|
||||
@@ -32,21 +32,6 @@ Prompt Control uses graph generation, and tries to delegate functionality to co
|
||||
|
||||
If you encounter issues as a user or if you're a node developer and Prompt Control somehow breaks something, feel free to file a bug report.
|
||||
|
||||
## Prompt Control v2
|
||||
|
||||
Prompt control has been almost completely rewritten. It now uses ComfyUI's lazy execution to build graphs from the text prompt at runtime. The generated graph is often exactly equivalent to a manually built workflow using native ComfyUI nodes. There are no more weird sampling hooks that could cause problems with other nodes
|
||||
|
||||
### Removed features
|
||||
|
||||
- Prompt interpolation syntax; it was too cumbersome to maintain
|
||||
- LoRA block weight integration; ditto, for now.
|
||||
|
||||
### Everything broke, where are the old nodes?
|
||||
|
||||
If you really need them, you can install the [legacy nodes](https://github.com/asagi4/comfyui-prompt-control-legacy). However, I will not fix bugs in those nodes, and I strongly recommend just migrating your workflows to the new nodes.
|
||||
|
||||
You can have both installed at the same time; none of the nodes conflict.
|
||||
|
||||
## Requirements
|
||||
|
||||
For LoRA scheduling to work, you'll need at least version 0.3.7 of ComfyUI (0.3.36 of ComfyUI desktop).
|
||||
@@ -100,8 +85,4 @@ This node configures `PCTextEncode` default values for some functions by attachi
|
||||
|
||||
- ComfyUI's caching mechanism has an issue that makes it unnecessarily invalidate caches for certain inputs; you'll still get some benefit from the lazy nodes, but changing inputs that shouldn't affect downstream nodes (especially if using filtering) will still cause them to be recomputed because ComfyUI doesn't realize the inputs haven't changed.
|
||||
|
||||
If you want to enable a hack to fix this, set `PROMPTCONTROL_ENABLE_CACHE_HACK=1` in your environment. Unset it to disable.
|
||||
|
||||
It's a purely optional performance optimization that allows Prompt Control nodes to override their cache keys in a way that should not interfere with other nodes. Note that the optimization only works if the text input to the lazy nodes is a constant (so either directly on the node or from a primitive); outputs from other nodes can't be optimized.
|
||||
|
||||
- Cutoff does not work with models that use non-CLIP text encoders, like Flux. This might be fixable, but it's uncertain if cutoff even makes sense for those models.
|
||||
|
||||
@@ -23,9 +23,6 @@ if os.environ.get("PROMPTCONTROL_DEBUG"):
|
||||
else:
|
||||
log.setLevel(logging.INFO)
|
||||
|
||||
cache_hack = importlib.import_module(".prompt_control.cache_hack", package=__name__)
|
||||
cache_hack.init()
|
||||
|
||||
NODE_CLASS_MAPPINGS = {}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {}
|
||||
|
||||
|
||||
@@ -457,7 +457,7 @@ def substitute_def(text, search, replace):
|
||||
|
||||
def substitute_defcall(text, search, replace):
|
||||
name, default_args = search
|
||||
text, defns = get_function(text, name, defaults=None, placeholder=f"DEFNCALL{name}")
|
||||
text, defns = get_function(text, name, defaults=None, placeholder=f"DEFNCALL{name}", require_args=False)
|
||||
for i, parameters in enumerate(defns):
|
||||
ph = f"\0DEFNCALL{name}{i}\0"
|
||||
paramvals = []
|
||||
|
||||
@@ -231,7 +231,7 @@ def encode_prompt_segment(
|
||||
|
||||
# Chunks to ConditioningAverage:
|
||||
|
||||
text, averages = split_by_function(text, "AVG", ["0.5"])
|
||||
text, averages = split_by_function(text, "AVG", ["0.5"], require_args=False)
|
||||
prompts_to_avg = []
|
||||
for avg in averages:
|
||||
w = safe_float(avg["args"][0], 0.5)
|
||||
@@ -573,7 +573,7 @@ def encode_prompt(clip, text, start_pct, end_pct, defaults, masks):
|
||||
return f"MASK({args})"
|
||||
|
||||
for prompt in prompts:
|
||||
base_prompt, attn_couple_prompts = split_by_function(prompt, "COUPLE", defaults=None)
|
||||
base_prompt, attn_couple_prompts = split_by_function(prompt, "COUPLE", defaults=None, require_args=False)
|
||||
|
||||
prompts = [base_prompt] + [couple_mask(p["args"]) + p["text"] for p in attn_couple_prompts]
|
||||
encoded = []
|
||||
|
||||
@@ -80,6 +80,12 @@ class TestEncode(unittest.TestCase):
|
||||
c = c2 # Used in later tests
|
||||
self.condEqual(c1, c2)
|
||||
|
||||
with self.subTest("Function cornercase"):
|
||||
(c1,) = run(pc, clip, "test SDXL function")
|
||||
(c2,) = run(comfy, clip, "test SDXL function")
|
||||
(c3,) = run(pc, clip, "test SDXL() function")
|
||||
self.condEqual(c1, c2)
|
||||
|
||||
with self.subTest("Weights"):
|
||||
(c1,) = run(pc, clip, "(test:1.2) (test:0.6)")
|
||||
(c2,) = run(comfy, clip, "(test:1.2) (test:0.6)")
|
||||
@@ -104,8 +110,20 @@ class TestEncode(unittest.TestCase):
|
||||
(c1,) = run(comfy, clip, "test1")
|
||||
(c2,) = run(comfy, clip, "test2")
|
||||
(c3,) = run(pc, clip, "test1 AVG() test2")
|
||||
(c4,) = run(pc, clip, "test1 AVG test2")
|
||||
(avg,) = run(average, c1, c2, 0.5)
|
||||
self.condEqual(avg, c3)
|
||||
self.condEqual(avg, c4)
|
||||
|
||||
@unittest.expectedFailure
|
||||
def test_failure(self):
|
||||
pc = PCTextEncode()
|
||||
comfy = nodes.CLIPTextEncode()
|
||||
for k, clip in clips:
|
||||
with self.subTest(k):
|
||||
(c1,) = run(comfy, clip, "test SDXL function")
|
||||
(c2,) = run(pc, clip, "test SDXL() function")
|
||||
self.condEqual(c1, c2)
|
||||
|
||||
def test_weight(self):
|
||||
pc = PCTextEncode()
|
||||
|
||||
+10
-5
@@ -88,14 +88,19 @@ def find_closing_paren(text, start):
|
||||
return len(text)
|
||||
|
||||
|
||||
def get_function(text, func, defaults, return_func_name=False, placeholder="", return_dict=False):
|
||||
rex = re.compile(rf"\b{func}\b", re.MULTILINE)
|
||||
def get_function(text, func, defaults, return_func_name=False, placeholder="", return_dict=False, require_args=True):
|
||||
if require_args:
|
||||
rex = re.compile(rf"\b{func}\(", re.MULTILINE)
|
||||
else:
|
||||
rex = re.compile(rf"\b{func}\b", re.MULTILINE)
|
||||
instances = []
|
||||
match = rex.search(text)
|
||||
count = 0
|
||||
while match:
|
||||
# Match start, content start
|
||||
start, at_paren = match.span()
|
||||
if require_args:
|
||||
at_paren = at_paren - 1
|
||||
funcname = text[start:at_paren]
|
||||
after_first_paren = at_paren + 1
|
||||
if text[at_paren:after_first_paren] == "(":
|
||||
@@ -104,7 +109,7 @@ def get_function(text, func, defaults, return_func_name=False, placeholder="", r
|
||||
end += 1
|
||||
else:
|
||||
end = at_paren
|
||||
args = None
|
||||
args = defaults
|
||||
ph = None
|
||||
if placeholder:
|
||||
ph = f"\0{placeholder}{count}\0"
|
||||
@@ -131,11 +136,11 @@ def get_function(text, func, defaults, return_func_name=False, placeholder="", r
|
||||
return text, instances
|
||||
|
||||
|
||||
def split_by_function(text, func, defaults=None):
|
||||
def split_by_function(text, func, defaults=None, require_args=True):
|
||||
"""
|
||||
Splits a string by function calls, returning the text preceding the first call and a list of dictionaries with a "text" key with the prompt before the next split or until hthe end of the text.
|
||||
"""
|
||||
text, functions = get_function(text, func, defaults, return_dict=True)
|
||||
text, functions = get_function(text, func, defaults, return_dict=True, require_args=require_args)
|
||||
chunks = []
|
||||
prev = 0
|
||||
for f in functions:
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "comfyui-prompt-control"
|
||||
description = "Provides nodes for prompt editing and LoRA scheduling, advanced regional prompting (including attention masking) and more, all controlled through your text prompt"
|
||||
version = "2.0.0-rc.9"
|
||||
version = "2.0.1"
|
||||
license = { file = "LICENSE" }
|
||||
# some lark versions older than 1.1.9 apparently have a bug that breaks things, see https://github.com/asagi4/comfyui-prompt-control/issues/35
|
||||
dependencies = ["lark >= 1.1.9"]
|
||||
|
||||
Reference in New Issue
Block a user