diff --git a/.markdownlint.jsonc b/.markdownlint.jsonc index ad8477a..3cc93e8 100644 --- a/.markdownlint.jsonc +++ b/.markdownlint.jsonc @@ -2,5 +2,8 @@ "default": true, "MD013": false, "MD024": false, - "MD033": false + "MD033": false, + "MD060": { + "style": "any" + } } \ No newline at end of file diff --git a/docs/CONFIG.md b/docs/CONFIG.md index 7398bb7..6322335 100644 --- a/docs/CONFIG.md +++ b/docs/CONFIG.md @@ -23,6 +23,7 @@ The model variants now support regular expressions instead of a list of strings * **on_warnings**: Warn on the console or stop the generation. * **process_wildcards**: Activates the wildcard processing. * **do_cleanup**: Activates the cleanup processing. +* **cleanup_variables**: Do a cleanup of the output variables (depends on do_cleanup). * **wc_options**: Connection to a Wildcards options node. * **stn_options**: Connection to a Send-To-Negative options node. * **cup_options**: Connection to a Cleanup options node. diff --git a/ppp.py b/ppp.py index b5b4530..4abf2e7 100644 --- a/ppp.py +++ b/ppp.py @@ -77,7 +77,8 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in DEFAULT_IF_WILDCARDS = IFWILDCARDS_CHOICES.stop.value DEFAULT_CHOICE_SEPARATOR = ", " DEFAULT_KEEP_CHOICES_ORDER = True - DEFAULT_DO_CLEANUP = (True,) + DEFAULT_DO_CLEANUP = True + DEFAULT_CLEANUP_VARIABLES = True DEFAULT_CUP_EXTRA_SPACES = True DEFAULT_CUP_EMPTY_CONSTRUCTS = True DEFAULT_CUP_EXTRA_SEPARATORS = True @@ -187,6 +188,7 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in self.stn_separator = options.get("stn_separator", self.DEFAULT_STN_SEPARATOR) # Cleanup and remove options self.cup_do_cleanup = options.get("do_cleanup", self.DEFAULT_DO_CLEANUP) + self.cup_cleanup_variables = options.get("cleanup_variables", self.DEFAULT_CLEANUP_VARIABLES) self.cup_extraspaces = self.cup_do_cleanup and options.get( "cleanup_extra_spaces", self.DEFAULT_CUP_EXTRA_SPACES ) @@ -570,24 +572,17 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in negative_prompt = self.stn_separator.join(add_at_end) return negative_prompt - def __cleanup(self, text: str, is_negative: bool) -> str: + def __cleanup(self, text: str, where: int = 0) -> str: """ Trims the given text based on the specified cleanup options. Args: text (str): The text to be cleaned up. - is_negative (bool): Indicates if the text is a negative prompt. + where (int): Indicates the context or position for cleanup (0=generic, -1=negative prompt, 1=positive prompt). Returns: str: The resulting text. """ - escapedSeparator = re.escape(self.stn_separator) - 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 break_processing = self.host_config.get("break", "ok") # break_processing == "ok" (and always) if self.cup_breaks_eol: @@ -609,7 +604,7 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in # remove at start of prompt text = re.sub(r"\A(?:\s*BREAK\b\s*)+", "", text) # remove at end of prompt - text = re.sub(r"(?:\s*\bBREAK\s*)+\Z", "", text) + text = re.sub(r"(?:\s*\bBREAK\s*)+\Z", "", text) if break_processing == "eol": text2 = re.sub(r"\b\s*BREAK\s*\b", "\n", text) if text2 != text: @@ -624,7 +619,29 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in self.logger.debug("BREAK construct removed") elif break_processing == "error": if re.search(r"\bBREAK\b", text): - self.warn_or_stop(is_negative, "BREAK constructs are not allowed!") + self.warn_or_stop(where == -1, "BREAK constructs are not allowed!") + + if self.cup_ands: + # collapse ANDs with space after + text = re.sub(r"\bAND(?:\s+AND)+\s+", "AND ", text) + # collapse ANDs without space after + text = re.sub(r"\bAND(?:\s+AND)+\b", "AND", text) + # collapse separators and spaces before ANDs + text = re.sub(r"[, ]+AND\b", " AND", text) + # collapse separators and spaces after ANDs + text = re.sub(r"\bAND[, ]+", "AND ", text) + # remove at start of prompt + text = re.sub(r"\A(?:AND\b\s*)+", "", text) + # remove at end of prompt + text = re.sub(r"(\s*\bAND)+\Z", "", text) + + escapedSeparator = re.escape(self.stn_separator) + 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 @@ -646,19 +663,6 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in 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_ands: - # collapse ANDs with space after - text = re.sub(r"\bAND(?:\s+AND)+\s+", "AND ", text) - # collapse ANDs without space after - text = re.sub(r"\bAND(?:\s+AND)+\b", "AND", text) - # collapse separators and spaces before ANDs - text = re.sub(r"[, ]+AND\b", " AND", text) - # collapse separators and spaces after ANDs - text = re.sub(r"\bAND[, ]+", "AND ", text) - # remove at start of prompt - text = re.sub(r"\A(?:AND\b\s*)+", "", text) - # remove at end of prompt - text = re.sub(r"(\s*\bAND)+\Z", "", text) if self.cup_extranetworktags: # remove spaces before < text = re.sub(r"\B\s+<(?!!)", "<", text) @@ -682,6 +686,7 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in text = re.sub(r"[ ]{2,}", " ", text) # remove spaces at start and end text = text.strip() + return text def __get_best_parser(self, prompt: str) -> tuple[lark.Lark, str]: @@ -778,6 +783,7 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in ) negative_prompt = n_processor.start_visit("negative prompt", n_parsed, True) + # Complete variables var_keys = set(self.user_variables.keys()).union(set(self.echoed_variables.keys())) for k in var_keys: ev = self.echoed_variables.get(k) @@ -787,7 +793,7 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in if self.debug_level == DEBUG_LEVEL.full: self.logger.debug(self.format_output(f"Completing variable: {k}")) ev = p_processor.get_final_user_variable(k) - all_variables[k] = ev + all_variables[k] = self.__cleanup(ev, 0) if self.cup_cleanup_variables else ev if self.debug_level == DEBUG_LEVEL.full: self.logger.debug(self.format_output(f"All variables: {all_variables}")) @@ -804,8 +810,8 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in negative_prompt = self.__add_to_end(negative_prompt, p_processor.add_at["end"]) # Clean up - prompt = self.__cleanup(prompt, False) - negative_prompt = self.__cleanup(negative_prompt, True) + prompt = self.__cleanup(prompt, 1) + negative_prompt = self.__cleanup(negative_prompt, -1) # Check for wildcards not processed foundP = bool(p_processor.detectedWildcards) diff --git a/ppp_comfyui.py b/ppp_comfyui.py index d68a3fa..14b737b 100644 --- a/ppp_comfyui.py +++ b/ppp_comfyui.py @@ -115,6 +115,15 @@ class PromptPostProcessorComfyUINode: "label_off": "No", }, ), + "cleanup_variables": ( + "BOOLEAN", + { + "default": PromptPostProcessor.DEFAULT_CLEANUP_VARIABLES, + "tooltip": "Do a cleanup of the output variables", + "label_on": "Yes", + "label_off": "No", + }, + ), "wc_options": ( "PPP_OPTIONS_WC", { @@ -186,6 +195,7 @@ class PromptPostProcessorComfyUINode: on_warnings, process_wildcards, do_cleanup, + cleanup_variables, wc_options, stn_options, cup_options, @@ -204,6 +214,7 @@ class PromptPostProcessorComfyUINode: on_warnings, process_wildcards, do_cleanup, + cleanup_variables, wc_options, stn_options, cup_options, @@ -297,6 +308,7 @@ class PromptPostProcessorComfyUINode: stn_options["stn_ignore_repeats"] if stn_options else PromptPostProcessor.DEFAULT_STN_IGNORE_REPEATS ), "do_cleanup": do_cleanup, + "cleanup_variables": cleanup_variables, "cleanup_extra_spaces": ( cup_options["cup_extra_spaces"] if cup_options else PromptPostProcessor.DEFAULT_CUP_EXTRA_SPACES ), diff --git a/scripts/ppp_script.py b/scripts/ppp_script.py index 5f34303..8f2d0e4 100644 --- a/scripts/ppp_script.py +++ b/scripts/ppp_script.py @@ -320,6 +320,7 @@ class PromptPostProcessorA1111Script(scripts.Script): opts, "ppp_stn_ignorerepeats", PromptPostProcessor.DEFAULT_STN_IGNORE_REPEATS ), "do_cleanup": True, + "cleanup_variables": True, "cleanup_extra_spaces": getattr(opts, "ppp_cup_extraspaces", PromptPostProcessor.DEFAULT_CUP_EXTRA_SPACES), "cleanup_empty_constructs": getattr( opts, "ppp_cup_emptyconstructs", PromptPostProcessor.DEFAULT_CUP_EMPTY_CONSTRUCTS