ACB Prompt Post Processor
The prompt node that hides a tiny programming language
- model
- wc_options
- stn_options
- cup_options
- en_options
- rm_options
- pos_prompt
- neg_prompt
- variables
This is the node that makes you question whether you're writing a prompt or scripting. ACB Prompt Post Processor takes your positive and negative prompt as plain text and rewrites both before they ever reach the CLIP encoder: it expands wildcards, rolls choices, moves chunks of the positive into the negative, swaps LoRAs by model family, and can even run a whole batch of every combination. It's the ComfyUI face of Prompt PostProcessor (PPP), the pack by acorderob that started life in 2023 as sd-webui-sendtonegative and has been quietly maintained ever since. If you're still living in the A1111 wildcard world but working in ComfyUI, this is the bridge.
How it works
The node parses both prompt boxes with a real grammar (lark, if you care) and walks them left to right. Wildcards use the Dynamic Prompts formats - __hair_colors__ and {this|that} both work, with extras like weighted choices and filters. Its own commands look like <>-tags with a ppp: prefix: <ppp:if _is_sdxl>...<ppp:/if> for conditional content, <ppp:set var>...<ppp:/set> to stash a value, <ppp:stn>content<ppp:/stn> to send content to the negative, and <ppp:ext $lora name/> for model-aware LoRA loading. The output isn't one string - it's processed text plus a dictionary of every variable that got set, which is where a lot of the power lives.
To make the _is_* model variables work, it needs to know what's loaded. Pass a MODEL (or a model class name as text) into model, and set modelname to the checkpoint's relative path. If you give it only the filename it tries to detect the class from the file itself. Skip both and it warns you and runs without model awareness - fine for plain wildcard rolls, useless for conditional prompts.
The inputs that actually matter
- pos_prompt / neg_prompt - the raw text. Paste your wildcards and commands here.
- seed - drives the wildcard/choice picks;
-1randomizes. The value actually used lands in the_input_seedoutput variable. - model / modelname - the model-awareness pair above.
- process_wildcards - on by default. Turn it off if another extension should do the rolling.
- do_cleanup - strips the leftover cruft (double commas, dead parentheses) after processing. Leave it on.
- do_combinatorial - the dangerous one. Enables combinatorial mode where the node returns every combination of your choices/wildcards.
combinatorial_limit(default 100) caps it. The docs warn for good reason: a few constructs can explode into thousands of generations.combinatorial_shufflerandomizes the order if you want variety before the cap hits.
Outputs
Three outputs, all lists (ComfyUI runs each element sequentially): pos_prompt, neg_prompt, and variables - a PPP_DICT of everything your prompt set, which you can feed into the ACB PPP Select Variable node or chain into detailer prompts.
Installing it
Grab it via ComfyUI Manager (Custom Nodes Manager → search "Prompt PostProcessor") or:
cd ComfyUI/custom_nodes
git clone https://github.com/acorderob/sd-webui-prompt-postprocessor
Then restart ComfyUI. Dependencies are light - lark, numpy, ruamel.yaml, pydantic - and there are no model downloads. It's MIT-licensed and cross-platform (A1111, Forge, SD.Next, ComfyUI), though on A1111 the README warns it must load after other prompt extensions - rename the folder with a z prefix if yours gets overwritten.
Where people get burned
First, ComfyUI doesn't natively support A1111's BREAK, AND, alternation, or scheduling - PPP converts brackets to parentheses and turns AND/BREAK into commas by default, so those tricks silently degrade unless you add nodes like ComfyUI_smZNodes. Second, missing model info: the log nags you with "Model class was not provided nor detected" when you skip model/modelname, and every condition then evaluates against nothing. Third, combinatorial mode. Start with a limit of 1, check the "Estimated combinations" line in the log, then raise it. Fourth - the unglamorous one - remember that on distilled models at CFG 1 the negative prompt is math-multiplied by zero, so all the stn cleverness does nothing there. On SDXL-lineage it's very much alive.
If the node refuses to fire every time (it forces re-execution because wildcard files can change between runs - that's by design), that's IS_CHANGED returning NaN, not a bug.
Inputs (18)
| Name | Type | Default | Description |
|---|---|---|---|
| pos_prompt | STRING | Positive prompt to process | |
| neg_prompt | STRING | Negative prompt to process | |
| modelopt | MODEL,STRING | Model or model class name. Optional if you set modelname. | |
| modelnameopt | COMBO | (none) | Relative path of the model. Needed to detect variants. |
| seedopt | INT | -1 | Seed value for the prompt processing (use -1 for random) |
| debug_levelopt | COMBO | minimal | Debug level |
| on_warningsopt | COMBO | warn | How to handle invalid content warnings |
| strict_operatorsopt | BOOLEAN | true | Use strict operators |
| process_wildcardsopt | BOOLEAN | true | Process wildcards in the prompt |
| do_cleanupopt | BOOLEAN | true | Do a cleanup of the prompt |
| cleanup_variablesopt | BOOLEAN | true | Do a cleanup of the output variables |
| results_fileopt | STRING | Filename to save processing results. Supports %datetime%, %date%, %time%, %host% tokens. Empty = disabled. | |
| run_modeopt | COMBO | single | Run mode |
| wc_optionsopt | PPP_OPTIONS_WC | Wildcard processing options | |
| stn_optionsopt | PPP_OPTIONS_STN | Send-To-Negative options | |
| cup_optionsopt | PPP_OPTIONS_CUP | Cleanup options | |
| en_optionsopt | PPP_OPTIONS_EN | ExtraNetworks mapping options | |
| rm_optionsopt | PPP_OPTIONS_RM | Run mode options |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| pos_prompt | STRING | Processed positive prompt (list of prompts if combinatorial/multiple mode is enabled) |
| neg_prompt | STRING | Processed negative prompt (list of prompts if combinatorial/multiple mode is enabled) |
| variables | PPP_DICT | Output variables (list of dictionaries if combinatorial/multiple mode is enabled) |