* Better logging.
* Refactored tests.
This commit is contained in:
+4
-2
@@ -10,9 +10,11 @@ This file contains some options for how the host applications (WebUIs) should ac
|
||||
|
||||
The model variants now support regular expressions instead of a list of strings to detect the variant. If you used a non default value in previous versions you should create a configuration file and add them with the new format. As before, the default file defines variants for *Pony* and *Illustrious* models.
|
||||
|
||||
## Notes
|
||||
## Important
|
||||
|
||||
Beware of the combinatorial mode with no limits. Very few choice/wildcard constructs can cause a combinatorial explosion!
|
||||
**Beware of the combinatorial mode with no limits**. Even very few choice/wildcard constructs can cause a *combinatorial explosion*!
|
||||
|
||||
The console log can help you determine the number of combinations that it is trying to generate. There will be an **"Estimated combinations"** message that shows an estimate. You can try first with a limit of 1, then check this message in the log. But note that it is a lower bound estimate, and there could be more combinations.
|
||||
|
||||
## ComfyUI
|
||||
|
||||
|
||||
@@ -869,16 +869,12 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in
|
||||
if rem_wildcards:
|
||||
w_found_p = [wc for wc, n in rem_wildcards if not n]
|
||||
w_found_n = [wc for wc, n in rem_wildcards if n]
|
||||
if self.state.options.if_wildcards == IFWILDCARDS_CHOICES.stop:
|
||||
self.log(logging.ERROR, "Found unprocessed wildcards!")
|
||||
else:
|
||||
self.log(logging.INFO, "Found unprocessed wildcards.")
|
||||
ppwl = ", ".join(w_found_p)
|
||||
npwl = ", ".join(w_found_n)
|
||||
if ppwl:
|
||||
self.log(logging.ERROR, f"In the prompt: {ppwl}")
|
||||
self.log(logging.WARN, f"Unprocessed wildcards in the prompt: {ppwl}")
|
||||
if npwl:
|
||||
self.log(logging.ERROR, f"In the negative prompt: {npwl}")
|
||||
self.log(logging.WARN, f"Unprocessed wildcards in the negative prompt: {npwl}")
|
||||
if self.state.options.if_wildcards == IFWILDCARDS_CHOICES.warn:
|
||||
prompt = self.WILDCARD_WARNING + prompt
|
||||
elif self.state.options.if_wildcards == IFWILDCARDS_CHOICES.stop:
|
||||
@@ -929,6 +925,7 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in
|
||||
|
||||
# Parse both prompts
|
||||
processor = TreeProcessor(self.state, rng)
|
||||
# We use the ASCII Group Separator character between prompt and negative prompt since it's unlikely to appear in prompts
|
||||
unified_prompt = prompt + "\x1d" + negative_prompt
|
||||
prompt_parser, parser_description = self.__get_best_parser(unified_prompt)
|
||||
self.log(logging.DEBUG, f"Using {parser_description} for prompt")
|
||||
@@ -967,6 +964,11 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in
|
||||
self.log(logging.INFO, "Combinations shuffled")
|
||||
return final_results
|
||||
|
||||
def process_prompts_group_start(self):
|
||||
"""Start of a prompt processing group."""
|
||||
self.log(logging.INFO, f"System variables: {self.state.variables.get_all_system()}")
|
||||
self.log(logging.INFO, f"Combinatorial: {self.state.options.do_combinatorial}")
|
||||
|
||||
def process_prompt(
|
||||
self,
|
||||
original_prompt: str,
|
||||
@@ -989,11 +991,9 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in
|
||||
seed = np.random.randint(0, 2**32, dtype=np.int64)
|
||||
prompt = original_prompt
|
||||
negative_prompt = original_negative_prompt
|
||||
self.log(logging.INFO, f"System variables: {self.state.variables.get_all_system()}")
|
||||
self.log(logging.INFO, f"Input seed: {seed}")
|
||||
self.log(logging.INFO, f"Input prompt: {prompt}")
|
||||
self.log(logging.INFO, f"Input negative_prompt: {negative_prompt}")
|
||||
self.log(logging.INFO, f"Combinatorial: {self.state.options.do_combinatorial}")
|
||||
t1 = time.monotonic_ns()
|
||||
if self.state.cyclical_state.last_prompt_pair != (original_prompt, original_negative_prompt):
|
||||
self.state.cyclical_state.reset()
|
||||
@@ -1015,3 +1015,6 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in
|
||||
except Exception: # pylint: disable=broad-exception-caught
|
||||
self.log(logging.ERROR, "Unexpected error", exc_info=True)
|
||||
return [(original_prompt, original_negative_prompt, {})]
|
||||
|
||||
def process_prompts_group_end(self):
|
||||
"""End of a prompt processing group."""
|
||||
|
||||
@@ -412,7 +412,9 @@ class PromptPostProcessorComfyUINode:
|
||||
self.wildcards_obj,
|
||||
self.extranetwork_mappings_obj,
|
||||
)
|
||||
self.ppp.process_prompts_group_start()
|
||||
results = self.ppp.process_prompt(pos_prompt, neg_prompt, seed if seed is not None else 1)
|
||||
self.ppp.process_prompts_group_end()
|
||||
|
||||
# with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "logs", "last_prompts_comfyui.txt"), "w", encoding="utf-8") as f:
|
||||
# f.write(f"Seed: {seed if seed is not None else 1}\n")
|
||||
|
||||
+12
-7
@@ -1,4 +1,5 @@
|
||||
from collections import namedtuple
|
||||
from functools import reduce
|
||||
from itertools import combinations, combinations_with_replacement, permutations, product
|
||||
import logging
|
||||
import math
|
||||
@@ -129,6 +130,10 @@ class TreeProcessor(lark.visitors.Interpreter):
|
||||
results.append(
|
||||
(self.__result, self.__detectedWildcards.copy(), self.state.variables.backup_user_and_echoed())
|
||||
)
|
||||
if len(results) == 1:
|
||||
first_run_estimate = reduce(lambda x, y: x * y, self.__comb_trace, 1)
|
||||
self.log(logging.INFO, f"Estimated combinations (lower bound): {first_run_estimate}")
|
||||
self.log(logging.INFO, f"Added combination {len(results)}")
|
||||
return tuple(self.__comb_trace)
|
||||
|
||||
limit_reached = False
|
||||
@@ -1425,29 +1430,29 @@ class TreeProcessor(lark.visitors.Interpreter):
|
||||
found_mappings.append(v)
|
||||
else:
|
||||
else_mapping = v
|
||||
if found_mappings:
|
||||
num_mappings = len(found_mappings)
|
||||
if num_mappings > 0:
|
||||
if self.state.options.do_combinatorial:
|
||||
N = len(found_mappings)
|
||||
decision_idx = len(self.__comb_trace)
|
||||
self.__comb_trace.append(N)
|
||||
self.__comb_trace.append(num_mappings)
|
||||
chosen_idx = (
|
||||
min(self.__comb_forced_path[decision_idx], N - 1)
|
||||
min(self.__comb_forced_path[decision_idx], num_mappings - 1)
|
||||
if decision_idx < len(self.__comb_forced_path)
|
||||
else 0
|
||||
)
|
||||
found = found_mappings[chosen_idx]
|
||||
elif len(found_mappings) == 1:
|
||||
elif num_mappings == 1:
|
||||
found = found_mappings[0]
|
||||
else:
|
||||
found = found_mappings[
|
||||
self.__rng.choice(
|
||||
len(found_mappings),
|
||||
num_mappings,
|
||||
p=[v.weight or 1 for v in found_mappings],
|
||||
)
|
||||
]
|
||||
else:
|
||||
found = else_mapping
|
||||
if len(found_mappings) < 2:
|
||||
if num_mappings < 2:
|
||||
self.state.extranetwork_mappings_obj.cached_mappings[extnet_id] = found
|
||||
if found:
|
||||
if found.name:
|
||||
|
||||
+13
-3
@@ -159,7 +159,15 @@ class PromptPostProcessorA1111Script(scripts.Script):
|
||||
min_width=120,
|
||||
elem_id="ppp_combinatorial_limit",
|
||||
)
|
||||
return [force_equal_seeds, unlink_seed, seed, incremental_seed, combinatorial, combinatorial_shuffle, combinatorial_limit]
|
||||
return [
|
||||
force_equal_seeds,
|
||||
unlink_seed,
|
||||
seed,
|
||||
incremental_seed,
|
||||
combinatorial,
|
||||
combinatorial_shuffle,
|
||||
combinatorial_limit,
|
||||
]
|
||||
|
||||
def process(
|
||||
self,
|
||||
@@ -386,6 +394,7 @@ class PromptPostProcessorA1111Script(scripts.Script):
|
||||
if hiresfix_exists:
|
||||
prompts_list[(hiresfix_type, i)] = None
|
||||
|
||||
ppp.process_prompts_group_start()
|
||||
if input_combinatorial:
|
||||
seed_for_comb = calculated_seeds[0] if calculated_seeds else 0
|
||||
regular_copy = (rpr.copy() if rpr else None, rnr.copy() if rnr else None)
|
||||
@@ -399,7 +408,7 @@ class PromptPostProcessorA1111Script(scripts.Script):
|
||||
for i in range(len(rpr)): # pylint: disable=consider-using-enumerate
|
||||
posp, negp, _ = comb_results[i % num_comb]
|
||||
prompts_list[(regular_type, i)] = (posp, negp)
|
||||
extra_params["PPP combination"] = [1 + (i % num_comb) for i in range(len(rpr))]
|
||||
extra_params["PPP combination"] = [str(1 + (i % num_comb)) for i in range(len(rpr))]
|
||||
if hiresfix_exists:
|
||||
hiresfix_equal = regular_exists and rph == rpr and rnh == rnr
|
||||
if hiresfix_equal:
|
||||
@@ -423,7 +432,7 @@ class PromptPostProcessorA1111Script(scripts.Script):
|
||||
for i in range(len(rph)): # pylint: disable=consider-using-enumerate
|
||||
posp, negp, _ = comb_results_hr[i % num_comb_hr]
|
||||
prompts_list[(hiresfix_type, i)] = (posp, negp)
|
||||
extra_params["PPP HR combination"] = [1 + (i % num_comb_hr) for i in range(len(rph))]
|
||||
extra_params["PPP HR combination"] = [str(1 + (i % num_comb_hr)) for i in range(len(rph))]
|
||||
else:
|
||||
# processes prompts
|
||||
for prompttype, typeindex in prompts_list.keys():
|
||||
@@ -450,6 +459,7 @@ class PromptPostProcessorA1111Script(scripts.Script):
|
||||
else:
|
||||
log(self.ppp_logger, self.ppp_debug_level, logging.INFO, "result already in cache")
|
||||
prompts_list[(prompttype, typeindex)] = cached
|
||||
ppp.process_prompts_group_end()
|
||||
|
||||
# with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "logs", f"last_prompts_{app.value}.txt"), "w", encoding="utf-8") as f:
|
||||
# for (prompttype, typeindex), (posp, negp) in prompts_list.items():
|
||||
|
||||
+31
-5
@@ -48,13 +48,12 @@ class TestPromptPostProcessorBase(unittest.TestCase):
|
||||
self.ppp_logger = self.lf.log
|
||||
self.ppp_logger.setLevel(logging.DEBUG)
|
||||
self.grammar_content = None
|
||||
self.interrupted = False
|
||||
self.defopts = PPPStateOptions(
|
||||
debug_level=DEBUG_LEVEL.full,
|
||||
on_warning=ONWARNING_CHOICES.stop,
|
||||
strict_operators=True,
|
||||
process_wildcards=True,
|
||||
if_wildcards=IFWILDCARDS_CHOICES.ignore,
|
||||
if_wildcards=IFWILDCARDS_CHOICES.stop,
|
||||
choice_separator=", ",
|
||||
keep_choices_order=False,
|
||||
stn_separator=", ",
|
||||
@@ -75,6 +74,7 @@ class TestPromptPostProcessorBase(unittest.TestCase):
|
||||
cup_remove_extranetwork_tags=False,
|
||||
do_combinatorial=False,
|
||||
combinatorial_limit=0,
|
||||
combinatorial_shuffle=False,
|
||||
)
|
||||
self.def_env_info = {
|
||||
"app": "tests",
|
||||
@@ -115,7 +115,10 @@ class TestPromptPostProcessorBase(unittest.TestCase):
|
||||
self.interrupted = True
|
||||
|
||||
def init_obj(
|
||||
self, ppp: Optional[str | PromptPostProcessor] = None, combinatorial: bool = False
|
||||
self,
|
||||
ppp: Optional[str | PromptPostProcessor] = None,
|
||||
combinatorial: bool = False,
|
||||
combinatorial_limit: int = 0,
|
||||
) -> PromptPostProcessor:
|
||||
if isinstance(ppp, str):
|
||||
if ppp == "nocup":
|
||||
@@ -138,6 +141,7 @@ class TestPromptPostProcessorBase(unittest.TestCase):
|
||||
cup_extranetwork_tags=False,
|
||||
cup_merge_attention=False,
|
||||
do_combinatorial=combinatorial,
|
||||
combinatorial_limit=combinatorial_limit,
|
||||
),
|
||||
self.grammar_content,
|
||||
self.interrupt,
|
||||
@@ -152,6 +156,7 @@ class TestPromptPostProcessorBase(unittest.TestCase):
|
||||
self.defopts,
|
||||
strict_operators=False,
|
||||
do_combinatorial=combinatorial,
|
||||
combinatorial_limit=combinatorial_limit,
|
||||
),
|
||||
self.grammar_content,
|
||||
self.interrupt,
|
||||
@@ -167,6 +172,7 @@ class TestPromptPostProcessorBase(unittest.TestCase):
|
||||
replace(
|
||||
self.defopts,
|
||||
do_combinatorial=combinatorial,
|
||||
combinatorial_limit=combinatorial_limit,
|
||||
),
|
||||
self.grammar_content,
|
||||
self.interrupt,
|
||||
@@ -183,6 +189,9 @@ class TestPromptPostProcessorBase(unittest.TestCase):
|
||||
ppp: Optional[str | PromptPostProcessor] = None,
|
||||
interrupted: bool = False,
|
||||
combinatorial: bool = False,
|
||||
combinatorial_limit: int = 0,
|
||||
specific_wc_folders: Optional[list[str]] = None,
|
||||
specific_em_folders: Optional[list[str]] = None,
|
||||
):
|
||||
"""
|
||||
Process the prompt and compare the results with the expected prompts.
|
||||
@@ -194,11 +203,24 @@ class TestPromptPostProcessorBase(unittest.TestCase):
|
||||
ppp (Optional[str | PromptPostProcessor], optional): The PromptPostProcessor instance or type. Defaults to None.
|
||||
interrupted (bool, optional): The interrupted flag. Defaults to False.
|
||||
combinatorial (bool, optional): The combinatorial flag. Defaults to False.
|
||||
combinatorial_limit (int, optional): The combinatorial limit. Defaults to 0.
|
||||
specific_wc_folders (Optional[list[str]], optional): A list of specific wildcard folders to refresh. Defaults to None.
|
||||
specific_em_folders (Optional[list[str]], optional): A list of specific extranetwork mapping folders to refresh. Defaults to None.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
the_obj: PromptPostProcessor = self.init_obj(ppp, combinatorial)
|
||||
if specific_wc_folders is not None:
|
||||
self.wildcards_obj.refresh_wildcards(
|
||||
DEBUG_LEVEL.full,
|
||||
specific_wc_folders,
|
||||
)
|
||||
if specific_em_folders is not None:
|
||||
self.extranetwork_maps_obj.refresh_extranetwork_mappings(
|
||||
DEBUG_LEVEL.full,
|
||||
specific_em_folders,
|
||||
)
|
||||
the_obj: PromptPostProcessor = self.init_obj(ppp, combinatorial, combinatorial_limit)
|
||||
out = (
|
||||
[OutputTuple("", "", None)]
|
||||
if expected_output is None
|
||||
@@ -207,11 +229,13 @@ class TestPromptPostProcessorBase(unittest.TestCase):
|
||||
if the_obj.state.options.do_combinatorial:
|
||||
# combinatorial
|
||||
errors = []
|
||||
the_obj.process_prompts_group_start()
|
||||
result = the_obj.process_prompt(
|
||||
input_prompts.prompt,
|
||||
input_prompts.negative_prompt,
|
||||
seed,
|
||||
)
|
||||
the_obj.process_prompts_group_end()
|
||||
if self.interrupted != interrupted:
|
||||
errors.append(f"Interrupted flag is incorrect: expected {interrupted}, got {self.interrupted}")
|
||||
elif not self.interrupted and expected_output is not None:
|
||||
@@ -245,6 +269,7 @@ class TestPromptPostProcessorBase(unittest.TestCase):
|
||||
return
|
||||
# non-combinatorial
|
||||
errors = []
|
||||
the_obj.process_prompts_group_start()
|
||||
for eo in out:
|
||||
result = the_obj.process_prompt(
|
||||
input_prompts.prompt,
|
||||
@@ -254,7 +279,7 @@ class TestPromptPostProcessorBase(unittest.TestCase):
|
||||
if self.interrupted != interrupted:
|
||||
errors.append(f"Interrupted flag is incorrect: expected {interrupted}, got {self.interrupted}")
|
||||
elif not self.interrupted and expected_output is not None:
|
||||
result_prompt, result_negative_prompt, output_variables = (result[0] if result else (None, None, None))
|
||||
result_prompt, result_negative_prompt, output_variables = result[0] if result else (None, None, None)
|
||||
if result_prompt != eo.prompt or result_negative_prompt != eo.negative_prompt:
|
||||
errors.append(
|
||||
f"Incorrect result '{eo.prompt}' / '{eo.negative_prompt}', got '{result_prompt}' / '{result_negative_prompt}'"
|
||||
@@ -273,6 +298,7 @@ class TestPromptPostProcessorBase(unittest.TestCase):
|
||||
f"Result '{eo.prompt}' / '{eo.negative_prompt}' found, but variables do not match: expected {expected_values}, got {unmatched_vars}"
|
||||
)
|
||||
seed += 1
|
||||
the_obj.process_prompts_group_end()
|
||||
self.assertFalse(
|
||||
bool(errors),
|
||||
"\n" + "\n".join(errors),
|
||||
|
||||
@@ -125,6 +125,7 @@ class TestWildcards(TestPromptPostProcessorBase):
|
||||
InputTuple("the choices are: ___invalid__", ""),
|
||||
OutputTuple("the choices are: ___invalid__", ""),
|
||||
ppp="nocup",
|
||||
interrupted=True,
|
||||
)
|
||||
|
||||
def test_wc_wildcard1a_text(self): # simple text wildcard
|
||||
|
||||
Reference in New Issue
Block a user