diff --git a/.vscode/settings.json b/.vscode/settings.json index 14fb72d..3fc9ddc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { + "python.analysis.extraPaths": ["../.."], "python.testing.unittestArgs": [ "-v", "-s", @@ -9,8 +10,6 @@ "python.testing.pytestEnabled": false, "python.testing.unittestEnabled": true, "python.analysis.typeCheckingMode": "basic", - "python.linting.pylintEnabled": true, - "python.linting.enabled": true, "black-formatter.args": [ "--line-length=120" ] diff --git a/README.md b/README.md index 81a4358..a85412d 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,16 @@ you will have to rename one of the folders, so that it loads before than "Send t When in doubt, just rename this extension's folder with a "z" in front (for example) so that it is the last one to load, or manually set such folder name when installing it. +Note: it only recognizes regular A1111 prompt formats. So: + +* Attention: \[prompt\] (prompt) (prompt:weight) +* Alternation: \[prompt1|prompt2|...\] +* Scheduling: \[prompt1:prompt2:step\] + +In SD.Next that means only the A1111 or Full parsers. + +It does not build AND/BREAK separations into the negative prompt. + ## Installation 1. Go to Extensions > Install from URL diff --git a/scripts/extension.py b/scripts/extension.py index 8ba3999..a58ee1e 100644 --- a/scripts/extension.py +++ b/scripts/extension.py @@ -13,11 +13,14 @@ from modules import scripts, shared, script_callbacks from modules.processing import StableDiffusionProcessing from modules.shared import opts from sendtonegative import SendToNegative +from stnlogging import SendToNegativeLogFactory class SendToNegativeScript(scripts.Script): def __init__(self): if not hasattr(self, "callbacks_added"): + lf = SendToNegativeLogFactory() + self.__logstn = lf.log script_callbacks.on_ui_settings(self.__on_ui_settings) self.callbacks_added = True @@ -28,7 +31,7 @@ class SendToNegativeScript(scripts.Script): return scripts.AlwaysVisible def process(self, p: StableDiffusionProcessing, *args, **kwargs): - stn = SendToNegative(opts=opts) + stn = SendToNegative(self.__logstn, opts=opts) for i in range(len(p.all_prompts)): # pylint: disable=consider-using-enumerate p.all_prompts[i], p.all_negative_prompts[i] = stn.process_prompt( p.all_prompts[i], p.all_negative_prompts[i] diff --git a/sendtonegative.py b/sendtonegative.py index 70be69c..e5280de 100644 --- a/sendtonegative.py +++ b/sendtonegative.py @@ -1,17 +1,18 @@ from collections import namedtuple import logging -import lark import re +import lark class SendToNegative: # pylint: disable=too-few-public-methods NAME = "Send to Negative" - VERSION = "2.0" + VERSION = "2.1" DEFAULT_SEPARATOR = ", " def __init__( self, + log, separator=None, ignore_repeats=None, join_attention=None, @@ -33,7 +34,9 @@ class SendToNegative: # pylint: disable=too-few-public-methods iN - tags the position of insertion point N. Used only in the negative prompt and does not accept content. N can be 0 to 9. """ - self.__logger = logging.getLogger(__name__) + self.__logger = log + if opts is not None and opts.prompt_attention == "Compel parser": + self.__logger.warning("Compel parser is not supported!") self.__ignore_repeats = ( ignore_repeats if ignore_repeats is not None else getattr(opts, "stn_ignorerepeats", True) ) @@ -194,14 +197,14 @@ class SendToNegative: # pylint: disable=too-few-public-methods else: start += "(" end = f":{s.info1})" + end - #case "sc": + # case "sc": case "scb": start += "[" end = f"::{s.info1}]" + end case "sca": start += "[" end = f":{s.info1}]" + end - #case "al": + # case "al": case "alo": start += "[" + ("|" * int(s.info1 - 1)) end = ("|" * int(s.info2 - s.info1)) + "]" + end @@ -253,7 +256,7 @@ class SendToNegative: # pylint: disable=too-few-public-methods def __find_tags(self, prompt): add_at = {"start": [], "insertion_point": [[] for x in range(10)], "end": []} tree = self.__schedule_parser.parse(prompt) - self.__logger.debug(f"Initial tree: {tree.pretty()}") + self.__logger.debug(f"Initial tree:\n{tree.pretty()}") readtree = self.ReadTree(self.__logger, self.__ignore_repeats, self.__join_attention, prompt, add_at) readtree.visit(tree) @@ -261,9 +264,9 @@ class SendToNegative: # pylint: disable=too-few-public-methods for r in readtree.remove[::-1]: prompt = prompt[: r[0]] + prompt[r[1] :] if self.__cleanup: - prompt = re.sub(r"\((?::[\d\.]+)?\)", "", prompt) # clean up empty attention + prompt = re.sub(r"\((?::[+-]?[\d\.]+)?\)", "", prompt) # clean up empty attention prompt = re.sub(r"\[\]", "", prompt) # clean up empty attention - prompt = re.sub(r"\[:?:[\d\.]+\]", "", prompt) # clean up empty scheduling + prompt = re.sub(r"\[:?:[+-]?[\d\.]+\]", "", prompt) # clean up empty scheduling prompt = re.sub(r"\[\|+\]", "", prompt) # clean up empty alternation # clean up whitespace and extra separators prompt = ( diff --git a/tests/tests.py b/tests/tests.py index b9ef4b7..80a67e5 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -6,17 +6,15 @@ import os sys.path.insert(1, os.path.join(sys.path[0], "..")) from sendtonegative import SendToNegative # pylint: disable=import-error +from stnlogging import SendToNegativeLogFactory class TestSendToNegative(unittest.TestCase): def setUp(self): - self.defstn = SendToNegative( - separator=", ", - ignore_repeats=True, - join_attention=True, - cleanup=True, - ) - logging.basicConfig(level=logging.DEBUG) + lf = SendToNegativeLogFactory() + self.__log = lf.log + self.__log.setLevel(logging.DEBUG) + self.defstn = SendToNegative(self.__log, separator=", ", ignore_repeats=True, join_attention=True, cleanup=True) def process( self, @@ -121,12 +119,7 @@ class TestSendToNegative(unittest.TestCase): "normal quality, , bad quality, worse quality", " (), flowers , , ", "red, (pink), normal quality, yellow, bad quality, green, worse quality, purple, blue", - SendToNegative( - separator=", ", - ignore_repeats=True, - join_attention=True, - cleanup=False, - ), + SendToNegative(self.__log, separator=", ", ignore_repeats=True, join_attention=True, cleanup=False), ) def test_inside_attention1(self):