SwarmUI has a fairly robust wildcard system with some interesting features, but it doesn’t come close to what’s possible with A1111’s Dynamic Prompts extension.
DP advantages:
This all depends on a Python library called, appropriately enough,
dynamicprompts by the
same person, available through a pip install
near you. It does not
come with a simple CLI interface allowing me to just paste processed
prompts into SwarmUI, so I wrote one:
#!/usr/bin/env python
import re
import sys
import argparse
from pathlib import Path
from dynamicprompts.generators import RandomPromptGenerator
from dynamicprompts.wildcards.wildcard_manager import WildcardManager
def multi_replace(text, replacements):
"""
Perform multiple regex search-and-replace actions in sequence
on the given text.
:param text: The input string to modify.
:param replacements: A list of tuples (pattern, replacement) where:
- pattern: regex pattern to search for
- replacement: string to replace the matched pattern
:return: The modified string after all replacements.
"""
for pattern, replacement in replacements:
text = re.sub(pattern, replacement, text, flags=re.MULTILINE)
return text
parser = argparse.ArgumentParser(
prog='sd-dynamic',
formatter_class = argparse.RawDescriptionHelpFormatter,
description = """
Thin CLI wrapper around dynamicprompts library for
randomizing SD prompts.
"""
)
parser.add_argument('-w', '--wildcards',
action = 'store_true',
help = 'list all valid wildcard collections'
)
parser.add_argument('-c', '--count',
type=int, default=1, help='number of prompts to generate'
)
parser.add_argument('-d', '--directory',
default='/usr/local/lib/dynamic-prompts/collections',
help='directory to load wildcard files from'
)
parser.add_argument('prompts',
nargs = '*', help='Prompts to process'
)
args=parser.parse_args()
wm = WildcardManager(Path(args.directory))
generator = RandomPromptGenerator(wildcard_manager=wm)
if args.wildcards:
for name in sorted(wm.get_collection_names()):
print(name)
sys.exit()
for prompt in args.prompts:
for result in generator.generate(prompt, args.count):
# normalize the punctuation and spacing
cleaned = multi_replace(result, [
( r' ,', ',' ), ( r',(?=[^ ])', ', ' ),
( r' +', ' ' ), ( r'\.(?=[^ ])', '. ' ),
( r'\n', ' ' )
])
print(cleaned)
The library loads recursively from a single directory, where text
files are called by their name without the .txt
, and YAML files are
called by their internal structure, as if it were a path in the
filesystem (so, basedir/jstuff.yaml
containing a section
j/race/elf
would be called as “j/race/elf”, while
basedir/j/jstuff.yaml
would get called as “j/j/race/elf”,
potentially breaking all internal references).
I added the -w
option to print all the possible wildcards that were
loaded from the directory (optionally specified with -d
), so that
you can figure out what’s in a collection you downloaded, or confirm
that your work-in-progress has the correct structure. (because YAML is
forgiving, but this library blows chunks at the slightest error)
I had ChatGPT write a quick multi-regexp search-and-replace function to clean up the formatting problems common with downloaded wildcard libraries. The task was trivial enough that it got it right. I even followed it up with a trick question and it answered correctly, then updated the code to be more robust.
I’ve been playing with the Dynamic Wildcard Unleashed collection to learn the features, and one problem with the creator’s structure immediately became obvious: you can’t dress an elf in a pirate costume. He designed each character archetype as a silo, so that if you want to make an elf, she has to have elf hair and elf clothing in an elf setting:
(__cf-elf/beauty-adj__ elf with long pointed ears)
__cof-basemodel/age__ __cof-basemodel/gender-no-hair__,
__cf-elf/ethnicity__, __cf-elf/hairstyle__ hair, wearing a
__cf-elf/color__ {__cf-elf/top__, __cf-elf/bottom__|__cf-elf/dress__
{, __cf-elf/stockings__|}}, __cf-elf/shoes__,
{1-2$$__cf-elf/jewelry__}, __cf-elf/makeup__,
{1-2$$__cf-elf/accessory__}, __cf-elf/location__
You can cross the streams, but you need to manually cut-and-paste from multiple sections of the 21,000-line file after learning the internal structure. As I convert my wildcard files into YAML, I’ll avoid that problem and make it possible to build up sets to mix-and-match, so I can easily produce consistent detailed prompts out of concepts like “white-haired dark-elf wearing bridal costume, grinning and offering a slice of wedding cake to the viewer, making a sexy pose, surrounded by steampunk goblins in a festive medieval banquet hall.”
Actually, just for fun, here’s what DALL-E came back with for that sample prompt. The first pass was barely tan (sigh), so I asked it to make her much darker. Then the second pass was more terrifying than pretty, so I asked it to make her much prettier:
Anyway, while I was cutting-and-pasting through his wildcards, with occasional outbursts of tentacles, I eventually settled on the theme of a dark-elf pirate gal dangling from the rigging while happily taking one (or two) for the sake of the crew’s morale.
(the tentacles are from my canned one-liner; after extensive experimentation, I determined that putting “((cum))” in the negative prompt and only referencing “stringy purple oil” keeps most models from filling the scene with random giant globs of thick white frosting (my negative prompt for NSFW pics generally includes “((cum)), ((blood)), stomach bulge, piercing, tattoo, boob job”, etc))
Side note: “crow’s nest” does not produce the expected location on a ship. Other hilarious mishaps during this random excursion included several young ladies with a third breast, and one where her partner seemed to think there was an entrance in the middle of her forehead.
(the generated prompt included “remote campground with no amenities”, and sure enough, she’s sitting with a remote; this happened in about 70% of the pictures using this phrase. Our jobs remain safe from AI)
The Elflands Beach Volleyball Team brought a thicc ringer:
Notes:
I didn’t order the other two gals; they just showed up.
as is often the case, mentioning things like nipples nudges NSFW-capable models towards baring them, so I had to play with the wording until I got “visible through” instead of some combination of “bra pulled up” and “crotchless shorts”. It still happens occasionally, because we’re just tokenizing strings and throwing vectors at the wall to see what sticks.
I avoided the term “camel-toe” due to the risk that it would summon an actual camel.
I was also careful to place the sweat on specific body parts to keep the clothes from disappearing.
“tanned skin” works better than his “tan skin” phrasing.
Using “sportsbra” instead of “sports bra” got rid of a layer of lace.
his “___ build” phrasing is unreliable, with words like “busty” and “voluptuous” often producing small-breasted women. Believe it or not, this gal just has “large breasts”, a sure sign that the model was trained on recent anime fan-art…
SD models often spontaneously generate additional gals, and some of the descriptive words get applied to them, so several times it decided to add lolis; somehow it always kept the NSFW “visible through” part (deletedeletedelete). See previous reference to “recent anime fan-art” (seriously, I love Pixiv, but I really wish there was a switch to hide hardcore loli non-consensual bondage gang-bangs; they’ve got R-18 and R-18G, but R-18F-for-felony would be a useful block-by-default setting).
I used my ChatGPT-based poses wildcard set for more variety than his theme-specific sets offered.
I specifically requested the puffies. Because puffies:
Oh, and I’m getting good upscaling results by setting Refiner Control
Percentage at 15%, Refiner CFG Scale at 8, Refiner Upscale at 2.25,
and using this SDXL upscaler
model,
mostly because it’s the only one I’ve found that’s been converted to
.safetensors
format (the older format wasn’t “safe” because it could
contain arbitrary Python code…). With these settings, I haven’t seen
it go haywire and cover a woman’s body with nipples or weird skin
bulges.
There’s a standalone upscaling app called Upscayl, which can use some specialized models to produce crisp details, but since it lacks the context available when generating the original image, it can go off the rails in ways that make it a last-resort choice. Honestly, I think I was getting better anime-style upscales with the old AI-less Waifu2x code.
Model is WAI-NSFW-Illustrious-SDXL, by the way. Being on the Illustrious side of the fence means it should work well with LoRAs like this brand-new one of Torture Tortura…
Markdown formatting and simple HTML accepted.
Sometimes you have to double-click to enter text in the form (interaction between Isso and Bootstrap?). Tab is more reliable.