* Support for more base models.
* Enhanced the processing of "BREAK" and "AND" constructs to support replacing with a comma. Set both to comma as default in ComfyUI. * Added a new command `setwcdeffilter` to set default filters for a wildcard. * Fixed bug with echoed variables. * ComfyUI: Individual help documentation for the nodes. * Can now test for output variables. * Improved documentation.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Prompt PostProcessor for Stable Diffusion WebUI and ComfyUI
|
||||
|
||||
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:
|
||||
The Prompt PostProcessor (or PPP, formerly known as "sd-webui-sendtonegative"), is an extension designed to process the prompt in several ways. This extension is compatible with:
|
||||
|
||||
* [ComfyUI](https://github.com/comfyanonymous/ComfyUI)
|
||||
* [AUTOMATIC1111 Stable Diffusion WebUI](https://github.com/AUTOMATIC1111/stable-diffusion-webui)
|
||||
@@ -8,40 +8,52 @@ The Prompt PostProcessor (PPP), formerly known as "sd-webui-sendtonegative", is
|
||||
* [reForge](https://github.com/Panchovix/stable-diffusion-webui-reForge)
|
||||
* ...and probably other forks
|
||||
|
||||
Currently this extension has these functions:
|
||||
These are some features:
|
||||
|
||||
* Sending parts of the prompt to the negative prompt. This allows for useful tricks when using wildcards since you can add negative content from choices made in the positive prompt.
|
||||
* Process wildcards. Compatible with the Dynamic Prompts extension.
|
||||
* Sending parts of the prompt to the negative prompt (the original function of the extension). This allows for useful tricks when using wildcards since you can add negative content from choices made in the positive prompt.
|
||||
* Set and modify local variables.
|
||||
* Filter content based on the loaded SD model or a variable.
|
||||
* Process wildcards. Compatible with Dynamic Prompts formats. Can also detect invalid wildcards and act as you choose.
|
||||
* Map extranetworks (LoRAs) depending on conditions (like the loaded model variant).
|
||||
* Clean up the prompt and negative prompt.
|
||||
* Define model variants (pony, illustrious, ...).
|
||||
* Filter content based on the loaded SD model/variant or a variable.
|
||||
* Map extranetworks (LoRAs) depending on conditions (like the loaded model variant). This allows you to add "virtual" loras to the prompt that will be translated to the correct one.
|
||||
* Clean up the prompt of unnecessary separators or spaces.
|
||||
|
||||
Note: when used in an *A1111* compatible webui, the extension must be loaded after any other extension that modifies the prompt (like another wildcards extension). Usually extensions load by their folder name in alphanumeric order, so if the extensions are not loading in the correct order just rename this extension's folder so the ordering works out. 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.
|
||||
|
||||
If the extension runs before others, like Dynamic Prompts, and the "Process wildcards" is enabled, the wildcards will be processed by PPP and those extensions will not get them. If you disable processing the wildcards, and intend another extension to process them, you should keep the "What to do with remaining wildcards?" option as "ignore".
|
||||
If the extension runs before others, like Dynamic Prompts, and the "Process wildcards" option is enabled, the wildcards will be processed by PPP and those extensions will not get them. If you disable processing the wildcards, and you intend another extension to process them, you should keep the "What to do with remaining wildcards?" option as "ignore".
|
||||
|
||||
Notes:
|
||||
|
||||
1. Other than its own commands, it only recognizes regular *A1111* prompt formats. So:
|
||||
|
||||
* **Extra networks**: `<kind:model...>`
|
||||
* **Attention**: `[prompt] (prompt) (prompt:weight)`
|
||||
* **Alternation**: `[prompt1|prompt2|...]`
|
||||
* **Scheduling**: `[prompt1:prompt2:step]`
|
||||
* **Extra networks**: `<kind:model...>`
|
||||
* **BREAK**: `prompt1 BREAK prompt2`
|
||||
* **Composable Diffusion**: `prompt1:weight1 AND prompt2:weight2`
|
||||
|
||||
*ComfyUI* only supports natively the attention using parentheses, so the ones with the brackets will be converted. The other constructs are not natively supported but some custom nodes implement them. For example:
|
||||
[ComfyUI_smZNodes](https://github.com/shiimizu/ComfyUI_smZNodes)
|
||||
|
||||
I also recommend a node to add loras from the prompt, like:
|
||||
[LoRA Tag Loader for ComfyUI](https://github.com/badjeff/comfyui_lora_tag_loader)
|
||||
|
||||
In *SD.Next* that means only the *A1111* or *Full* parsers. It will warn you if you use the *Compel* parser.
|
||||
|
||||
Does not recognize tokenizer separators like `TE2:` and `TE3:`, so sending to negative prompt from those sections of the prompt will not add them in the corresponding section of the negative prompt.
|
||||
|
||||
*ComfyUI* only supports natively the attention using parentheses, so the ones with the braces will be converted. The other constructs are not natively supported but some custom nodes implement them.
|
||||
2. It recognizes wildcards in the `__wildcard__` and {choice|choice} formats (and almost everything that [Dynamic Prompts](https://github.com/adieyal/sd-dynamic-prompts) supports).
|
||||
3. It does not create *AND/BREAK* constructs when moving content to the negative prompt.
|
||||
|
||||
## Installation
|
||||
|
||||
On *ComfyUI*:
|
||||
|
||||
1. Go to Manager > Custom Nodes Manager
|
||||
2. Search for "Prompt PostProcessor" and install or click Install via Git URL and enter <https://github.com/acorderob/sd-webui-prompt-postprocessor>
|
||||
3. Restart
|
||||
|
||||
On *A1111* compatible webuis:
|
||||
|
||||
1. Go to Extensions > Install from URL
|
||||
@@ -51,24 +63,18 @@ On *A1111* compatible webuis:
|
||||
|
||||
On *SD.Next* I recommend you to disable the native wildcard processing and use the old UI.
|
||||
|
||||
On *ComfyUI*:
|
||||
## Configuration
|
||||
|
||||
1. Go to Manager > Custom Nodes Manager
|
||||
2. Search for "Prompt PostProcessor" and install or click Install via Git URL and enter <https://github.com/acorderob/sd-webui-prompt-postprocessor>
|
||||
3. Restart
|
||||
See the [configuration documentation](docs/CONFIG.md).
|
||||
|
||||
## Usage
|
||||
|
||||
See the [syntax documentation](docs/SYNTAX.md).
|
||||
|
||||
## Configuration
|
||||
|
||||
See the [configuration documentation](docs/CONFIG.md).
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
## Contact
|
||||
|
||||
If you have any questions or concerns, please leave an issue, or start a thread in the discussions.
|
||||
If you have any questions or concerns, please start a thread in the discussions.
|
||||
|
||||
+2
-1
@@ -35,5 +35,6 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"ACBPPPCleanupOptions": "ACB PPP Cleanup Options",
|
||||
"ACBPPPSelectVariable": "ACB PPP Select Variable",
|
||||
}
|
||||
WEB_DIRECTORY = "./web"
|
||||
|
||||
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
|
||||
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@ The model variants now support regular expressions instead of a list of strings
|
||||
### ACB Prompt Post Processor node 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).
|
||||
* **modelname**: Name of the model. Needed for the detection of model variants.
|
||||
* **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.
|
||||
@@ -37,7 +37,7 @@ The outputs are the final positive and negative prompt and a variables dictionar
|
||||
|
||||
You can use the "**ACB PPP Select Variable**" node to choose one and output its value. You can use this to send only part of the prompt to, for example, a detailer node. For example:
|
||||
|
||||
With this prompt: `__quality__, 1girl, ${head=!__eyes__, __hair__, __expression__}${head}, __body__, __clothes__, __background__, __style__` then you extract the `head` variable and use `${head}` as prompt for the head/face detailer.
|
||||
With this prompt: `__quality__, 1girl, ${head:!__eyes__, __hair__, __expression__}, __body__, __clothes__, __background__, __style__` then you extract the `head` variable and use `${head}` as prompt for the head/face detailer.
|
||||
|
||||
### ACB PPP Wildcard Options node
|
||||
|
||||
|
||||
+41
-6
@@ -12,6 +12,21 @@ When a command is associated with any content, it will be between an opening and
|
||||
|
||||
For wildcards and choices it uses the formats from the *Dynamic Prompts* extension, but sometimes with some additional options for extra functionality.
|
||||
|
||||
## Special characters
|
||||
|
||||
Remember that to use any special character (like parentheses, brackets or braces) as-is in the prompt (not as part of a construct), you need to escape them with a backslash. Like:
|
||||
|
||||
```text
|
||||
velma \(from scooby doo\)
|
||||
```
|
||||
|
||||
And inside a yaml/json file that backslash must be itself escaped again to be preserved.
|
||||
|
||||
```yaml
|
||||
characters:
|
||||
- velma \\(from scooby doo\\)
|
||||
```
|
||||
|
||||
## Choices
|
||||
|
||||
The generic format is: `{parameters$$opt1::choice1|opt2::choice2|opt3::choice3}`
|
||||
@@ -78,7 +93,12 @@ Wildcards cannot be used inside an extranetwork tag (because some lora names con
|
||||
|
||||
The filter can be used to filter specific choices from the wildcard. The filtering works before applying the choice conditions (if any). The surrounding quotes can be single or double.
|
||||
|
||||
The filter is a comma separated list of an integer (positional choice index, zero-based) or choice label. You can also compound them with `+`. That is, the comma separated items act as an OR and the `+` inside them as an AND. Using labels can simplify the definitions of complex wildcards where you want to have direct access to specific choices on occasion (you don't need to create wildcards for each individual choice). There are some additional formats when using filters. You can specify `^wildcard` as a filter to use the filter of a previous wildcard in the chain. You can start the filter (regular or inherited) with `#` and it will not be applied to the current wildcard choices, but the filter will remain in memory to use by other descendant wildcards. You use `#` and `^` when you want to pass a filter to inner wildcards (see the test files).
|
||||
The filter is a comma separated list of an integer (positional choice index, zero-based) or choice label. You can also compound them with `+`. That is, the comma separated items act as an OR and the `+` inside them as an AND. Using labels can simplify the definitions of complex wildcards where you want to have direct access to specific choices on occasion (you don't need to create wildcards for each individual choice).
|
||||
|
||||
There are some additional formats when using filters.
|
||||
|
||||
* You can specify `^wildcard` as a filter to use the filter of a previous wildcard in the chain.
|
||||
* You can start the filter (regular or inherited) with `#` and it will not be applied to the current wildcard choices, but the filter will remain in memory to use by other descendant wildcards. You use `#` and `^` when you want to pass a filter to inner wildcards (see the test files).
|
||||
|
||||
### Variable
|
||||
|
||||
@@ -131,7 +151,7 @@ It is recommended to use the object format for the wildcard parameters and for c
|
||||
|
||||
Wildcards can contain just one choice. In json and yaml formats this allows the use of a string value for the keys, rather than an array.
|
||||
|
||||
A choice inside a wildcard can also be a list or a dictionary of one element containing a list. These are considered anonymous wildcards. With a list it will be an anonymous wildcard with no choice options, and with a dictionary the key will be the options for the choice containing the anonymous wildcard and the value the choices of the anonymous wildcard. Anonymous wildcards can help formatting complex choice values that are used in only one place and thus creating a regular wildcard is not necessary. See test.yaml for examples.
|
||||
A choice inside a wildcard can also be a list or a dictionary of one element containing a list. These are considered anonymous wildcards. With a list it will be an anonymous wildcard with no choice options, and with a dictionary the key will be the options for the choice containing the anonymous wildcard and the value the choices of the anonymous wildcard. Anonymous wildcards can help formatting complex choice values that are used in only one place and thus creating a regular wildcard is not necessary. See test.yaml for examples. Use an anonymous wildcard to group options inside a wildcard, and attach a label to it to be able to choose only from that group.
|
||||
|
||||
Remember you can use the include command on choices to compose a wildcard from other wildcards' choices.
|
||||
|
||||
@@ -143,6 +163,19 @@ Wildcard definitions are reloaded automatically on each generation if they chang
|
||||
|
||||
This extension should run after any other wildcard extensions, so if you don't use the internal wildcards processing, any remaining wildcards present in the prompt or negative_prompt at this point must be invalid. Usually you might not notice this problem until you check the image metadata, so this option gives you some ways to detect and treat the problem.
|
||||
|
||||
## Set Wildcard Default Filter command
|
||||
|
||||
This command can be used to set a default filter for a wildcard, before it is used.
|
||||
|
||||
The format is:
|
||||
|
||||
| Construct | Meaning |
|
||||
| --------- | ------- |
|
||||
| `<ppp:setwcdeffilter 'identifier' 'filter'/>` | Sets a filter |
|
||||
| `<ppp:setwcdeffilter 'identifier'/>` | Removes the filter |
|
||||
|
||||
The wildcard identifier supports globbing. The filter does not allow the `^` or `#` flags.
|
||||
|
||||
## Set command
|
||||
|
||||
This command sets the value of a variable that can be checked later.
|
||||
@@ -296,6 +329,8 @@ The extranetwork command supports specifying mappings of extranetworks (like LoR
|
||||
|
||||
If the type of extranetwork is prefixed with a `$` the command will look for a mapping.
|
||||
|
||||
If you have loras that do the same but for different models, create a mapping to group them, configuring there the weight and triggers for each one.
|
||||
|
||||
The mappings are configured in yaml files in any of the configured extranetwork mappings folders. The format is like this:
|
||||
|
||||
```yaml
|
||||
@@ -311,10 +346,10 @@ extnettype:
|
||||
|
||||
Used like this:
|
||||
|
||||
```text
|
||||
<ppp:ext $lora mappingname/>
|
||||
<ppp:ext $lora mappingname>inline triggers<ppp:/ext>
|
||||
```
|
||||
| Construct | Meaning |
|
||||
| --------- | ------- |
|
||||
| `<ppp:ext $lora mappingname/>` | Mapping without additional triggers |
|
||||
| `<ppp:ext $lora mappingname>inline triggers<ppp:/ext>` | Mapping with additional triggers |
|
||||
|
||||
Each mapping can have any number of elements in its list of mappings. There are no mandatory properties for a mapping. The properties mean the following:
|
||||
|
||||
|
||||
+13
-8
@@ -38,12 +38,12 @@ promptcomppart: content
|
||||
?content_alternate.2: ( old_content | new_content | plain_alternate | specialchars_alternate )*
|
||||
?content_en.2: (new_content_en | plain | specialchars )*
|
||||
//#if ALLOW_WILDCARDS ALLOW_CHOICES ALLOW_COMMVARS
|
||||
?new_content.3: ( variableset | variableuse | commandstn | commandstni | commandset | commandecho | commandif | commandext | wildcard | choices )+
|
||||
?new_content_negtag.3: ( variableset | variableuse | commandset | commandecho | commandif | commandext | wildcard | choices )+
|
||||
?new_content.3: ( variableset | variableuse | commandstn | commandstni | commandset | commandecho | commandif | commandext | commandsetwcdeffilter | wildcard | choices )+
|
||||
?new_content_negtag.3: ( variableset | variableuse | commandset | commandecho | commandif | commandext | commandsetwcdeffilter | wildcard | choices )+
|
||||
?new_content_en.3: ( variableset | variableuse | commandset | commandecho | commandif | choices )+
|
||||
//#elif ALLOW_WILDCARDS !ALLOW_CHOICES ALLOW_COMMVARS
|
||||
?new_content.3: ( variableset | variableuse | commandstn | commandstni | commandset | commandecho | commandif | commandext | wildcard )+
|
||||
?new_content_negtag.3: ( variableset | variableuse | commandset | commandecho | commandif | commandext | wildcard )+
|
||||
?new_content.3: ( variableset | variableuse | commandstn | commandstni | commandset | commandecho | commandif | commandext | commandsetwcdeffilter | wildcard )+
|
||||
?new_content_negtag.3: ( variableset | variableuse | commandset | commandecho | commandif | commandext | commandsetwcdeffilter | wildcard )+
|
||||
?new_content_en.3: ( variableset | variableuse | commandset | commandecho | commandif )+
|
||||
//#elif !ALLOW_WILDCARDS ALLOW_CHOICES ALLOW_COMMVARS
|
||||
?new_content.3: ( variableset | variableuse | commandstn | commandstni | commandset | commandecho | commandif | commandext | choices )+
|
||||
@@ -66,8 +66,8 @@ promptcomppart: content
|
||||
?new_content_negtag.3: ( variableset | variableuse | commandset | commandecho | commandif | commandext )+
|
||||
?new_content_en.3: ( variableset | variableuse | commandset | commandecho | commandif )+
|
||||
//#else
|
||||
?new_content.3: ( variableset | variableuse | commandstn | commandstni | commandset | commandecho | commandif | commandext | wildcard | choices )+
|
||||
?new_content_negtag.3: ( variableset | variableuse | commandset | commandecho | commandif | wildcard | commandext | choices )+
|
||||
?new_content.3: ( variableset | variableuse | commandstn | commandstni | commandset | commandecho | commandif | commandext | commandsetwcdeffilter | wildcard | choices )+
|
||||
?new_content_negtag.3: ( variableset | variableuse | commandset | commandecho | commandif | commandext | commandsetwcdeffilter | wildcard | choices )+
|
||||
?new_content_en.3: ( variableset | variableuse | commandset | commandecho | commandif | choices )+
|
||||
//#endif
|
||||
//#else
|
||||
@@ -128,8 +128,8 @@ commandecho: "<ppp:echo" _WHITESPACE IDENTIFIER _WHITESPACE? ( ">" [ commandecho
|
||||
// command: ext
|
||||
commandext: "<ppp:ext" _WHITESPACE commandexttype _WHITESPACE commandextid [ _WHITESPACE? commandextparams ] [ _WHITESPACE? commandextif ] _WHITESPACE? ( ">" [ commandexttriggers "<ppp:/ext>" ] | "/>" )
|
||||
commandexttype: [/\$/] IDENTIFIER
|
||||
?commandextid: STRING | CNAME
|
||||
?commandextparams: STRING | SIGNED_NUMBER
|
||||
?commandextid.2: STRING | CNAME
|
||||
?commandextparams.2: STRING | SIGNED_NUMBER
|
||||
?commandextif: "if" _WHITESPACE condition
|
||||
?commandexttriggers.3: content
|
||||
|
||||
@@ -149,6 +149,11 @@ wc_filter_or: wc_filter_and ( _WHITESPACE? "," _WHITESPACE? wc_filter_and )*
|
||||
wc_filter_and: INDEX ( _WHITESPACE? "+" _WHITESPACE? INDEX )*
|
||||
wildcardvar.7: "(" _WHITESPACE? IDENTIFIER _WHITESPACE? "=" varvalue ")"
|
||||
|
||||
// command: setwcdeffilter
|
||||
commandsetwcdeffilter: "<ppp:setwcdeffilter" _WHITESPACE wc_name_str [_WHITESPACE wc_filter_simple ] _WHITESPACE? "/>"
|
||||
wc_name_str: /["']/ wildcard_name /["']/
|
||||
wc_filter_simple: /["']/ wc_filter_or /["']/
|
||||
|
||||
// choices
|
||||
choices.2: "{" [ choicesoptions_sampler | ( choicesoptions _WHITESPACE? "$$" ) ] choice ( "|" choice )* "}"
|
||||
|
||||
|
||||
@@ -96,13 +96,31 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in
|
||||
UNPROCESSED_STOP = "UNPROCESSED CONSTRUCTS!\nBREAK "
|
||||
INVALID_CONTENT_STOP = "INVALID CONTENT! {0}\nBREAK "
|
||||
|
||||
SUPPORTED_MODELS = [
|
||||
"sd1",
|
||||
"sd2",
|
||||
"sdxl",
|
||||
"sd3",
|
||||
"flux",
|
||||
"auraflow",
|
||||
KNOWN_MODELS = [
|
||||
"sd1", # Stable Diffusion 1
|
||||
"sd2", # Stable Diffusion 2
|
||||
"ssd", # Segmind Stable Diffusion 1B
|
||||
"sdxl", # Stable Diffusion XL
|
||||
"sd3", # Stable Diffusion 3
|
||||
"flux", # Flux 1
|
||||
"auraflow", # AuraFlow
|
||||
"pixart", # PixArt
|
||||
"lumina2", # Lumina2 & ZImage
|
||||
"ltxv", # LTXV
|
||||
"cosmos", # Cosmos
|
||||
"cosmospredict2", # CosmosPredict2
|
||||
"genmomochi", # GenmoMochi
|
||||
"hunyuan", # Hunyuan
|
||||
"hunyuanvideo", # HunyuanVideo
|
||||
"hunyuan3d", # Hunyuan3D
|
||||
"wan", # Wan21 & Wan22
|
||||
"hidream", # HiDream
|
||||
"qwenimage", # QwenImage & QwenImageEdit
|
||||
"chroma", # Chroma
|
||||
"omnigen2", # Omnigen 2
|
||||
"flux2", # Flux 2
|
||||
"kandinsky5", # Kandinsky 5
|
||||
"anima", # Anima
|
||||
]
|
||||
|
||||
def __init__(
|
||||
@@ -173,7 +191,7 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in
|
||||
self.gen_onwarning = self.ONWARNING_CHOICES(options.get("on_warning", self.DEFAULT_ONWARNING))
|
||||
self.variants_definitions = {
|
||||
v: (m, [vo["find_in_filename"]] if isinstance(vo["find_in_filename"], str) else vo["find_in_filename"])
|
||||
for m in self.SUPPORTED_MODELS
|
||||
for m in self.KNOWN_MODELS
|
||||
for v, vo in (((self.config.get("models") or {}).get(m) or {}).get("variants") or {}).items()
|
||||
}
|
||||
if self.debug_level != DEBUG_LEVEL.none:
|
||||
@@ -458,9 +476,9 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in
|
||||
Initializes the system variables.
|
||||
"""
|
||||
self.system_variables = {}
|
||||
sdchecks = {x: self.env_info.get("is_" + x, False) for x in self.SUPPORTED_MODELS}
|
||||
sdchecks = {x: self.env_info.get("is_" + x, False) for x in self.KNOWN_MODELS}
|
||||
sdchecks.update({"": True})
|
||||
self.system_variables["_model"] = [k for k, v in sdchecks.items() if v][0]
|
||||
self.system_variables["_model"] = next((k for k, v in sdchecks.items() if v), "")
|
||||
self.system_variables["_sd"] = self.system_variables["_model"] # deprecated
|
||||
model_filename = self.env_info.get("model_filename", "")
|
||||
self.system_variables["_sdfullname"] = model_filename # deprecated
|
||||
@@ -472,7 +490,7 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in
|
||||
model_name: (model_type_and_substrings[0] == "" or sdchecks.get(model_type_and_substrings[0], False))
|
||||
and any((re.search(s, model_filename, re.IGNORECASE) is not None) for s in model_type_and_substrings[1])
|
||||
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.KNOWN_MODELS
|
||||
}
|
||||
is_models_true = [k for k, v in is_models.items() if v]
|
||||
if len(is_models_true) > 1:
|
||||
@@ -605,18 +623,17 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in
|
||||
text = re.sub(r"\A(?:\s*BREAK\b\s*)+", "", text)
|
||||
# remove at end of prompt
|
||||
text = re.sub(r"(?:\s*\bBREAK\s*)+\Z", "", text)
|
||||
if break_processing == "eol":
|
||||
text2 = re.sub(r"\b\s*BREAK\s*\b", "\n", text)
|
||||
break_replacements = {
|
||||
"eol": ("replaced with EOL", "\n"),
|
||||
"comma": ("replaced with COMMA", ", "),
|
||||
"remove": ("removed", " "),
|
||||
}
|
||||
if break_processing in break_replacements.keys():
|
||||
text2 = re.sub(r"\b\s*BREAK\s*\b", break_replacements[break_processing][1], text)
|
||||
if text2 != text:
|
||||
text = text2
|
||||
if self.debug_level == DEBUG_LEVEL.full:
|
||||
self.logger.debug("BREAK construct replaced with EOL")
|
||||
elif break_processing == "remove":
|
||||
text2 = re.sub(r"\b\s*BREAK\s*\b", " ", text)
|
||||
if text2 != text:
|
||||
text = text2
|
||||
if self.debug_level == DEBUG_LEVEL.full:
|
||||
self.logger.debug("BREAK construct removed")
|
||||
self.logger.debug(f"BREAK construct {break_replacements[break_processing][0]}")
|
||||
elif break_processing == "error":
|
||||
if re.search(r"\bBREAK\b", text):
|
||||
self.warn_or_stop(where == -1, "BREAK constructs are not allowed!")
|
||||
@@ -646,15 +663,23 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in
|
||||
if self.cup_extraseparators:
|
||||
# collapse separators
|
||||
text = re.sub(r"(?:" + sep + r"){2,}", replacement, text)
|
||||
# remove separator after starting parenthesis or bracket
|
||||
# remove separator after starting parenthesis, starting bracket
|
||||
text = re.sub(
|
||||
r"(" + sep + r"[([])(?:" + sep + r")+",
|
||||
# r"(" + sep + r"[([]\s*)(?:" + sep + r")+",
|
||||
r"([([]\s*)(?:" + sep + r")+",
|
||||
r"\1",
|
||||
text,
|
||||
)
|
||||
# remove before colon or ending parenthesis or bracket
|
||||
# remove separator before ending parenthesis, ending bracket
|
||||
text = re.sub(
|
||||
r"(?:" + sep + r")+([:)\]]" + sep + r")",
|
||||
# r"(?:" + sep + r")+(\s*[:)\]]" + sep + r")",
|
||||
r"(?:" + sep + r")+(\s*[)\]])",
|
||||
r"\1",
|
||||
text,
|
||||
)
|
||||
# remove separator before colon
|
||||
text = re.sub(
|
||||
r"(?:" + sep + r")+(\s*:" + sep + r")",
|
||||
r"\1",
|
||||
text,
|
||||
)
|
||||
@@ -1310,19 +1335,22 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in
|
||||
self.__visit(tree.children[0])
|
||||
and_processing = self.__ppp.host_config.get("and", "ok")
|
||||
if len(tree.children) > 1:
|
||||
and_replacements = {
|
||||
"eol": ("replaced with EOL", "\n"),
|
||||
"comma": ("replaced with COMMA", ", "),
|
||||
"remove": ("removed", " "),
|
||||
}
|
||||
if tree.children[1] is not None:
|
||||
self.result += f":{tree.children[1]}"
|
||||
for i in range(2, len(tree.children), 3):
|
||||
if and_processing == "eol":
|
||||
if and_processing in and_replacements.keys():
|
||||
self.result = (
|
||||
self.result.rstrip() + "\n" + self.__visit(tree.children[i + 1], False, True).lstrip()
|
||||
self.result.rstrip()
|
||||
+ and_replacements[and_processing][1]
|
||||
+ self.__visit(tree.children[i + 1], False, True).lstrip()
|
||||
)
|
||||
if self.__ppp.debug_level == DEBUG_LEVEL.full:
|
||||
self.__ppp.logger.debug("AND construct replaced with EOL")
|
||||
elif and_processing == "remove":
|
||||
self.result += self.__visit(tree.children[i + 1], False, True)
|
||||
if self.__ppp.debug_level == DEBUG_LEVEL.full:
|
||||
self.__ppp.logger.debug("AND construct removed")
|
||||
self.__ppp.logger.debug(f"AND construct {and_replacements[and_processing][0]}")
|
||||
elif and_processing == "error":
|
||||
self.warn_or_stop("AND constructs are not allowed!")
|
||||
else: # and_processing == "ok":
|
||||
@@ -1670,6 +1698,8 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in
|
||||
self.result += v
|
||||
else:
|
||||
self.warn_or_stop(f"Unknown variable {variable}")
|
||||
else:
|
||||
self.__ppp.echoed_variables[variable] = value
|
||||
t2 = time.monotonic_ns()
|
||||
info = variable
|
||||
if default is not None:
|
||||
@@ -1849,6 +1879,34 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in
|
||||
t2 = time.monotonic_ns()
|
||||
self.__debug_end("commandext", start_result, t2 - t1, extnet)
|
||||
|
||||
def commandsetwcdeffilter(self, tree: lark.Tree):
|
||||
"""
|
||||
Process a setwcdeffilter (Set Wildcard Default Filter) command in the tree.
|
||||
"""
|
||||
t1 = time.monotonic_ns()
|
||||
start_result = self.result
|
||||
wildcard_key: str = self.__visit(tree.children[0].children[1], False, True)
|
||||
selected_wildcards = [x.key for x in self.__ppp.wildcard_obj.get_wildcards(wildcard_key)]
|
||||
if not selected_wildcards:
|
||||
self.warn_or_stop(f"Wildcard '{wildcard_key}' not found for default filter setting!")
|
||||
else:
|
||||
filter_object = tree.children[1].children[1] if tree.children[1] is not None else None
|
||||
if filter_object is None:
|
||||
for wc in selected_wildcards:
|
||||
if self.__ppp.debug_level == DEBUG_LEVEL.full:
|
||||
self.__ppp.logger.debug(f"Removed default filter for wildcard '{wc}'")
|
||||
self.__ppp.wildcard_obj.set_wildcard_default_filter(wc, None)
|
||||
else:
|
||||
filter_specifier: list[list[str]] = [
|
||||
[str(label) for label in option.children] for option in filter_object.children
|
||||
]
|
||||
for wc in selected_wildcards:
|
||||
if self.__ppp.debug_level == DEBUG_LEVEL.full:
|
||||
self.__ppp.logger.debug(f"Set default filter for wildcard '{wc}'")
|
||||
self.__ppp.wildcard_obj.set_wildcard_default_filter(wc, filter_specifier)
|
||||
t2 = time.monotonic_ns()
|
||||
self.__debug_end("commandsetwcdeffilter", start_result, t2 - t1)
|
||||
|
||||
def extranetworktag(self, tree: lark.Tree):
|
||||
"""
|
||||
Process an extra network construct in the tree.
|
||||
@@ -2302,6 +2360,12 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in
|
||||
if self.__ppp.debug_level == DEBUG_LEVEL.full:
|
||||
self.__ppp.logger.debug("Ignoring filter")
|
||||
filter_specifier = None
|
||||
else:
|
||||
filter_specifier = self.__ppp.wildcard_obj.get_wildcard_default_filter(wildcard_key)
|
||||
if filter_specifier is not None:
|
||||
self.__wildcard_filters[wildcard_key] = filter_specifier
|
||||
if self.__ppp.debug_level == DEBUG_LEVEL.full:
|
||||
self.__ppp.logger.debug("Applying default filter")
|
||||
if (
|
||||
len(selected_wildcards) > 1
|
||||
and filter_specifier is not None
|
||||
|
||||
+62
-44
@@ -127,28 +127,28 @@ class PromptPostProcessorComfyUINode:
|
||||
"wc_options": (
|
||||
"PPP_OPTIONS_WC",
|
||||
{
|
||||
"forceInput": True,
|
||||
"default": None,
|
||||
"tooltip": "Wildcard processing options",
|
||||
},
|
||||
),
|
||||
"stn_options": (
|
||||
"PPP_OPTIONS_STN",
|
||||
{
|
||||
"forceInput": True,
|
||||
"default": None,
|
||||
"tooltip": "Send-To-Negative options",
|
||||
},
|
||||
),
|
||||
"cup_options": (
|
||||
"PPP_OPTIONS_CUP",
|
||||
{
|
||||
"forceInput": True,
|
||||
"default": None,
|
||||
"tooltip": "Cleanup options",
|
||||
},
|
||||
),
|
||||
"en_options": (
|
||||
"PPP_OPTIONS_EN",
|
||||
{
|
||||
"forceInput": True,
|
||||
"default": None,
|
||||
"tooltip": "ExtraNetworks mapping options",
|
||||
},
|
||||
),
|
||||
@@ -184,23 +184,7 @@ class PromptPostProcessorComfyUINode:
|
||||
CATEGORY = "ACB"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(
|
||||
cls,
|
||||
model,
|
||||
modelname,
|
||||
pos_prompt,
|
||||
neg_prompt,
|
||||
seed,
|
||||
debug_level,
|
||||
on_warnings,
|
||||
process_wildcards,
|
||||
do_cleanup,
|
||||
cleanup_variables,
|
||||
wc_options,
|
||||
stn_options,
|
||||
cup_options,
|
||||
en_options,
|
||||
): # pylint: disable=unused-argument
|
||||
def IS_CHANGED(cls, **kwargs): # pylint: disable=unused-argument
|
||||
return float("NaN") # always process because we don't control the content of wildcards and the config file
|
||||
|
||||
def process(
|
||||
@@ -215,10 +199,10 @@ class PromptPostProcessorComfyUINode:
|
||||
process_wildcards,
|
||||
do_cleanup,
|
||||
cleanup_variables,
|
||||
wc_options,
|
||||
stn_options,
|
||||
cup_options,
|
||||
en_options,
|
||||
wc_options=None,
|
||||
stn_options=None,
|
||||
cup_options=None,
|
||||
en_options=None,
|
||||
):
|
||||
modelclass = (
|
||||
model.model.model_config.__class__.__name__ if model is not None and not isinstance(model, str) else model
|
||||
@@ -233,26 +217,60 @@ class PromptPostProcessorComfyUINode:
|
||||
"models_path": folder_paths.models_dir,
|
||||
"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", "LotusD"),
|
||||
"is_sdxl": (
|
||||
modelclass in ("SDXL", "SDXLRefiner", "SDXL_instructpix2pix", "Segmind_Vega", "KOALA_700M", "KOALA_1B")
|
||||
),
|
||||
"is_ssd": modelclass in ("SSD1B",),
|
||||
"is_sd3": modelclass in ("SD3",),
|
||||
"is_flux": modelclass in ("Flux", "FluxInpaint", "FluxSchnell"),
|
||||
"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",),
|
||||
}
|
||||
env_info.update({"is_" + k: False for k in PromptPostProcessor.KNOWN_MODELS})
|
||||
env_info.update(
|
||||
{
|
||||
"is_sd1": modelclass in ("SD15", "SD15_instructpix2pix"),
|
||||
"is_sd2": modelclass in ("SD20", "SD21UnclipL", "SD21UnclipH", "LotusD"),
|
||||
"is_sdxl": (
|
||||
modelclass
|
||||
in ("SDXL", "SDXLRefiner", "SDXL_instructpix2pix", "Segmind_Vega", "KOALA_700M", "KOALA_1B")
|
||||
),
|
||||
"is_ssd": modelclass in ("SSD1B",),
|
||||
"is_sd3": modelclass in ("SD3",),
|
||||
"is_flux": modelclass in ("Flux", "FluxInpaint", "FluxSchnell"),
|
||||
"is_auraflow": modelclass in ("AuraFlow",),
|
||||
"is_pixart": modelclass in ("PixArtAlpha", "PixArtSigma"),
|
||||
"is_lumina2": modelclass in ("Lumina2", "ZImage"),
|
||||
"is_ltxv": modelclass in ("LTXV", "LTXAV"),
|
||||
"is_cosmos": modelclass in ("CosmosT2V", "CosmosI2V"),
|
||||
"is_cosmospredict2": modelclass in ("CosmosT2IPredict2", "CosmosI2VPredict2"),
|
||||
"is_genmomochi": modelclass in ("GenmoMochi",),
|
||||
"is_hunyuan": modelclass in ("HunyuanDiT", "HunyuanDiT1"),
|
||||
"is_hunyuanvideo": modelclass
|
||||
in (
|
||||
"HunyuanVideo",
|
||||
"HunyuanVideoI2V",
|
||||
"HunyuanVideoSkyreelsI2V",
|
||||
"HunyuanImage21",
|
||||
"HunyuanImage21Refiner",
|
||||
"HunyuanVideo15",
|
||||
"HunyuanVideo15_SR_Distilled",
|
||||
),
|
||||
"is_hunyuan3d": modelclass in ("Hunyuan3Dv2", "Hunyuan3Dv2_1", "Hunyuan3Dv2mini"),
|
||||
"is_wan": modelclass
|
||||
in (
|
||||
"WAN21_T2V",
|
||||
"WAN21_I2V",
|
||||
"WAN21_FunControl2V",
|
||||
"WAN21_Camera",
|
||||
"WAN22_Camera",
|
||||
"WAN21_Vace",
|
||||
"WAN21_HuMo",
|
||||
"WAN22_S2V",
|
||||
"WAN22_Animate",
|
||||
"WAN22_T2V",
|
||||
),
|
||||
"is_hidream": modelclass in ("HiDream",),
|
||||
"is_chroma": modelclass in ("Chroma", "ChromaRadiance"),
|
||||
"is_omnigen2": modelclass in ("Omnigen2",),
|
||||
"is_kandinsky5": modelclass in ("Kandinsky5", "Kandinsky5Image"),
|
||||
"is_qwenimage": modelclass in ("QwenImage",),
|
||||
"is_flux2": modelclass in ("Flux2"),
|
||||
"is_anima": modelclass in ("Anima",),
|
||||
}
|
||||
)
|
||||
# Also supported: SVD_img2vid, SVD3D_u, SVD3_p, Stable_Zero123, SD_X4Upscaler, Stable_Cascade_C, Stable_Cascade_B, StableAudio
|
||||
|
||||
wc_wildcards_folders = wc_options["wc_wildcards_folders"] if wc_options else ""
|
||||
|
||||
@@ -25,11 +25,13 @@ hosts: # Options for supported host applications
|
||||
# and: how to handle "AND" operators
|
||||
# - ok: leave as is
|
||||
# - eol: replace with end-of-line (newline)
|
||||
# - comma: replace with comma
|
||||
# - remove: remove the operator
|
||||
# - error: raise an error
|
||||
# break: how to handle "BREAK" operators
|
||||
# - ok: leave as is
|
||||
# - eol: replace with end-of-line (newline)
|
||||
# - comma: replace with comma
|
||||
# - remove: remove the operator
|
||||
# - error: raise an error
|
||||
|
||||
@@ -63,8 +65,8 @@ hosts: # Options for supported host applications
|
||||
# by default comfyui does not support scheduling, alternation, AND or BREAK operators, but you may be able to use them with a custom text encoder node
|
||||
scheduling: error
|
||||
alternation: error
|
||||
and: remove
|
||||
break: eol
|
||||
and: comma
|
||||
break: comma
|
||||
|
||||
models: # Options for supported base models and variants
|
||||
sd1:
|
||||
|
||||
+32
-2
@@ -57,6 +57,7 @@ class PPPWildcards:
|
||||
self.__debug_level = DEBUG_LEVEL.none
|
||||
self.__wildcards_folders = []
|
||||
self.__wildcard_files = {}
|
||||
self.__wildcard_default_filters: dict[str, list[list[str]]] = {}
|
||||
self.wildcards: dict[str, PPPWildcard] = {}
|
||||
|
||||
def __hash__(self) -> int:
|
||||
@@ -378,7 +379,9 @@ class PPPWildcards:
|
||||
if choices is None:
|
||||
self.__logger.warning(f"Invalid wildcard '{fullkey}' in file '{full_path}'!")
|
||||
elif fullkey.startswith("_"):
|
||||
self.__logger.warning(f"Invalid wildcard name '{fullkey}' in file '{full_path}'! (cannot start with underscore)")
|
||||
self.__logger.warning(
|
||||
f"Invalid wildcard name '{fullkey}' in file '{full_path}'! (cannot start with underscore)"
|
||||
)
|
||||
else:
|
||||
self.wildcards[fullkey] = PPPWildcard(full_path, fullkey, choices)
|
||||
return
|
||||
@@ -399,7 +402,9 @@ class PPPWildcards:
|
||||
if choices is None:
|
||||
self.__logger.warning(f"Invalid wildcard '{fullkey}' in file '{full_path}'!")
|
||||
elif fullkey.startswith("_"):
|
||||
self.__logger.warning(f"Invalid wildcard name '{fullkey}' in file '{full_path}'! (cannot start with underscore)")
|
||||
self.__logger.warning(
|
||||
f"Invalid wildcard name '{fullkey}' in file '{full_path}'! (cannot start with underscore)"
|
||||
)
|
||||
else:
|
||||
self.wildcards[fullkey] = PPPWildcard(full_path, fullkey, choices)
|
||||
|
||||
@@ -462,3 +467,28 @@ class PPPWildcards:
|
||||
self.__get_wildcards_in_directory(base, full_path)
|
||||
elif os.path.isfile(full_path):
|
||||
self.__get_wildcards_in_file(base, full_path)
|
||||
|
||||
def set_wildcard_default_filter(self, wildcard_key: str, filter_options: Optional[list[list[str]]]):
|
||||
"""
|
||||
Set the default filter for a wildcard.
|
||||
|
||||
Args:
|
||||
wildcard_key (str): The key of the wildcard.
|
||||
filter_options (list[list[str]]): The filter options.
|
||||
"""
|
||||
if filter_options is None:
|
||||
if wildcard_key in self.__wildcard_default_filters:
|
||||
del self.__wildcard_default_filters[wildcard_key]
|
||||
else:
|
||||
self.__wildcard_default_filters[wildcard_key] = filter_options
|
||||
|
||||
def get_wildcard_default_filter(self, wildcard_key: str) -> Optional[list[list[str]]]:
|
||||
"""
|
||||
Get the default filter for a wildcard.
|
||||
|
||||
Args:
|
||||
wildcard_key (str): The key of the wildcard.
|
||||
Returns:
|
||||
Optional[list[list[str]]]: The filter options or None if not set.
|
||||
"""
|
||||
return self.__wildcard_default_filters.get(wildcard_key, None)
|
||||
|
||||
+5
-2
@@ -8,8 +8,11 @@ dependencies = ["lark", "numpy", "pyyaml"]
|
||||
[project.urls]
|
||||
Repository = "https://github.com/acorderob/sd-webui-prompt-postprocessor"
|
||||
# Used by Comfy Registry https://comfyregistry.org
|
||||
Documentation = "https://github.com/acorderob/sd-webui-prompt-postprocessor/main/README.md"
|
||||
"Bug Tracker" = "https://github.com/acorderob/sd-webui-prompt-postprocessor/issues"
|
||||
|
||||
[tool.comfy]
|
||||
PublisherId = "acorderob"
|
||||
DisplayName = "sd-webui-prompt-postprocessor"
|
||||
Icon = "ppp_icon.png"
|
||||
#DisplayName = "sd-webui-prompt-postprocessor"
|
||||
DisplayName = "ACB Prompt PostProcessor"
|
||||
Icon = "https://raw.githubusercontent.com/acorderob/sd-webui-prompt-postprocessor/main/images/prompt-postprocessor-icon.png"
|
||||
|
||||
+6
-38
@@ -209,65 +209,37 @@ class PromptPostProcessorA1111Script(scripts.Script):
|
||||
|
||||
if self.ppp_debug_level != DEBUG_LEVEL.none:
|
||||
self.ppp_logger.info(f"Post-processing prompts ({'i2i' if is_i2i else 't2i'})")
|
||||
models_supported = {x: True for x in PromptPostProcessor.SUPPORTED_MODELS}
|
||||
if app == SUPPORTED_APPS.sdnext:
|
||||
models_supported["ssd"] = False
|
||||
elif app == SUPPORTED_APPS.forge:
|
||||
models_supported["ssd"] = False
|
||||
models_supported["auraflow"] = False
|
||||
elif app == SUPPORTED_APPS.reforge:
|
||||
models_supported["flux"] = False
|
||||
models_supported["auraflow"] = False
|
||||
else: # assume A1111 compatible
|
||||
models_supported["flux"] = False
|
||||
models_supported["auraflow"] = False
|
||||
env_info = {
|
||||
"app": app.value,
|
||||
"models_path": models_path,
|
||||
"model_filename": getattr(p.sd_model.sd_checkpoint_info, "filename", ""),
|
||||
"model_class": "",
|
||||
"is_sd1": False, # Stable Diffusion 1
|
||||
"is_sd2": False, # Stable Diffusion 2
|
||||
"is_sdxl": False, # Stable Diffusion XL
|
||||
"is_ssd": False, # Segmind Stable Diffusion 1B
|
||||
"is_sd3": False, # Stable Diffusion 3
|
||||
"is_flux": False, # Flux
|
||||
"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
|
||||
}
|
||||
env_info.update({"is_" + k: False for k in PromptPostProcessor.KNOWN_MODELS})
|
||||
if app == SUPPORTED_APPS.sdnext:
|
||||
# cannot differentiate SD1 and SD2, we set True to both
|
||||
# cannot differentiate SD1 and SD2, we set both to True
|
||||
# LatentDiffusion is for the original backend, StableDiffusionPipeline is for the diffusers backend
|
||||
env_info["model_class"] = p.sd_model.__class__.__name__
|
||||
env_info["is_sd1"] = p.sd_model.__class__.__name__ in ("LatentDiffusion", "StableDiffusionPipeline")
|
||||
env_info["is_sd2"] = p.sd_model.__class__.__name__ in ("LatentDiffusion", "StableDiffusionPipeline")
|
||||
env_info["is_sdxl"] = p.sd_model.__class__.__name__ == "StableDiffusionXLPipeline"
|
||||
env_info["is_ssd"] = False # ?
|
||||
env_info["is_sd3"] = p.sd_model.__class__.__name__ == "StableDiffusion3Pipeline"
|
||||
env_info["is_flux"] = p.sd_model.__class__.__name__ == "FluxPipeline"
|
||||
env_info["is_auraflow"] = p.sd_model.__class__.__name__ == "AuraFlowPipeline"
|
||||
# also supports 'Latent Consistency Model': LatentConsistencyModelPipeline', 'PixArt-Alpha': 'PixArtAlphaPipeline', 'UniDiffuser': 'UniDiffuserPipeline', 'Wuerstchen': 'WuerstchenCombinedPipeline', 'Kandinsky 2.1': 'KandinskyPipeline', 'Kandinsky 2.2': 'KandinskyV22Pipeline', 'Kandinsky 3': 'Kandinsky3Pipeline', 'DeepFloyd IF': 'IFPipeline', 'Custom Diffusers Pipeline': 'DiffusionPipeline', 'InstaFlow': 'StableDiffusionPipeline', 'SegMoE': 'StableDiffusionPipeline', 'Kolors': 'KolorsPipeline', 'AuraFlow': 'AuraFlowPipeline', 'CogView': 'CogView3PlusPipeline'
|
||||
env_info["is_pixart"] = p.sd_model.__class__.__name__ == "PixArtAlphaPipeline"
|
||||
# also supports 'Latent Consistency Model': LatentConsistencyModelPipeline', 'UniDiffuser': 'UniDiffuserPipeline', 'Wuerstchen': 'WuerstchenCombinedPipeline', 'Kandinsky 2.1': 'KandinskyPipeline', 'Kandinsky 2.2': 'KandinskyV22Pipeline', 'Kandinsky 3': 'Kandinsky3Pipeline', 'DeepFloyd IF': 'IFPipeline', 'Custom Diffusers Pipeline': 'DiffusionPipeline', 'InstaFlow': 'StableDiffusionPipeline', 'SegMoE': 'StableDiffusionPipeline', 'Kolors': 'KolorsPipeline', 'CogView': 'CogView3PlusPipeline'
|
||||
elif app == SUPPORTED_APPS.forge:
|
||||
# from repositories\huggingface_guess\huggingface_guess\model_list.py
|
||||
env_info["model_class"] = p.sd_model.model_config.__class__.__name__
|
||||
env_info["is_sd1"] = getattr(p.sd_model, "is_sd1", False)
|
||||
env_info["is_sd2"] = getattr(p.sd_model, "is_sd2", False)
|
||||
env_info["is_sdxl"] = getattr(p.sd_model, "is_sdxl", False)
|
||||
env_info["is_ssd"] = False # ?
|
||||
# env_info["is_ssd"] = False # ?
|
||||
env_info["is_sd3"] = getattr(
|
||||
p.sd_model, "is_sd3", False
|
||||
) # p.sd_model.model_config.__class__.__name__ == "SD3" # not actually supported?
|
||||
env_info["is_flux"] = p.sd_model.model_config.__class__.__name__ in ("Flux", "FluxSchnell")
|
||||
env_info["is_auraflow"] = False # p.sd_model.model_config.__class__.__name__ == "AuraFlow" # not supported
|
||||
# env_info["is_auraflow"] = False # p.sd_model.model_config.__class__.__name__ == "AuraFlow" # not supported
|
||||
elif app == SUPPORTED_APPS.reforge:
|
||||
env_info["model_class"] = p.sd_model.__class__.__name__
|
||||
env_info["is_sd1"] = getattr(p.sd_model, "is_sd1", False)
|
||||
@@ -275,8 +247,6 @@ class PromptPostProcessorA1111Script(scripts.Script):
|
||||
env_info["is_sdxl"] = getattr(p.sd_model, "is_sdxl", False)
|
||||
env_info["is_ssd"] = getattr(p.sd_model, "is_ssd", False)
|
||||
env_info["is_sd3"] = getattr(p.sd_model, "is_sd3", False)
|
||||
env_info["is_flux"] = False
|
||||
env_info["is_auraflow"] = False
|
||||
else: # assume A1111 compatible (p.sd_model.__class__.__name__=="DiffusionEngine")
|
||||
env_info["model_class"] = p.sd_model.__class__.__name__
|
||||
env_info["is_sd1"] = getattr(p.sd_model, "is_sd1", False)
|
||||
@@ -284,8 +254,6 @@ class PromptPostProcessorA1111Script(scripts.Script):
|
||||
env_info["is_sdxl"] = getattr(p.sd_model, "is_sdxl", False)
|
||||
env_info["is_ssd"] = getattr(p.sd_model, "is_ssd", False)
|
||||
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 == "":
|
||||
|
||||
+100
-12
@@ -50,6 +50,8 @@ class TestPromptPostProcessorBase(unittest.TestCase):
|
||||
"keep_choices_order": False,
|
||||
"stn_separator": ", ",
|
||||
"stn_ignore_repeats": True,
|
||||
"do_cleanup": True,
|
||||
"cleanup_variables": True,
|
||||
"cleanup_empty_constructs": True,
|
||||
"cleanup_extra_separators": True,
|
||||
"cleanup_extra_separators2": True,
|
||||
@@ -66,13 +68,7 @@ class TestPromptPostProcessorBase(unittest.TestCase):
|
||||
self.def_env_info = {
|
||||
"app": "tests",
|
||||
"ppp_config": None,
|
||||
"is_sd1": False,
|
||||
"is_sd2": False,
|
||||
"is_sdxl": True,
|
||||
"is_ssd": False,
|
||||
"is_sd3": False,
|
||||
"is_flux": False,
|
||||
"is_auraflow": False,
|
||||
"model_class": "DiffusionEngine",
|
||||
"models_path": "./webui/models",
|
||||
"model_filename": "./webui/models/Stable-diffusion/testmodel.safetensors",
|
||||
@@ -114,6 +110,7 @@ class TestPromptPostProcessorBase(unittest.TestCase):
|
||||
seed: int = 1,
|
||||
ppp: Optional[str | PromptPostProcessor] = None,
|
||||
interrupted: bool = False,
|
||||
variables: dict[str, str] | None = None,
|
||||
):
|
||||
"""
|
||||
Process the prompt and compare the results with the expected prompts.
|
||||
@@ -124,6 +121,7 @@ class TestPromptPostProcessorBase(unittest.TestCase):
|
||||
seed (int, optional): The seed value. Defaults to 1.
|
||||
ppp (Optional[str | PromptPostProcessor], optional): The PromptPostProcessor instance or type. Defaults to None.
|
||||
interrupted (bool, optional): The interrupted flag. Defaults to False.
|
||||
variables (dict[str,str]|None, optional): Output variables to check. Defaults to None.
|
||||
|
||||
Returns:
|
||||
None
|
||||
@@ -136,6 +134,8 @@ class TestPromptPostProcessorBase(unittest.TestCase):
|
||||
self.def_env_info,
|
||||
{
|
||||
**self.defopts,
|
||||
"do_cleanup": False,
|
||||
"cleanup_variables": False,
|
||||
"cleanup_empty_constructs": False,
|
||||
"cleanup_extra_separators": False,
|
||||
"cleanup_extra_separators2": False,
|
||||
@@ -184,15 +184,24 @@ class TestPromptPostProcessorBase(unittest.TestCase):
|
||||
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(
|
||||
result_prompt, result_negative_prompt, output_variables = 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 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")
|
||||
if not self.interrupted:
|
||||
if 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")
|
||||
if variables is not None:
|
||||
for var_name, var_value in variables.items():
|
||||
self.assertIn(var_name, output_variables, f"Variable '{var_name}' not found in output variables")
|
||||
self.assertEqual(
|
||||
output_variables[var_name],
|
||||
var_value,
|
||||
f"Variable '{var_name}' has incorrect value",
|
||||
)
|
||||
seed += 1
|
||||
|
||||
|
||||
@@ -566,6 +575,36 @@ class TestPromptPostProcessor(TestPromptPostProcessorBase):
|
||||
PromptPair("this test is OK", ""),
|
||||
)
|
||||
|
||||
def test_var_nested_1(self): # variable default nested in variable set
|
||||
self.process(
|
||||
PromptPair(
|
||||
"${v1=test ${v2:OK}}${v1}",
|
||||
"",
|
||||
),
|
||||
PromptPair("test OK", ""),
|
||||
variables={"v1": "test OK", "v2": "OK"},
|
||||
)
|
||||
|
||||
def test_var_nested_2(self): # variable set nested in variable default
|
||||
self.process(
|
||||
PromptPair(
|
||||
"${v1:test ${v2=OK}${v2}}",
|
||||
"",
|
||||
),
|
||||
PromptPair("test OK", ""),
|
||||
variables={"v1": "test OK", "v2": "OK"},
|
||||
)
|
||||
|
||||
def test_var_nested_3(self): # variable default nested in variable default
|
||||
self.process(
|
||||
PromptPair(
|
||||
"${v1:test ${v2:OK}}",
|
||||
"",
|
||||
),
|
||||
PromptPair("test OK", ""),
|
||||
variables={"v1": "test OK", "v2": "OK"},
|
||||
)
|
||||
|
||||
def test_cmd_set_if2(self): # set and more complex if commands
|
||||
self.process(
|
||||
PromptPair(
|
||||
@@ -655,11 +694,11 @@ class TestPromptPostProcessor(TestPromptPostProcessorBase):
|
||||
def test_cmd_ext(self): # ext
|
||||
self.process(
|
||||
PromptPair(
|
||||
"<ppp:ext lora lora1name if not _is_pony>trigger1<ppp:/ext><ppp:ext lora 'lora2 name' -0.8 if not _is_pony>trigger2<ppp:/ext><ppp:ext lora lora3__name '0.5:0.8' if not _is_pony><ppp:ext lora lora4name>trigger4<ppp:/ext>",
|
||||
"<ppp:ext lora lora1name if not _is_pony>trigger1<ppp:/ext><ppp:ext lora 'lora2 name' -0.8 if not _is_pony>trigger2<ppp:/ext><ppp:ext lora lora3__name '0.5:0.8' if not _is_pony><ppp:ext lora lora4name>trigger4<ppp:/ext><ppp:ext lora \"lora5 (name)\" 1/>trigger5",
|
||||
"",
|
||||
),
|
||||
PromptPair(
|
||||
"<lora:lora1name:1>trigger1,<lora:lora2 name:-0.8>trigger2,<lora:lora3__name:0.5:0.8><lora:lora4name:1>trigger4",
|
||||
"<lora:lora1name:1>trigger1,<lora:lora2 name:-0.8>trigger2,<lora:lora3__name:0.5:0.8><lora:lora4name:1>trigger4,<lora:lora5 (name):1>trigger5",
|
||||
"",
|
||||
),
|
||||
)
|
||||
@@ -1100,6 +1139,13 @@ class TestPromptPostProcessor(TestPromptPostProcessorBase):
|
||||
ppp="nocup",
|
||||
)
|
||||
|
||||
def test_wc_wildcard_default_filter(self): # wildcard with default filter
|
||||
self.process(
|
||||
PromptPair("<ppp:setwcdeffilter 'yaml/wildcard2' 'label1+label3' />the choice is: __yaml/wildcard2__, <ppp:setwcdeffilter 'yaml/wildcard2' />__yaml/wildcard2__", ""),
|
||||
PromptPair("the choice is: choice3-choice3, choice3-choice1- choice2 ", ""),
|
||||
ppp="nocup",
|
||||
)
|
||||
|
||||
def test_wc_nested_wildcard_text(self): # nested text wildcard with repeating multiple choices
|
||||
self.process(
|
||||
PromptPair("the choices are: __r3$$-$$text/wildcard3__", ""),
|
||||
@@ -1507,6 +1553,27 @@ class TestPromptPostProcessor(TestPromptPostProcessorBase):
|
||||
),
|
||||
)
|
||||
|
||||
def test_host_and_comma(self):
|
||||
self.process(
|
||||
PromptPair(
|
||||
"test1 AND test2:2",
|
||||
"",
|
||||
),
|
||||
PromptPair("test1, test2", ""),
|
||||
ppp=PromptPostProcessor(
|
||||
self.ppp_logger,
|
||||
self.interrupt,
|
||||
{
|
||||
**self.def_env_info,
|
||||
"ppp_config": {"hosts": {"tests": {"and": "comma"}}},
|
||||
},
|
||||
self.defopts,
|
||||
self.grammar_content,
|
||||
self.wildcards_obj,
|
||||
self.extranetwork_maps_obj,
|
||||
),
|
||||
)
|
||||
|
||||
def test_host_and_remove(self):
|
||||
self.process(
|
||||
PromptPair(
|
||||
@@ -1571,6 +1638,27 @@ class TestPromptPostProcessor(TestPromptPostProcessorBase):
|
||||
),
|
||||
)
|
||||
|
||||
def test_host_break_comma(self):
|
||||
self.process(
|
||||
PromptPair(
|
||||
"test1 BREAK test2",
|
||||
"",
|
||||
),
|
||||
PromptPair("test1, test2", ""),
|
||||
ppp=PromptPostProcessor(
|
||||
self.ppp_logger,
|
||||
self.interrupt,
|
||||
{
|
||||
**self.def_env_info,
|
||||
"ppp_config": {"hosts": {"tests": {"break": "comma"}}},
|
||||
},
|
||||
self.defopts,
|
||||
self.grammar_content,
|
||||
self.wildcards_obj,
|
||||
self.extranetwork_maps_obj,
|
||||
),
|
||||
)
|
||||
|
||||
def test_host_break_remove(self):
|
||||
self.process(
|
||||
PromptPair(
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# ACB PPP Cleanup Options node
|
||||
|
||||
* **extra_spaces**: Removes other unnecessary spaces.
|
||||
* **empty_constructs**: Removes attention/scheduling/alternation constructs when they are invalid.
|
||||
* **extra_separators**: Removes unnecessary separators. This applies to the configured separator and regular commas.
|
||||
* **extra_separators_additional**: Removes unnecessary separators at start or end of lines. This applies to the configured separator and regular commas.
|
||||
* **extra_separators_include_eol**: In the previous two options it also removes EOLs attached to the separators.
|
||||
* **around_breaks**: Removes consecutive BREAKs and unnecessary commas and space around them.
|
||||
* **breaks_with_eol**: Add a newline before BREAKs.
|
||||
* **around_ands**: Removes consecutive ANDs and unnecessary commas and space around them.
|
||||
* **ands_with_eol**: Add a newline before ANDs.
|
||||
* **around_extranetwork_tags**: Removes spaces around extra network tags.
|
||||
* **merge_attention**: It merges attention modifiers when possible (merges into one, multiplying their values). Only merges individually nested modifiers.
|
||||
* **remove_extranetwork_tags**: Removes all extra network tags.
|
||||
|
||||
Please note that *ComfyUI* does not natively support the `BREAK` and `AND` constructs, but the related settings are kept in that UI in case you use a node that supports them and the extension is configured to allow them (see the configuration file below).
|
||||
@@ -0,0 +1,4 @@
|
||||
# ACB PPP ExtraNetwork Mapping Options node
|
||||
|
||||
* **folders**: You can enter multiple folders separated by commas. You can leave it empty (the default) and add a `ppp_extranetworkmappings` entry in the **extra_model_paths.yaml** file (recommended).
|
||||
* **definitions**: Extranetwork Mappings definitions (in yaml format). Direct input added to the ones found in the extranetwork mappings folders. Allows the mappings to be included in the workflow.
|
||||
@@ -0,0 +1,4 @@
|
||||
# ACB PPP Wildcard Options node
|
||||
|
||||
* **variables**: Connect to the variables output from the main node.
|
||||
* **name**: Name of the variable to get, or empty to get all of them.
|
||||
@@ -0,0 +1,4 @@
|
||||
# ACB PPP Send-To-Negative Options node
|
||||
|
||||
* **separator**: You can specify the separator used when adding to the negative prompt (by default it's ", ").
|
||||
* **ignore_repeats**: It ignores repeated content to avoid repetitions in the negative prompt.
|
||||
@@ -0,0 +1,11 @@
|
||||
# ACB PPP Wildcard Options node
|
||||
|
||||
* **folders**: You can enter multiple folders separated by commas. You can leave it empty (the default) and add a `ppp_wildcards` or `wildcards` entry in the **extra_model_paths.yaml** file (recommended).
|
||||
* **definitions**: 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.
|
||||
* **if_wildcards**: Select what do you want to do with any found wildcards/choices (when process wildcards is off or after the processing).
|
||||
* **Ignore**: do not try to detect wildcards.
|
||||
* **Remove**: detect wildcards and remove them.
|
||||
* **Add visible warning**: detect wildcards and add a warning text to the prompt, that hopefully produces a noticeable generation.
|
||||
* **Stop the generation**: detect wildcards and stop the generation.
|
||||
* **choice_separator**: What do you want to use by default to separate multiple choices when the options allow it (by default it's ', ").
|
||||
* **keep_choices_order**: If checked, a multiple choice construct will return them in the order they are in the construct.
|
||||
@@ -0,0 +1,28 @@
|
||||
# ACB Prompt Post Processor node
|
||||
|
||||
## 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 detection of model variants.
|
||||
* **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.
|
||||
* **debug_level**: What to write to the console.
|
||||
* **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.
|
||||
* **en_options**: Connection to a ExtraNetworkMapping options node.
|
||||
|
||||
The options nodes are optional. If you don't need to change any of the default values then you don't need to use them.
|
||||
|
||||
## Outputs
|
||||
|
||||
The outputs are the final positive and negative prompt and a variables dictionary.
|
||||
|
||||
You can use the "**ACB PPP Select Variable**" node to choose one and output its value. You can use this to send only part of the prompt to, for example, a detailer node. For example:
|
||||
|
||||
With this prompt: `__quality__, 1girl, ${head:!__eyes__, __hair__, __expression__}, __body__, __clothes__, __background__, __style__` then you extract the `head` variable and use `${head}` as prompt for the head/face detailer.
|
||||
Reference in New Issue
Block a user