Compare commits

...
3 Commits
Author SHA1 Message Date
Antonio Cordero Balcazar bfc47d0d53 * A1111: add prompts to metadata only if they change 2025-05-03 12:07:18 +02:00
Antonio Cordero Balcazar b837f38422 * if_wildcards option defaults now to stop.
* ComfyUI: new wildcards_input.
* removed json import since yaml can read them.
* more models detected in ComfyUI.
* ComfyUI: example workflow.
2025-05-03 12:07:18 +02:00
Antonio Cordero Balcazar 5e3c8cbc44 * ComfyUI: updates to the ComfyUI registry workflow.
* A1111: option to add original prompts to metadata.
* fixed some bogus import error shenanigans.
* warn when a model name detects multiple variants.
2025-04-11 17:50:26 +02:00
10 changed files with 228 additions and 153 deletions
+5 -3
View File
@@ -8,17 +8,19 @@ on:
paths: paths:
- "pyproject.toml" - "pyproject.toml"
permissions:
issues: write
jobs: jobs:
publish-node: publish-node:
name: Publish Custom Node to registry name: Publish Custom Node to registry
runs-on: ubuntu-latest runs-on: ubuntu-latest
# if this is a forked repository. Skipping the workflow. if: ${{ github.repository_owner == 'acorderob' }}
if: github.event.repository.fork == false
steps: steps:
- name: Check out code - name: Check out code
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Publish Custom Node - name: Publish Custom Node
uses: Comfy-Org/publish-node-action@main uses: Comfy-Org/publish-node-action@v1
with: with:
## Add your own personal access token to your Github Repository secrets and reference it here. ## Add your own personal access token to your Github Repository secrets and reference it here.
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }} personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
+2
View File
@@ -9,6 +9,7 @@
* **seed**: Connect here the seed used. By default it is -1 (random). * **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. * **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. * **neg_prompt**: Connect here the negative prompt text, or fill it as a widget.
* **wc_wildcards_input**: Wildcards definitions (in yaml or json format). Direct input added to the ones found in the wildcards folders. Allows wildcards to be included in the workflow.
Other common settings (see [below](#common-settings)) also appear as inputs or widgets. Other common settings (see [below](#common-settings)) also appear as inputs or widgets.
@@ -40,6 +41,7 @@ With this prompt: `__quality__, 1girl, ${head:__eyes__, __hair__, __expression__
The default value defines strings for *Pony* and *Illustrious* models. The default value defines strings for *Pony* and *Illustrious* models.
* **Apply in img2img**: check if you want to do the processing in img2img processes (*does not apply to the ComfyUI node*). * **Apply in img2img**: check if you want to do the processing in img2img processes (*does not apply to the ComfyUI node*).
* **Add original prompts to metadata**: adds original prompts to the metadata if they have changed (*does not apply to the ComfyUI node*).
### Wildcard settings ### Wildcard settings
+11 -4
View File
@@ -2,6 +2,7 @@ import logging
import math import math
import os import os
import re import re
import sys
import textwrap import textwrap
import time import time
from collections import namedtuple from collections import namedtuple
@@ -9,11 +10,12 @@ from enum import Enum
from typing import Any, Callable, Optional from typing import Any, Callable, Optional
import lark import lark
import lark.parsers
import numpy as np import numpy as np
from ppp_logging import DEBUG_LEVEL sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from ppp_wildcards import PPPWildcard, PPPWildcards
from ppp_logging import DEBUG_LEVEL # pylint: disable=import-error
from ppp_wildcards import PPPWildcard, PPPWildcards # pylint: disable=import-error
class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-instance-attributes class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-instance-attributes
@@ -119,7 +121,7 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in
self.wil_keep_choices_order = options.get("keep_choices_order", False) self.wil_keep_choices_order = options.get("keep_choices_order", False)
self.wil_choice_separator = options.get("choice_separator", self.DEFAULT_CHOICE_SEPARATOR) self.wil_choice_separator = options.get("choice_separator", self.DEFAULT_CHOICE_SEPARATOR)
self.wil_ifwildcards = self.IFWILDCARDS_CHOICES( self.wil_ifwildcards = self.IFWILDCARDS_CHOICES(
options.get("if_wildcards", self.IFWILDCARDS_CHOICES.ignore.value) options.get("if_wildcards", self.IFWILDCARDS_CHOICES.stop.value)
) )
# Send to negative options # Send to negative options
self.stn_ignore_repeats = options.get("stn_ignore_repeats", True) self.stn_ignore_repeats = options.get("stn_ignore_repeats", True)
@@ -220,6 +222,11 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in
for model_name, model_type_and_substrings in self.variants_definitions.items() for model_name, model_type_and_substrings in self.variants_definitions.items()
if model_name not in self.SUPPORTED_MODELS if model_name not in self.SUPPORTED_MODELS
} }
is_models_true = [k for k, v in is_models.items() if v]
if len(is_models_true) > 1:
self.logger.warning(
f"Multiple model variants detected at the same time in the filename!: {', '.join(is_models_true)}"
)
self.system_variables.update({"_is_" + x: y for x, y in is_models.items()}) self.system_variables.update({"_is_" + x: y for x, y in is_models.items()})
for x in sdchecks.keys(): for x in sdchecks.keys():
if x != "": if x != "":
+1 -1
View File
@@ -2,7 +2,7 @@ from collections import OrderedDict
from logging import Logger from logging import Logger
from typing import Tuple from typing import Tuple
from ppp_logging import DEBUG_LEVEL from ppp_logging import DEBUG_LEVEL # pylint: disable=import-error
class PPPLRUCache: class PPPLRUCache:
+37 -57
View File
@@ -1,5 +1,3 @@
# pylint: disable=missing-module-docstring, missing-class-docstring, missing-function-docstring, invalid-name
import os import os
# pylint: disable=import-error # pylint: disable=import-error
@@ -48,8 +46,6 @@ class PromptPostProcessorComfyUINode:
"multiline": True, "multiline": True,
"default": "", "default": "",
"dynamicPrompts": False, "dynamicPrompts": False,
"defaultInput": True,
"forceInput": False,
}, },
), ),
"neg_prompt": ( "neg_prompt": (
@@ -58,8 +54,6 @@ class PromptPostProcessorComfyUINode:
"multiline": True, "multiline": True,
"default": "", "default": "",
"dynamicPrompts": False, "dynamicPrompts": False,
"defaultInput": True,
"forceInput": False,
}, },
), ),
}, },
@@ -77,16 +71,13 @@ class PromptPostProcessorComfyUINode:
{ {
"default": "", "default": "",
"placeholder": "full path of the model", "placeholder": "full path of the model",
"defaultInput": True, "dynamicPrompts": False,
"forceInput": False,
}, },
), ),
"seed": ( "seed": (
"INT", "INT",
{ {
"default": -1, "default": -1,
"defaultInput": True,
"forceInput": False,
}, },
), ),
"debug_level": ( "debug_level": (
@@ -94,8 +85,6 @@ class PromptPostProcessorComfyUINode:
{ {
"default": DEBUG_LEVEL.minimal.value, "default": DEBUG_LEVEL.minimal.value,
"tooltip": "Debug level", "tooltip": "Debug level",
"defaultInput": False,
"forceInput": False,
}, },
), ),
"variants_definitions": ( "variants_definitions": (
@@ -105,8 +94,7 @@ class PromptPostProcessorComfyUINode:
"multiline": True, "multiline": True,
"placeholder": "", "placeholder": "",
"tooltip": "Definitions for variant models to be recognized based on strings found in the full filename. Format for each line is: 'name(kind)=comma separated list of substrings (case insensitive)' with kind being one of the base model types or not specified", "tooltip": "Definitions for variant models to be recognized based on strings found in the full filename. Format for each line is: 'name(kind)=comma separated list of substrings (case insensitive)' with kind being one of the base model types or not specified",
"defaultInput": False, "dynamicPrompts": False,
"forceInput": False,
}, },
), ),
"wc_process_wildcards": ( "wc_process_wildcards": (
@@ -116,8 +104,6 @@ class PromptPostProcessorComfyUINode:
"tooltip": "Process wildcards in the prompt", "tooltip": "Process wildcards in the prompt",
"label_on": "Yes", "label_on": "Yes",
"label_off": "No", "label_off": "No",
"defaultInput": False,
"forceInput": False,
}, },
), ),
"wc_wildcards_folders": ( "wc_wildcards_folders": (
@@ -125,17 +111,24 @@ class PromptPostProcessorComfyUINode:
{ {
"default": "", "default": "",
"tooltip": "Comma separated list of wildcards folders", "tooltip": "Comma separated list of wildcards folders",
"defaultInput": False, "dynamicPrompts": False,
"forceInput": False, },
),
"wc_wildcards_input": (
"STRING",
{
"default": "",
"multiline": True,
"placeholder": "wildcards definitions",
"tooltip": "Wildcards definitions in yaml/json format",
"dynamicPrompts": False,
}, },
), ),
"wc_if_wildcards": ( "wc_if_wildcards": (
[e.value for e in PromptPostProcessor.IFWILDCARDS_CHOICES], [e.value for e in PromptPostProcessor.IFWILDCARDS_CHOICES],
{ {
"default": PromptPostProcessor.IFWILDCARDS_CHOICES.ignore.value, "default": PromptPostProcessor.IFWILDCARDS_CHOICES.stop.value,
"tooltip": "How to handle invalid wildcards in the prompt", "tooltip": "How to handle invalid wildcards in the prompt",
"defaultInput": False,
"forceInput": False,
}, },
), ),
"wc_choice_separator": ( "wc_choice_separator": (
@@ -143,8 +136,7 @@ class PromptPostProcessorComfyUINode:
{ {
"default": PromptPostProcessor.DEFAULT_CHOICE_SEPARATOR, "default": PromptPostProcessor.DEFAULT_CHOICE_SEPARATOR,
"tooltip": "Default separator for selected choices", "tooltip": "Default separator for selected choices",
"defaultInput": False, "dynamicPrompts": False,
"forceInput": False,
}, },
), ),
"wc_keep_choices_order": ( "wc_keep_choices_order": (
@@ -154,8 +146,6 @@ class PromptPostProcessorComfyUINode:
"tooltip": "Keep the order of the choices in the prompt", "tooltip": "Keep the order of the choices in the prompt",
"label_on": "Yes", "label_on": "Yes",
"label_off": "No", "label_off": "No",
"defaultInput": False,
"forceInput": False,
}, },
), ),
"stn_separator": ( "stn_separator": (
@@ -163,8 +153,7 @@ class PromptPostProcessorComfyUINode:
{ {
"default": PromptPostProcessor.DEFAULT_STN_SEPARATOR, "default": PromptPostProcessor.DEFAULT_STN_SEPARATOR,
"tooltip": "Separator for the content added to the negative prompt", "tooltip": "Separator for the content added to the negative prompt",
"defaultInput": False, "dynamicPrompts": False,
"forceInput": False,
}, },
), ),
"stn_ignore_repeats": ( "stn_ignore_repeats": (
@@ -174,8 +163,6 @@ class PromptPostProcessorComfyUINode:
"tooltip": "Ignore repeated content added to the negative prompt", "tooltip": "Ignore repeated content added to the negative prompt",
"label_on": "Yes", "label_on": "Yes",
"label_off": "No", "label_off": "No",
"defaultInput": False,
"forceInput": False,
}, },
), ),
"cleanup_extra_spaces": ( "cleanup_extra_spaces": (
@@ -185,8 +172,6 @@ class PromptPostProcessorComfyUINode:
"tooltip": "Remove extra spaces", "tooltip": "Remove extra spaces",
"label_on": "Yes", "label_on": "Yes",
"label_off": "No", "label_off": "No",
"defaultInput": False,
"forceInput": False,
}, },
), ),
"cleanup_empty_constructs": ( "cleanup_empty_constructs": (
@@ -196,8 +181,6 @@ class PromptPostProcessorComfyUINode:
"tooltip": "Remove empty constructs", "tooltip": "Remove empty constructs",
"label_on": "Yes", "label_on": "Yes",
"label_off": "No", "label_off": "No",
"defaultInput": False,
"forceInput": False,
}, },
), ),
"cleanup_extra_separators": ( "cleanup_extra_separators": (
@@ -207,8 +190,6 @@ class PromptPostProcessorComfyUINode:
"tooltip": "Remove extra separators", "tooltip": "Remove extra separators",
"label_on": "Yes", "label_on": "Yes",
"label_off": "No", "label_off": "No",
"defaultInput": False,
"forceInput": False,
}, },
), ),
"cleanup_extra_separators2": ( "cleanup_extra_separators2": (
@@ -218,8 +199,6 @@ class PromptPostProcessorComfyUINode:
"tooltip": "Remove extra separators (additional cases)", "tooltip": "Remove extra separators (additional cases)",
"label_on": "Yes", "label_on": "Yes",
"label_off": "No", "label_off": "No",
"defaultInput": False,
"forceInput": False,
}, },
), ),
"cleanup_breaks": ( "cleanup_breaks": (
@@ -229,8 +208,6 @@ class PromptPostProcessorComfyUINode:
"tooltip": "Cleanup around BREAKs", "tooltip": "Cleanup around BREAKs",
"label_on": "Yes", "label_on": "Yes",
"label_off": "No", "label_off": "No",
"defaultInput": False,
"forceInput": False,
}, },
), ),
"cleanup_breaks_eol": ( "cleanup_breaks_eol": (
@@ -240,8 +217,6 @@ class PromptPostProcessorComfyUINode:
"tooltip": "Set BREAKs in their own line", "tooltip": "Set BREAKs in their own line",
"label_on": "Yes", "label_on": "Yes",
"label_off": "No", "label_off": "No",
"defaultInput": False,
"forceInput": False,
}, },
), ),
"cleanup_ands": ( "cleanup_ands": (
@@ -251,8 +226,6 @@ class PromptPostProcessorComfyUINode:
"tooltip": "Cleanup around ANDs", "tooltip": "Cleanup around ANDs",
"label_on": "Yes", "label_on": "Yes",
"label_off": "No", "label_off": "No",
"defaultInput": False,
"forceInput": False,
}, },
), ),
"cleanup_ands_eol": ( "cleanup_ands_eol": (
@@ -262,8 +235,6 @@ class PromptPostProcessorComfyUINode:
"tooltip": "Set ANDs in their own line", "tooltip": "Set ANDs in their own line",
"label_on": "Yes", "label_on": "Yes",
"label_off": "No", "label_off": "No",
"defaultInput": False,
"forceInput": False,
}, },
), ),
"cleanup_extranetwork_tags": ( "cleanup_extranetwork_tags": (
@@ -273,8 +244,6 @@ class PromptPostProcessorComfyUINode:
"tooltip": "Clean up around extra network tags", "tooltip": "Clean up around extra network tags",
"label_on": "Yes", "label_on": "Yes",
"label_off": "No", "label_off": "No",
"defaultInput": False,
"forceInput": False,
}, },
), ),
"cleanup_merge_attention": ( "cleanup_merge_attention": (
@@ -284,8 +253,6 @@ class PromptPostProcessorComfyUINode:
"tooltip": "Merge nested attention constructs", "tooltip": "Merge nested attention constructs",
"label_on": "Yes", "label_on": "Yes",
"label_off": "No", "label_off": "No",
"defaultInput": False,
"forceInput": False,
}, },
), ),
"remove_extranetwork_tags": ( "remove_extranetwork_tags": (
@@ -295,8 +262,6 @@ class PromptPostProcessorComfyUINode:
"tooltip": "Remove extra network tags", "tooltip": "Remove extra network tags",
"label_on": "Yes", "label_on": "Yes",
"label_off": "No", "label_off": "No",
"defaultInput": False,
"forceInput": False,
}, },
), ),
}, },
@@ -342,6 +307,7 @@ class PromptPostProcessorComfyUINode:
variants_definitions, variants_definitions,
wc_process_wildcards, wc_process_wildcards,
wc_wildcards_folders, wc_wildcards_folders,
wc_wildcards_input,
wc_if_wildcards, wc_if_wildcards,
wc_choice_separator, wc_choice_separator,
wc_keep_choices_order, wc_keep_choices_order,
@@ -372,6 +338,7 @@ class PromptPostProcessorComfyUINode:
"variants_definitions": variants_definitions, "variants_definitions": variants_definitions,
"process_wildcards": wc_process_wildcards, "process_wildcards": wc_process_wildcards,
"wildcards_folders": wc_wildcards_folders, "wildcards_folders": wc_wildcards_folders,
"wildcards_input": wc_wildcards_input,
"if_wildcards": wc_if_wildcards, "if_wildcards": wc_if_wildcards,
"choice_separator": wc_choice_separator, "choice_separator": wc_choice_separator,
"keep_choices_order": wc_keep_choices_order, "keep_choices_order": wc_keep_choices_order,
@@ -403,6 +370,7 @@ class PromptPostProcessorComfyUINode:
variants_definitions, variants_definitions,
wc_process_wildcards, wc_process_wildcards,
wc_wildcards_folders, wc_wildcards_folders,
wc_wildcards_input,
wc_if_wildcards, wc_if_wildcards,
wc_choice_separator, wc_choice_separator,
wc_keep_choices_order, wc_keep_choices_order,
@@ -427,13 +395,14 @@ class PromptPostProcessorComfyUINode:
self.logger.warning("Model class is not provided. System variables might not be properly set.") self.logger.warning("Model class is not provided. System variables might not be properly set.")
if modelname == "": if modelname == "":
self.logger.warning("Modelname is not provided. System variables will not be properly set.") self.logger.warning("Modelname is not provided. System variables will not be properly set.")
# model class values in ComfyUI\comfy\supported_models.py
env_info = { env_info = {
"app": "comfyui", "app": "comfyui",
"models_path": folder_paths.models_dir, "models_path": folder_paths.models_dir,
"model_filename": modelname or "", # path is relative to checkpoints folder "model_filename": modelname or "", # path is relative to checkpoints folder
"model_class": modelclass, "model_class": modelclass,
"is_sd1": modelclass in ("SD15", "SD15_instructpix2pix"), "is_sd1": modelclass in ("SD15", "SD15_instructpix2pix"),
"is_sd2": modelclass in ("SD20", "SD21UnclipL", "SD21UnclipH"), "is_sd2": modelclass in ("SD20", "SD21UnclipL", "SD21UnclipH", "LotusD"),
"is_sdxl": ( "is_sdxl": (
modelclass in ("SDXL", "SDXLRefiner", "SDXL_instructpix2pix", "Segmind_Vega", "KOALA_700M", "KOALA_1B") modelclass in ("SDXL", "SDXLRefiner", "SDXL_instructpix2pix", "Segmind_Vega", "KOALA_700M", "KOALA_1B")
), ),
@@ -441,9 +410,18 @@ class PromptPostProcessorComfyUINode:
"is_sd3": modelclass in ("SD3",), "is_sd3": modelclass in ("SD3",),
"is_flux": modelclass in ("Flux", "FluxInpaint", "FluxSchnell"), "is_flux": modelclass in ("Flux", "FluxInpaint", "FluxSchnell"),
"is_auraflow": modelclass in ("AuraFlow",), "is_auraflow": modelclass in ("AuraFlow",),
"is_pixart": modelclass in ("PixArtAlpha", "PixArtSigma"),
"is_lumina2": modelclass in ("Lumina2",),
"is_ltxv": modelclass in ("LTXV",),
"is_cosmos": modelclass in ("CosmosT2V", "CosmosI2V"),
"is_genmomochi": modelclass in ("GenmoMochi",),
"is_hunyuan": modelclass in ("HunyuanDiT", "HunyuanDiT1"),
"is_hunyuanvideo": modelclass in ("HunyuanVideo", "HunyuanVideoI2V", "HunyuanVideoSkyreelsI2V"),
"is_hunyuan3d": modelclass in ("Hunyuan3Dv2", "Hunyuan3Dv2mini"),
"is_wanvideo": modelclass in ("WAN21_T2V", "WAN21_I2V", "WAN21_FunControl2V"),
"is_hidream": modelclass in ("HiDream",),
} }
# Also supported: SVD_img2vid, SVD3D_u, SVD3_p, Stable_Zero123, SD_X4Upscaler, # Also supported: SVD_img2vid, SVD3D_u, SVD3_p, Stable_Zero123, SD_X4Upscaler, Stable_Cascade_C, Stable_Cascade_B, StableAudio
# Stable_Cascade_C, Stable_Cascade_B, StableAudio, HunyuanDiT, HunyuanDiT1, GenmoMochi, LTXV
if wc_wildcards_folders == "": if wc_wildcards_folders == "":
wc_wildcards_folders = ",".join(folder_paths.get_folder_paths("wildcards") or []) wc_wildcards_folders = ",".join(folder_paths.get_folder_paths("wildcards") or [])
@@ -477,7 +455,11 @@ class PromptPostProcessorComfyUINode:
"cleanup_merge_attention": cleanup_merge_attention, "cleanup_merge_attention": cleanup_merge_attention,
"remove_extranetwork_tags": remove_extranetwork_tags, "remove_extranetwork_tags": remove_extranetwork_tags,
} }
self.wildcards_obj.refresh_wildcards(debug_level, wildcards_folders if options["process_wildcards"] else None) self.wildcards_obj.refresh_wildcards(
debug_level,
wildcards_folders if options["process_wildcards"] else None,
wc_wildcards_input,
)
ppp = PromptPostProcessor( ppp = PromptPostProcessor(
self.logger, self.interrupt, env_info, options, self.grammar_content, self.wildcards_obj self.logger, self.interrupt, env_info, options, self.grammar_content, self.wildcards_obj
) )
@@ -519,8 +501,6 @@ class PromptPostProcessorSelectVariableComfyUINode:
"multiline": False, "multiline": False,
"default": "", "default": "",
"dynamicPrompts": False, "dynamicPrompts": False,
"defaultInput": False,
"forceInput": False,
}, },
), ),
}, },
+59 -29
View File
@@ -1,11 +1,10 @@
import fnmatch import fnmatch
import os import os
import json
from typing import Optional from typing import Optional
import logging import logging
import yaml import yaml
from ppp_logging import DEBUG_LEVEL from ppp_logging import DEBUG_LEVEL # pylint: disable=import-error
def deep_freeze(obj): def deep_freeze(obj):
@@ -20,12 +19,11 @@ def deep_freeze(obj):
""" """
if isinstance(obj, dict): if isinstance(obj, dict):
return tuple((k, deep_freeze(v)) for k, v in sorted(obj.items())) return tuple((k, deep_freeze(v)) for k, v in sorted(obj.items()))
elif isinstance(obj, list): if isinstance(obj, list):
return tuple(deep_freeze(i) for i in obj) return tuple(deep_freeze(i) for i in obj)
elif isinstance(obj, set): if isinstance(obj, set):
return tuple(deep_freeze(i) for i in sorted(obj)) return tuple(deep_freeze(i) for i in sorted(obj))
else: return obj
return obj
class PPPWildcard: class PPPWildcard:
@@ -70,6 +68,7 @@ class PPPWildcards:
""" """
DEFAULT_WILDCARDS_FOLDER = "wildcards" DEFAULT_WILDCARDS_FOLDER = "wildcards"
LOCALINPUT_FILENAME = "#INPUT"
def __init__(self, logger): def __init__(self, logger):
self.__logger: logging.Logger = logger self.__logger: logging.Logger = logger
@@ -84,31 +83,38 @@ class PPPWildcards:
def __sizeof__(self): def __sizeof__(self):
return self.wildcards.__sizeof__() + self.__wildcards_folders.__sizeof__() + self.__wildcard_files.__sizeof__() return self.wildcards.__sizeof__() + self.__wildcards_folders.__sizeof__() + self.__wildcard_files.__sizeof__()
def refresh_wildcards(self, debug_level: DEBUG_LEVEL, wildcards_folders: Optional[list[str]]): def refresh_wildcards(
self, debug_level: DEBUG_LEVEL, wildcards_folders: Optional[list[str]], wildcards_input: str = None
):
""" """
Initialize the wildcards. Initialize the wildcards.
""" """
self.__debug_level = debug_level self.__debug_level = debug_level
self.__wildcards_folders = wildcards_folders self.__wildcards_folders = wildcards_folders or []
if wildcards_folders is not None: # if self.debug_level != DEBUG_LEVEL.none:
# if self.debug_level != DEBUG_LEVEL.none: # self.logger.info("Refreshing wildcards...")
# self.logger.info("Initializing wildcards...") # t1 = time.time()
# t1 = time.time() for fullpath in list(self.__wildcard_files.keys()):
for fullpath in list(self.__wildcard_files.keys()): if fullpath != self.LOCALINPUT_FILENAME:
path = os.path.dirname(fullpath) path = os.path.dirname(fullpath)
if not os.path.exists(fullpath) or not any( if not os.path.exists(fullpath) or not any(
os.path.commonpath([path, folder]) == folder for folder in self.__wildcards_folders os.path.commonpath([path, folder]) == folder for folder in self.__wildcards_folders
): ):
self.__remove_wildcards_from_file(fullpath) self.__remove_wildcards_from_path(fullpath)
for f in self.__wildcards_folders: elif wildcards_input is None:
self.__get_wildcards_in_directory(f, f) self.__remove_wildcards_from_path(fullpath)
# t2 = time.time() if wildcards_folders is not None or wildcards_input is not None:
# if self.debug_level != DEBUG_LEVEL.none: if wildcards_folders is not None:
# self.logger.info(f"Wildcards init time: {t2 - t1:.3f} seconds") for f in self.__wildcards_folders:
self.__get_wildcards_in_directory(f, f)
if wildcards_input is not None:
self.__get_wildcards_in_input(wildcards_input)
else: else:
self.__wildcards_folders = []
self.wildcards = {} self.wildcards = {}
self.__wildcard_files = {} self.__wildcard_files = {}
# t2 = time.time()
# 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]: def get_wildcards(self, key: str) -> list[PPPWildcard]:
""" """
@@ -161,7 +167,7 @@ class PPPWildcards:
return None return None
return current_dict return current_dict
def __remove_wildcards_from_file(self, full_path: str, debug=True): def __remove_wildcards_from_path(self, full_path: str, debug=True):
""" """
Clear all wildcards in a file. Clear all wildcards in a file.
@@ -169,9 +175,12 @@ class PPPWildcards:
full_path (str): The path to the file. full_path (str): The path to the file.
debug (bool): Whether to print debug messages or not. debug (bool): Whether to print debug messages or not.
""" """
last_modified_cached = self.__wildcard_files.get(full_path, None) last_modified_cached = self.__wildcard_files.get(full_path, None) # a time or a hash
if debug and last_modified_cached is not None and self.__debug_level != DEBUG_LEVEL.none: if debug and last_modified_cached is not None and self.__debug_level != DEBUG_LEVEL.none:
self.__logger.debug(f"Removing wildcards from file: {full_path}") if full_path == self.LOCALINPUT_FILENAME:
self.__logger.debug("Removing wildcards from input")
else:
self.__logger.debug(f"Removing wildcards from file: {full_path}")
if full_path in self.__wildcard_files.keys(): if full_path in self.__wildcard_files.keys():
del self.__wildcard_files[full_path] del self.__wildcard_files[full_path]
for key in list(self.wildcards.keys()): for key in list(self.wildcards.keys()):
@@ -194,7 +203,7 @@ class PPPWildcards:
_, extension = os.path.splitext(filename) _, extension = os.path.splitext(filename)
if extension not in (".txt", ".json", ".yaml", ".yml"): if extension not in (".txt", ".json", ".yaml", ".yml"):
return return
self.__remove_wildcards_from_file(full_path, False) self.__remove_wildcards_from_path(full_path, False)
if last_modified_cached is not None and self.__debug_level != DEBUG_LEVEL.none: if last_modified_cached is not None and self.__debug_level != DEBUG_LEVEL.none:
self.__logger.debug(f"Updating wildcards from file: {full_path}") self.__logger.debug(f"Updating wildcards from file: {full_path}")
if extension == ".txt": if extension == ".txt":
@@ -203,6 +212,31 @@ class PPPWildcards:
self.__get_wildcards_in_structured_file(full_path, base) self.__get_wildcards_in_structured_file(full_path, base)
self.__wildcard_files[full_path] = last_modified self.__wildcard_files[full_path] = last_modified
def __get_wildcards_in_input(self, wildcards_input: str):
"""
Get all wildcards in the string.
Args:
wildcards_input (str): The input string containing wildcards in json or yaml format.
"""
new_h = hash(wildcards_input)
h = self.__wildcard_files.get(self.LOCALINPUT_FILENAME, None)
if h == new_h:
return
self.__remove_wildcards_from_path(self.LOCALINPUT_FILENAME, False)
if h is not None and self.__debug_level != DEBUG_LEVEL.none:
self.__logger.debug("Updating wildcards from input")
wildcards_input = wildcards_input.strip()
if wildcards_input != "":
try:
content = yaml.safe_load(wildcards_input)
except yaml.YAMLError as e:
self.__logger.warning(f"Invalid format for input wildcards: {e}")
return
if content is not None:
self.__add_wildcard(content, self.LOCALINPUT_FILENAME, [self.LOCALINPUT_FILENAME])
self.__wildcard_files[self.LOCALINPUT_FILENAME] = new_h
def is_dict_choices_options(self, d: dict) -> bool: def is_dict_choices_options(self, d: dict) -> bool:
""" """
Check if a dictionary is a valid choices options dictionary. Check if a dictionary is a valid choices options dictionary.
@@ -374,12 +408,8 @@ class PPPWildcards:
""" """
external_key: str = os.path.relpath(os.path.splitext(full_path)[0], base) external_key: str = os.path.relpath(os.path.splitext(full_path)[0], base)
external_key_parts = external_key.split(os.sep) external_key_parts = external_key.split(os.sep)
_, extension = os.path.splitext(full_path)
with open(full_path, "r", encoding="utf-8") as file: with open(full_path, "r", encoding="utf-8") as file:
if extension == ".json": content = yaml.safe_load(file)
content = json.loads(file.read())
else:
content = yaml.safe_load(file)
self.__add_wildcard(content, full_path, external_key_parts) self.__add_wildcard(content, full_path, external_key_parts)
def __get_wildcards_in_text_file(self, full_path, base): def __get_wildcards_in_text_file(self, full_path, base):
+1 -1
View File
@@ -1,7 +1,7 @@
[project] [project]
name = "sd-webui-prompt-postprocessor" 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." 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.9.0" version = "2.10.0"
license = { file = "LICENSE.txt" } license = { file = "LICENSE.txt" }
dependencies = ["lark", "numpy", "pyyaml"] dependencies = ["lark", "numpy", "pyyaml"]
+63 -31
View File
@@ -6,18 +6,17 @@ import os
import time import time
import numpy as np import numpy as np
sys.path.append(os.path.join(sys.path[0], "..")) sys.path.append(os.path.join(os.path.realpath(__file__), "..")) # base path for the extension
from modules import scripts, shared, script_callbacks # pylint: disable=import-error
from modules import scripts, shared, script_callbacks from modules.processing import StableDiffusionProcessing # pylint: disable=import-error
from modules.processing import StableDiffusionProcessing from modules.shared import opts # pylint: disable=import-error
from modules.shared import opts from modules.paths import models_path # pylint: disable=import-error
from modules.paths import models_path import gradio as gr # pylint: disable=import-error
import gradio as gr from ppp import PromptPostProcessor # pylint: disable=import-error
from ppp import PromptPostProcessor from ppp_logging import DEBUG_LEVEL, PromptPostProcessorLogFactory # pylint: disable=import-error
from ppp_logging import DEBUG_LEVEL, PromptPostProcessorLogFactory from ppp_cache import PPPLRUCache # pylint: disable=import-error
from ppp_cache import PPPLRUCache from ppp_wildcards import PPPWildcards # pylint: disable=import-error
from ppp_wildcards import PPPWildcards
class PromptPostProcessorA1111Script(scripts.Script): class PromptPostProcessorA1111Script(scripts.Script):
@@ -82,7 +81,7 @@ class PromptPostProcessorA1111Script(scripts.Script):
""" """
return PromptPostProcessor.NAME return PromptPostProcessor.NAME
def show(self, is_img2img): def show(self, is_img2img): # pylint: disable=unused-argument
""" """
Determines whether the script should be shown based on the kind of processing. Determines whether the script should be shown based on the kind of processing.
@@ -94,7 +93,7 @@ class PromptPostProcessorA1111Script(scripts.Script):
""" """
return scripts.AlwaysVisible return scripts.AlwaysVisible
def ui(self, is_img2img): def ui(self, is_img2img): # pylint: disable=unused-argument
with gr.Accordion(PromptPostProcessor.NAME, open=False): with gr.Accordion(PromptPostProcessor.NAME, open=False):
force_equal_seeds = gr.Checkbox( force_equal_seeds = gr.Checkbox(
label="Force equal seeds", label="Force equal seeds",
@@ -171,6 +170,7 @@ class PromptPostProcessorA1111Script(scripts.Script):
is_i2i = bool(init_images[0]) is_i2i = bool(init_images[0])
self.ppp_debug_level = DEBUG_LEVEL(getattr(opts, "ppp_gen_debug_level", DEBUG_LEVEL.none.value)) self.ppp_debug_level = DEBUG_LEVEL(getattr(opts, "ppp_gen_debug_level", DEBUG_LEVEL.none.value))
do_i2i = getattr(opts, "ppp_gen_doi2i", False) do_i2i = getattr(opts, "ppp_gen_doi2i", False)
add_prompts = getattr(opts, "ppp_gen_addpromptstometadata", True)
if is_i2i and not do_i2i: if is_i2i and not do_i2i:
if self.ppp_debug_level != DEBUG_LEVEL.none: if self.ppp_debug_level != DEBUG_LEVEL.none:
self.ppp_logger.info("Not processing the prompt for i2i") self.ppp_logger.info("Not processing the prompt for i2i")
@@ -226,6 +226,16 @@ class PromptPostProcessorA1111Script(scripts.Script):
"is_sd3": False, # Stable Diffusion 3 "is_sd3": False, # Stable Diffusion 3
"is_flux": False, # Flux "is_flux": False, # Flux
"is_auraflow": False, # AuraFlow "is_auraflow": False, # AuraFlow
"is_pixart": False, # PixArt
"is_lumina2": False, # Lumina2
"is_ltxv": False, # LTXV
"is_cosmos": False, # Cosmos
"is_genmomochi": False, # GenmoMochi
"is_hunyuan": False, # Hunyuan
"is_hunyuanvideo": False, # HunyuanVideo
"is_hunyuan3d": False, # Hunyuan3D
"is_wanvideo": False, # WanVideo
"is_hidream": False, # HiDream
} }
if app == "sdnext": if app == "sdnext":
# cannot differentiate SD1 and SD2, we set True to both # cannot differentiate SD1 and SD2, we set True to both
@@ -283,7 +293,7 @@ class PromptPostProcessorA1111Script(scripts.Script):
opts, "ppp_gen_variantsdefinitions", PromptPostProcessor.DEFAULT_VARIANTS_DEFINITIONS opts, "ppp_gen_variantsdefinitions", PromptPostProcessor.DEFAULT_VARIANTS_DEFINITIONS
), ),
"process_wildcards": getattr(opts, "ppp_wil_processwildcards", True), "process_wildcards": getattr(opts, "ppp_wil_processwildcards", True),
"if_wildcards": getattr(opts, "ppp_wil_ifwildcards", PromptPostProcessor.IFWILDCARDS_CHOICES.ignore.value), "if_wildcards": getattr(opts, "ppp_wil_ifwildcards", PromptPostProcessor.IFWILDCARDS_CHOICES.stop.value),
"choice_separator": getattr(opts, "ppp_wil_choice_separator", PromptPostProcessor.DEFAULT_CHOICE_SEPARATOR), "choice_separator": getattr(opts, "ppp_wil_choice_separator", PromptPostProcessor.DEFAULT_CHOICE_SEPARATOR),
"keep_choices_order": getattr(opts, "ppp_wil_keep_choices_order", False), "keep_choices_order": getattr(opts, "ppp_wil_keep_choices_order", False),
"stn_separator": getattr(opts, "ppp_stn_separator", PromptPostProcessor.DEFAULT_STN_SEPARATOR), "stn_separator": getattr(opts, "ppp_stn_separator", PromptPostProcessor.DEFAULT_STN_SEPARATOR),
@@ -349,12 +359,6 @@ class PromptPostProcessorA1111Script(scripts.Script):
rpr: list[str] = getattr(p, "all_prompts", None) rpr: list[str] = getattr(p, "all_prompts", None)
rnr: list[str] = getattr(p, "all_negative_prompts", None) rnr: list[str] = getattr(p, "all_negative_prompts", None)
if rpr is not None and rnr is not None: if rpr is not None and rnr is not None:
extra_params.update(
{
"PPP original prompts": rpr.copy(),
"PPP original negative prompts": rnr.copy(),
}
)
prompts_list += [ prompts_list += [
("regular", seed, prompt, negative_prompt) ("regular", seed, prompt, negative_prompt)
for seed, prompt, negative_prompt in zip(calculated_seeds, rpr, rnr) for seed, prompt, negative_prompt in zip(calculated_seeds, rpr, rnr)
@@ -364,23 +368,12 @@ class PromptPostProcessorA1111Script(scripts.Script):
rph: list[str] = getattr(p, "all_hr_prompts", None) rph: list[str] = getattr(p, "all_hr_prompts", None)
rnh: list[str] = getattr(p, "all_hr_negative_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:
extra_params.update(
{
"PPP original HR prompts": rph.copy(),
"PPP original HR negative prompts": rnh.copy(),
}
)
prompts_list += [ prompts_list += [
("hiresfix", seed, prompt, negative_prompt) ("hiresfix", seed, prompt, negative_prompt)
for seed, prompt, negative_prompt in zip(calculated_seeds, rph, rnh) for seed, prompt, negative_prompt in zip(calculated_seeds, rph, rnh)
if (seed, prompt, negative_prompt) not in prompts_list if (seed, prompt, negative_prompt) not in prompts_list
] ]
# fill extra generation parameters only if not already present
for k, v in extra_params.items():
if p.extra_generation_params.get(k) is None:
p.extra_generation_params[k] = v
# processes prompts # processes prompts
for i, (prompttype, seed, prompt, negative_prompt) in enumerate(prompts_list): for i, (prompttype, seed, prompt, negative_prompt) in enumerate(prompts_list):
if self.ppp_debug_level != DEBUG_LEVEL.none: if self.ppp_debug_level != DEBUG_LEVEL.none:
@@ -395,17 +388,48 @@ class PromptPostProcessorA1111Script(scripts.Script):
# updates the prompts # updates the prompts
if rpr is not None and rnr is not 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)): 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((seed, hash(self.wildcards_obj), prompt, negative_prompt))
if found is not None: if found is not None:
if rpr[i].strip() != found[0].strip():
rpr_changes = True
if rnr[i].strip() != found[1].strip():
rnr_changes = True
rpr[i] = found[0] rpr[i] = found[0]
rnr[i] = found[1] rnr[i] = found[1]
if add_prompts:
if rpr_changes:
extra_params["PPP original prompts"] = rpr_copy
if rnr_changes:
extra_params["PPP original negative prompts"] = rnr_copy
if rph is not None and rnh is not None: if rph is not None and rnh is not None:
rph_changes = False
rnh_changes = False
rph_copy = rph.copy()
rnh_copy = rnh.copy()
for i, (seed, prompt, negative_prompt) in enumerate(zip(calculated_seeds, rph, rnh)): 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((seed, hash(self.wildcards_obj), prompt, negative_prompt))
if found is not None: if found is not None:
if rph[i].strip() != found[0].strip():
rph_changes = True
if rnh[i].strip() != found[1].strip():
rnh_changes = True
rph[i] = found[0] rph[i] = found[0]
rnh[i] = found[1] rnh[i] = found[1]
if add_prompts:
if rph_changes:
extra_params["PPP original HR prompts"] = rph_copy
if rnh_changes:
extra_params["PPP original HR negative prompts"] = rnh_copy
# fill extra generation parameters only if not already present
for k, v in extra_params.items():
if p.extra_generation_params.get(k) is None:
p.extra_generation_params[k] = v
t2 = time.time() t2 = time.time()
if self.ppp_debug_level != DEBUG_LEVEL.none: if self.ppp_debug_level != DEBUG_LEVEL.none:
@@ -500,6 +524,14 @@ def on_ui_settings():
section=section, section=section,
), ),
) )
shared.opts.add_option(
key="ppp_gen_addpromptstometadata",
info=shared.OptionInfo(
True,
label="Add original prompts to metadata (if they change)",
section=section,
),
)
# wildcard settings # wildcard settings
shared.opts.add_option( shared.opts.add_option(
+48 -27
View File
@@ -4,12 +4,11 @@ import unittest
import sys import sys
import os import os
from ppp_wildcards import PPPWildcards sys.path.append(os.path.join(os.path.realpath(__file__), "..")) # base path for the extension
sys.path.append(os.path.join(sys.path[0], "..")) from ppp_wildcards import PPPWildcards # pylint: disable=import-error
from ppp import PromptPostProcessor # pylint: disable=import-error
from ppp import PromptPostProcessor from ppp_logging import DEBUG_LEVEL, PromptPostProcessorLogFactory # pylint: disable=import-error
from ppp_logging import DEBUG_LEVEL, PromptPostProcessorLogFactory
PromptPair = namedtuple("PromptPair", ["prompt", "negative_prompt"], defaults=["", ""]) PromptPair = namedtuple("PromptPair", ["prompt", "negative_prompt"], defaults=["", ""])
@@ -69,6 +68,13 @@ class TestPromptPostProcessor(unittest.TestCase):
os.path.abspath(os.path.join(os.path.dirname(__file__), "wildcards")), os.path.abspath(os.path.join(os.path.dirname(__file__), "wildcards")),
os.path.abspath(os.path.join(os.path.dirname(__file__), "wildcards2")), os.path.abspath(os.path.join(os.path.dirname(__file__), "wildcards2")),
], ],
"""
yaml_input:
wildcardI:
- choice1
- choice2
- choice3
"""
) )
grammar_filename = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../grammar.lark") grammar_filename = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../grammar.lark")
with open(grammar_filename, "r", encoding="utf-8") as file: with open(grammar_filename, "r", encoding="utf-8") as file:
@@ -355,7 +361,8 @@ class TestPromptPostProcessor(unittest.TestCase):
def test_cmd_if_nested(self): # nested if command def test_cmd_if_nested(self): # nested if command
self.__process( self.__process(
PromptPair( PromptPair(
"this is <ppp:if _sd eq 'sd1'>SD1<ppp:else><ppp:if _is_pony>PONY<ppp:else>SD2<ppp:/if><ppp:/if><ppp:if _is_sdxl_no_pony>NOPONY<ppp:/if><ppp:if _is_pure_sdxl>NOPONY<ppp:/if>", "" "this is <ppp:if _sd eq 'sd1'>SD1<ppp:else><ppp:if _is_pony>PONY<ppp:else>SD2<ppp:/if><ppp:/if><ppp:if _is_sdxl_no_pony>NOPONY<ppp:/if><ppp:if _is_pure_sdxl>NOPONY<ppp:/if>",
"",
), ),
PromptPair("this is PONY", ""), PromptPair("this is PONY", ""),
ppp=PromptPostProcessor( ppp=PromptPostProcessor(
@@ -394,13 +401,19 @@ class TestPromptPostProcessor(unittest.TestCase):
def test_cmd_set_if_complex_conditions_1(self): # complex conditions (or) def test_cmd_set_if_complex_conditions_1(self): # complex conditions (or)
self.__process( self.__process(
PromptPair("<ppp:set v1>true<ppp:/set><ppp:set v2>false<ppp:/set>this test is <ppp:if v1 or v2>OK<ppp:else>not OK<ppp:/if>", ""), PromptPair(
"<ppp:set v1>true<ppp:/set><ppp:set v2>false<ppp:/set>this test is <ppp:if v1 or v2>OK<ppp:else>not OK<ppp:/if>",
"",
),
PromptPair("this test is OK", ""), PromptPair("this test is OK", ""),
) )
def test_cmd_set_if_complex_conditions_2(self): # complex conditions (and) def test_cmd_set_if_complex_conditions_2(self): # complex conditions (and)
self.__process( self.__process(
PromptPair("<ppp:set v1>true<ppp:/set><ppp:set v2>true<ppp:/set>this test is <ppp:if v1 and v2>OK<ppp:else>not OK<ppp:/if>", ""), PromptPair(
"<ppp:set v1>true<ppp:/set><ppp:set v2>true<ppp:/set>this test is <ppp:if v1 and v2>OK<ppp:else>not OK<ppp:/if>",
"",
),
PromptPair("this test is OK", ""), PromptPair("this test is OK", ""),
) )
@@ -412,25 +425,37 @@ class TestPromptPostProcessor(unittest.TestCase):
def test_cmd_set_if_complex_conditions_4(self): # complex conditions (not, precedence) def test_cmd_set_if_complex_conditions_4(self): # complex conditions (not, precedence)
self.__process( self.__process(
PromptPair("<ppp:set v1>true<ppp:/set><ppp:set v2>false<ppp:/set>this test is <ppp:if not (v1 and v2)>OK<ppp:else>not OK<ppp:/if>", ""), PromptPair(
"<ppp:set v1>true<ppp:/set><ppp:set v2>false<ppp:/set>this test is <ppp:if not (v1 and v2)>OK<ppp:else>not OK<ppp:/if>",
"",
),
PromptPair("this test is OK", ""), PromptPair("this test is OK", ""),
) )
def test_cmd_set_if_complex_conditions_5(self): # complex conditions (not, precedence, comparison) def test_cmd_set_if_complex_conditions_5(self): # complex conditions (not, precedence, comparison)
self.__process( self.__process(
PromptPair("<ppp:set v1>1<ppp:/set><ppp:set v2>false<ppp:/set>this test is <ppp:if not(v1 eq '1' and v2)>OK<ppp:else>not OK<ppp:/if>", ""), PromptPair(
"<ppp:set v1>1<ppp:/set><ppp:set v2>false<ppp:/set>this test is <ppp:if not(v1 eq '1' and v2)>OK<ppp:else>not OK<ppp:/if>",
"",
),
PromptPair("this test is OK", ""), PromptPair("this test is OK", ""),
) )
def test_cmd_set_if_complex_conditions_6(self): # complex conditions def test_cmd_set_if_complex_conditions_6(self): # complex conditions
self.__process( self.__process(
PromptPair("<ppp:set v1>1<ppp:/set><ppp:set v2>2<ppp:/set><ppp:set v3>3<ppp:/set>this test is <ppp:if v1 eq '1' and v2 eq '2' and v3 eq '3'>OK<ppp:else>not OK<ppp:/if>", ""), PromptPair(
"<ppp:set v1>1<ppp:/set><ppp:set v2>2<ppp:/set><ppp:set v3>3<ppp:/set>this test is <ppp:if v1 eq '1' and v2 eq '2' and v3 eq '3'>OK<ppp:else>not OK<ppp:/if>",
"",
),
PromptPair("this test is OK", ""), PromptPair("this test is OK", ""),
) )
def test_cmd_set_if_complex_conditions_7(self): # complex conditions def test_cmd_set_if_complex_conditions_7(self): # complex conditions
self.__process( self.__process(
PromptPair("<ppp:set v1>1<ppp:/set><ppp:set v2>2<ppp:/set><ppp:set v3>3<ppp:/set>this test is <ppp:if v1 eq '1' and v2 not eq '2' or v3 eq '3'>OK<ppp:else>not OK<ppp:/if>", ""), PromptPair(
"<ppp:set v1>1<ppp:/set><ppp:set v2>2<ppp:/set><ppp:set v3>3<ppp:/set>this test is <ppp:if v1 eq '1' and v2 not eq '2' or v3 eq '3'>OK<ppp:else>not OK<ppp:/if>",
"",
),
PromptPair("this test is OK", ""), PromptPair("this test is OK", ""),
) )
@@ -916,11 +941,21 @@ class TestPromptPostProcessor(unittest.TestCase):
ppp=self.__nocupppp, ppp=self.__nocupppp,
) )
def test_wc_wildcard_input(self): # simple yaml wildcard input
self.__process(
PromptPair("the choices are: __yaml_input/wildcardI__", ""),
PromptPair("the choices are: choice2", ""),
ppp=self.__nocupppp,
)
# Model variants tests # Model variants tests
def test_variants(self): def test_variants(self):
self.__process( self.__process(
PromptPair("<ppp:if _is_test1>test1<ppp:/if><ppp:if _is_test2>test2<ppp:/if><ppp:if _is_test3>test3<ppp:/if><ppp:if _is_test4>test4<ppp:/if>", ""), PromptPair(
"<ppp:if _is_test1>test1<ppp:/if><ppp:if _is_test2>test2<ppp:/if><ppp:if _is_test3>test3<ppp:/if><ppp:if _is_test4>test4<ppp:/if>",
"",
),
PromptPair("test1test2", ""), PromptPair("test1test2", ""),
ppp=PromptPostProcessor( ppp=PromptPostProcessor(
self.__ppp_logger, self.__ppp_logger,
@@ -947,7 +982,6 @@ class TestPromptPostProcessor(unittest.TestCase):
ppp=self.__comfyuippp, ppp=self.__comfyuippp,
) )
# def test_mix(self): # def test_mix(self):
# self.__process( # self.__process(
# PromptPair( # PromptPair(
@@ -961,19 +995,6 @@ class TestPromptPostProcessor(unittest.TestCase):
# ppp=self.__nocupppp, # ppp=self.__nocupppp,
# ) # )
# def test_real(self):
# self.__wildcards_obj.refresh_wildcards(
# DEBUG_LEVEL.full,
# ["D:\\AI\\SD\\_configuraciones\\acb-wildcards\\wildcards"],
# )
# self.__process(
# PromptPair(
# "${separator=()}, __quality/high__ __misc/sep__, photograph of a __character__",
# "__negatives/ng_generic__",
# ),
# PromptPair("", ""),
# )
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()
File diff suppressed because one or more lines are too long