From 0b03e1773fff31bc6067f2fa31cd4f17fb579f14 Mon Sep 17 00:00:00 2001 From: Antonio Cordero Balcazar Date: Sat, 5 Oct 2024 11:30:33 +0200 Subject: [PATCH] * Added pyproject.toml to publish to ComfyUI registry. * ComfyUI: optional modelname and model, and allowed sending a classname string instead of a model (#12) --- README.md | 8 +++ ppp.py | 2 +- ppp_comfyui.py | 133 +++++++++++++++++++++++++++++++++++++------------ pyproject.toml | 15 ++++++ 4 files changed, 125 insertions(+), 33 deletions(-) create mode 100644 pyproject.toml diff --git a/README.md b/README.md index 56038a7..8c7056a 100644 --- a/README.md +++ b/README.md @@ -336,6 +336,14 @@ This should still work as intended, and the only negative point i see is the unn ## Configuration +### ComfyUI specific inputs + +* **model**: Connect here the MODEL or a string with the model class name used by ComfyUI. Needed for the model kind system variables. +* **modelname**: Name of the model. Needed for the model name system variables and detection of pony (this also requieres for the model to be SDXL). +* **seed**: Connect here the seed used. By default it is -1 (random). +* **pos_prompt**: Connect here the prompt text, or fill it as a widget. +* **neg_prompt**: Connect here the negative prompt text, or fill it as a widget. + ### 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. diff --git a/ppp.py b/ppp.py index 81de671..47ebb32 100644 --- a/ppp.py +++ b/ppp.py @@ -23,7 +23,7 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in """ NAME = "Prompt Post-Processor" - VERSION = (2, 6, 0) + VERSION = (2, 7, 0) class IFWILDCARDS_CHOICES(Enum): ignore = "ignore" diff --git a/ppp_comfyui.py b/ppp_comfyui.py index eb72147..620a443 100644 --- a/ppp_comfyui.py +++ b/ppp_comfyui.py @@ -28,30 +28,26 @@ def __init__(self): self.grammar_content = file.read() self.wildcards_obj = PPPWildcards(lf.log) + class SmartType(str): + def __ne__(self, other): + if self == "*" or other == "*": + return False + selfset = set(self.split(",")) + otherset = set(other.split(",")) + return not otherset.issubset(selfset) + @classmethod def INPUT_TYPES(cls): return { "required": { - "model": ( - "MODEL", - { - "forceInput": True, - }, - ), - "modelname": ( - "STRING", - { - "default": "", - "forceInput": True, - }, - ), "pos_prompt": ( "STRING", { "multiline": True, "default": "", "dynamicPrompts": False, - "forceInput": True, + "defaultInput": True, + "forceInput": False, }, ), "neg_prompt": ( @@ -60,15 +56,34 @@ def INPUT_TYPES(cls): "multiline": True, "default": "", "dynamicPrompts": False, - "forceInput": True, + "defaultInput": True, + "forceInput": False, }, ), }, "optional": { + "model": ( + cls.SmartType("MODEL,STRING"), + { + "default": "", + "placeholder": "internal model class name", + "forceInput": True, + }, + ), + "modelname": ( + "STRING", + { + "default": "", + "placeholder": "full path of the model", + "defaultInput": True, + "forceInput": False, + }, + ), "seed": ( "INT", { "default": -1, + "defaultInput": True, "forceInput": False, }, ), @@ -77,6 +92,8 @@ def INPUT_TYPES(cls): { "default": DEBUG_LEVEL.minimal.value, "tooltip": "Debug level", + "defaultInput": False, + "forceInput": False, }, ), "pony_substrings": ( @@ -85,6 +102,8 @@ def INPUT_TYPES(cls): "default": PromptPostProcessor.DEFAULT_PONY_SUBSTRINGS, "placeholder": "comma separated list", "tooltip": "Comma separated list of substrings to look for in the modelname to determine if the model is a pony model", + "defaultInput": False, + "forceInput": False, }, ), "wc_process_wildcards": ( @@ -94,6 +113,8 @@ def INPUT_TYPES(cls): "tooltip": "Process wildcards in the prompt", "label_on": "Yes", "label_off": "No", + "defaultInput": False, + "forceInput": False, }, ), "wc_wildcards_folders": ( @@ -101,6 +122,8 @@ def INPUT_TYPES(cls): { "default": "", "tooltip": "Comma separated list of wildcards folders", + "defaultInput": False, + "forceInput": False, }, ), "wc_if_wildcards": ( @@ -108,6 +131,8 @@ def INPUT_TYPES(cls): { "default": PromptPostProcessor.IFWILDCARDS_CHOICES.ignore.value, "tooltip": "How to handle invalid wildcards in the prompt", + "defaultInput": False, + "forceInput": False, }, ), "wc_choice_separator": ( @@ -115,6 +140,8 @@ def INPUT_TYPES(cls): { "default": PromptPostProcessor.DEFAULT_CHOICE_SEPARATOR, "tooltip": "Default separator for selected choices", + "defaultInput": False, + "forceInput": False, }, ), "wc_keep_choices_order": ( @@ -124,6 +151,8 @@ def INPUT_TYPES(cls): "tooltip": "Keep the order of the choices in the prompt", "label_on": "Yes", "label_off": "No", + "defaultInput": False, + "forceInput": False, }, ), "stn_separator": ( @@ -131,6 +160,8 @@ def INPUT_TYPES(cls): { "default": PromptPostProcessor.DEFAULT_STN_SEPARATOR, "tooltip": "Separator for the content added to the negative prompt", + "defaultInput": False, + "forceInput": False, }, ), "stn_ignore_repeats": ( @@ -140,6 +171,8 @@ def INPUT_TYPES(cls): "tooltip": "Ignore repeated content added to the negative prompt", "label_on": "Yes", "label_off": "No", + "defaultInput": False, + "forceInput": False, }, ), "cleanup_extra_spaces": ( @@ -149,6 +182,8 @@ def INPUT_TYPES(cls): "tooltip": "Remove extra spaces", "label_on": "Yes", "label_off": "No", + "defaultInput": False, + "forceInput": False, }, ), "cleanup_empty_constructs": ( @@ -158,6 +193,8 @@ def INPUT_TYPES(cls): "tooltip": "Remove empty constructs", "label_on": "Yes", "label_off": "No", + "defaultInput": False, + "forceInput": False, }, ), "cleanup_extra_separators": ( @@ -167,6 +204,8 @@ def INPUT_TYPES(cls): "tooltip": "Remove extra separators", "label_on": "Yes", "label_off": "No", + "defaultInput": False, + "forceInput": False, }, ), "cleanup_extra_separators2": ( @@ -176,6 +215,8 @@ def INPUT_TYPES(cls): "tooltip": "Remove extra separators (additional cases)", "label_on": "Yes", "label_off": "No", + "defaultInput": False, + "forceInput": False, }, ), "cleanup_breaks": ( @@ -185,6 +226,8 @@ def INPUT_TYPES(cls): "tooltip": "Cleanup around BREAKs", "label_on": "Yes", "label_off": "No", + "defaultInput": False, + "forceInput": False, }, ), "cleanup_breaks_eol": ( @@ -194,6 +237,8 @@ def INPUT_TYPES(cls): "tooltip": "Set BREAKs in their own line", "label_on": "Yes", "label_off": "No", + "defaultInput": False, + "forceInput": False, }, ), "cleanup_ands": ( @@ -203,6 +248,8 @@ def INPUT_TYPES(cls): "tooltip": "Cleanup around ANDs", "label_on": "Yes", "label_off": "No", + "defaultInput": False, + "forceInput": False, }, ), "cleanup_ands_eol": ( @@ -212,6 +259,8 @@ def INPUT_TYPES(cls): "tooltip": "Set ANDs in their own line", "label_on": "Yes", "label_off": "No", + "defaultInput": False, + "forceInput": False, }, ), "cleanup_extranetwork_tags": ( @@ -221,6 +270,8 @@ def INPUT_TYPES(cls): "tooltip": "Clean up around extra network tags", "label_on": "Yes", "label_off": "No", + "defaultInput": False, + "forceInput": False, }, ), "cleanup_merge_attention": ( @@ -230,6 +281,8 @@ def INPUT_TYPES(cls): "tooltip": "Merge nested attention constructs", "label_on": "Yes", "label_off": "No", + "defaultInput": False, + "forceInput": False, }, ), "remove_extranetwork_tags": ( @@ -239,11 +292,26 @@ def INPUT_TYPES(cls): "tooltip": "Remove extra network tags", "label_on": "Yes", "label_off": "No", + "defaultInput": False, + "forceInput": False, }, ), }, } + @classmethod + def VALIDATE_INPUTS(cls, input_types: dict[str, str]): + it = cls.INPUT_TYPES() + expected = { + k: cls.SmartType("COMBO,STRING") if isinstance(v[0], list) else v[0] # we allow string for combos + for k, v in {**it["required"], **it["optional"]}.items() + } + for input_name, input_type in input_types.items(): + t = expected[input_name] + if input_type != t: + return f"Invalid type for input '{input_name}': {input_type} (expected {t})" + return True + RETURN_TYPES = ( "STRING", "STRING", @@ -286,7 +354,7 @@ def IS_CHANGED( cleanup_merge_attention, remove_extranetwork_tags, ): - new_run = { + new_run = { # everything except debug_level "model": model, "modelname": modelname, "pos_prompt": pos_prompt, @@ -343,26 +411,27 @@ def process( cleanup_merge_attention, remove_extranetwork_tags, ): + modelclass = ( + model.model.model_config.__class__.__name__ if model is not None and not isinstance(model, str) else model + ) or "" + if modelclass == "": + self.logger.warning("Model class is not provided. System variables might not be properly set.") + if modelname == "": + self.logger.warning("Modelname is not provided. System variables will not be properly set.") env_info = { "app": "comfyui", "models_path": folder_paths.models_dir, - "model_filename": modelname, # path is relative to checkpoints folder - "model_class": model.model.model_config.__class__.__name__, - "is_sd1": model.model.model_config.__class__.__name__ in ("SD15", "SD15_instructpix2pix"), - "is_sd2": model.model.model_config.__class__.__name__ in ("SD20", "SD21UnclipL", "SD21UnclipH"), - "is_sdxl": model.model.model_config.__class__.__name__ - in ( - "SDXL", - "SDXLRefiner", - "SDXL_instructpix2pix", - "Segmind_Vega", - "KOALA_700M", - "KOALA_1B", + "model_filename": modelname or "", # path is relative to checkpoints folder + "model_class": modelclass, + "is_sd1": modelclass in ("SD15", "SD15_instructpix2pix"), + "is_sd2": modelclass in ("SD20", "SD21UnclipL", "SD21UnclipH"), + "is_sdxl": ( + modelclass in ("SDXL", "SDXLRefiner", "SDXL_instructpix2pix", "Segmind_Vega", "KOALA_700M", "KOALA_1B") ), - "is_ssd": model.model.model_config.__class__.__name__ in ("SSD1B"), - "is_sd3": model.model.model_config.__class__.__name__ in ("SD3"), - "is_flux": model.model.model_config.__class__.__name__ in ("Flux"), - "is_auraflow": model.model.model_config.__class__.__name__ in ("AuraFlow"), + "is_ssd": modelclass in ("SSD1B",), + "is_sd3": modelclass in ("SD3",), + "is_flux": modelclass in ("Flux",), + "is_auraflow": modelclass in ("AuraFlow",), } # SVD_img2vid, SVD3D_u, SVD3_p, Stable_Zero123, SD_X4Upscaler, # Stable_Cascade_C, Stable_Cascade_B, StableAudio diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..c4d0301 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,15 @@ +[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.7.0" +license = {file = "LICENSE.txt"} +dependencies = ["lark"] + +[project.urls] +Repository = "https://github.com/acorderob/sd-webui-prompt-postprocessor" +# Used by Comfy Registry https://comfyregistry.org + +[tool.comfy] +PublisherId = "acorderob" +DisplayName = "sd-webui-prompt-postprocessor" +Icon = ""