From c03339a6188b4df6970f5c0acd338bdd3ddcfff4 Mon Sep 17 00:00:00 2001 From: Antonio Cordero Balcazar Date: Tue, 13 May 2025 21:38:02 +0200 Subject: [PATCH] * Better processing of interruptions using an exception. * New option on_warning to stop on invalid content. * New option cleanup_extra_separators_include_eol to choose whether to also remove adjacent EOLs when removing extra separators or not. Default is not to do it. In previous versions it was forced. * Refactoring of the cleanup method. * Only simplify attention modifiers when the merge attention option is set. * A1111: avoid logging about HR prompts and avoid saving them in extra metadata when not different. * A1111: consider options and environment info in the caching of results. * Some refactorings. --- .gitignore | 2 + README.md | 2 +- docs/CONFIG.md | 2 + ppp.py | 212 ++++++++------ ppp_comfyui.py | 24 ++ ppp_wildcards.py | 8 +- pyproject.toml | 2 +- scripts/ppp_script.py | 61 +++- tests/__init__.py | 0 tests/tests.py | 560 ++++++++++++++++++++----------------- workflows/PPP example.json | 2 +- 11 files changed, 527 insertions(+), 348 deletions(-) create mode 100644 tests/__init__.py diff --git a/.gitignore b/.gitignore index c35a98a..6bc46b7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ .vscode/**/* !.vscode/settings.json !.vscode/launch.json + +tests/tests_local.py diff --git a/README.md b/README.md index 7897693..0ce737d 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,12 @@ The Prompt PostProcessor (PPP), formerly known as "sd-webui-sendtonegative", is an extension designed to process the prompt, possibly after other extensions have modified it. This extension is compatible with: +* [ComfyUI](https://github.com/comfyanonymous/ComfyUI) * [AUTOMATIC1111 Stable Diffusion WebUI](https://github.com/AUTOMATIC1111/stable-diffusion-webui) * [Forge](https://github.com/lllyasviel/stable-diffusion-webui-forge) * [reForge](https://github.com/Panchovix/stable-diffusion-webui-reForge) * [SD.Next](https://github.com/vladmandic/automatic) * ...and probably other forks -* [ComfyUI](https://github.com/comfyanonymous/ComfyUI) Currently this extension has these functions: diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 7dfb7e6..577a5c2 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -33,6 +33,7 @@ With this prompt: `__quality__, 1girl, ${head:__eyes__, __hair__, __expression__ ### General settings * **Debug level**: what to write to the console. Note: in *SD.Next* debug messages only show if you launch it with the `--debug` argument. +* **What to do on invalid content warnings?**: warn on the console or stop the generation. * **Model variant definitions**: definitions for model variants to be recognized based on strings found in the full filename. The format for each line is (with *kind* being one of the base model identifiers or not defined): @@ -65,6 +66,7 @@ With this prompt: `__quality__, 1girl, ${head:__eyes__, __hair__, __expression__ * **Remove empty constructs**: removes attention/scheduling/alternation constructs when they are invalid. * **Remove extra separators**: removes unnecessary separators. This applies to the configured separator and regular commas. * **Remove additional extra separators**: removes unnecessary separators at start or end of lines. This applies to the configured separator and regular commas. +* **The extra separators options also remove EOLs**: in the previous two options it also removes EOLs attached to the separators. * **Clean up around BREAKs**: removes consecutive BREAKs and unnecessary commas and space around them. * **Use EOL instead of Space before BREAKs**: add a newline before BREAKs. * **Clean up around ANDs**: removes consecutive ANDs and unnecessary commas and space around them. diff --git a/ppp.py b/ppp.py index 25f11e6..b405870 100644 --- a/ppp.py +++ b/ppp.py @@ -2,20 +2,30 @@ import logging import math import os import re -import sys import textwrap import time + from collections import namedtuple from enum import Enum from typing import Any, Callable, Optional - import lark import numpy as np -sys.path.append(os.path.dirname(os.path.realpath(__file__))) +from ppp_logging import DEBUG_LEVEL # pylint: disable=import-error +from ppp_wildcards import PPPWildcard, PPPWildcards # pylint: disable=import-error -from ppp_logging import DEBUG_LEVEL # pylint: disable=import-error -from ppp_wildcards import PPPWildcard, PPPWildcards # pylint: disable=import-error + +class PPPInterrupt(Exception): + """ + Custom exception to handle interruptions in the PromptPostProcessor. + This exception can be raised to stop the processing of prompts. + """ + + def __init__(self, message: str = "Processing interrupted.", pos_prefix: str = "", neg_prefix: str = ""): + super().__init__(message) + self.message = message + self.pos_prefix = pos_prefix + self.neg_prefix = neg_prefix class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-instance-attributes @@ -52,12 +62,17 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in warn = "warn" stop = "stop" + class ONWARNING_CHOICES(Enum): + warn = "warn" + stop = "stop" + DEFAULT_STN_SEPARATOR = ", " DEFAULT_VARIANTS_DEFINITIONS = "pony(sdxl)=pony,pny,pdxl\nillustrious(sdxl)=illustrious,illust,ilxl" DEFAULT_CHOICE_SEPARATOR = ", " WILDCARD_WARNING = '(WARNING TEXT "INVALID WILDCARD" IN BRIGHT RED:1.5)\nBREAK ' WILDCARD_STOP = "INVALID WILDCARD! {0}\nBREAK " UNPROCESSED_STOP = "UNPROCESSED CONSTRUCTS!\nBREAK " + INVALID_CONTENT_STOP = "INVALID CONTENT! {0}\nBREAK " SUPPORTED_MODELS = [ "sd1", @@ -97,6 +112,7 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in # General options self.debug_level = DEBUG_LEVEL(options.get("debug_level", DEBUG_LEVEL.none.value)) + self.gen_onwarning = self.ONWARNING_CHOICES(options.get("on_warning", self.ONWARNING_CHOICES.warn.value)) variants_definitions_option = str(options.get("variants_definitions", self.DEFAULT_VARIANTS_DEFINITIONS)) self.variants_definitions = {} if variants_definitions_option: @@ -131,6 +147,7 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in self.cup_emptyconstructs = options.get("cleanup_empty_constructs", True) self.cup_extraseparators = options.get("cleanup_extra_separators", True) self.cup_extraseparators2 = options.get("cleanup_extra_separators2", True) + self.cup_extraseparators_include_eol = options.get("cleanup_extra_separators_include_eol", False) self.cup_breaks = options.get("cleanup_breaks", True) self.cup_breaks_eol = options.get("cleanup_breaks_eol", False) self.cup_ands = options.get("cleanup_ands", True) @@ -331,36 +348,33 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in str: The resulting text. """ escapedSeparator = re.escape(self.stn_separator) - if self.cup_extraseparators: - # - # sendtonegative separator - # - # collapse separators - text = re.sub(r"(?:\s*" + escapedSeparator + r"\s*){2,}", self.stn_separator, text) - # remove separator after starting parenthesis or bracket - text = re.sub(r"(\s*" + escapedSeparator + r"\s*[([])(?:\s*" + escapedSeparator + r"\s*)+", r"\1", text) - # remove before colon or ending parenthesis or bracket - text = re.sub(r"(?:\s*" + escapedSeparator + r"\s*)+([:)\]]\s*" + escapedSeparator + r"\s*)", r"\1", text) - if self.cup_extraseparators2: - # remove at start of prompt or line - text = re.sub(r"^(?:\s*" + escapedSeparator + r"\s*)+", "", text, flags=re.MULTILINE) - # remove at end of prompt or line - text = re.sub(r"(?:\s*" + escapedSeparator + r"\s*)+$", "", text, flags=re.MULTILINE) - if self.cup_extraseparators: - # - # regular comma separator - # - # collapse separators - text = re.sub(r"(?:\s*,\s*){2,}", ", ", text) - # remove separators after starting parenthesis or bracket - text = re.sub(r"(\s*,\s*[([])(?:\s*,\s*)+", r"\1", text) - # remove separators before colon or ending parenthesis or bracket - text = re.sub(r"(?:\s*,\s*)+([:)\]]\s*,\s*)", r"\1", text) - if self.cup_extraseparators2: - # remove at start of prompt or line - text = re.sub(r"^(?:\s*,\s*)+", "", text, flags=re.MULTILINE) - # remove at end of prompt or line - text = re.sub(r"(?:\s*,\s*)+$", "", text, flags=re.MULTILINE) + optwhitespace = r"\s*" if self.cup_extraseparators_include_eol else r"[ \t\v\f]*" + optwhitespace_separator = optwhitespace + escapedSeparator + optwhitespace + optwhitespace_comma = optwhitespace + "," + optwhitespace + sep_options = [(optwhitespace_separator, self.stn_separator)] # sendtonegative separator + if optwhitespace_comma != optwhitespace_separator: + sep_options.append((optwhitespace_comma, ", ")) # regular comma separator + for sep, replacement in sep_options: + if self.cup_extraseparators: + # collapse separators + text = re.sub(r"(?:" + sep + r"){2,}", replacement, text) + # remove separator after starting parenthesis or bracket + text = re.sub( + r"(" + sep + r"[([])(?:" + sep + r")+", + r"\1", + text, + ) + # remove before colon or ending parenthesis or bracket + text = re.sub( + r"(?:" + sep + r")+([:)\]]" + sep + r")", + r"\1", + text, + ) + if self.cup_extraseparators2: + # remove at start of prompt or line + text = re.sub(r"^(?:" + sep + r")+", "", text, flags=re.MULTILINE) + # remove at end of prompt or line + text = re.sub(r"(?:" + sep + r")+$", "", text, flags=re.MULTILINE) if self.cup_breaks_eol: # replace spaces before break with EOL text = re.sub(r"[, ]+BREAK\b", "\nBREAK", text) @@ -482,12 +496,11 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in if self.wil_ifwildcards == self.IFWILDCARDS_CHOICES.warn: prompt = self.WILDCARD_WARNING + prompt elif self.wil_ifwildcards == self.IFWILDCARDS_CHOICES.stop: - self.logger.error("Stopping the generation.") - if foundP: - prompt = self.WILDCARD_STOP.format(ppwl) + prompt - if foundNP: - negative_prompt = self.WILDCARD_STOP.format(npwl) + negative_prompt - self.interrupt() + raise PPPInterrupt( + "Found unprocessed wildcards!", + self.WILDCARD_STOP.format(ppwl) if foundP else "", + self.WILDCARD_STOP.format(npwl) if foundNP else "", + ) # Check for special character sequences that should not be in the result compound_prompt = prompt + "\n" + negative_prompt @@ -541,9 +554,20 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in # Check for constructs not processed due to parsing problems fullcontent: str = prompt + negative_prompt if fullcontent.find("= 0: - self.logger.error("Found unprocessed constructs in prompt or negative prompt! Stopping the generation.") - prompt = self.UNPROCESSED_STOP + prompt - self.interrupt() + raise PPPInterrupt( + "Found unprocessed constructs!", + self.UNPROCESSED_STOP if prompt.find("= 0 else "", + self.UNPROCESSED_STOP if negative_prompt.find("= 0 else "", + ) + return prompt, negative_prompt, all_variables + except PPPInterrupt as e: + self.logger.error(e.message) + if e.pos_prefix: + prompt = e.pos_prefix + prompt + if e.neg_prefix: + negative_prompt = e.neg_prefix + negative_prompt + self.logger.error("Interrupting!") + self.interrupt() return prompt, negative_prompt, all_variables except Exception as e: # pylint: disable=broad-exception-caught self.logger.exception(e) @@ -613,6 +637,15 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in self.detectedWildcards: list[str] = [] self.result = "" + def warn_or_stop(self, message: str, e: Exception = None): + if self.__ppp.gen_onwarning == self.__ppp.ONWARNING_CHOICES.stop: + raise PPPInterrupt( + message, + self.__ppp.INVALID_CONTENT_STOP.format(message) if not self.__is_negative else "", + self.__ppp.INVALID_CONTENT_STOP.format(message) if self.__is_negative else "", + ) from e + self.__ppp.logger.warning(message) + def start_visit(self, prompt_description: str, parsed_prompt: lark.Tree, is_negative: bool = False) -> str: """ Start the visit process. @@ -780,12 +813,12 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in var_value = self.__ppp.system_variables.get(cond_var, None) if var_value is None: var_value = "" - self.__ppp.logger.warning(f"Unknown system variable {cond_var}") + self.warn_or_stop(f"Unknown system variable {cond_var}") else: # user variable var_value = self.__get_user_variable_value(cond_var) if var_value is None: var_value = "" - self.__ppp.logger.warning(f"Unknown user variable {cond_var}") + self.warn_or_stop(f"Unknown user variable {cond_var}") if isinstance(var_value, str): var_value = var_value.lower() if isinstance(cond_value, list): @@ -987,11 +1020,14 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in if len(tree.children) == 2: weight_str = tree.children[-1] if weight_str is not None: + weight_kind = 2 # specific weight weight = float(weight_str) else: + weight_kind = 1 # increase attention weight = 1.1 weight_str = "1.1" else: + weight_kind = 0 # decrease attention weight = 0.9 weight_str = "0.9" if self.__ppp.debug_level == DEBUG_LEVEL.full: @@ -1012,13 +1048,22 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in current_tree = current_tree.children[0] weight = math.floor(weight * 100) / 100 # we round to 2 decimals weight_str = f"{weight:.2f}".rstrip("0").rstrip(".") - self.__shell.append(self.AccumulatedShell("at", weight)) - if weight == 0.9 and not self.__ppp.is_comfy_ui(): + if weight_str == "0.9": + weight_kind = 0 + elif weight_str == "1.1": + weight_kind = 1 + else: + weight_kind = 2 + if weight_kind == 0 and self.__ppp.is_comfy_ui(): + weight_kind = 2 + weight_str = "0.9" + self.__shell.append(self.AccumulatedShell("at", (weight_kind, weight_str))) + if weight_kind == 0: starttag = "[" self.result += starttag self.__visit(current_tree) endtag = "]" - elif weight == 1.1: + elif weight_kind == 1: starttag = "(" self.result += starttag self.__visit(current_tree) @@ -1055,7 +1100,7 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in ) info = f"with {parameters or 'no parameters'} : {content}" else: - self.__ppp.logger.warning("Ignored negative command in negative prompt") + self.warn_or_stop("Ignored negative command in negative prompt") self.__visit(tree.children[1::]) t2 = time.time() self.__debug_end("commandstn", start_result, t2 - t1, info) @@ -1078,7 +1123,7 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in ) info = f"with {parameters or 'no parameters'}" else: - self.__ppp.logger.warning("Ignored negative insertion point command in positive prompt") + self.warn_or_stop("Ignored negative insertion point command in positive prompt") t2 = time.time() self.__debug_end("commandstni", start_result, t2 - t1, info) @@ -1095,8 +1140,7 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in t1 = time.time() start_result = self.result if variable.startswith("_"): - self.__ppp.logger.warning(f"Invalid variable name '{variable}' detected!") - self.__ppp.interrupt() + self.warn_or_stop(f"Invalid variable name '{variable}' detected! System variables cannot be set.") return info = variable value_description = self.__get_original_node_content(content, None) @@ -1107,7 +1151,7 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in raw_oldvalue = self.__ppp.user_variables.get(variable, None) if raw_oldvalue is None: newvalue = value - self.__ppp.logger.warning(f"Unknown variable {variable}") + self.warn_or_stop(f"Unknown variable {variable}") elif isinstance(raw_oldvalue, str): newvalue = lark.Tree( lark.Token("RULE", "varvalue"), @@ -1177,7 +1221,7 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in self.__ppp.echoed_variables[variable] = v self.result += v else: - self.__ppp.logger.warning(f"Unknown variable {variable}") + self.warn_or_stop(f"Unknown variable {variable}") t2 = time.time() info = variable if default is not None: @@ -1264,9 +1308,8 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in separator: str = options.get("separator", self.__ppp.wil_choice_separator) if sampler != "~": msg = f"wildcard '{wildcard_key}'" if wildcard_key else "choices" - self.__ppp.logger.warning(f"Unsupported sampler '{sampler}' in {msg} options!") - self.__ppp.interrupt() - return ("", [], separator, "") + self.warn_or_stop(f"Unsupported sampler '{sampler}' in {msg} options!") + sampler = "~" if filter_specifier is not None: filtered_choice_values = [] for i, c in enumerate(choice_values): @@ -1287,7 +1330,7 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in if passes: filtered_choice_values.append(c) if not filtered_choice_values: - self.__ppp.logger.warning( + self.warn_or_stop( f"Wildcard filter specifier '{','.join(['+'.join(y for y in x) for x in filter_specifier])}' found no matches in choices for wildcard '{wildcard_key}'!" ) else: @@ -1376,7 +1419,7 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in # remove comments results = [re.sub(r"\s*#[^\n]*(?:\n|$)", "", r, flags=re.DOTALL) for r in selected_choices_text] return (prefix, results, separator, suffix) - return ("", "", separator, "") + return ("", [], separator, "") def __get_choices( self, @@ -1473,10 +1516,10 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in "choicevalue", prefix, self.__ppp.parser_choicevalue, True ) except lark.exceptions.UnexpectedInput as e: - self.__ppp.logger.warning( - f"Error parsing choice prefix '{prefix}' in wildcard '{wildcard.key}'! : {e.__class__.__name__}" + self.warn_or_stop( + f"Error parsing choice prefix '{prefix}' in wildcard '{wildcard.key}'! : {e.__class__.__name__}", + e, ) - del options["prefix"] suffix = options.get("suffix", None) if suffix is not None and isinstance(suffix, str): try: @@ -1484,10 +1527,10 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in "choicevalue", suffix, self.__ppp.parser_choicevalue, True ) except lark.exceptions.UnexpectedInput as e: - self.__ppp.logger.warning( - f"Error parsing choice suffix '{suffix}' in wildcard '{wildcard.key}'! : {e.__class__.__name__}" + self.warn_or_stop( + f"Error parsing choice suffix '{suffix}' in wildcard '{wildcard.key}'! : {e.__class__.__name__}", + e, ) - del options["suffix"] n = 1 else: try: @@ -1516,8 +1559,9 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in "condition", condition, self.__ppp.parser_condition, True ) except lark.exceptions.UnexpectedInput as e: - self.__ppp.logger.warning( - f"Error parsing condition '{condition}' in wildcard '{wildcard.key}'! : {e.__class__.__name__}" + self.warn_or_stop( + f"Error parsing condition '{condition}' in wildcard '{wildcard.key}'! : {e.__class__.__name__}", + e, ) cv["if"] = None content = cv.get("content", cv.get("text", None)) @@ -1530,8 +1574,9 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in "choicevalue", content, self.__ppp.parser_choicevalue, True ) except lark.exceptions.UnexpectedInput as e: - self.__ppp.logger.warning( - f"Error parsing choice content '{content}' in wildcard '{wildcard.key}'! : {e.__class__.__name__}" + self.warn_or_stop( + f"Error parsing choice content '{content}' in wildcard '{wildcard.key}'! : {e.__class__.__name__}", + e, ) cv["content"] = None if cv["content"] is not None: @@ -1539,9 +1584,9 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in self.__ppp.logger.debug(f"Processed choice {cv}") choice_values.append(cv) else: - self.__ppp.logger.warning(f"Invalid choice {cv} in wildcard '{wildcard.key}'!") + self.warn_or_stop(f"Invalid choice {cv} in wildcard '{wildcard.key}'!") else: - self.__ppp.logger.warning(f"Invalid choice '{cv}' in wildcard '{wildcard.key}'!") + self.warn_or_stop(f"Invalid choice {cv} in wildcard '{wildcard.key}'!") else: try: choice_values.append( @@ -1550,8 +1595,8 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in ) ) except lark.exceptions.UnexpectedInput as e: - self.__ppp.logger.warning( - f"Error parsing choice '{cv}' in wildcard '{wildcard.key}'! : {e.__class__.__name__}" + self.warn_or_stop( + f"Error parsing choice '{cv}' in wildcard '{wildcard.key}'! : {e.__class__.__name__}", e ) wildcard.choices = choice_values t2 = time.time() @@ -1683,11 +1728,19 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in for i in range(len(negtag.shell) - 1, 0, -1): if negtag.shell[i].type == "at" and negtag.shell[i - 1].type == "at": new_weight = ( # we limit the new weight to two decimals - math.floor(100 * negtag.shell[i - 1].data * negtag.shell[i].data) / 100 + math.floor(100 * float(negtag.shell[i - 1].data[1]) * float(negtag.shell[i].data[1])) + / 100 ) + new_weight_str = f"{new_weight:.2f}".rstrip("0").rstrip(".") + if new_weight_str == "0.9" and not self.__ppp.is_comfy_ui(): + new_kind = 0 + elif new_weight_str == "1.1": + new_kind = 1 + else: + new_kind = 2 negtag.shell[i - 1] = self.AccumulatedShell( "at", - new_weight, + (new_kind, new_weight_str), ) negtag.shell.pop(i) start = "" @@ -1695,16 +1748,15 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in for s in negtag.shell: match s.type: case "at": - if s.data == 0.9: + if s.data[0] == 0: start += "[" end = "]" + end - elif s.data == 1.1: + elif s.data[0] == 1: start += "(" end = ")" + end else: start += "(" - weight_str = f"{s.data:.2f}".rstrip("0").rstrip(".") - end = f":{weight_str})" + end + end = f":{s.data[1]})" + end # case "sc": case "scb": start += "[" diff --git a/ppp_comfyui.py b/ppp_comfyui.py index 8ef877d..776cd02 100644 --- a/ppp_comfyui.py +++ b/ppp_comfyui.py @@ -87,6 +87,13 @@ class PromptPostProcessorComfyUINode: "tooltip": "Debug level", }, ), + "on_warnings": ( + [e.value for e in PromptPostProcessor.ONWARNING_CHOICES], + { + "default": PromptPostProcessor.ONWARNING_CHOICES.warn.value, + "tooltip": "How to handle invalid content warnings", + }, + ), "variants_definitions": ( "STRING", { @@ -201,6 +208,15 @@ class PromptPostProcessorComfyUINode: "label_off": "No", }, ), + "cleanup_extra_separators_include_eol": ( + "BOOLEAN", + { + "default": False, + "tooltip": "Extra separators options also remove EOLs", + "label_on": "Yes", + "label_off": "No", + }, + ), "cleanup_breaks": ( "BOOLEAN", { @@ -304,6 +320,7 @@ class PromptPostProcessorComfyUINode: neg_prompt, seed, debug_level, # pylint: disable=unused-argument + on_warnings, variants_definitions, wc_process_wildcards, wc_wildcards_folders, @@ -317,6 +334,7 @@ class PromptPostProcessorComfyUINode: cleanup_empty_constructs, cleanup_extra_separators, cleanup_extra_separators2, + cleanup_extra_separators_include_eol, cleanup_breaks, cleanup_breaks_eol, cleanup_ands, @@ -335,6 +353,7 @@ class PromptPostProcessorComfyUINode: "pos_prompt": pos_prompt, "neg_prompt": neg_prompt, "seed": seed, + "on_warnings": on_warnings, "variants_definitions": variants_definitions, "process_wildcards": wc_process_wildcards, "wildcards_folders": wc_wildcards_folders, @@ -348,6 +367,7 @@ class PromptPostProcessorComfyUINode: "cleanup_empty_constructs": cleanup_empty_constructs, "cleanup_extra_separators": cleanup_extra_separators, "cleanup_extra_separators2": cleanup_extra_separators2, + "cleanup_extra_separators_include_eol": cleanup_extra_separators_include_eol, "cleanup_breaks": cleanup_breaks, "cleanup_breaks_eol": cleanup_breaks_eol, "cleanup_ands": cleanup_ands, @@ -367,6 +387,7 @@ class PromptPostProcessorComfyUINode: neg_prompt, seed, debug_level, + on_warnings, variants_definitions, wc_process_wildcards, wc_wildcards_folders, @@ -380,6 +401,7 @@ class PromptPostProcessorComfyUINode: cleanup_empty_constructs, cleanup_extra_separators, cleanup_extra_separators2, + cleanup_extra_separators_include_eol, cleanup_breaks, cleanup_breaks_eol, cleanup_ands, @@ -436,6 +458,7 @@ class PromptPostProcessorComfyUINode: raise ValueError("Invalid variants_definitions format") options = { "debug_level": debug_level, + "on_warnings": on_warnings, "variants_definitions": variants_definitions, "process_wildcards": wc_process_wildcards, "if_wildcards": wc_if_wildcards, @@ -447,6 +470,7 @@ class PromptPostProcessorComfyUINode: "cleanup_empty_constructs": cleanup_empty_constructs, "cleanup_extra_separators": cleanup_extra_separators, "cleanup_extra_separators2": cleanup_extra_separators2, + "cleanup_extra_separators_include_eol": cleanup_extra_separators_include_eol, "cleanup_breaks": cleanup_breaks, "cleanup_breaks_eol": cleanup_breaks_eol, "cleanup_ands": cleanup_ands, diff --git a/ppp_wildcards.py b/ppp_wildcards.py index dea87d8..fed587e 100644 --- a/ppp_wildcards.py +++ b/ppp_wildcards.py @@ -91,8 +91,8 @@ class PPPWildcards: """ self.__debug_level = debug_level self.__wildcards_folders = wildcards_folders or [] - # if self.debug_level != DEBUG_LEVEL.none: - # self.logger.info("Refreshing wildcards...") + # if self.__debug_level != DEBUG_LEVEL.none: + # self.__logger.info("Refreshing wildcards...") # t1 = time.time() for fullpath in list(self.__wildcard_files.keys()): if fullpath != self.LOCALINPUT_FILENAME: @@ -113,8 +113,8 @@ class PPPWildcards: self.wildcards = {} self.__wildcard_files = {} # t2 = time.time() - # if self.debug_level != DEBUG_LEVEL.none: - # self.logger.info(f"Wildcards refresh time: {t2 - t1:.3f} seconds") + # if self.__debug_level != DEBUG_LEVEL.none: + # self.__logger.info(f"Wildcards refresh time: {t2 - t1:.3f} seconds") def get_wildcards(self, key: str) -> list[PPPWildcard]: """ diff --git a/pyproject.toml b/pyproject.toml index 910868a..644cb27 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "sd-webui-prompt-postprocessor" description = "Stable Diffusion WebUI & ComfyUI extension to post-process the prompt, including sending content from the prompt to the negative prompt and wildcards." -version = "2.10.0" +version = "2.11.0" license = { file = "LICENSE.txt" } dependencies = ["lark", "numpy", "pyyaml"] diff --git a/scripts/ppp_script.py b/scripts/ppp_script.py index 02e861e..3e25e63 100644 --- a/scripts/ppp_script.py +++ b/scripts/ppp_script.py @@ -4,9 +4,10 @@ if __name__ == "__main__": import sys import os import time +from pathlib import Path import numpy as np -sys.path.append(os.path.join(os.path.realpath(__file__), "..")) # base path for the extension +sys.path.append(str(Path(__file__).parent)) # base path for the extension from modules import scripts, shared, script_callbacks # pylint: disable=import-error from modules.processing import StableDiffusionProcessing # pylint: disable=import-error @@ -279,6 +280,7 @@ class PromptPostProcessorA1111Script(scripts.Script): env_info["is_sd3"] = getattr(p.sd_model, "is_sd3", False) env_info["is_flux"] = False env_info["is_auraflow"] = False + hash_envinfo = hash(tuple(sorted(env_info.items()))) wc_wildcards_folders = getattr(opts, "ppp_wil_wildcardsfolders", "") if wc_wildcards_folders == "": wc_wildcards_folders = os.getenv("WILDCARD_DIR", PPPWildcards.DEFAULT_WILDCARDS_FOLDER) @@ -289,6 +291,7 @@ class PromptPostProcessorA1111Script(scripts.Script): ] options = { "debug_level": getattr(opts, "ppp_gen_debug_level", DEBUG_LEVEL.none.value), + "on_warning": getattr(opts, "ppp_gen_onwarning", PromptPostProcessor.ONWARNING_CHOICES.warn.value), "variants_definitions": getattr( opts, "ppp_gen_variantsdefinitions", PromptPostProcessor.DEFAULT_VARIANTS_DEFINITIONS ), @@ -302,6 +305,7 @@ class PromptPostProcessorA1111Script(scripts.Script): "cleanup_empty_constructs": getattr(opts, "ppp_cup_emptyconstructs", True), "cleanup_extra_separators": getattr(opts, "ppp_cup_extraseparators", True), "cleanup_extra_separators2": getattr(opts, "ppp_cup_extraseparators2", True), + "cleanup_extra_separators_include_eol": getattr(opts, "ppp_cup_extraseparators_include_eol", True), "cleanup_breaks": getattr(opts, "ppp_cup_breaks", True), "cleanup_breaks_eol": getattr(opts, "ppp_cup_breaks_eol", False), "cleanup_ands": getattr(opts, "ppp_cup_ands", True), @@ -310,6 +314,7 @@ class PromptPostProcessorA1111Script(scripts.Script): "cleanup_merge_attention": getattr(opts, "ppp_cup_mergeattention", True), "remove_extranetwork_tags": getattr(opts, "ppp_rem_removeextranetworktags", False), } + hash_options = hash(tuple(sorted(options.items()))) self.wildcards_obj.refresh_wildcards( self.ppp_debug_level, wildcards_folders if options["process_wildcards"] else None ) @@ -367,7 +372,7 @@ class PromptPostProcessorA1111Script(scripts.Script): # make it compatible with A1111 hires fix rph: list[str] = getattr(p, "all_hr_prompts", None) rnh: list[str] = getattr(p, "all_hr_negative_prompts", None) - if rph is not None and rnh is not None: + if rph is not None and rnh is not None and (rph != rpr or rnh != rnr): prompts_list += [ ("hiresfix", seed, prompt, negative_prompt) for seed, prompt, negative_prompt in zip(calculated_seeds, rph, rnh) @@ -378,22 +383,35 @@ class PromptPostProcessorA1111Script(scripts.Script): for i, (prompttype, seed, prompt, negative_prompt) in enumerate(prompts_list): if self.ppp_debug_level != DEBUG_LEVEL.none: self.ppp_logger.info(f"processing prompts[{i+1}] ({prompttype})") - if self.lru_cache.get((seed, hash(self.wildcards_obj), prompt, negative_prompt)) is None: + if ( + self.lru_cache.get( + (hash_envinfo, hash_options, seed, hash(self.wildcards_obj), prompt, negative_prompt) + ) + is None + ): posp, negp, _ = ppp.process_prompt(prompt, negative_prompt, seed) - self.lru_cache.put((seed, hash(self.wildcards_obj), prompt, negative_prompt), (posp, negp)) + self.lru_cache.put( + (hash_envinfo, hash_options, seed, hash(self.wildcards_obj), prompt, negative_prompt), (posp, negp) + ) # adds also the result so i2i doesn't process it unnecessarily - self.lru_cache.put((seed, hash(self.wildcards_obj), posp, negp), (posp, negp)) + self.lru_cache.put( + (hash_envinfo, hash_options, seed, hash(self.wildcards_obj), posp, negp), (posp, negp) + ) elif self.ppp_debug_level != DEBUG_LEVEL.none: self.ppp_logger.info("result already in cache") # updates the prompts + rpr_copy = None + rnr_copy = None if rpr is not None and rnr is not None: rpr_changes = False rnr_changes = False rpr_copy = rpr.copy() rnr_copy = rnr.copy() for i, (seed, prompt, negative_prompt) in enumerate(zip(calculated_seeds, rpr, rnr)): - found = self.lru_cache.get((seed, hash(self.wildcards_obj), prompt, negative_prompt)) + found = self.lru_cache.get( + (hash_envinfo, hash_options, seed, hash(self.wildcards_obj), prompt, negative_prompt) + ) if found is not None: if rpr[i].strip() != found[0].strip(): rpr_changes = True @@ -412,11 +430,13 @@ class PromptPostProcessorA1111Script(scripts.Script): rph_copy = rph.copy() rnh_copy = rnh.copy() for i, (seed, prompt, negative_prompt) in enumerate(zip(calculated_seeds, rph, rnh)): - found = self.lru_cache.get((seed, hash(self.wildcards_obj), prompt, negative_prompt)) + found = self.lru_cache.get( + (hash_envinfo, hash_options, seed, hash(self.wildcards_obj), prompt, negative_prompt) + ) if found is not None: - if rph[i].strip() != found[0].strip(): + if rph[i].strip() != found[0].strip() and (not rpr_copy or rph[i].strip() != rpr_copy[i].strip()): rph_changes = True - if rnh[i].strip() != found[1].strip(): + if rnh[i].strip() != found[1].strip() and (not rnr_copy or rnh[i].strip() != rnr_copy[i].strip()): rnh_changes = True rph[i] = found[0] rnh[i] = found[1] @@ -502,6 +522,21 @@ def on_ui_settings(): section=section, ), ) + shared.opts.add_option( + key="ppp_gen_onwarning", + info=shared.OptionInfo( + default=PromptPostProcessor.ONWARNING_CHOICES.warn.value, + label="What to do on invalid content warnings?", + component=gr.Radio, + component_args={ + "choices": ( + ("Show warning in console", PromptPostProcessor.ONWARNING_CHOICES.warn.value), + ("Stop the generation", PromptPostProcessor.ONWARNING_CHOICES.stop.value), + ) + }, + section=section, + ), + ) shared.opts.add_option( key="ppp_gen_variantsdefinitions", info=shared.OptionInfo( @@ -656,6 +691,14 @@ def on_ui_settings(): section=section, ), ) + shared.opts.add_option( + key="ppp_cup_extraseparators_include_eol", + info=shared.OptionInfo( + False, + label="The extra separators options also remove EOLs", + section=section, + ), + ) shared.opts.add_option( key="ppp_cup_breaks", info=shared.OptionInfo( diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/tests.py b/tests/tests.py index fd8e63f..4f1f567 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,20 +1,19 @@ -from collections import namedtuple -import logging -import unittest -import sys import os - -sys.path.append(os.path.join(os.path.realpath(__file__), "..")) # base path for the extension +import logging +from typing import NamedTuple, Optional +import unittest from ppp_wildcards import PPPWildcards # pylint: disable=import-error from ppp import PromptPostProcessor # pylint: disable=import-error from ppp_logging import DEBUG_LEVEL, PromptPostProcessorLogFactory # pylint: disable=import-error -PromptPair = namedtuple("PromptPair", ["prompt", "negative_prompt"], defaults=["", ""]) +class PromptPair(NamedTuple): + prompt: str = "" + negative_prompt: str = "" -class TestPromptPostProcessor(unittest.TestCase): +class TestPromptPostProcessorBase(unittest.TestCase): """ A test case class for testing the PromptPostProcessor class. """ @@ -23,11 +22,17 @@ class TestPromptPostProcessor(unittest.TestCase): """ Set up the test case by initializing the necessary objects and configurations. """ - lf = PromptPostProcessorLogFactory() - self.__ppp_logger = lf.log - self.__ppp_logger.setLevel(logging.DEBUG) - self.__defopts = { + self.lf = PromptPostProcessorLogFactory() + self.ppp_logger = self.lf.log + self.ppp_logger.setLevel(logging.DEBUG) + self.grammar_content = None + self.defppp = None + self.nocupppp = None + self.comfyuippp = None + self.interrupted = False + self.defopts = { "debug_level": DEBUG_LEVEL.full.value, + "on_warning": PromptPostProcessor.ONWARNING_CHOICES.stop.value, "variants_definitions": PromptPostProcessor.DEFAULT_VARIANTS_DEFINITIONS, "process_wildcards": True, "if_wildcards": PromptPostProcessor.IFWILDCARDS_CHOICES.ignore.value, @@ -38,6 +43,7 @@ class TestPromptPostProcessor(unittest.TestCase): "cleanup_empty_constructs": True, "cleanup_extra_separators": True, "cleanup_extra_separators2": True, + "cleanup_extra_separators_include_eol": False, "cleanup_extra_spaces": True, "cleanup_breaks": True, "cleanup_breaks_eol": False, @@ -47,7 +53,7 @@ class TestPromptPostProcessor(unittest.TestCase): "cleanup_merge_attention": True, "remove_extranetwork_tags": False, } - self.__def_env_info = { + self.def_env_info = { "app": "tests", "is_sd1": False, "is_sd2": False, @@ -60,9 +66,9 @@ class TestPromptPostProcessor(unittest.TestCase): "models_path": "./webui/models", "model_filename": "./webui/models/Stable-diffusion/testmodel.safetensors", } - self.__interrupted = False - self.__wildcards_obj = PPPWildcards(lf.log) - self.__wildcards_obj.refresh_wildcards( + self.interrupted = False + self.wildcards_obj = PPPWildcards(self.lf.log) + self.wildcards_obj.refresh_wildcards( DEBUG_LEVEL.full, [ os.path.abspath(os.path.join(os.path.dirname(__file__), "wildcards")), @@ -74,28 +80,29 @@ class TestPromptPostProcessor(unittest.TestCase): - choice1 - choice2 - choice3 - """ + """, ) grammar_filename = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../grammar.lark") with open(grammar_filename, "r", encoding="utf-8") as file: - self.__grammar_content = file.read() - self.__defppp = PromptPostProcessor( - self.__ppp_logger, - self.__interrupt, - self.__def_env_info, - self.__defopts, - self.__grammar_content, - self.__wildcards_obj, + self.grammar_content = file.read() + self.defppp = PromptPostProcessor( + self.ppp_logger, + self.interrupt, + self.def_env_info, + self.defopts, + self.grammar_content, + self.wildcards_obj, ) - self.__nocupppp = PromptPostProcessor( - self.__ppp_logger, - self.__interrupt, - self.__def_env_info, + self.nocupppp = PromptPostProcessor( + self.ppp_logger, + self.interrupt, + self.def_env_info, { - **self.__defopts, + **self.defopts, "cleanup_empty_constructs": False, "cleanup_extra_separators": False, "cleanup_extra_separators2": False, + "cleanup_extra_separators_include_eol": False, "cleanup_extra_spaces": False, "cleanup_breaks": False, "cleanup_breaks_eol": False, @@ -104,39 +111,39 @@ class TestPromptPostProcessor(unittest.TestCase): "cleanup_extranetwork_tags": False, "cleanup_merge_attention": False, }, - self.__grammar_content, - self.__wildcards_obj, + self.grammar_content, + self.wildcards_obj, ) - self.__comfyuippp = PromptPostProcessor( - self.__ppp_logger, - self.__interrupt, + self.comfyuippp = PromptPostProcessor( + self.ppp_logger, + self.interrupt, { - **self.__def_env_info, + **self.def_env_info, "app": "comfyui", "model_class": "SDXL", }, - self.__defopts, - self.__grammar_content, - self.__wildcards_obj, + self.defopts, + self.grammar_content, + self.wildcards_obj, ) - def __interrupt(self): - self.__interrupted = True + def interrupt(self): + self.interrupted = True - def __process( + def process( self, input_prompts: PromptPair, - expected_output_prompts: PromptPair | list[PromptPair], + expected_output_prompts: Optional[PromptPair | list[PromptPair]] = None, seed: int = 1, - ppp=None, - interrupted=False, + ppp: Optional[PromptPostProcessor] = None, + interrupted: bool = False, ): """ Process the prompt and compare the results with the expected prompts. Args: input_prompts (PromptPair): The input prompts. - expected_output_prompts (PromptPair | list[PromptPair]): The expected prompts. + expected_output_prompts (PromptPair | list[PromptPair], optional): The expected prompts. seed (int, optional): The seed value. Defaults to 1. ppp (object, optional): The post-processor object. Defaults to None. interrupted (bool, optional): The interrupted flag. Defaults to False. @@ -144,24 +151,31 @@ class TestPromptPostProcessor(unittest.TestCase): Returns: None """ - the_obj = ppp or self.__defppp - out = expected_output_prompts if isinstance(expected_output_prompts, list) else [expected_output_prompts] + the_obj = ppp or self.defppp + out = ( + [PromptPair("", "")] + if expected_output_prompts is None + else expected_output_prompts if isinstance(expected_output_prompts, list) else [expected_output_prompts] + ) for eo in out: result_prompt, result_negative_prompt, _ = the_obj.process_prompt( input_prompts.prompt, input_prompts.negative_prompt, seed, ) - self.assertEqual(self.__interrupted, interrupted, "Interrupted flag is incorrect") - if not self.__interrupted: + self.assertEqual(self.interrupted, interrupted, "Interrupted flag is incorrect") + if not self.interrupted and expected_output_prompts is not None: self.assertEqual(result_prompt, eo.prompt, "Incorrect prompt") self.assertEqual(result_negative_prompt, eo.negative_prompt, "Incorrect negative prompt") seed += 1 + +class TestPromptPostProcessor(TestPromptPostProcessorBase): + # Send To Negative tests def test_stn_simple(self): # negtags with different parameters and separations - self.__process( + self.process( PromptPair( "flowersred, green, blueyellow, purpleblack", "normal quality, worse quality", @@ -170,7 +184,7 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_stn_complex(self): # complex negtags - self.__process( + self.process( PromptPair( "red ((pink)), flowers purple, mauveblue, yellow green", "normal quality, , bad quality, worse quality", @@ -182,7 +196,7 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_stn_complex_nocleanup(self): # complex negtags with no cleanup - self.__process( + self.process( PromptPair( "red ((pink)), flowers purple, mauveblue, yellow green", "normal quality, , bad quality, worse quality", @@ -191,11 +205,11 @@ class TestPromptPostProcessor(unittest.TestCase): " (()), flowers , , ", "red, ((pink)), normal quality, mauve, yellow, bad quality, green, worse quality, purple, blue", ), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_stn_inside_attention(self): # negtag inside attention - self.__process( + self.process( PromptPair( "[neg1] this is a ((testneg2) (test:2.0): 1.5 ) (red[square]:1.5)", "normal quality", @@ -206,7 +220,7 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_stn_inside_alternation(self): # negtag inside alternation - self.__process( + self.process( PromptPair( "this is a (([complexneg1|simpleneg2|regularneg3] test)(test:2.0):1.5)", "normal quality", @@ -218,7 +232,7 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_stn_inside_alternation_recursive(self): # negtag inside alternation (recursive alternation) - self.__process( + self.process( PromptPair( "this is a (([complexneg1[one|twoneg12||three|four(neg14)]|simpleneg2|regularneg3] test)(test:2.0):1.5)", "normal quality", @@ -230,13 +244,13 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_stn_inside_scheduling(self): # negtag inside scheduling - self.__process( + self.process( PromptPair("this is [abcneg1:defneg2: 5 ]", "normal quality"), [PromptPair("this is [abc:def:5]", "[neg1::5], normal quality, [neg2:5]")], ) def test_stn_complex_features(self): # complex negtags with AND, BREAK and other features - self.__process( + self.process( PromptPair( "[neg5] this \\(is\\): a (([complex|simpleneg6|regular] testneg1)(test:2.0):1.5) \nBREAK, BREAK with [abcneg4:defneg2(neg3:1.6):5]:0.5 AND loratrigger AND AND hypernettrigger :0.3", "normal quality, ", @@ -248,7 +262,7 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_stn_complex_features_newformat(self): # complex negtags with AND, BREAK and other features (new format) - self.__process( + self.process( PromptPair( "[neg5] this \\(is\\): a (([complex|simpleneg6|regular] testneg1)(test:2.0):1.5) \nBREAK, BREAK with [abcneg4:defneg2(neg3:1.6):5]:0.5 AND loratrigger AND AND hypernettrigger :0.3", "normal quality, ", @@ -260,7 +274,7 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_stn_inside_alternation_recursive_2(self): # negtag inside alternation (recursive alternation) - self.__process( + self.process( PromptPair( "[pos1neg1[pos11|pos12neg12||pos14|pos15neg15]|pos2neg2|pos3neg3]", "", @@ -275,13 +289,13 @@ class TestPromptPostProcessor(unittest.TestCase): # Cleanup tests def test_cl_simple(self): # simple cleanup - self.__process( + self.process( PromptPair(" this is a ((test ), , , (), , [] ( , test ,:2.0):1.5) (red:1.5) ", " normal quality "), PromptPair("this is a ((test), (test,:2):1.5) (red:1.5)", "normal quality"), ) def test_cl_complex(self): # complex cleanup - self.__process( + self.process( PromptPair( " this is BREAKABLE a ((test)), ,AND AND(() [] ANDERSON (test:2.0):1.5) :o BREAK \n BREAK (red:1.5) ", " [:hands, feet, :0.15]normal quality ", @@ -293,49 +307,103 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_cl_removenetworktags(self): # remove network tags - self.__process( + self.process( PromptPair("this is a test__yaml/wildcard7__", ""), PromptPair("this is a test", ""), ppp=PromptPostProcessor( - self.__ppp_logger, - self.__interrupt, - self.__def_env_info, - {**self.__defopts, "remove_extranetwork_tags": True}, - self.__grammar_content, - self.__wildcards_obj, + self.ppp_logger, + self.interrupt, + self.def_env_info, + {**self.defopts, "remove_extranetwork_tags": True}, + self.grammar_content, + self.wildcards_obj, ), ) def test_cl_dontremoveseparatorsoneol(self): # don't remove separators on eol - self.__process( + self.process( PromptPair("this is a test,\nsecond line", ""), PromptPair("this is a test,\nsecond line", ""), ppp=PromptPostProcessor( - self.__ppp_logger, - self.__interrupt, - self.__def_env_info, - {**self.__defopts, "cleanup_extra_separators2": False}, - self.__grammar_content, - self.__wildcards_obj, + self.ppp_logger, + self.interrupt, + self.def_env_info, + { + **self.defopts, + "cleanup_extra_separators2": False, + "cleanup_extra_separators_include_eol": False, + }, + self.grammar_content, + self.wildcards_obj, + ), + ) + + def test_cl_separatorswitheol(self): # don't remove eols with the separators + self.process( + PromptPair( + """{ (d:0.9) ,, (l:1.1) | (l:1.1) (d:0.9),,, } + (l:1.1) + (d:0.9)""", + "", + ), + PromptPair( + """ (l:1.1) (d:0.9), + (l:1.1) + (d:0.9)""", + "", + ), + ppp=PromptPostProcessor( + self.ppp_logger, + self.interrupt, + self.def_env_info, + { + **self.defopts, + "cleanup_empty_constructs": False, + "cleanup_extra_separators": True, + "cleanup_extra_separators2": False, + "cleanup_extra_separators_include_eol": False, + "cleanup_extra_spaces": False, + "cleanup_breaks": False, + "cleanup_breaks_eol": False, + "cleanup_ands": False, + "cleanup_ands_eol": False, + "cleanup_extranetwork_tags": False, + "cleanup_merge_attention": False, + }, + self.grammar_content, + self.wildcards_obj, ), ) def test_cl_mergeattention(self): # merge attention - self.__process( + self.process( PromptPair( - "this is (a test:1.5) of (attention (merging:1.2)) where ((this)) ((is joined:1.2)) and ([this too]:1.3)", + "this is (a test:0.9) of (attention (merging:1.2)) where ((this)) ((is joined:1.2)) and ([this too]:1.3)", "", ), PromptPair( - "this is (a test:1.5) of (attention (merging:1.2)) where (this:1.21) (is joined:1.32) and (this too:1.17)", + "this is [a test] of (attention (merging:1.2)) where (this:1.21) (is joined:1.32) and (this too:1.17)", "", ), ) + def test_cl_not_mergeattention(self): # not merge attention + self.process( + PromptPair( + "this is (a test:0.9) of not (attention (merging:1.2)) where ((this)) ((is not joined:1.2)) and neither is ([this]:1.3)", + "", + ), + PromptPair( + "this is (a test:0.9) of not (attention (merging:1.2)) where ((this)) ((is not joined:1.2)) and neither is ([this]:1.3)", + "", + ), + ppp=self.nocupppp, + ) + # Command tests def test_cmd_stn_complex_features(self): # complex stn command with AND, BREAK and other features - self.__process( + self.process( PromptPair( "[neg5] this \\(is\\): a (([complex|simpleneg6|regular] testneg1)(test:2.0):1.5) \nBREAK, BREAK with [abcneg4:defneg2(neg3:1.6):5]:0.5 AND loratrigger AND AND hypernettrigger :0.3", "normal quality, ", @@ -347,7 +415,7 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_cmd_if_complex_features(self): # complex if command - self.__process( + self.process( PromptPair( "this \\(is\\): a (([complex|simple|regular] test)(test:2.0):1.5) \nBREAK, BREAK with [abcneg4:def:5]:0.5 AND loratrigger hypernettrigger nothing:0.3", "normal quality", @@ -359,39 +427,39 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_cmd_if_nested(self): # nested if command - self.__process( + self.process( PromptPair( "this is SD1PONYSD2NOPONYNOPONY", "", ), PromptPair("this is PONY", ""), ppp=PromptPostProcessor( - self.__ppp_logger, - self.__interrupt, + self.ppp_logger, + self.interrupt, { - **self.__def_env_info, + **self.def_env_info, "model_filename": "./webui/models/Stable-diffusion/ponymodel.safetensors", }, - self.__defopts, - self.__grammar_content, - self.__wildcards_obj, + self.defopts, + self.grammar_content, + self.wildcards_obj, ), ) def test_cmd_set_if(self): # set and if commands - self.__process( + self.process( PromptPair("valuethis test is OKnot OK", ""), PromptPair("this test is OK", ""), ) def test_cmd_set_eval_if(self): # set and if commands - self.__process( + self.process( PromptPair("valuethis test is OKnot OK", ""), PromptPair("this test is OK", ""), ) def test_cmd_set_if_echo_nested(self): # nested set, if and echo commands - self.__process( + self.process( PromptPair( "1OKnot OK NOK OK", "", @@ -400,7 +468,7 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_cmd_set_if_complex_conditions_1(self): # complex conditions (or) - self.__process( + self.process( PromptPair( "truefalsethis test is OKnot OK", "", @@ -409,7 +477,7 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_cmd_set_if_complex_conditions_2(self): # complex conditions (and) - self.__process( + self.process( PromptPair( "truetruethis test is OKnot OK", "", @@ -418,13 +486,13 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_cmd_set_if_complex_conditions_3(self): # complex conditions (not) - self.__process( + self.process( PromptPair("falsethis test is OKnot OK", ""), PromptPair("this test is OK", ""), ) def test_cmd_set_if_complex_conditions_4(self): # complex conditions (not, precedence) - self.__process( + self.process( PromptPair( "truefalsethis test is OKnot OK", "", @@ -433,7 +501,7 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_cmd_set_if_complex_conditions_5(self): # complex conditions (not, precedence, comparison) - self.__process( + self.process( PromptPair( "1falsethis test is OKnot OK", "", @@ -442,7 +510,7 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_cmd_set_if_complex_conditions_6(self): # complex conditions - self.__process( + self.process( PromptPair( "123this test is OKnot OK", "", @@ -451,7 +519,7 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_cmd_set_if_complex_conditions_7(self): # complex conditions - self.__process( + self.process( PromptPair( "123this test is OKnot OK", "", @@ -460,7 +528,7 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_cmd_set_if2(self): # set and more complex if commands - self.__process( + self.process( PromptPair( "First: value1this test is OKnot OK\nSecond: value3this test is OKnot OK", "", @@ -469,7 +537,7 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_cmd_set_add_if(self): # set, add and if commands - self.__process( + self.process( PromptPair( "value2this test is OKnot OK", "", @@ -478,7 +546,7 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_cmd_set_add_DP_if(self): # set, add (DP format) and if commands - self.__process( + self.process( PromptPair( "${v=value}${v+=2}this test is OKnot OK", "", @@ -487,17 +555,17 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_cmd_set_immediateeval(self): # set (DP format) with mixed evaluation - self.__process( + self.process( PromptPair( "${var=!__yaml/wildcard1__}the choices are: ${var}, ${var}, ${var2:default}, ${var3=__yaml/wildcard1__}${var3}, ${var3}", "", ), PromptPair("the choices are: choice2, choice2, default, choice3, choice1", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_cmd_set_mixeval(self): # set and add (DP format) with mixed evaluation - self.__process( + self.process( PromptPair( "${var=__yaml/wildcard1__}the choices are: ${var}, ${var}, ${var+=, __yaml/wildcard2__}${var}, ${var}, ${var+=!, __yaml/wildcard3__}${var}, ${var}", "", @@ -506,11 +574,11 @@ class TestPromptPostProcessor(unittest.TestCase): "the choices are: choice2, choice3, choice1, choice1- choice2 -choice3, choice2, choice2 -choice1-choice3, choice2, choice3-choice1- choice2 , choice1, choice2 , choice2, choice3-choice1- choice2 , choice1, choice2 ", "", ), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_cmd_set_ifundefined_if(self): # set, ifundefined and if commands - self.__process( + self.process( PromptPair( "valuethis test is OKnot OK", "", @@ -519,7 +587,7 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_cmd_set_ifundefined_if_2(self): # set, ifundefined and if commands - self.__process( + self.process( PromptPair( "valuevalue2this test is OKnot OK", "", @@ -528,7 +596,7 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_cmd_set_ifundefined_DP_if(self): # set, ifundefined (DP format) and if commands - self.__process( + self.process( PromptPair( "${v?=value}this test is OKnot OK", "", @@ -537,7 +605,7 @@ class TestPromptPostProcessor(unittest.TestCase): ) def test_cmd_set_ifundefined_DP_if_2(self): # set, ifundefined (DP format) and if commands - self.__process( + self.process( PromptPair( "${v=!value}${v?=!value2}this test is OKnot OK", "", @@ -548,104 +616,104 @@ class TestPromptPostProcessor(unittest.TestCase): # Choices tests def test_ch_choices(self): # simple choices with weights - self.__process( + self.process( PromptPair("the choices are: {3::choice1|2::choice2|choice3}", ""), PromptPair("the choices are: choice2", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_ch_unsupportedsampler(self): # unsupported sampler - self.__process( + self.process( PromptPair("the choices are: {@choice1|choice2|choice3}", ""), PromptPair("", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, interrupted=True, ) def test_ch_choices_withcomments(self): # choices with comments and multiline - self.__process( + self.process( PromptPair( "the choices are: {\n3::choice1 # this is option 1\n|2::choice2\n# this was option 2\n|choice3 # this is option 3\n}", "", ), PromptPair("the choices are: choice2", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_ch_choices_multiple(self): # choices with multiple selection - self.__process( + self.process( PromptPair("the choices are: {~2$$, $$3::choice1|2:: choice2 |choice3}", ""), PromptPair("the choices are: choice2 , choice3", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_ch_choices_if_multiple(self): # choices with if and multiple selection - self.__process( + self.process( PromptPair("the choices are: {2$$, $$3::choice1|2 if _is_sd1::choice2|choice3}", ""), PromptPair("the choices are: choice1, choice3", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_ch_choices_set_if_multiple(self): # choices with if user variable and multiple selection - self.__process( + self.process( PromptPair("${var=test}the choices are: {2$$, $$3::choice1|2 if not var eq 'test'::choice2|choice3}", ""), PromptPair("the choices are: choice1, choice3", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_ch_choices_set_if_nested(self): # nested choices with if user variable and multiple selection - self.__process( + self.process( PromptPair( "${var=test}the choices are: {2$$, $$3::choice1${var2=test2} {if var2 eq 'test2'::choice11|choice12}|2 if not var eq 'test'::choice2|choice3}", "", ), PromptPair("the choices are: choice1 choice11, choice3", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_ch_choicesinsidelora(self): # simple choices inside a lora - self.__process( + self.process( PromptPair("", ""), PromptPair("", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_ch_removelorawithchoices(self): # remove lora with choices inside - self.__process( + self.process( PromptPair("", ""), PromptPair("", ""), ppp=PromptPostProcessor( - self.__ppp_logger, - self.__interrupt, - self.__def_env_info, - {**self.__defopts, "remove_extranetwork_tags": True}, - self.__grammar_content, - self.__wildcards_obj, + self.ppp_logger, + self.interrupt, + self.def_env_info, + {**self.defopts, "remove_extranetwork_tags": True}, + self.grammar_content, + self.wildcards_obj, ), ) # Wildcards tests def test_wc_ignore(self): # wildcards with ignore option - self.__process( + self.process( PromptPair("__bad_wildcard__", "{option1|option2}"), PromptPair("__bad_wildcard__", "{option1|option2}"), ppp=PromptPostProcessor( - self.__ppp_logger, - self.__interrupt, - self.__def_env_info, + self.ppp_logger, + self.interrupt, + self.def_env_info, { - **self.__defopts, + **self.defopts, "process_wildcards": False, "if_wildcards": PromptPostProcessor.IFWILDCARDS_CHOICES.ignore.value, }, - self.__grammar_content, - self.__wildcards_obj, + self.grammar_content, + self.wildcards_obj, ), ) def test_wc_remove(self): # wildcards with remove option - self.__process( + self.process( PromptPair( "[neg5] this is: __bad_wildcard__ a (([complex|simpleneg6|regular] testneg1)(test:2.0):1.5) \nBREAK, BREAK with [abcneg4:defneg2(neg3:1.6):5] ", "normal quality, {option1|option2}", @@ -655,346 +723,334 @@ class TestPromptPostProcessor(unittest.TestCase): "[neg5], ([|neg6|]:1.65), (neg1:1.65), [neg4::5], normal quality, [neg2(neg3:1.6):5]", ), ppp=PromptPostProcessor( - self.__ppp_logger, - self.__interrupt, - self.__def_env_info, + self.ppp_logger, + self.interrupt, + self.def_env_info, { - **self.__defopts, + **self.defopts, "process_wildcards": False, "if_wildcards": PromptPostProcessor.IFWILDCARDS_CHOICES.remove.value, }, - self.__grammar_content, - self.__wildcards_obj, + self.grammar_content, + self.wildcards_obj, ), ) def test_wc_warn(self): # wildcards with warn option - self.__process( + self.process( PromptPair("__bad_wildcard__", "{option1|option2}"), PromptPair(PromptPostProcessor.WILDCARD_WARNING + "__bad_wildcard__", "{option1|option2}"), ppp=PromptPostProcessor( - self.__ppp_logger, - self.__interrupt, - self.__def_env_info, + self.ppp_logger, + self.interrupt, + self.def_env_info, { - **self.__defopts, + **self.defopts, "process_wildcards": False, "if_wildcards": PromptPostProcessor.IFWILDCARDS_CHOICES.warn.value, }, - self.__grammar_content, - self.__wildcards_obj, + self.grammar_content, + self.wildcards_obj, ), ) def test_wc_stop(self): # wildcards with stop option - self.__process( + self.process( PromptPair("__bad_wildcard__", "{option1|option2}"), PromptPair( PromptPostProcessor.WILDCARD_STOP.format("__bad_wildcard__") + "__bad_wildcard__", "{option1|option2}", ), ppp=PromptPostProcessor( - self.__ppp_logger, - self.__interrupt, - self.__def_env_info, + self.ppp_logger, + self.interrupt, + self.def_env_info, { - **self.__defopts, + **self.defopts, "process_wildcards": False, "if_wildcards": PromptPostProcessor.IFWILDCARDS_CHOICES.stop.value, }, - self.__grammar_content, - self.__wildcards_obj, + self.grammar_content, + self.wildcards_obj, ), interrupted=True, ) def test_wcinvar_warn(self): # wildcards in var with warn option - self.__process( + self.process( PromptPair("${v=__bad_wildcard__}${v}", ""), PromptPair(PromptPostProcessor.WILDCARD_WARNING + "__bad_wildcard__", ""), ppp=PromptPostProcessor( - self.__ppp_logger, - self.__interrupt, - self.__def_env_info, + self.ppp_logger, + self.interrupt, + self.def_env_info, { - **self.__defopts, + **self.defopts, "process_wildcards": False, "if_wildcards": PromptPostProcessor.IFWILDCARDS_CHOICES.warn.value, }, - self.__grammar_content, - self.__wildcards_obj, + self.grammar_content, + self.wildcards_obj, ), ) def test_wc_wildcard1a_text(self): # simple text wildcard - self.__process( + self.process( PromptPair("the choices are: __text/wildcard1__", ""), PromptPair("the choices are: choice2", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_wildcard1a_json(self): # simple json wildcard - self.__process( + self.process( PromptPair("the choices are: __json/wildcard1__", ""), PromptPair("the choices are: choice2", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_wildcard1a_yaml(self): # simple yaml wildcard - self.__process( + self.process( PromptPair("the choices are: __yaml/wildcard1__", ""), PromptPair("the choices are: choice2", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_wildcard1b_text(self): # simple text wildcard with multiple choices - self.__process( + self.process( PromptPair("the choices are: __2-$$text/wildcard1__", ""), PromptPair("the choices are: choice3, choice1", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_wildcard1b_json(self): # simple json wildcard with multiple choices - self.__process( + self.process( PromptPair("the choices are: __2-$$json/wildcard1__", ""), PromptPair("the choices are: choice3, choice1", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_wildcard1b_yaml(self): # simple yaml wildcard with multiple choices - self.__process( + self.process( PromptPair("the choices are: __2-$$yaml/wildcard1__", ""), PromptPair("the choices are: choice3, choice1", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_wildcard2_text(self): # simple text wildcard with default options - self.__process( + self.process( PromptPair("the choices are: __text/wildcard2__", ""), PromptPair("the choices are: choice3-choice1", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_wildcard2_json(self): # simple json wildcard with default options - self.__process( + self.process( PromptPair("the choices are: __json/wildcard2__", ""), PromptPair("the choices are: choice3-choice1", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_wildcard2_yaml(self): # simple yaml wildcard with default options - self.__process( + self.process( PromptPair("the choices are: __yaml/wildcard2__", ""), PromptPair("the choices are: choice3-choice1", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_test2_yaml(self): # simple yaml wildcard - self.__process( + self.process( PromptPair("the choice is: __testwc/test2__", ""), PromptPair("the choice is: 2", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_test3_yaml(self): # simple yaml wildcard - self.__process( + self.process( PromptPair("the choice is: __testwc/test3__", ""), PromptPair("the choice is: one choice", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_wildcard_filter_index(self): # wildcard with positional index filter - self.__process( + self.process( PromptPair("the choice is: __yaml/wildcard2'2'__", ""), PromptPair("the choice is: choice3-choice3", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_wildcard_filter_label(self): # wildcard with label filter - self.__process( + self.process( PromptPair("the choice is: __yaml/wildcard2'label1'__", ""), PromptPair("the choice is: choice3-choice1", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_wildcard_filter_label2(self): # wildcard with label filter in multiple choices - self.__process( + self.process( PromptPair("the choice is: __yaml/wildcard2'label2'__", ""), PromptPair("the choice is: choice1-choice1", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_wildcard_filter_label3(self): # wildcard with multiple label filter - self.__process( + self.process( PromptPair("the choice is: __yaml/wildcard2'label1,label2'__", ""), PromptPair("the choice is: choice3-choice1", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_wildcard_filter_indexlabel(self): # wildcard with mixed index and label filter - self.__process( + self.process( PromptPair("the choice is: __yaml/wildcard2'2,label2'__", ""), PromptPair("the choice is: choice3-choice1", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_wildcard_filter_compound(self): # wildcard with compound filter - self.__process( + self.process( PromptPair("the choice is: __yaml/wildcard2'label1+label3'__", ""), PromptPair("the choice is: choice3-choice3", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_wildcard_filter_compound2(self): # wildcard with inherited compound filter - self.__process( + self.process( PromptPair("the choice is: __yaml/wildcard2bis'#label1+label3'__", ""), PromptPair("the choice is: choice3bis", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_wildcard_filter_compound3(self): # wildcard with doubly inherited compound filter - self.__process( + self.process( PromptPair("the choice is: __yaml/wildcard2bisbis'#label1+label3'__", ""), PromptPair("the choice is: choice3bisbis", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_nested_wildcard_text(self): # nested text wildcard with repeating multiple choices - self.__process( + self.process( PromptPair("the choices are: __r3$$-$$text/wildcard3__", ""), PromptPair("the choices are: choice3,choice1- choice2 ,choice3", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_nested_wildcard_json(self): # nested json wildcard with repeating multiple choices - self.__process( + self.process( PromptPair("the choices are: __r3$$-$$json/wildcard3__", ""), PromptPair("the choices are: choice3,choice1- choice2 ,choice3", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_nested_wildcard_yaml(self): # nested yaml wildcard with repeating multiple choices - self.__process( + self.process( PromptPair("the choices are: __r3$$-$$yaml/wildcard3__", ""), PromptPair("the choices are: choice3,choice1- choice2 ,choice3", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_wildcard4_yaml(self): # simple yaml wildcard with one option - self.__process( + self.process( PromptPair("the choices are: __yaml/wildcard4__", ""), PromptPair("the choices are: inline text", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_wildcard6_yaml(self): # simple yaml wildcard with object formatted choices - self.__process( + self.process( PromptPair("the choices are: __yaml/wildcard6__", ""), PromptPair("the choices are: choice2", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_choice_wildcard_mix(self): # choices with wildcard mix - self.__process( + self.process( PromptPair("the choices are: {__~2$$yaml/wildcard2__|choice0}", ""), [ PromptPair("the choices are: choice0", ""), PromptPair("the choices are: choice1, choice3", ""), PromptPair("the choices are: choice1, choice3", ""), ], - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_unsupportedsampler(self): # unsupported sampler - self.__process( + self.process( PromptPair("the choices are: __@yaml/wildcard2__", ""), PromptPair("", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, interrupted=True, ) def test_wc_wildcard_globbing(self): # wildcard with globbing - self.__process( + self.process( PromptPair("the choices are: __yaml/wildcard[12]__, __yaml/wildcard?__", ""), PromptPair("the choices are: choice3-choice2, - choice2 -choice3", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_wildcardwithvar(self): # wildcard with inline variable - self.__process( + self.process( PromptPair("the choices are: __yaml/wildcard5(var=test)__, __yaml/wildcard5__", ""), PromptPair("the choices are: inline test, inline default", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_wildcardPS_yaml(self): # yaml wildcard with object formatted choices and options and prefix and suffix - self.__process( + self.process( PromptPair("the choices are: __yaml/wildcardPS__", ""), PromptPair("the choices are: prefix-choice2/choice3-suffix", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_anonymouswildcard_yaml(self): # yaml anonymous wildcard - self.__process( + self.process( PromptPair("the choices are: __yaml/anonwildcards__", ""), PromptPair("the choices are: six", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) def test_wc_wildcard_input(self): # simple yaml wildcard input - self.__process( + self.process( PromptPair("the choices are: __yaml_input/wildcardI__", ""), PromptPair("the choices are: choice2", ""), - ppp=self.__nocupppp, + ppp=self.nocupppp, ) # Model variants tests def test_variants(self): - self.__process( + self.process( PromptPair( "test1test2test3test4", "", ), PromptPair("test1test2", ""), ppp=PromptPostProcessor( - self.__ppp_logger, - self.__interrupt, + self.ppp_logger, + self.interrupt, { - **self.__def_env_info, + **self.def_env_info, "model_filename": "./webui/models/Stable-diffusion/testmodel.safetensors", }, { - **self.__defopts, + **self.defopts, + "on_warning": PromptPostProcessor.ONWARNING_CHOICES.warn.value, "variants_definitions": "test1(sdxl)=testmodel\ntest2=testmodel\ntest3(sd1)=testmodel\ntest4(invalid)=testmodel\nsdxl()=testmodel", }, - self.__grammar_content, - self.__wildcards_obj, + self.grammar_content, + self.wildcards_obj, ), ) # ComfyUI tests def test_comfyui_attention(self): # attention conversion - self.__process( + self.process( PromptPair("(test1) (test2:1.5) [test3] [(test4)]", ""), PromptPair("(test1) (test2:1.5) (test3:0.9) (test4:0.99)", ""), - ppp=self.__comfyuippp, + ppp=self.comfyuippp, ) - # def test_mix(self): - # self.__process( - # PromptPair( - # "__text/wildcard1__ (__text/wildcard2__) (__text/wildcard3__:1.5) [__text/wildcard1__] [__text/wildcard2__:__text/wildcard3__:0.5] [__text/wildcard1__|__text/wildcard2__] # {opt1_1|opt1_2} ({opt2_1|opt2_2}) ({opt3_1|opt3_2}:1.5) [{opt4_1|opt4_2}] [{opt5_1|opt5_2}:{opt6_1|opt6_2}:0.5] [{opt7_1|opt7_2}|{opt8_1|opt8_2}] # {opt1_1|__text/wildcard1__} ({opt2_1|__text/wildcard2__}) ({opt3_1|__text/wildcard3__}:1.5) [{opt4_1|__text/wildcard1__}] [{opt5_1|__text/wildcard2__}# :{opt6_1|__text/wildcard3__}:0.5] [{opt7_1|__text/wildcard1__}|{opt8_1|__text/wildcard2__}] {|}", - # "", - # ), - # PromptPair( - # "choice2 ( choice2 -choice1) (choice1, choice2 :1.5) [choice1] [choice1-choice1:choice3, choice2 :0.5] [choice2| choice2 - choice2 ] opt1_1 # (opt2_2) (opt3_1:1.5) [opt4_2] [opt5_1:opt6_2:0.5] [opt7_1|opt8_1] choice3 (opt2_1) (choice1,choice3:1.5) [choice1] [choice3-choice3:opt6_1:0.5] [opt7_1|# opt8_1] ", - # "", - # ), - # ppp=self.__nocupppp, - # ) - if __name__ == "__main__": unittest.main() diff --git a/workflows/PPP example.json b/workflows/PPP example.json index fd59c95..56a1746 100644 --- a/workflows/PPP example.json +++ b/workflows/PPP example.json @@ -1 +1 @@ -{"id":"0f75c56e-adb4-41f6-b957-81b6772cb8b5","revision":0,"last_node_id":24,"last_link_id":52,"nodes":[{"id":8,"type":"VAEDecode","pos":[1620,-560],"size":[210,46],"flags":{},"order":9,"mode":0,"inputs":[{"localized_name":"samples","name":"samples","type":"LATENT","link":7},{"localized_name":"vae","name":"vae","type":"VAE","link":20}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"IMAGE","slot_index":0,"links":[9]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.27","Node name for S&R":"VAEDecode"},"widgets_values":[]},{"id":9,"type":"SaveImage","pos":[1860,-560],"size":[210,58],"flags":{},"order":10,"mode":0,"inputs":[{"localized_name":"images","name":"images","type":"IMAGE","link":9},{"localized_name":"filename_prefix","name":"filename_prefix","type":"STRING","widget":{"name":"filename_prefix"},"link":null}],"outputs":[],"properties":{"cnr_id":"comfy-core","ver":"0.3.27"},"widgets_values":["ComfyUI"]},{"id":17,"type":"PrimitiveInt","pos":[0,-480],"size":[315,82],"flags":{},"order":0,"mode":0,"inputs":[{"localized_name":"value","name":"value","type":"INT","widget":{"name":"value"},"link":null}],"outputs":[{"localized_name":"INT","name":"INT","type":"INT","links":[24,49]}],"title":"Seed","properties":{"cnr_id":"comfy-core","ver":"0.3.27","Node name for S&R":"PrimitiveInt"},"widgets_values":[801,"randomize"]},{"id":16,"type":"Checkpoint Loader with Name (Image Saver)","pos":[-200,-660],"size":[516.5999755859375,118],"flags":{},"order":1,"mode":0,"inputs":[{"localized_name":"ckpt_name","name":"ckpt_name","type":"COMBO","widget":{"name":"ckpt_name"},"link":null}],"outputs":[{"localized_name":"MODEL","name":"MODEL","type":"MODEL","links":[16,47]},{"localized_name":"CLIP","name":"CLIP","type":"CLIP","links":[19,45]},{"localized_name":"VAE","name":"VAE","type":"VAE","links":[20]},{"localized_name":"model_name","name":"model_name","type":"STRING","links":[48]}],"properties":{"cnr_id":"comfyui-image-saver","ver":"f8520bcfe5339ba7b4bb64ba96f6ed03da87fb89","Node name for S&R":"Checkpoint Loader with Name (Image Saver)"},"widgets_values":["ponyDiffusionV6XL_v6StartWithThisOne.safetensors"]},{"id":5,"type":"EmptyLatentImage","pos":[0,-840],"size":[315,106],"flags":{},"order":2,"mode":0,"inputs":[{"localized_name":"width","name":"width","type":"INT","widget":{"name":"width"},"link":null},{"localized_name":"height","name":"height","type":"INT","widget":{"name":"height"},"link":null},{"localized_name":"batch_size","name":"batch_size","type":"INT","widget":{"name":"batch_size"},"link":null}],"outputs":[{"localized_name":"LATENT","name":"LATENT","type":"LATENT","slot_index":0,"links":[2]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.27","Node name for S&R":"EmptyLatentImage"},"widgets_values":[1024,1024,1]},{"id":3,"type":"KSampler","pos":[1220,-540],"size":[315,262],"flags":{},"order":7,"mode":0,"inputs":[{"localized_name":"model","name":"model","type":"MODEL","link":16},{"localized_name":"positive","name":"positive","type":"CONDITIONING","link":4},{"localized_name":"negative","name":"negative","type":"CONDITIONING","link":6},{"localized_name":"latent_image","name":"latent_image","type":"LATENT","link":2},{"localized_name":"seed","name":"seed","type":"INT","widget":{"name":"seed"},"link":24},{"localized_name":"steps","name":"steps","type":"INT","widget":{"name":"steps"},"link":null},{"localized_name":"cfg","name":"cfg","type":"FLOAT","widget":{"name":"cfg"},"link":null},{"localized_name":"sampler_name","name":"sampler_name","type":"COMBO","widget":{"name":"sampler_name"},"link":null},{"localized_name":"scheduler","name":"scheduler","type":"COMBO","widget":{"name":"scheduler"},"link":null},{"localized_name":"denoise","name":"denoise","type":"FLOAT","widget":{"name":"denoise"},"link":null}],"outputs":[{"localized_name":"LATENT","name":"LATENT","type":"LATENT","slot_index":0,"links":[7]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.27","Node name for S&R":"KSampler"},"widgets_values":[523681707962952,"randomize",20,8,"euler","normal",1]},{"id":6,"type":"CLIPTextEncode","pos":[940,-380],"size":[240,88],"flags":{"collapsed":true},"order":4,"mode":0,"inputs":[{"localized_name":"clip","name":"clip","type":"CLIP","link":19},{"localized_name":"text","name":"text","type":"STRING","widget":{"name":"text"},"link":50}],"outputs":[{"localized_name":"CONDITIONING","name":"CONDITIONING","type":"CONDITIONING","slot_index":0,"links":[4]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.27","Node name for S&R":"CLIPTextEncode"},"widgets_values":[""]},{"id":7,"type":"CLIPTextEncode","pos":[940,-300],"size":[220,88],"flags":{"collapsed":true},"order":5,"mode":0,"inputs":[{"localized_name":"clip","name":"clip","type":"CLIP","link":45},{"localized_name":"text","name":"text","type":"STRING","widget":{"name":"text"},"link":51}],"outputs":[{"localized_name":"CONDITIONING","name":"CONDITIONING","type":"CONDITIONING","slot_index":0,"links":[6]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.27","Node name for S&R":"CLIPTextEncode"},"widgets_values":[""]},{"id":22,"type":"ShowText|pysssss","pos":[1260,100],"size":[260,200],"flags":{},"order":8,"mode":0,"inputs":[{"localized_name":"text","name":"text","type":"STRING","link":36}],"outputs":[{"localized_name":"STRING","name":"STRING","shape":6,"type":"STRING","links":null}],"properties":{"cnr_id":"comfyui-custom-scripts","ver":"9f7b3215e6af317603056a9a1666bf6e83e28835","Node name for S&R":"ShowText|pysssss"},"widgets_values":[]},{"id":20,"type":"ACBPPPSelectVariable","pos":[900,100],"size":[315,58],"flags":{},"order":6,"mode":0,"inputs":[{"localized_name":"variables","name":"variables","type":"PPP_DICT","link":52},{"localized_name":"name","name":"name","shape":7,"type":"STRING","widget":{"name":"name"},"link":null}],"outputs":[{"localized_name":"value","name":"value","type":"STRING","links":[36]}],"properties":{"cnr_id":"sd-webui-prompt-postprocessor","ver":"5e3c8cbc44bacd3b4da2ef33588f152a571775dd","Node name for S&R":"ACBPPPSelectVariable"},"widgets_values":[""]},{"id":24,"type":"ACBPromptPostProcessor","pos":[420,-380],"size":[380,820],"flags":{},"order":3,"mode":0,"inputs":[{"localized_name":"model","name":"model","shape":7,"type":"MODEL,STRING","link":47},{"localized_name":"pos_prompt","name":"pos_prompt","type":"STRING","widget":{"name":"pos_prompt"},"link":null},{"localized_name":"neg_prompt","name":"neg_prompt","type":"STRING","widget":{"name":"neg_prompt"},"link":null},{"localized_name":"modelname","name":"modelname","shape":7,"type":"STRING","widget":{"name":"modelname"},"link":48},{"localized_name":"seed","name":"seed","shape":7,"type":"INT","widget":{"name":"seed"},"link":49},{"localized_name":"debug_level","name":"debug_level","shape":7,"type":"COMBO","widget":{"name":"debug_level"},"link":null},{"localized_name":"variants_definitions","name":"variants_definitions","shape":7,"type":"STRING","widget":{"name":"variants_definitions"},"link":null},{"localized_name":"wc_process_wildcards","name":"wc_process_wildcards","shape":7,"type":"BOOLEAN","widget":{"name":"wc_process_wildcards"},"link":null},{"localized_name":"wc_wildcards_folders","name":"wc_wildcards_folders","shape":7,"type":"STRING","widget":{"name":"wc_wildcards_folders"},"link":null},{"localized_name":"wc_wildcards_input","name":"wc_wildcards_input","shape":7,"type":"STRING","widget":{"name":"wc_wildcards_input"},"link":null},{"localized_name":"wc_if_wildcards","name":"wc_if_wildcards","shape":7,"type":"COMBO","widget":{"name":"wc_if_wildcards"},"link":null},{"localized_name":"wc_choice_separator","name":"wc_choice_separator","shape":7,"type":"STRING","widget":{"name":"wc_choice_separator"},"link":null},{"localized_name":"wc_keep_choices_order","name":"wc_keep_choices_order","shape":7,"type":"BOOLEAN","widget":{"name":"wc_keep_choices_order"},"link":null},{"localized_name":"stn_separator","name":"stn_separator","shape":7,"type":"STRING","widget":{"name":"stn_separator"},"link":null},{"localized_name":"stn_ignore_repeats","name":"stn_ignore_repeats","shape":7,"type":"BOOLEAN","widget":{"name":"stn_ignore_repeats"},"link":null},{"localized_name":"cleanup_extra_spaces","name":"cleanup_extra_spaces","shape":7,"type":"BOOLEAN","widget":{"name":"cleanup_extra_spaces"},"link":null},{"localized_name":"cleanup_empty_constructs","name":"cleanup_empty_constructs","shape":7,"type":"BOOLEAN","widget":{"name":"cleanup_empty_constructs"},"link":null},{"localized_name":"cleanup_extra_separators","name":"cleanup_extra_separators","shape":7,"type":"BOOLEAN","widget":{"name":"cleanup_extra_separators"},"link":null},{"localized_name":"cleanup_extra_separators2","name":"cleanup_extra_separators2","shape":7,"type":"BOOLEAN","widget":{"name":"cleanup_extra_separators2"},"link":null},{"localized_name":"cleanup_breaks","name":"cleanup_breaks","shape":7,"type":"BOOLEAN","widget":{"name":"cleanup_breaks"},"link":null},{"localized_name":"cleanup_breaks_eol","name":"cleanup_breaks_eol","shape":7,"type":"BOOLEAN","widget":{"name":"cleanup_breaks_eol"},"link":null},{"localized_name":"cleanup_ands","name":"cleanup_ands","shape":7,"type":"BOOLEAN","widget":{"name":"cleanup_ands"},"link":null},{"localized_name":"cleanup_ands_eol","name":"cleanup_ands_eol","shape":7,"type":"BOOLEAN","widget":{"name":"cleanup_ands_eol"},"link":null},{"localized_name":"cleanup_extranetwork_tags","name":"cleanup_extranetwork_tags","shape":7,"type":"BOOLEAN","widget":{"name":"cleanup_extranetwork_tags"},"link":null},{"localized_name":"cleanup_merge_attention","name":"cleanup_merge_attention","shape":7,"type":"BOOLEAN","widget":{"name":"cleanup_merge_attention"},"link":null},{"localized_name":"remove_extranetwork_tags","name":"remove_extranetwork_tags","shape":7,"type":"BOOLEAN","widget":{"name":"remove_extranetwork_tags"},"link":null}],"outputs":[{"localized_name":"pos_prompt","name":"pos_prompt","type":"STRING","links":[50]},{"localized_name":"neg_prompt","name":"neg_prompt","type":"STRING","links":[51]},{"localized_name":"variables","name":"variables","type":"PPP_DICT","links":[52]}],"properties":{"cnr_id":"sd-webui-prompt-postprocessor","ver":"5e3c8cbc44bacd3b4da2ef33588f152a571775dd","Node name for S&R":"ACBPromptPostProcessor"},"widgets_values":["score_9, score_8_up, score_7_up, score_6_up, woman with __color__ hair, __action__","score_4, score_5","",-1,"randomize","minimal","pony(sdxl)=pony,pny,pdxl\nillustrious(sdxl)=illustrious,illust,ilxl",true,"","color:\n - red\n - blonde\n - brunette\naction:\n - running\n - sleeping","stop",", ",true,", ",true,true,true,true,true,false,false,false,false,false,true,false]}],"links":[[2,5,0,3,3,"LATENT"],[4,6,0,3,1,"CONDITIONING"],[6,7,0,3,2,"CONDITIONING"],[7,3,0,8,0,"LATENT"],[9,8,0,9,0,"IMAGE"],[16,16,0,3,0,"MODEL"],[19,16,1,6,0,"CLIP"],[20,16,2,8,1,"VAE"],[24,17,0,3,4,"INT"],[36,20,0,22,0,"STRING"],[45,16,1,7,0,"CLIP"],[47,16,0,24,0,"MODEL,STRING"],[48,16,3,24,3,"STRING"],[49,17,0,24,4,"INT"],[50,24,0,6,1,"STRING"],[51,24,1,7,1,"STRING"],[52,24,2,20,0,"PPP_DICT"]],"groups":[],"config":{},"extra":{"ds":{"scale":0.9438000000000003,"offset":[357.48769227266587,962.8709670594865]},"frontendVersion":"1.16.8","reroutes":[{"id":2,"pos":[1560,-620],"linkIds":[20]},{"id":3,"pos":[880,-480],"linkIds":[19,45]},{"id":4,"pos":[380,-540],"linkIds":[16]}],"VHS_latentpreview":true,"VHS_latentpreviewrate":0,"VHS_MetadataImage":true,"VHS_KeepIntermediate":true,"linkExtensions":[{"id":16,"parentId":4},{"id":19,"parentId":3},{"id":20,"parentId":2},{"id":45,"parentId":3}]},"version":0.4} \ No newline at end of file +{"id":"0f75c56e-adb4-41f6-b957-81b6772cb8b5","revision":0,"last_node_id":39,"last_link_id":100,"nodes":[{"id":17,"type":"PrimitiveInt","pos":[0,-480],"size":[315,82],"flags":{},"order":0,"mode":0,"inputs":[{"localized_name":"value","name":"value","type":"INT","widget":{"name":"value"},"link":null}],"outputs":[{"localized_name":"INT","name":"INT","type":"INT","links":[24,94]}],"title":"Seed","properties":{"cnr_id":"comfy-core","ver":"0.3.27","Node name for S&R":"PrimitiveInt"},"widgets_values":[189718398195680,"randomize"]},{"id":5,"type":"EmptyLatentImage","pos":[0,-840],"size":[315,106],"flags":{},"order":1,"mode":0,"inputs":[{"localized_name":"width","name":"width","type":"INT","widget":{"name":"width"},"link":null},{"localized_name":"height","name":"height","type":"INT","widget":{"name":"height"},"link":null},{"localized_name":"batch_size","name":"batch_size","type":"INT","widget":{"name":"batch_size"},"link":null}],"outputs":[{"localized_name":"LATENT","name":"LATENT","type":"LATENT","slot_index":0,"links":[2]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.27","Node name for S&R":"EmptyLatentImage"},"widgets_values":[1024,1024,1]},{"id":27,"type":"PrimitiveStringMultiline","pos":[-100,-20],"size":[400,200],"flags":{},"order":2,"mode":0,"inputs":[{"localized_name":"value","name":"value","type":"STRING","widget":{"name":"value"},"link":null}],"outputs":[{"localized_name":"STRING","name":"STRING","type":"STRING","links":[95]}],"title":"wildcards","properties":{"cnr_id":"comfy-core","ver":"0.3.34","Node name for S&R":"PrimitiveStringMultiline"},"widgets_values":["color:\n - red\n - blonde\n - brunette\naction:\n - running\n - sleeping"]},{"id":9,"type":"SaveImage","pos":[1880,-820],"size":[210,270],"flags":{},"order":15,"mode":0,"inputs":[{"localized_name":"images","name":"images","type":"IMAGE","link":9},{"localized_name":"filename_prefix","name":"filename_prefix","type":"STRING","widget":{"name":"filename_prefix"},"link":null}],"outputs":[],"properties":{"cnr_id":"comfy-core","ver":"0.3.27"},"widgets_values":["ComfyUI"]},{"id":6,"type":"CLIPTextEncode","pos":[940,-520],"size":[240,88],"flags":{"collapsed":true},"order":7,"mode":0,"inputs":[{"localized_name":"clip","name":"clip","type":"CLIP","link":19},{"localized_name":"text","name":"text","type":"STRING","widget":{"name":"text"},"link":96}],"outputs":[{"localized_name":"CONDITIONING","name":"CONDITIONING","type":"CONDITIONING","slot_index":0,"links":[4]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.27","Node name for S&R":"CLIPTextEncode"},"widgets_values":[""]},{"id":7,"type":"CLIPTextEncode","pos":[940,-460],"size":[220,88],"flags":{"collapsed":true},"order":9,"mode":0,"inputs":[{"localized_name":"clip","name":"clip","type":"CLIP","link":45},{"localized_name":"text","name":"text","type":"STRING","widget":{"name":"text"},"link":98}],"outputs":[{"localized_name":"CONDITIONING","name":"CONDITIONING","type":"CONDITIONING","slot_index":0,"links":[6]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.27","Node name for S&R":"CLIPTextEncode"},"widgets_values":[""]},{"id":8,"type":"VAEDecode","pos":[1700,-800],"size":[210,46],"flags":{"collapsed":true},"order":14,"mode":0,"inputs":[{"localized_name":"samples","name":"samples","type":"LATENT","link":7},{"localized_name":"vae","name":"vae","type":"VAE","link":20}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"IMAGE","slot_index":0,"links":[9]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.27","Node name for S&R":"VAEDecode"},"widgets_values":[]},{"id":3,"type":"KSampler","pos":[1320,-740],"size":[315,571],"flags":{},"order":12,"mode":0,"inputs":[{"localized_name":"model","name":"model","type":"MODEL","link":16},{"localized_name":"positive","name":"positive","type":"CONDITIONING","link":4},{"localized_name":"negative","name":"negative","type":"CONDITIONING","link":6},{"localized_name":"latent_image","name":"latent_image","type":"LATENT","link":2},{"localized_name":"seed","name":"seed","type":"INT","widget":{"name":"seed"},"link":24},{"localized_name":"steps","name":"steps","type":"INT","widget":{"name":"steps"},"link":null},{"localized_name":"cfg","name":"cfg","type":"FLOAT","widget":{"name":"cfg"},"link":null},{"localized_name":"sampler_name","name":"sampler_name","type":"COMBO","widget":{"name":"sampler_name"},"link":null},{"localized_name":"scheduler","name":"scheduler","type":"COMBO","widget":{"name":"scheduler"},"link":null},{"localized_name":"denoise","name":"denoise","type":"FLOAT","widget":{"name":"denoise"},"link":null}],"outputs":[{"localized_name":"LATENT","name":"LATENT","type":"LATENT","slot_index":0,"links":[7]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.27","Node name for S&R":"KSampler"},"widgets_values":[982251382500348,"randomize",20,8,"euler","normal",1]},{"id":25,"type":"PrimitiveStringMultiline","pos":[-100,-320],"size":[400,100],"flags":{},"order":3,"mode":0,"inputs":[{"localized_name":"value","name":"value","type":"STRING","widget":{"name":"value"},"link":null}],"outputs":[{"localized_name":"STRING","name":"STRING","type":"STRING","links":[91]}],"title":"positive input","properties":{"cnr_id":"comfy-core","ver":"0.3.34","Node name for S&R":"PrimitiveStringMultiline"},"widgets_values":["score_9, score_8_up, score_7_up, score_6_up, woman with __color__ hair, __action__"]},{"id":26,"type":"PrimitiveStringMultiline","pos":[-100,-160],"size":[400,88],"flags":{},"order":4,"mode":0,"inputs":[{"localized_name":"value","name":"value","type":"STRING","widget":{"name":"value"},"link":null}],"outputs":[{"localized_name":"STRING","name":"STRING","type":"STRING","links":[92]}],"title":"negative input","properties":{"cnr_id":"comfy-core","ver":"0.3.34","Node name for S&R":"PrimitiveStringMultiline"},"widgets_values":["score_4, score_5"]},{"id":35,"type":"PreviewAny","pos":[940,-400],"size":[320,120],"flags":{},"order":8,"mode":0,"inputs":[{"localized_name":"source","name":"source","type":"*","link":97}],"outputs":[],"title":"positive result","properties":{"cnr_id":"comfy-core","ver":"0.3.34","Node name for S&R":"PreviewAny"},"widgets_values":[]},{"id":36,"type":"PreviewAny","pos":[940,-240],"size":[320,120],"flags":{},"order":10,"mode":0,"inputs":[{"localized_name":"source","name":"source","type":"*","link":99}],"outputs":[],"title":"negative result","properties":{"cnr_id":"comfy-core","ver":"0.3.34","Node name for S&R":"PreviewAny"},"widgets_values":[]},{"id":20,"type":"ACBPPPSelectVariable","pos":[900,-20],"size":[220,60],"flags":{},"order":11,"mode":0,"inputs":[{"localized_name":"variables","name":"variables","type":"PPP_DICT","link":100},{"localized_name":"name","name":"name","shape":7,"type":"STRING","widget":{"name":"name"},"link":null}],"outputs":[{"localized_name":"value","name":"value","type":"STRING","links":[87]}],"properties":{"cnr_id":"sd-webui-prompt-postprocessor","ver":"5e3c8cbc44bacd3b4da2ef33588f152a571775dd","Node name for S&R":"ACBPPPSelectVariable"},"widgets_values":[""]},{"id":37,"type":"PreviewAny","pos":[1180,-60],"size":[440,520],"flags":{},"order":13,"mode":0,"inputs":[{"localized_name":"source","name":"source","type":"*","link":87}],"outputs":[],"title":"variables result","properties":{"cnr_id":"comfy-core","ver":"0.3.34","Node name for S&R":"PreviewAny"},"widgets_values":[]},{"id":16,"type":"Checkpoint Loader with Name (Image Saver)","pos":[-200,-660],"size":[516.5999755859375,118],"flags":{},"order":5,"mode":0,"inputs":[{"localized_name":"ckpt_name","name":"ckpt_name","type":"COMBO","widget":{"name":"ckpt_name"},"link":null}],"outputs":[{"localized_name":"MODEL","name":"MODEL","type":"MODEL","links":[16,90]},{"localized_name":"CLIP","name":"CLIP","type":"CLIP","links":[19,45]},{"localized_name":"VAE","name":"VAE","type":"VAE","links":[20]},{"localized_name":"model_name","name":"model_name","type":"STRING","links":[93]}],"properties":{"cnr_id":"comfyui-image-saver","ver":"f8520bcfe5339ba7b4bb64ba96f6ed03da87fb89","Node name for S&R":"Checkpoint Loader with Name (Image Saver)"},"widgets_values":["PDXL\\ponyDiffusionV6XL_v6StartWithThisOne.safetensors"]},{"id":39,"type":"ACBPromptPostProcessor","pos":[420,-380],"size":[400,866],"flags":{},"order":6,"mode":0,"inputs":[{"localized_name":"model","name":"model","shape":7,"type":"MODEL,STRING","link":90},{"localized_name":"pos_prompt","name":"pos_prompt","type":"STRING","widget":{"name":"pos_prompt"},"link":91},{"localized_name":"neg_prompt","name":"neg_prompt","type":"STRING","widget":{"name":"neg_prompt"},"link":92},{"localized_name":"modelname","name":"modelname","shape":7,"type":"STRING","widget":{"name":"modelname"},"link":93},{"localized_name":"seed","name":"seed","shape":7,"type":"INT","widget":{"name":"seed"},"link":94},{"localized_name":"debug_level","name":"debug_level","shape":7,"type":"COMBO","widget":{"name":"debug_level"},"link":null},{"localized_name":"on_warnings","name":"on_warnings","shape":7,"type":"COMBO","widget":{"name":"on_warnings"},"link":null},{"localized_name":"variants_definitions","name":"variants_definitions","shape":7,"type":"STRING","widget":{"name":"variants_definitions"},"link":null},{"localized_name":"wc_process_wildcards","name":"wc_process_wildcards","shape":7,"type":"BOOLEAN","widget":{"name":"wc_process_wildcards"},"link":null},{"localized_name":"wc_wildcards_folders","name":"wc_wildcards_folders","shape":7,"type":"STRING","widget":{"name":"wc_wildcards_folders"},"link":null},{"localized_name":"wc_wildcards_input","name":"wc_wildcards_input","shape":7,"type":"STRING","widget":{"name":"wc_wildcards_input"},"link":95},{"localized_name":"wc_if_wildcards","name":"wc_if_wildcards","shape":7,"type":"COMBO","widget":{"name":"wc_if_wildcards"},"link":null},{"localized_name":"wc_choice_separator","name":"wc_choice_separator","shape":7,"type":"STRING","widget":{"name":"wc_choice_separator"},"link":null},{"localized_name":"wc_keep_choices_order","name":"wc_keep_choices_order","shape":7,"type":"BOOLEAN","widget":{"name":"wc_keep_choices_order"},"link":null},{"localized_name":"stn_separator","name":"stn_separator","shape":7,"type":"STRING","widget":{"name":"stn_separator"},"link":null},{"localized_name":"stn_ignore_repeats","name":"stn_ignore_repeats","shape":7,"type":"BOOLEAN","widget":{"name":"stn_ignore_repeats"},"link":null},{"localized_name":"cleanup_extra_spaces","name":"cleanup_extra_spaces","shape":7,"type":"BOOLEAN","widget":{"name":"cleanup_extra_spaces"},"link":null},{"localized_name":"cleanup_empty_constructs","name":"cleanup_empty_constructs","shape":7,"type":"BOOLEAN","widget":{"name":"cleanup_empty_constructs"},"link":null},{"localized_name":"cleanup_extra_separators","name":"cleanup_extra_separators","shape":7,"type":"BOOLEAN","widget":{"name":"cleanup_extra_separators"},"link":null},{"localized_name":"cleanup_extra_separators2","name":"cleanup_extra_separators2","shape":7,"type":"BOOLEAN","widget":{"name":"cleanup_extra_separators2"},"link":null},{"localized_name":"cleanup_extra_separators_include_eol","name":"cleanup_extra_separators_include_eol","shape":7,"type":"BOOLEAN","widget":{"name":"cleanup_extra_separators_include_eol"},"link":null},{"localized_name":"cleanup_breaks","name":"cleanup_breaks","shape":7,"type":"BOOLEAN","widget":{"name":"cleanup_breaks"},"link":null},{"localized_name":"cleanup_breaks_eol","name":"cleanup_breaks_eol","shape":7,"type":"BOOLEAN","widget":{"name":"cleanup_breaks_eol"},"link":null},{"localized_name":"cleanup_ands","name":"cleanup_ands","shape":7,"type":"BOOLEAN","widget":{"name":"cleanup_ands"},"link":null},{"localized_name":"cleanup_ands_eol","name":"cleanup_ands_eol","shape":7,"type":"BOOLEAN","widget":{"name":"cleanup_ands_eol"},"link":null},{"localized_name":"cleanup_extranetwork_tags","name":"cleanup_extranetwork_tags","shape":7,"type":"BOOLEAN","widget":{"name":"cleanup_extranetwork_tags"},"link":null},{"localized_name":"cleanup_merge_attention","name":"cleanup_merge_attention","shape":7,"type":"BOOLEAN","widget":{"name":"cleanup_merge_attention"},"link":null},{"localized_name":"remove_extranetwork_tags","name":"remove_extranetwork_tags","shape":7,"type":"BOOLEAN","widget":{"name":"remove_extranetwork_tags"},"link":null}],"outputs":[{"localized_name":"pos_prompt","name":"pos_prompt","type":"STRING","links":[96,97]},{"localized_name":"neg_prompt","name":"neg_prompt","type":"STRING","links":[98,99]},{"localized_name":"variables","name":"variables","type":"PPP_DICT","links":[100]}],"properties":{"cnr_id":"sd-webui-prompt-postprocessor","ver":"8f7957c4ae814cfb56515c9fc5adeea3cd75e630","Node name for S&R":"ACBPromptPostProcessor"},"widgets_values":["","","",1019,"randomize","minimal","stop","pony(sdxl)=pony,pny,pdxl\nillustrious(sdxl)=illustrious,illust,ilxl",true,"","true","stop",", ",true,", ",true,true,true,true,true,true,true,true,true,true,true,true,true]}],"links":[[2,5,0,3,3,"LATENT"],[4,6,0,3,1,"CONDITIONING"],[6,7,0,3,2,"CONDITIONING"],[7,3,0,8,0,"LATENT"],[9,8,0,9,0,"IMAGE"],[16,16,0,3,0,"MODEL"],[19,16,1,6,0,"CLIP"],[20,16,2,8,1,"VAE"],[24,17,0,3,4,"INT"],[45,16,1,7,0,"CLIP"],[87,20,0,37,0,"*"],[90,16,0,39,0,"MODEL,STRING"],[91,25,0,39,1,"STRING"],[92,26,0,39,2,"STRING"],[93,16,3,39,3,"STRING"],[94,17,0,39,4,"INT"],[95,27,0,39,10,"STRING"],[96,39,0,6,1,"STRING"],[97,39,0,35,0,"*"],[98,39,1,7,1,"STRING"],[99,39,1,36,0,"*"],[100,39,2,20,0,"PPP_DICT"]],"groups":[],"config":{},"extra":{"ds":{"scale":0.8580000000000002,"offset":[526.5913159217441,989.5715392146044]},"frontendVersion":"1.16.8","reroutes":[{"id":2,"pos":[1280,-820],"linkIds":[20]},{"id":3,"pos":[780,-500],"linkIds":[19,45]},{"id":4,"pos":[580,-720],"linkIds":[16]}],"VHS_latentpreview":true,"VHS_latentpreviewrate":0,"VHS_MetadataImage":true,"VHS_KeepIntermediate":true,"linkExtensions":[{"id":16,"parentId":4},{"id":19,"parentId":3},{"id":20,"parentId":2},{"id":45,"parentId":3}]},"version":0.4} \ No newline at end of file