Files
DumiFlex 5a3b15aa95 fix(canvas): show a WP node's run error on the node and in a toast (#431)
ComfyUI may only flash "Job failed" when a WP node raises (Var → Choice set to stop the run). Listen for execution_error on WP_ nodes: Var → Choice paints its last-execute row red with the reason, other WP nodes get a kit Banner strip on their first widget, and one error toast says the same. The next run reaching the node clears it.
2026-11-14 02:10:58 +01:00

2792 lines
465 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"pages": [
{
"id": "introduction",
"title": "Introduction",
"group": "overview",
"blurb": "What Wildcard Pipeline is and how the pieces fit.",
"keywords": [
"start",
"overview",
"mental model"
],
"sections": [
{
"title": "What does it do?",
"text": "Each time you press Generate, Wildcard Pipeline picks from your lists, follows your rules and fills in your prompt. You get a different prompt every run without typing.\nFor example: a wildcard called \"subject\" holds a cat, a dog and a fox. Your template says A photo of $subject. Each run picks one animal and writes the prompt."
},
{
"title": "How the pieces fit together",
"text": "You wire three nodes in ComfyUI:\n- WP Context holds your modules (wildcards, fixed values, combines and so on) and turns them into $variables every run.\n- WP Prompt Assembler fills your template with those variables.\n- CLIP Text Encode, the normal ComfyUI node, takes the prompt to your sampler.\nFigure: Modules run in the WP Context, which passes $variables to the Assembler, which passes the prompt to CLIP. Diagram of the pipeline. Across the top, labelled modules inside the Context, sit five module kinds: Wildcard, Fixed Values, Combine, Derivation and Constraint, with a down arrow into the flow below. The flow runs left to right: WP Context (rolls + resolves), an arrow labelled emits, a group of resolved $vars chips ($subject, $style, $lighting), an arrow labelled fills, Prompt Assembler (with a template beginning 'A $style portrait…'), an arrow labelled prompt, and CLIP Encode (conditioning). Caption: modules resolve inside WP Context into named $variables, which go to the WP Prompt Assembler and then to CLIP."
},
{
"title": "What are modules?",
"text": "Modules are the pieces you build in this manager. Each one sets or changes $variables:\n- Wildcard: a list of options; one is picked each run.\n- Fixed Values: text that stays the same.\n- Combine: builds one phrase from several variables.\n- Derivation: if/then rules that change variables based on what was picked.\n- Constraint: makes some options of a later wildcard likelier or rule them out, based on an earlier pick.\nMake and edit modules here in the manager, then add them to a WP Context on the canvas."
},
{
"title": "A finished graph",
"text": "Figure: The whole pipeline: a WP Context holding the Starter set bundle, then WP Prompt Assembler, CLIP Text Encode and KSampler. Screenshot of four nodes left to right on the ComfyUI canvas. A WP Context (seed 42, fixed, modules 6 / 6) holds the Starter set bundle, expanded to show Starter subject (wildcard), Starter pairing (constraint, #1-all), Starter mood (wildcard, #1), Starter style (fixed), Starter scene (combine) and Starter accent (derivation). Its context output feeds a WP Prompt Assembler with the template '$scene, $style, $accent, masterpiece, highly detailed', chips $style, $scene and $accent, and the resolved preview 'sleepy wolf, oil painting, soft lighting, masterpiece, highly detailed'. The prompt output goes into CLIP Text Encode (Prompt), whose CONDITIONING goes into the positive input of a KSampler. It shows the whole pipeline: modules in a Context, a template in the Assembler, then the usual ComfyUI nodes."
},
{
"title": "Good to know",
"text": "Try right-clicking. Most WP nodes, and the rows inside them, have a menu: save a row to the library, pull in a library change, duplicate, and more.\nSelect any WP node and ComfyUI's selection toolbar (the bar over the node) shows our buttons on an indigo dot, so you can tell them from ComfyUI's own: ? opens that node's page of these docs in a window over the canvas, with the search and every other page beside it (Open in manager takes it to a full tab, and Ask about this page opens it there with the assistant chat ready to ask), and on WP Toggles, WP Note and WP Bookmark a pencil opens or closes the node's editor. Both work in the classic canvas and in Nodes 2.0.\nFinding your way in these docs: / jumps to the search box, which searches the text of every page and opens a result at its section (Enter opens the first). On a wide window, On this page on the right lists a page's sections; hover a section title for a link to it. Click a screenshot to see it full size, and use Previous and Next at the foot of a page to read on."
},
{
"title": "Where to next",
"text": "Quick start Glossary Recommended pack structure Writing prompts for your model WP Context"
}
]
},
{
"id": "quick-start",
"title": "Quick start",
"group": "overview",
"blurb": "Build the smallest working pipeline in a few minutes.",
"keywords": [
"tutorial",
"first"
],
"sections": [
{
"title": "Step 1: Make a wildcard",
"text": "Click the button below. The wildcard editor opens with a \"subject\" wildcard filled in: a cat, a dog and a fox, all equally likely. Change the options if you like, then click Save.\nFigure: The New wildcard form filled in by the button: a subject wildcard with a cat, a dog and a fox. Nothing is saved until you press Save. Screenshot of the whole New wildcard page in the manager (breadcrumb Library › Wildcards › New wildcard) after pressing Create a starter wildcard. Identity: Name 'subject', Category None, Variable name $subject, an empty Description, Tags with an Add button, and Content rating with a Mark as NSFW (18+) switch. Sub-categories says No sub-categories yet. Options (3) lists 'a cat', 'a dog' and 'a fox', each with weight 1, sub-category (none), a + negative button and a 33% probability bar; above the table are Bulk edit, Add null and Add option. Cancel and Save sit at the bottom right; nothing is stored until you press Save."
},
{
"title": "Step 2: Wire it up in ComfyUI",
"text": "On the ComfyUI canvas:\n- Add a WP Context. Double-click the canvas and search \"WP Context\", or right-click → Add Node → wildcard-pipeline → WP Context.\n- Add your wildcard to the context. Click Add module on the WP Context and pick the \"subject\" wildcard you just saved.\n- Add a WP Prompt Assembler and wire the Context's context output into it. Type the template A photo of $subject.\n- Wire the Assembler's prompt into your CLIP Text Encode's text input.\n- Generate. Each run picks a subject and writes the prompt.\nFigure: The finished chain: a WP Context with one wildcard, a WP Prompt Assembler ('A photo of $subject'), CLIP Text Encode and KSampler. Screenshot of the quick start chain on the canvas, four nodes left to right. A WP Context (seed 42, fixed, modules 1 / 1) holds one wildcard row, Starter subject ('$subject · 4 options'). Its context feeds a WP Prompt Assembler with the template 'A photo of $subject', one $subject chip and the preview 'A photo of wolf'. The prompt output goes into the text input of CLIP Text Encode (Prompt), and its CONDITIONING goes into the positive input of a KSampler (steps 20, cfg 8.0, euler, simple). Each Generate rolls a new subject through the same chain."
},
{
"title": "Step 3: See what was picked",
"text": "Wire a WP Debug to the Context's context output to see what $subject picked on each run."
},
{
"title": "What to try next",
"text": "Add more modules: a Fixed Values for a style that stays the same, a Combine to join two variables, or a Constraint so two picks agree. A WP Context Loop makes several variations in one Generate."
},
{
"title": "See also",
"text": "WP Context WP Prompt Assembler Wildcard Glossary"
}
]
},
{
"id": "test-runner",
"title": "Test Runner",
"group": "overview",
"blurb": "Run modules on many seeds, inspect each one, and compare against a baseline.",
"keywords": [
"test",
"scenario",
"seed",
"baseline",
"compare",
"inspect",
"trace",
"preview"
],
"sections": [
{
"title": "What it's for",
"text": "The Test Runner, in the manager sidebar, runs a stack of your library modules through the real engine, once per seed, and shows what comes out. Use it to check that a wildcard's options all come up, that a constraint actually fires, or that a combine reads the variables you think it does, before you wire anything on the canvas.\nFigure: An empty Test Runner: saved scenarios on the left, the stack and pinned values in the middle, and Range / Random seeds with Run at the top. Screenshot of the manager's Test Runner page. On the left, Scenarios with a New button, a filter box and an unsaved Quick run ('No saved scenarios yet. Build a stack, run it, then Save to keep it here.'). In the middle, Quick run (0 items · seeds 0 to 99) with Range / Random, from 0 and 100 seeds, Save scenario and Run at the top; a Stack section ('runs left to right, like the modules on a Context node') with Add module or bundle; and Pinned values ('stand in for a value an upstream node would pass in') with Pin a value.\nFigure: A seed here is a real chain seed: the same seed on a canvas Context holding the same modules gives the same values. Steps: Scenario (stack + pins + seeds) → ctrl + enter → Run (real engine, seeded) → per seed → Results (values, traces, warnings) → save → Baseline (compare after edits). Flow diagram of four stages left to right: Scenario (stack + pins + seeds), an arrow labelled Ctrl + Enter, Run (real engine, seeded), an arrow labelled per seed, Results (values, traces, warnings), an arrow labelled save, and Baseline (compare after edits). Caption: a seed here is a real chain seed, so the same seed on a canvas Context holding the same modules gives the same values."
},
{
"title": "Build a scenario",
"text": "A scenario is what one Context node would hold, saved so you can run it again. Press New in the rail, then Add module or bundle. Cards run left to right, like the modules on a Context; drag them (or Alt + arrow keys) to reorder, and use a card's switch to leave it out of a run without removing it.\nStack\nThe modules and bundles to run, in order. A card warns when it reads a $variable nothing earlier sets, or when a combine template is plain text and will never vary.\nPinned values\nStand-ins for what a node further up the graph would pass in, such as $subject from an Injector. They are set before the first card runs.\nSeeds\nRange runs seeds from N upward; Random draws a fresh set each run. Up to 10,000 seeds per run.\nOutput\nThe variable treated as the prompt: the last combine's output unless you pick another on the Outputs tab.\nSave (Ctrl+S) keeps the scenario, and the pin icon keeps it at the top of the rail. From a module or bundle editor, Send to Test Runner opens a quick run with just that item, and In N scenarios lists the saved scenarios that use it."
},
{
"title": "Read a run",
"text": "The stats row gives runs, unique outputs, warnings and engine time. Below it, each tab answers one question:\nVariables\nEvery $variable and how often it took each value, with values that never changed listed once as constants.\nOutputs\nThe output for the first seeds, tinted by which variable wrote each part, with a NEG line for the negative words it carries.\nSamples\nOne row per seed with every variable (and the output's negative, when there is one); filter by seed or value.\nWarnings\nRuntime warnings grouped by kind, with the seeds that raised them, and any runs that failed.\nCompare\nWhat changed since the saved baseline. See below.\nClick any seed to open its trace: every card in run order with what it wrote, plus the nested @{…} picks it made on the way. Copy seed puts it on the clipboard for a canvas Context."
},
{
"title": "Inspect a module",
"text": "Click a stack card to open its inspector: how that module is set up next to what the last run did with it.\nWildcard\nEach option's share of the weight next to its share of the picks. Options that were never picked are flagged; picks through @{} references elsewhere in the stack count too.\nConstraint\nWhich pick it listens to, which wildcard it re-weights and how far it reaches, and how many times it applied. Zero usually means the target runs before the source.\nCombine\nThe template, the $variables it reads, and the outputs it produced.\nDerivation\nIts rules as plain if / else lines, and the values its target ended up with.\nFixed values\nEvery name and value it sets.\nBundle\nIts modules in run order (nested bundles indented) and the values each one wrote.\nEdit in the inspector opens that module in its editor."
},
{
"title": "Baselines: catch what an edit changed",
"text": "On the Compare tab of a saved scenario, Save as baseline keeps this run's output for each seed (the first 1,000), plus every variable's spread and the warnings. Edit your modules, run again, and the tab lists each seed whose output changed (with the changed words marked), values that appeared or disappeared, shares that moved, and warnings that came or went. A seed whose negative changed is listed too; a baseline saved before negatives existed says negative not recorded until you save it again.\nRe-run all in the rail's Pinned header runs every pinned scenario against its baseline in one go, and each row then reads matches or N changed. When the result is the new normal, Use this run as baseline replaces it.\nCompare needs the same seeds on both sides. A Range scenario always has them; for a Random one, Run the baseline's seeds reruns exactly the seeds the baseline used."
},
{
"title": "Works with",
"text": "Seeds & loops WP Debug Warning & conflict types"
}
]
},
{
"id": "library-health",
"title": "Library health",
"group": "overview",
"blurb": "Check the whole library: broken references, likely mistakes, unused items, and how varied each bundle is.",
"keywords": [
"health",
"unused",
"broken",
"missing",
"duplicate",
"orphan",
"clean up",
"tidy",
"ignore",
"variety",
"check"
],
"sections": [
{
"title": "What it's for",
"text": "After a few months of imports, AI packs and deletes, a library collects references to wildcards that are gone, constraint rules for options that were renamed, near-duplicates and items nothing uses. The canvas warnings only look at one chain. Library health, in the manager sidebar under Library, looks at everything at once.\nFigure: Library health: the three counts at the top, the filter and search, and one fold-out list per check, each row with Open and Ignore. Screenshot of the Library health page. At the top, three cards count 4 problems (print wrong or nothing), 3 warnings (probably a mistake) and 5 to tidy up, above a filter (All, Problems, Warnings, Tidy up) and a search box. Below, one fold-out list per check: Points at a wildcard that's gone lists the Fantasy hero bundle and the Outfit wildcard, which point at a deleted Shoes wildcard; Constraint rule for an option or tag that's gone lists Class → Weapon with two rules for \"heavy\" and \"warrior\"; Reads a variable nothing sets lists the Scene combine reading $pose; Wildcards with the same options lists Mood (2) with a link to Mood. Each row has Open and Ignore.\nThe sidebar entry shows a red count when there are problems and an amber one when there are only warnings, and the Dashboard shows the same counts with the first problem. The page checks again on its own a moment after you change the library; Check again runs it now."
},
{
"title": "Problems: print wrong or nothing",
"text": "Points at a wildcard that’s gone\nA @{reference} in an option, combine, fixed value, derivation, saved template or bundle copy names a wildcard no longer in the library. It prints nothing where it sits.\nConstraint lost its wildcard\nIts source or target wildcard was deleted, so it never applies.\nConstraint rule for an option or tag that’s gone\nA matrix tag or an exception value the wildcard no longer has. That rule never matches.\nBundle holds a bundle that’s gone\nA nested bundle was deleted, so inserting the outer one skips it.\nEmpty bundle\nInserting it adds nothing.\nWildcard can’t pick anything\nNo options, only the empty option, or every option at weight 0 with no fallback.\nReads a variable nothing sets\nA $variable no library item, WP Model Info, WP Context Loop or the open canvas sets. Unless a WP Context Injector sets it, it prints as empty."
},
{
"title": "Warnings: probably a mistake",
"text": "No name\nHard to find in pickers and references.\nSame name twice\nTwo items of one kind with one name. The row links to both.\nWildcards with the same options\nOften a pack imported twice.\nSame option listed twice\nIt rolls twice as often. Raise one option’s weight instead.\nOptions that never roll\nWeight 0 and not the fallback.\nTwo wildcards write the same variable\nOn one chain the later one wins, which is fine when they are alternatives.\nBundle copies of deleted items\nThe bundle still runs its copies, but they no longer follow the library. Open the bundle to relink them."
},
{
"title": "Tidy up: clutter",
"text": "Not used by any bundle, template or reference\nNothing in the library uses it, and it is not on the open canvas. Delete all removes every one shown, after one confirm.\nSets a variable nothing in the library reads\nA combine, derivation or fixed value whose variable no saved template, combine, derivation or open Assembler prints.\nBundle holds an older copy\nThe library item changed since the bundle was saved.\nTag used only once\nWith a hint when it looks like a typo of another tag (outfit / outfits).\nEmpty category\nNothing is filed under it. Delete removes the category only.\nNewer version on Community\nThe author published a newer version of a pack you installed. Update opens the same dialog as the list pages.\nInstalled from a newer format\nIt came from a newer Wildcard Pipeline, so settings this version doesn’t know were left out. Update the extension and reinstall it."
},
{
"title": "Open your canvas first",
"text": "A WP Context keeps its own copy of what you insert, so the library can't tell what sits on a canvas. When a ComfyUI tab is open, the page asks it which library items it holds, which variables its Injectors, Loops and Model Info set, and which $variables its Assemblers read, and counts those as used. Ids and names only, never values.\nWith no canvas open, the page says so: items you only use on a canvas show under Not used. Open your workflow in another tab and press Check again, or Ignore them."
},
{
"title": "Fix, ignore, filter",
"text": "Open\nOpens the item’s editor (a tag opens the items carrying it). Rows about two items, like duplicates, link to each one.\nDelete\nOnly on rows where it is safe: unused items and empty categories. Copies on a canvas keep working, and a backup from Settings › Library & data can bring an item back.\nIgnore\nHides the row for good and leaves it out of the counts. Show ignored brings ignored rows back so you can Unignore them. If the same item turns up for a different reason, it shows again.\nFilter and search\nAll, Problems, Warnings or Tidy up, and a search over item names, details and values."
},
{
"title": "Variety check",
"text": "Under the findings, every bundle has Check variety. It runs the bundle 200 times with random seeds through the real engine and shows how many of the results were different, which variables never changed although their wildcard has more options, which options never came up, and the run's warnings. Open in Test Runner takes the same bundle to the Test Runner for a closer look."
},
{
"title": "Works with",
"text": "Test Runner Warning & conflict types Bundles"
}
]
},
{
"id": "themes",
"title": "Themes and looks",
"group": "overview",
"blurb": "Pick the manager's colours and its look separately, and mix any of them.",
"keywords": [
"theme",
"colours",
"colors",
"look",
"dark",
"light",
"high contrast",
"accent",
"tui",
"terminal",
"font"
],
"sections": [
{
"title": "Colours",
"text": "- Orbit: Logo indigo on cool ink. The default.\n- Native: ComfyUI's own greys.\n- Nebula: Violet-tinted night with a violet accent.\n- Tide: Deep slate blue with a sky accent.\n- Ember: Warm charcoal and paper with a copper accent.\n- High contrast: Black and white, solid borders, a yellow focus ring.\n- ComfyUI palette: Your ComfyUI color palette, imported ones too. Switches when it does.\nModule kind colours (wildcard, fixed value, combine and the rest), variable colours and the teal of a picked option stay the same in every theme, so they always mean the same thing.\nComfyUI palette dresses the manager in the colour palette ComfyUI is using (ComfyUI Settings › Appearance › Color Palette), imported palettes and ones other extensions add included. On Dark and Light it wears Native, ComfyUI's own greys; on any other palette the pages, cards, borders and text come from the palette's node and text colours, with the text kept readable. The palette also decides dark or light, so the Mode setting and the top bar's theme button rest while it is the colour theme. Switch the palette in ComfyUI and an open manager tab follows at once; with no canvas open, it catches up when you come back to the tab. On Theme's own the accent is the palette's own when it has one that reads well, else indigo."
},
{
"title": "Look",
"text": "- Orbit: Soft corners, hairline borders. The default.\n- Crisp: Square corners, firm outlines, ruled lists.\n- Soft: Round pills, no outlines, cards lifted by shadow.\n- Terminal: Mono everywhere, sharp corners, dashed rules.\n- Editorial: Serif headings, open layout, underlined tabs.\n- Compact: Icon sidebar, tight rows, the most on screen.\n- TUI: Boxed panels, inverse selection and [x] checks.\nPicking Compact also switches the sidebar to icons; you can switch it back with the Sidebar setting. Density stays its own setting and works with every look.\nA look reaches every page: in Terminal and TUI every corner is square, Crisp keeps them nearly square, and the shapes inside controls follow (a switch's knob and a slider's handle turn square with them). Lists, tabs, steps, checkboxes and radios show what's selected the look's way (in TUI a checkbox reads [x] and a radio (*))."
},
{
"title": "Accent colour",
"text": "Accent color starts on Theme's own, which follows the colour theme. The presets are Indigo, Violet, Teal, Rose and Amber, and Custom opens a picker for any colour. A preset or custom accent you pick is kept when you change the colour theme. High contrast keeps its own accent so its text stays readable; your pick comes back when you leave it.\nThemes change the manager, and the Community tab follows them: it takes the colour theme, the look and light or dark, and changes with them. The canvas nodes follow ComfyUI's colour palette instead (below), so they sit well next to other nodes."
},
{
"title": "On the canvas",
"text": "WP nodes on the canvas follow ComfyUI's Color Palette (ComfyUI Settings › Appearance). On Dark and Light they use their own dark and light colours. On any other palette (Solarized, Arc, Nord, Github, a palette another extension adds such as Obsidian or Milk White, or one you imported) their rows, fields, borders and text take the palette's node, widget and text colours, so a WP node looks like the nodes around it. A palette that is light counts as light even when it doesn't say so.\nText always stays readable: when a palette's text is too faint on its own widget colour, WP nodes use a stronger one. The accent, module kind colours, variable colours and warnings keep their meaning on every palette, in their dark or light shades. High contrast still wins over the palette.\nTo keep the WP colours on every palette, set Wildcard Pipeline › Sizing › Canvas colors (also in the Display playground and in the manager's Settings › Canvas) to Wildcard Pipeline colors."
}
]
},
{
"id": "glossary",
"title": "Glossary",
"group": "overview",
"blurb": "The words you'll see in these docs, one line each.",
"keywords": [],
"sections": [
{
"title": "Terms",
"text": "Module\nOne piece you build in the manager, such as a wildcard or a fixed value. It lives in your library and you can add it to any WP Context.\nLibrary\nEverything you've saved in the manager: modules, bundles and templates.\n$variable\nA named value, such as $subject. Modules write variables; your template reads them.\nWildcard\nA list of options. Each run picks one and writes it to a $variable.\nWeight\nHow likely an option is next to the others. Weight 2 comes up twice as often as weight 1; 0 switches it off.\nNull option\nA wildcard option that prints nothing, for \"sometimes leave this out\".\nFallback\nThe option a wildcard uses when nothing else is left to pick, for example after a constraint ruled everything out.\nSub-category (tag)\nA label on a wildcard option, such as \"warm\". Filters and constraints work with tags.\nTag group\nTags sorted under a name, such as species or temperature. A group is either classify (the tags describe the option) or accepts (see below).\nAccepts group\nA tag group whose tags are choices the option offers, such as shoes that go with an outfit. One is rolled with each pick, and $outfit.SHOES prints it.\nLibrary tag\nA label on a whole module, bundle or template, used only to organise your library. It never changes a pick.\nFixed Values\nA module that sets variables to text you type. Nothing is rolled.\nCombine\nA module that builds a new variable from text and other variables, such as \"$style portrait of $subject\".\nDerivation\nA module with if/then rules: when a variable says one thing, set another variable.\nConstraint\nA module that changes a later wildcard's odds based on an earlier pick, such as \"ocean makes stormy likelier\".\nBundle\nSeveral modules saved together, so you can add them to a WP Context in one go.\nTemplate\nYour prompt with $variables in it, such as \"a $style portrait of $subject\". The WP Prompt Assembler fills it in.\nReference (@{…})\nA fresh pick from another wildcard, written inside an option. Type @ to insert one.\nChip\nThe small pill a text field shows for a $variable or an @{…} reference. A red one points at something missing.\nInline choice ({a|b})\nA quick random choice written straight into an option, fixed value or combine. Picks one of the parts each run. The Assembler prints it as typed.\nSeed\nThe number behind every random pick. Same seed and same modules give the same result.\nWP Context\nThe node that holds your modules, rolls them on every Generate and passes the variables on.\nChaining\nWiring one WP Context into another. The second starts with the first one's variables and can add to or replace them.\nHide from prompt\nA variable that prints nothing, while other modules can still use it. The globe button on a row.\nLock pick\nKeeps the same pick on every Generate until you unlock it.\nHold across run\nThe same pick on every frame of one loop run, and a new one next Generate.\nNegative\nWords that go to the negative prompt when a variable is printed. Options, fixed values, combines and derivations can carry them.\nLoop\nThe WP Context Loop node: runs your pipeline several times in one Generate, one frame each."
},
{
"title": "See also",
"text": "Introduction Quick start The $variable pipeline"
}
]
},
{
"id": "howto-examples",
"title": "Start from an example",
"group": "howto",
"blurb": "Three ready-made workflows that run on an empty library, with notes that walk you through each step.",
"keywords": [
"example",
"examples",
"template",
"templates",
"browse templates",
"starter",
"workflow",
"demo"
],
"sections": [
{
"title": "Open one",
"text": "In ComfyUI, open Workflow › Browse Templates and pick Wildcard Pipeline in the list of node packs. Each example is a complete workflow: choose your checkpoint in Load Checkpoint and press Run.\n- 1 - One image: the core chain (two WP Contexts, the Assembler and the Cleaner) with WP Debug. WP Toggles is the switchboard: a LoRA switch bypasses its loader and swaps the trigger words from the Context Injector into the prompt, and another switches the group that reads the image size, sampler and CFG from the loaded model (a Mode Relay passes it on to a Node Group).\n- 2 - Batches and grids: a WP Context Loop sweeps three shots against three lights on one held person, a second Assembler writes the labels and WP Label & Grid lays the images out as an XY plot. WP Toggles in Pick one mode chooses Quick or Full: its value sets the steps through WP Var → Int, and WP Var → Bool picks a smaller image for Quick.\n- 3 - Pick and refine: four drafts of one held person, a pause at WP Image Filter to pick the keepers (the contact sheet names each pick's look), then an upscale and a second pass on the picks only.\n- 4 - Sets and stories: one loop, three ways, each in its own canvas group. WP Toggles in Pick one mode mutes the groups you didn't pick. Every frame different rolls everything again each frame; Photo shoot holds the person, outfit, place and light and gives each frame its own shot; Storyboard holds a story and writes each frame's beat with a derivation on $story and $iteration. Each group pauses at WP Image Filter so you pick the frames that go on the Label & Grid sheet.\nEach workflow reads left to right, one canvas group per step, and long wires (the model, the CLIP, the settings) run straight along a lane under the groups on wire reroutes. In Sets and stories each mode's row has four parts, Lists, Prompt, Image and Pick and grid, and one checkpoint on the left feeds all three rows.\nA Start here note lists the steps and links to these docs, the Discord and the Community. Notes on the canvas explain each step, with links to the docs and to outside guides and checkpoints. The notes that open a step are also bookmarks that frame their whole group: the Bookmarks dock lists them (Start here too), and the number keys 1, 2… jump between them while the canvas has focus. The Start here note also points at the indigo ? in the toolbar over a selected node, which opens that node's help page."
},
{
"title": "The packs",
"text": "All four share one family of packs, each a bundle with sub-bundles. They describe people in plain words and name no style, so they suit anime, illustration and photo models alike: Person (gender, age, heritage, build, skin, hair, eyes, makeup or facial hair), Location (place and time of day), Wardrobe (an outfit that fits the place, with shoes it accepts), Light, Camera (shot, angle, focus, colour palette) and Direction (pose and expression). Constraints keep them coherent: the place picks the time and the light, the shot picks the focus and leaves out poses it can't see.\nRender reads $model_variant and $model_family from WP Model Info and writes only the quality words and their negative for the loaded model: Illustrious / NoobAI quality tags by default, Pony's score tags, or nothing for Flux and other sentence models. Your style (anime, photo, painting) goes in the template."
},
{
"title": "The lists come with the workflow",
"text": "Every list, rule and bundle in an example is stored on its WP Context, so nothing has to be in your library. Bundle headers show a grey EXAMPLE chip instead of the red MISSING badge: nothing is wrong, the bundle just lives only in this workflow.\nTo keep them all, press Add this workflow's lists to my library on the Start here note: a review lists every bundle and module, and one press of Add saves them (Undo on the message takes them back out). See WP Note.\nTo keep just one, right-click its header and choose Push to library… with Restore missing references ticked. It saves the bundle, its sub-bundles and every row in one step, and the chip goes away. See Bundles.\nThe Shared lists sub-bundle is switched off on purpose. Other lists read its colours through @{…} references, so it never adds words of its own."
},
{
"title": "Works with",
"text": "Quick start Bundles Share a pack or workflow"
}
]
},
{
"id": "howto-variations",
"title": "Make variations in one click",
"group": "howto",
"blurb": "Several prompts and images from one Generate, or every combination of a few options.",
"keywords": [
"batch",
"variations",
"many images",
"loop",
"sweep",
"combinations",
"count",
"seed list"
],
"sections": [
{
"title": "Several random variations",
"text": "- Add a WP Context Loop and wire its context output into your first WP Context's upstream input.\n- Set count to how many variations you want, say 4.\n- Add a WP Seed List: wire the loop's loop_config into it and its seed output into your sampler's seed.\n- Press Generate once. You get four prompts and four images.\nFigure: WP Context Loop (count 4) → WP Context → WP Prompt Assembler using $iteration of $iteration_total → one prompt per frame. Screenshot after a run. A WP Context Loop (count 9, strategy hash, Per-iteration seeds and a Sweep combinations button marked 9 frames, Edit frame chips base and #1 to #9, Override Context seed off, iteration var $iteration, Bypass loop off) has its context output wired through folded WP Context · Character and WP Context · Scene nodes into a WP Prompt Assembler with the template '$hero, $hair, $eyes, wearing $outfit, $scene, $style'. Its prompt output feeds a Preview as Text that shows the prompt the run made; the Assembler's own preview rolls frame 1's seeds.\nSkip the Seed List and the sampler uses the same seed for every frame, so two frames with the same prompt give the same image."
},
{
"title": "Every combination of a few options",
"text": "To compare options side by side, open Sweep combinations on the loop, switch it on and add the wildcards to sweep. Three hair colours against two moods make six frames, one per pair. Keep the sampler on one fixed seed so only the prompt changes. More on WP Context Loop."
},
{
"title": "Keep one thing the same across the batch",
"text": "Switch on Hold across run on a row, such as the character, and every frame keeps that pick while the rest changes. The next Generate picks a new one. See Keep something the same."
},
{
"title": "Number the frames",
"text": "$iteration is the frame number and $iteration_total the count. They're hidden from the prompt until you click their globe and Σ buttons on the loop. A derivation can also test $iteration to tell a story over the frames."
},
{
"title": "Related",
"text": "WP Context Loop WP Seed List Per-iteration overrides"
}
]
},
{
"id": "howto-picks-agree",
"title": "Make two picks agree",
"group": "howto",
"blurb": "Stop clashes: let one pick steer another with a constraint, an accepts group or a derivation.",
"keywords": [
"match",
"agree",
"clash",
"pair",
"depend",
"steer",
"winter",
"outfit"
],
"sections": [
{
"title": "Pick the right tool",
"text": "Constraint\nOne pick changes the odds of another: winter rules out summer clothes, stormy makes moody likelier. The usual answer.\nAccepts tag group\nThe matching choice travels with the option: each outfit lists the shoes that go with it, and $outfit.SHOES prints one of them.\nDerivation\nAdds or changes text after a pick: if $season is winter, add \"a scarf\". Use it for text, not for steering another pick.\nReference with a filter\nAn option draws from another wildcard, narrowed by tags: \"@{Color:warm} jeans\". Good when the match is always the same."
},
{
"title": "With a constraint",
"text": "- Tag the options of both wildcards, for example the season wildcard with winter and summer, the outfit wildcard with warm and light.\n- Create a Constraint. Source: the season wildcard. Target: the outfit wildcard.\n- In the rule matrix set winter × light to Exclude, and summer × warm too. Use Boost or Reduce for a nudge instead of a ban.\n- On the WP Context, put the constraint between the two wildcards: season, then constraint, then outfit.\nFigure: Source wildcard, then the constraint, then the target wildcard. The #1 badges link the constraint to its target. Screenshot of a WP Context (modules 3 / 3) wired into a WP Prompt Assembler. The Context rows, top to bottom: Starter subject ('$subject · 4 options'), Starter pairing, a constraint marked #1-all ('$subject → $mood · 2×2'), and Starter mood marked #1 ('$mood · 4 options'). The Assembler's template is 'a $mood $subject, masterpiece' and its preview reads 'a sleepy wolf, masterpiece'. It shows the order: source wildcard, then the constraint, then the target wildcard it changes.\nGive the target a fallback option, so a combination you didn't plan for still prints something. The constraint's Preview shows the odds for every pair before you run.\nFor a strict pairing, such as \"maid always wears the frilled apron\", use an Only rule. More on Constraint."
},
{
"title": "When the deciding value isn't a pick",
"text": "Constraints react to picks. To steer from a variable, such as the loaded model from WP Model Info, make a wildcard whose options spell the variable's values and set Match variable on it. It then picks the matching option and can be a constraint's source."
},
{
"title": "Check it",
"text": "Run the Test Runner over a few hundred seeds: the Samples tab shows every pair that came up. Or wire a WP Debug and look at the Trace tab to see which constraint changed which odds. A constraint that did nothing shows Constraint never applied; that usually means it sits in the wrong place. $season and $outfit here are only examples."
},
{
"title": "Related",
"text": "Constraint Constraints in depth Wildcard Derivation"
}
]
},
{
"id": "howto-keep-steady",
"title": "Keep something the same",
"group": "howto",
"blurb": "Keep a style, a character or a whole prompt while the rest changes.",
"keywords": [
"lock",
"hold",
"same",
"repeat",
"reproduce",
"fixed seed",
"consistent",
"character"
],
"sections": [
{
"title": "Choose how long it should stay",
"text": "Keep one pick for good: Lock pick\nClick the lock on a row (Lock seed on modules other than wildcards). It keeps its result on every Generate until you unlock it. Good for a style you've settled on.\nKeep one pick for a batch: Hold across run\nRight-click a row, or switch it on in the row's editor. Every loop frame of one Generate shares the pick; the next Generate picks again. Good for one character through a story.\nAlways the same option: untick the others\nIn the row's editor, untick every option but one. Or use a Fixed Values module if it never changes at all.\nKeep every pick: fix the Context seed\nSet the WP Context's seed to fixed (control after generate). The same seed and the same modules give the same prompt every time.\nKeep the image too: fix the sampler seed\nThe prompt and the sampler have separate seeds. For the same image, fix both. With a loop, lock the seeds in WP Seed List.\nKeep one frame: lock it on that frame\nWith a loop, pick the frame's chip and lock the row there. Only that frame keeps its pick.\nFound a prompt you like? In WP Debug, note the Context's seed and put it back in the seed box later, or lock the rows that matter. The Test Runner's Copy seed does the same for a seed you found there."
},
{
"title": "Good to know",
"text": "- A lock keeps the module's dice the same. If you edit its options, or a constraint above it changes the odds, the result can still change. Hold across run keeps the finished text instead.\n- Hide from prompt and Hold across run apply to every frame, so they're greyed out while you edit a single frame.\n- The full story of seeds is on Seeds & loops."
},
{
"title": "Related",
"text": "Seeds & loops WP Seed List Per-iteration overrides"
}
]
},
{
"id": "howto-share",
"title": "Share a pack or workflow",
"group": "howto",
"blurb": "Send a workflow, export modules to a file, or publish to the community.",
"keywords": [
"share",
"export",
"import",
"publish",
"community",
"send",
"file",
"workflow"
],
"sections": [
{
"title": "Share a workflow",
"text": "Save the workflow in ComfyUI as usual and send the file. Every row on a WP Context is a copy of the module, so the workflow runs on another machine even if that library is empty.\n- An @{…} reference looks on the node first, then in the library. Keep every wildcard a reference needs on the node (switched off is fine) and it works anywhere.\n- The person you send it to sees missing on rows they don't have. Right-click → Save to library adds them.\n- Per-frame changes and the loop's settings are saved in the workflow too."
},
{
"title": "Share modules as a file",
"text": "- In the manager open Import / Export → Export.\n- Tick what to share: the whole library, a kind, or single modules and bundles. Select with dependencies adds the wildcards they reference.\n- Download the file and send it.\nTo bring one in, use the Import tab: pick the file or paste its text, choose what to add, and decide what happens when a name already exists. An import can be undone from the message that follows."
},
{
"title": "Share with people who don't use Wildcard Pipeline",
"text": "Wildcard files (.zip), next to Export, writes the same picks as plain .txt wildcard files that Dynamic Prompts, Impact Pack, Prompt-PostProcessor, Easy-Use and A1111/Forge read: one file per wildcard, one option per line, in a folder named after what you picked. Use them as __folder/name__.\n- References to other wildcards become __folder/name__, and $hair becomes __folder/hair__. In those tools that is a new pick, not the same one.\n- Uneven weights become repeated lines, since those tools pick lines evenly.\n- Fixed values and combines get a one-line file each, templates go in templates/.\n- Rules (derivations, constraints), tags, the chance of nothing, fallbacks and negatives on options have no plain form. The zip's README.md lists what changed in each file.\nThe community site offers the same download on every pack."
},
{
"title": "Publish to the community",
"text": "On a module or bundle, the Publish to community button opens the publish form with everything filled in; the Export tab has the same button for a selection. Others find it in the Community tab and install it in one click, with the wildcards it needs.\nA pack that uses a newer feature asks people on an older version to update before it installs, so nothing gets dropped on the way."
},
{
"title": "Coming from A1111 or Forge",
"text": "Text and YAML wildcard packs import directly: see Import wildcard packs."
},
{
"title": "Related",
"text": "Bundles Import wildcard packs Recommended pack structure"
}
]
},
{
"id": "troubleshooting",
"title": "Fix common problems",
"group": "howto",
"blurb": "Something prints nothing, never changes, or won't install: causes and fixes.",
"keywords": [
"problem",
"not working",
"empty",
"missing",
"broken",
"help",
"faq",
"same image",
"nothing"
],
"sections": [
{
"title": "Something prints wrong",
"text": "A $variable prints nothing (MISSING VAR)\nNothing before the Assembler sets it, the name is misspelt, or it's hidden (the globe on its row is on). WP Model Info and the loop's $iteration are hidden by default.\nA wildcard prints nothing\nIt picked its null option, a constraint ruled out every option, or a tag filter matched none. Give it a fallback option; WP Debug's Warnings tab says which it was.\n{a|b} prints as typed\nThe Assembler doesn't roll anything. Put the choice in a wildcard, fixed value or combine and print its variable.\n@{…} prints nothing\nReferences only work in wildcard options and derivation actions. In a combine or the Assembler they print nothing.\n$name in a wildcard option prints as typed\nOptions and fixed values can't read variables. Use a Combine below them.\nCommas or words go missing around an empty variable\nThe Assembler tidies the commas and joining words around a variable that came out empty. Write the template so it still reads well when something is empty."
},
{
"title": "The run stops",
"text": "The run stops and only \"Job failed\" flashes\nA WP node stopped it, for example WP Var → Choice set to stop the run when nothing matches. The reason now shows on that node in red (Var → Choice puts it on its last execute line, other WP nodes in a strip at the top) and in a WP message at the corner of the canvas. It goes away when the next run gets past the node, or press its ×."
},
{
"title": "Picks don't change the way you expect",
"text": "The same prompt every Generate\nThe Context's seed is set to fixed, or the rows are locked or hold their pick. Set the seed to randomize, or unlock the rows.\nEvery loop frame gives the same image\nThe sampler uses one seed for all frames. Add a WP Seed List between the loop and the sampler.\nA constraint does nothing\nCheck the order: source wildcard, then the constraint, then the target. WP Debug's Trace tab shows whether it applied; Constraint never applied means the target didn't run after it.\nAn option never comes up\nIts weight is 0, it's unticked on the node, a constraint excludes it, or a filter leaves it out. The Test Runner shows each option's share over many seeds.\nA later Context undoes my pick\nWhen two Contexts set the same variable, the later one wins. The row shows a blue dot for this."
},
{
"title": "Library and packs",
"text": "A row says missing\nThe module isn't in your library, often because the workflow came from someone else. It still works; right-click → Save to library to keep it.\nA row says drift (library updated on a bundle)\nYou changed the library entry after adding it. Right-click → Refresh from library (Reset to library snapshot on a bundle) to take the change.\nA pack won't install and asks for an update\nIt uses a feature from a newer version. Update Wildcard Pipeline and try again.\nA constraint shows a missing wildcard\nOpen it and use Reattach to point it at a wildcard you have, or Download from community.\nThe manager stays on its loading screen\nThis happens when ComfyUI restarted or updated while the manager tab was open. The page now notices, waits for the server and reloads by itself. If it says the manager couldn't load, wait until ComfyUI has finished starting and press Reload.\nUndo a change to the library\nSettings › Library & data › Backups lists every backup; Restore puts one back right away, no restart, after backing up the current library as a Before restore backup; the toast's Undo puts it back. A backup from a newer version of Wildcard Pipeline, or a damaged one, is refused and nothing changes. Backups to keep keeps the newest ones, the ones you make yourself too. Pin a backup (the bookmark button) to keep it for good: pinned ones are never deleted for you and don't count toward the limit. A row marked Removed at next backup goes the next time a backup is made. Type a short note next to Back up now to tell your own backups apart."
},
{
"title": "In a subgraph",
"text": "A WP node's panel isn't on the subgraph node\nPanels with no socket (Context, Toggles, Label Grid, Debug …) stay inside. Show it with the eye beside its name in the subgraph's Parameters panel (Hidden / nested parameters), the eye in the toolbar over the selected node inside the subgraph, or right-click that node → Show on subgraph node. That puts a copy of its panel on every subgraph node made from that subgraph. Changes on either side reach the other, the node inside is still what runs, and the subgraph node remembers it when you save. The sliders button on the copy's header (Choose what shows) picks which parts show there, such as only Label Grid's Font and Title, a few toggles or some Context modules; the node inside keeps the whole panel. Its Move up / Move down items order the WP panels on the subgraph node (they sit below ComfyUI's own promoted widgets). The × (or any of the eyes, or Hide from subgraph node) takes the copy off again.\nThe Assembler's prompt box is empty on the subgraph node\nDrag its template or negative_template dot to the subgraph's input. The subgraph node then shows the same editor, with the $ list of the variables upstream of the Assembler inside."
},
{
"title": "Still stuck?",
"text": "Every warning is explained on Warning & conflict types. Problems with the AI assistant have their own page: Troubleshooting."
},
{
"title": "Related",
"text": "WP Debug Warning & conflict types Test Runner"
}
]
},
{
"id": "ai-assistant",
"title": "AI assistant",
"group": "ai",
"blurb": "Optional and off by default: what the assistant does and where to start.",
"keywords": [
"ai",
"llm",
"model",
"ollama",
"lm studio",
"llama.cpp",
"claude",
"openai",
"draft",
"assistant",
"uncensored"
],
"sections": [
{
"title": "What it does",
"text": "The assistant uses a model you bring: one running on your own PC (Ollama, LM Studio, llama.cpp or any OpenAI-compatible server) or the Claude or OpenAI API with your own key. Wildcard Pipeline ships no model and sends nothing anywhere until you turn the assistant on.\nOnce it's on, you can:\n- Draft in the editors. Press AI in any editor to write new wildcard options, rules, fixed values, combines or templates, or to rewrite, retag and re-weight what's there.\n- Chat. Ask about your library or how something works, or ask for a change. Every change comes back as a card you apply or dismiss.\n- Build a whole pack from one description: the wildcards, rules, template and a bundle, tested on the engine before you install them.\n- Organize your library: descriptions, tags, categories and ratings.\n- Ask about a Test Runner run: why an option never comes up, or how to fix a warning.\n- Look things up on the web or on Danbooru, if you set that up. It asks before each search.\nEvery request includes the item's name, description and tags, so a good description points the model the right way."
},
{
"title": "Start here",
"text": "- Set up a model: pick a provider and a model, then press Test connection.\n- Open a wildcard and press AI above its options. Draft with AI shows the rest of the editor tools.\n- Press AI in the top bar to open the chat.\n- When you're ready for more, try the Pack builder."
},
{
"title": "You stay in control",
"text": "- Nothing is saved until you press Apply, Add or Save. Most changes have Undo.\n- It can't delete anything unless you allow it in Settings, and every delete asks you first.\n- It only goes online through web search, and asks before each search.\nDetails are on Privacy and safety."
},
{
"title": "Which model?",
"text": "Any model can draft options. Rules, chat and the pack builder need a model that follows instructions well. A 9B model on a 16 GB card can run the chat. Claude and OpenAI don't write explicit content, so for NSFW packs run an uncensored model yourself. Set up a model lists suggestions, and Troubleshooting helps when a small model struggles."
},
{
"title": "Works with",
"text": "Set up a model Draft with AI Chat Pack builder Test Runner"
}
]
},
{
"id": "ai-setup",
"title": "Set up a model",
"group": "ai",
"blurb": "Turn the assistant on, connect a server or API key, and pick a model.",
"keywords": [
"ai",
"provider",
"ollama",
"lm studio",
"llama.cpp",
"claude",
"openai",
"openrouter",
"api key",
"model",
"context",
"thinking",
"tool format",
"embedding",
"test connection",
"modelfile",
"uncensored",
"vram"
],
"sections": [
{
"title": "Connect in four steps",
"text": "- Open Settings → AI assistant and switch on Enable.\n- Pick a Provider: Ollama, LM Studio, llama.cpp, Claude, OpenAI, or Other for any OpenAI-compatible server. The server address is filled in for you.\n- For Claude or OpenAI, paste your API key. A local server doesn't need one.\n- Pick a Model from the list your server reports, then press Test connection.\nTest connection lists the models, asks the chosen one for a tiny answer and tells you whether it worked and how long it took. It also checks that the model has enough room (context) for the chat, and warns when it doesn't.\nFigure: Settings › AI assistant: the switch that turns it on, the provider buttons, and the server address, model and API key fields. Screenshot of the manager's Settings › AI assistant page. Turn on the AI assistant is off ('Off by default, and nothing is sent anywhere until you turn it on'). Under Provider are buttons Claude, OpenAI and OpenRouter (CLOUD) and Ollama (selected), LM Studio, llama.cpp and Other (OpenAI-compatible) (LOCAL). Below are Server address (http://127.0.0.1:11434), Model (with a refresh button), API key ('Not needed for Ollama') and Test connection.\nLoad local models with at least 16k of context; that is the minimum. 32k or more is recommended for chats that take several steps, which often reach 20k to 26k on a 27B model. In LM Studio that's the Context Length in the model's load settings; load the model again after you change it."
},
{
"title": "The basic settings",
"text": "Enable\nTurns the assistant on. While it is off, the AI buttons are hidden and nothing is sent anywhere.\nProvider\nEach provider remembers its own address, model and key, so you can switch back and forth. A key icon means a key is saved. The settings that depend on the model are kept for each model too (see below).\nServer address\nChange it if your server runs on another port or machine.\nAPI key\nStored on the ComfyUI side and never shown again; Remove deletes it. A key is only sent to the address it was saved for.\nModel\nPicked from your server's list (refresh after downloading a new one), or typed in.\nUnload after\nFor local servers: how long an idle model stays loaded, so ComfyUI gets the GPU memory back. From right after each answer up to 1 hour, or Custom.\nAbout my library\nA short note (up to 600 characters) every chat, Draft with AI and Pack builder request reads. Suggest drafts one from your library. See the Chat page.\nThinking\nHow much a thinking model reasons before it answers. The list shows what the chosen model takes: Off and On for a model with an on/off switch, or its own levels (Low to Max) for a model with levels, and a line under it says which. The same picker sits in the chat header. For a small or medium local model, As the model does means Off, and a choice you pick holds for the whole answer (switching between steps made LM Studio re-read the whole prompt).\nEach setting shows its Recommended value under it, and lists mark the recommended choice. Reset to recommended puts every AI setting back after showing what will change; your provider, keys, addresses and chosen models stay as they are. Context is the one exception: Reset raises a window under 16k to 32k and leaves a bigger one alone.\nKept for each model. Thinking, Model size, Most context to use, Tool format, Sampling, Choose the tool first and Short tool list are saved for the provider and model you set them on, and Unload after for the provider. Switch from Claude at Medium thinking to a Qwen in LM Studio and you get the Qwen's own settings back, not Claude's. A model you haven't used yet keeps the provider's values but starts Thinking and Model size at automatic, since another model's level or size may not fit it. Permissions, web search, the About my library note and the chat display options are the same for every provider.\nAdvanced settings\nModel size\nHow much the assistant asks of the model. Small models (under 20B) get shorter instructions, smaller tool results and the extra help described on Troubleshooting. Automatic reads the size from the model's name; set it when the name doesn't say.\nMost context to use\nTokens of context the assistant uses (16384 by default; presets 4k to 128k, or Custom). 16k is the minimum and 32k is recommended for chats that take several steps. Ollama gets exactly this much, because its own default is far smaller and silently cuts the instructions off. Lower it if the model no longer fits on your GPU. A new chat also checks how much context the model really has and starts leaner when it is tight.\nTool format\nHow the chat calls its tools. Native tool calls are what most models are trained for; JSON steps work with any model. Automatic uses JSON steps for small and medium local models (a 9B on LM Studio did twice as well that way) and for any server that refuses or garbles native calls. A chat keeps the format it started with.\nChoose the tool first\nOff by default. A small or medium model names the tool it will call, then writes only that tool's arguments. Some models pick better this way; each step is a little slower.\nShort tool list\nOff by default. A small model in JSON steps gets one line per tool in its instructions, and a tool's full description (its arguments and an example) the first time a step offers it. About 2,000 fewer tokens per step, which helps when the context is tight. Applies to new chats.\nDebug log\nOff by default. Records every AI request you make in this browser: each model call with the prompt as sent, the reply, thinking, tool calls and tokens. A Debug button in the top bar lists them with Copy debug, a readable copy to paste into a bug report. Images are left out (only their size is noted) and keys are hidden. Copies are also saved in the ai-debug folder of the Wildcard Pipeline data folder; the last 300 are kept.\nSampling\nOptional top K, top P, min P, repeat penalty and presence penalty, sent with every request to a local or OpenAI-compatible server. Empty uses the server's own setting for the model (LM Studio's, Ollama's). The assistant still sets the temperature itself, low for tool steps. Claude and OpenAI's own API get none. For Qwen 3.5 and later, top K 20, top P 0.8, min P 0 and repeat penalty 1.0 follow the model card.\nEmbedding model\nUnder Search. Turns on meaning search, so “warm colors” finds a wildcard of amber and crimson. Pick a small embedding model such as nomic-embed-text or embeddinggemma (Ollama) or text-embedding-3-small (OpenAI). Off by default; word search already covers most questions. Claude has no embeddings.\nEmbedding server\nLeave empty to use the server above. A llama.cpp server only serves embeddings when started with --embeddings, so run a second one and put its address here.\nLibrary index\nHow many items the chat's search index holds. It lives next to your library database (wildcard-pipeline.ai-index.db) and keeps itself up to date; Rebuild builds it again. When the embedding server stops answering (LM Studio closed, say), it says meaning search is paused and why, prints that once in the ComfyUI console, and tries again on its own after 30 seconds, then waiting longer each time up to 10 minutes. Word search keeps working meanwhile; Rebuild or a working Test connection tries at once.\nKeys from the environment work too: ANTHROPIC_API_KEY and OPENAI_API_KEY only go to Claude's or OpenAI's own address; WP_AI_API_KEY goes to the provider's usual address, or to the one in WP_AI_API_KEY_HOST. Change the server address and a saved key stays saved but isn't sent until you paste it again or change the address back.\nTest connection also shows how many tokens every new chat starts with (instructions, tool list and your library summary), and for small models it tries one tool step. If the server still says a request is too long, the chat tries once more with less and otherwise tells you how much context to load the model with.\nOpenAI is reached through its Responses API, which its newest models need for tool calls; their reasoning is carried between steps in encrypted form.\nThinking per model. The assistant asks the server what the model takes: LM Studio and Ollama list each model's options, OpenRouter and Claude list each model's levels, and OpenAI models are known by name. A model with only on and off (Qwen 3 under LM Studio, most Ollama models) shows Off and On; a level you picked for another model counts as On. A model that must think (the newest Claude and GPT models) has no Off, so Off asks for its lowest level. A level a model doesn't have becomes the nearest one it has. If the server refuses a choice, the assistant asks again without it and remembers that for the model. If thinking uses up a whole reply, it asks again with thinking off and keeps it off for that model until you change Thinking.\nDraft with AI panels. The panels in the editors (Add, Remove, Rewrite, Retag and Weights on a wildcard, and the rule, text, description and organize panels) write short, checked answers, so with Thinking on Auto a local model answers them without thinking. Pick On or a level to have it think there too. A model that keeps thinking for about 4,000 tokens without starting its answer is stopped and asked again without thinking, and the working line counts its thinking as it goes.\nLM Studio turns a model's thinking only on or off: it never hands a level to the model's chat template. A template with its own levels (Qwen 3.8: low, medium, xhigh) then always runs at its default, so On there means the template's default effort. llama.cpp does hand the level to the template.\nTo get the levels in LM Studio, give the model a small model.yaml that declares them. Make a folder such as ~/.lmstudio/hub/models/you/qwen3.8-27b-effort/ with a model.yaml whose base names the GGUF you already have, and add two customFields entries, each with the effect setJinjaVariable: a boolean on enable_thinking (without it, thinking can't be turned off) and a select on reasoning_effort with the template's levels (low, medium, xhigh). It shows up as a second model that uses the same file, so nothing is stored twice. LM Studio then lists its levels and the Thinking picker offers them. A level only counts while thinking is on: in LM Studio's own chat, turn its Thinking switch on as well. The new model doesn't take the old one's load settings, so put its context length, KV cache and thinking budget in the yaml too (a config section), or a full-precision cache can make it too slow to answer."
},
{
"title": "Which model",
"text": "Settings › AI assistant › Suggested models lists picks for the provider you chose. Only the ones marked Tested were tried with Wildcard Pipeline; the rest come from benchmarks and community reports.\n- Claude or OpenAI are good for organizing, rules and safe packs (GPT-6 Luna is plenty; GPT-6.1 Sol costs more and isn't needed). They don't allow explicit writing.\n- OpenRouter reaches many more models, most paid with credits, including unmoderated ones.\n- Your own server: the list names the model only, in two groups. Which build you download is up to you: who uploaded it, the quant, and whether it is uncensored (\"heretic\" and \"abliterated\" builds have refusals removed). None is proven to know Danbooru tags, so check a draft before relying on it.\nFor the chat and packs. Tested with Wildcard Pipeline. Any of these can be your only model.\n- Qwen3.6 35B A3B (16 GB GPU): The one we use most, for chat, packs and writing. Mixture of experts, so it stays fast with part of it in system RAM: about 50 tok/s on a 16 GB card with 32 GB RAM.\n- Qwen3.8 27B (16-24 GB GPU): The best at following instructions. A 3-bit file fits a 16 GB card, 24 GB is comfortable. Slower than the 35B A3B.\n- Qwen3.5 9B (8 GB GPU): Fits 8 to 12 GB, or beside ComfyUI. Fine for single items, tags and negatives; it misses parts of bigger asks with several items.\nFor writing options. Not tested here. They can't call tools, so keep them for drafting.\n- Cydonia 24B (16 GB GPU): Suggested because community reports rate it highly for explicit creative writing that still follows instructions (a Mistral Small 24B fine-tune).\n- Gemma 4 31B (24 GB GPU): Suggested because the UGI benchmark puts its uncensored builds first for world knowledge among writers.\nSearch the name in Ollama, LM Studio or Hugging Face and pick a build that fits your GPU.\nWith two models, keep a writer selected for drafting and switch to a chat model when you organize, chat or build a whole pack.\nIn Ollama, a Qwen 3.5 / 3.6 model pulled straight from Hugging Face (ollama pull hf.co/…) misses Ollama's tool-call reader, so tool calls can come back as plain text. Fix: make a file named Modelfile holding\nModelfile:\nFROM hf.co/<repo>:<quant>\nRENDERER qwen3.5\nPARSER qwen3.5\nrun ollama create my-model -f Modelfile and pick my-model. For Gemma 4 use RENDERER gemma4-large (or gemma4-small below 12B) and PARSER gemma4. Ollama's own qwen3.5 and gemma4 models already have it."
},
{
"title": "Works with",
"text": "AI assistant Troubleshooting Privacy and safety"
}
]
},
{
"id": "ai-drafts",
"title": "Draft with AI",
"group": "ai",
"blurb": "The AI button in each editor: write or change options, rules, values and templates.",
"keywords": [
"ai",
"draft",
"add",
"remove",
"rewrite",
"retag",
"weights",
"describe",
"refine",
"rules",
"template"
],
"sections": [
{
"title": "Add options to a wildcard",
"text": "- Open a wildcard and press AI above the options. Stay on the Add tab.\n- Say what you want and how many, for example 8 more outfits for a rainy city at night. Press Draft (Ctrl+Enter). A number in the request sets How many (6 more, bolder is 6; one option each is not a total); without one the box decides (10 to start).\n- Untick anything you don't want and press Add. The rest go in like pasted options, with new tags marked.\nThe model sees the wildcard's name, options and the tags each option has, so it writes in the same style and tags new options the way the list is tagged. Draft again asks for another set, and Stop cancels a slow one.\nTags and negatives have to earn their place. A new tag the model put on every new option (desert on every desert weather) is left off, unless you asked for that tag, and so are the wildcard's own library tags and tags that only repeat its name. Negatives stay only when you ask for them or the wildcard's options already have them, and never name another option. What was left off is listed under the result.\nWhen the model writes more than you asked for, the extra is cut evenly across the tags you asked for, so \"10 tagged modest, revealing or sheer\" keeps some of each. Ask for tags and get none back, and the model is asked once more; if it still tags nothing, a warning says so.\nOptions can use the syntax you'd type: {red|blue} picks one each run, and @{Colors:warm} draws from another wildcard. Ask for \"a scarf in a warm colour from my Colors wildcard\" and the assistant writes the reference for you.\nAsk for tag groups and it makes them too. \"Outfits and the shoes they go with\" makes an accepts group (SHOES: loafers, sneakers, boots) and tags each outfit with the shoes that suit it. A constraint on that group then makes your Shoes wildcard agree with the outfit.\nDrafts can't add broken options. Repeats, options that read a $variable, and references to a wildcard or tag you don't have are left out and listed under the result."
},
{
"title": "Change the options you have",
"text": "The other tabs work on every option, or only on the ones you ticked in Bulk edit. You get a before/after list with a tick box per change; press Apply (or Remove) for the ones you keep.\nRemove\nFinds options matching what you describe ('drop anything modern') and says why. A removal whose own reason says the option fits is left out, and an answer that removes every option is asked once more (and warns if it still does). Options other modules point at stay; delete those by hand.\nRewrite\nChanges the wording: tags to sentences, another style, shorter, typos fixed. Ask for Danbooru tags with a tag list installed and each rewrite names the tags the list doesn't have.\nRetag\nSets each option's tags ('tag each by the seasons it suits'). The new tags replace the old ones, unless the request adds ('add an intensity tag: mild or strong'), which keeps them.\nWeights\nMakes options rarer or more common. 1 is normal, 0.5 half as likely, 2 twice, 0 never. 'Half as likely' halves each option's current weight (0.1 becomes 0.05), and a change that goes the other way than you asked is left out.\nUp to 300 options go in one request; select some first for a longer list."
},
{
"title": "Rules, values and templates",
"text": "The other editors have an AI button too. Describe what you want in your own words:\nDerivation\nFor example: when $class is mage, set $title to Archmage and add robes to $outfit. It uses the real variable and option names from your library and warns about anything nothing sets, or that nothing reads. New rules go to the end. To change a rule you have ('rogues should be called Shadowblade'), it edits that rule: the card says Changes rule N, marks the new lines, and keeps the rule's other branches and its ELSE.\nConstraint\nPick the source and target wildcards first, then press AI on the Rule matrix. For example: mages never carry swords, warriors rarely get staves. Switch to Preview to see each option's chances with the ticked rules.\nFixed values\nA style preset or quality tags, each with the negative words it should add. Each value is its own row (variable, value, negative) marked New or Changed, with the old text struck through, and a tick box: only the ticked rows change.\nCombine\nJoins variables into one phrase. Set the output variable first. You see the template and its negative apart, the variables it reads (new ones dashed), and a few lines of what it prints with your own options.\nTemplate\nThe Assembler prompt from your variables, plus a standing negative with $negatives. Name a model (Pony, Flux) and it writes in that model's style. The prompt and the negative each have a tick box, so you can take one and keep the other.\nYou see the result first, with a warning for any variable nothing sets. Add, Apply or Use puts it in the editor.\nRules need a model that follows instructions well. If the answer comes back unusable, switch to a builder model."
},
{
"title": "Describe and tag",
"text": "Every editor's Identity card has a Describe button. The model reads what the item holds and writes a short description plus tags to find it by, reusing your tags (new ones are dashed). Type a note first to steer it. Apply replaces the description and adds the tags."
},
{
"title": "Close, but not quite? Refine it",
"text": "Type a follow-up in the request box (\"darker\", \"shorter names\", \"keep the first three\") and press Refine. The model gets the conversation so far, including what you unticked, and sends a revised answer.\n- Try again asks the same request afresh for another take. The new try goes on that request's own line as try 2 of 2, so tries never mix with refinements.\n- Your requests are listed above the answer, one line per step. A line's arrows flip between its tries, and between different refines of the same answer (refine 1 of 2). The lines under it follow the branch you flip to.\n- Click an earlier line to see its answer. Refine or Try again from there starts a new branch; nothing is overwritten.\n- Start over puts your first request back in the box.\n- Adding or applying ends the conversation; the next request starts fresh."
},
{
"title": "Images and web search",
"text": "With a model that sees images, paste, drop or pick up to 4 reference images under the request box (\"options from this photo\"). They go with each request of that panel until you remove them.\nWith web search set up, the wildcard and text panels may suggest a search for facts the model doesn't know well (a game's characters, an artist's style). Nothing is searched until you press Search. Then pick which results to use: Use what fits and refine, Use N ticked, or None of these.\nHow it works\n- Changes land in the editor like your own edits, so you can still tweak or undo them.\n- Retag on every option that comes back empty is asked once more, unless the model says why nothing changes; that reason shows in the panel. A rewrite or retag of \"every\" or \"each\" option that leaves some out is asked once more for those. A rewrite that would make two options the same is left out.\n- Derivation option names are fixed to the exact spelling, since the test is case-sensitive. Rules split into \"when A\", \"when B\", \"otherwise\" become one rule with IF / ELSE IF / ELSE, and an ELSE that would overwrite an earlier rule warns. \"Add 'dry ground' to $season\" adds instead of replacing, and an action on $pose.neg becomes an add-to-negative on $pose.\n- Fixed values: a value that repeats another word for word is left out, repeated negative words are cut, and a negative word the value itself prints is dropped. A value that only adds negatives is kept.\n- Combines and templates: {name} for a variable becomes $name; a combine never reads its own variable; loop and model variables ($iteration, $model_family…) are left out of the text unless you name them, since they are for rules. Words like bad hands move to the negative, brackets that don't close and repeated pieces go, a word after a variable whose options already end in it goes (\"$hair_color hair\"), and a \"short\" negative is kept short. A Pony template starts with the score tags; a Flux or \"natural sentences\" template that comes back as a tag list, or one that is only variables when you asked for words around them, is asked once more.\n- Text in another writing system than your library (a stray 曝光 in an English list) is left out.\n- Constraint rules between two tags go in the matrix, between two options become exceptions, and a tag paired with one option becomes an exception per option. A rule for a pair that already has one is marked “replaces”.\n- Fixed values with a name you already have are rewritten in place. Values can use {a|b} but not $variables. A template never gets {a|b}, because the Assembler has no seed and would print it as is.\n- A refine works on the same rows the first request saw, even if you edit the list meanwhile.\n- The image strip is hidden for a model that can't see images."
},
{
"title": "Works with",
"text": "Wildcard Derivation Constraint Chat"
}
]
},
{
"id": "ai-chat",
"title": "Chat",
"group": "ai",
"blurb": "Ask about your library or the docs, or ask for a change you apply as a card.",
"keywords": [
"ai",
"chat",
"card",
"apply",
"undo",
"mention",
"explain",
"images",
"saved chats",
"context",
"thinking"
],
"sections": [
{
"title": "Ask anything",
"text": "Press AI in the top bar to open the chat. Some things to try:\n- \"Which wildcards set $outfit?\"\n- \"What's missing for a fantasy pack?\"\n- \"Make my Weather wildcard gloomier.\"\n- \"Why does wool coat never come up?\"\n- \"Where do I set the context size?\"\nThe assistant searches and reads your items and these docs by itself; the lines above each reply say what it looked at. It also sees the page you're on: in an editor it reads the item as it is now, unsaved changes included, so \"this wildcard\" means what you see.\nA new chat offers a few starter questions that fit the page you're on. What can you do? lists the rest."
},
{
"title": "Point at things with @",
"text": "Type @ and a few letters to pick a wildcard, module or bundle, so \"add more options to @Hairstyle\" can't land on a look-alike. Type $ for a variable. With tag and model completion on, a few letters also offer Danbooru tags, LoRAs and embeddings. This works in the chat box, every Draft with AI box and the Pack builder.\nA picked item shows as a chip in the box while you type, so you can see it will go as that exact item. A name you type yourself without picking it stays plain text and goes as words. Pick a wildcard and ask for a \"reference\" or a \"nested\" pick of it, and the assistant puts @{Name} in the new options; if it leaves that out, it is told and fixes it before it answers."
},
{
"title": "When it asks you first",
"text": "If a request could go several ways (a style, how many, which item), the assistant asks up to four short questions with answers to click. Pick one, type your own under Other…, or press Decide for me. With several questions, press Send answers. It remembers your answers for the rest of the chat."
},
{
"title": "Cards: how changes reach your library",
"text": "The assistant only ever proposes. Every change is a card, and nothing happens until you press Apply.\nApply\nMakes the change. A change to the item open in your editor goes into the editor; keep it with Save there. Other changes save to the library.\nUndo\nPuts back what the card replaced, or deletes what it created.\nDismiss\nTells the assistant you said no.\nOpen in editor\nOn a new item: fills the editor with it so you can change things before you save.\nApply all\nOn a card that needs others first (a constraint between two new wildcards): applies them in order.\nMagnifier\nOpens the whole card in a larger window.\nA card draws the change the way its editor shows the item. A wildcard change lists the options it touches: + for a new one, struck through for a removed one, and old → new for a changed text, share of the draw, tag, negative or fallback. The options it leaves alone fold into \"N unchanged\"; the magnifier lists them too. A change to an item's description, tags, category or rating shows each as old → new, with the category's own chip. Category, tag and bulk cards show the chips you see in the library. A derivation change shows its IF / ELIF / ELSE blocks with the changed branches marked (a dropped ELSE struck through, an added action with +), a constraint change marks the changed cells of its grid and its changed exceptions, and a bundle change lists its items in order with + added, struck through removed, ↕ moved (from its old place), on → off and use settings old → new. A fixed values change shows its Variable / Value / Negative table with the changed cells old → new, a template or combine change marks the words it adds and drops along with the variables it now reads or no longer reads, and a delete card lists what goes with its category chip and rating, how many items a dropped category or tag leaves, and that Undo makes them again. Parts a change leaves alone fold into \"N unchanged\".\nThree or more cards of one kind come as one group card with a row per item. Untick the rows you don't want and press Apply. Undo all takes them all back.\nEvery card is checked before you see it, and warns about things like a wildcard that mixes two subjects, repeated options, or a constraint that can leave a wildcard empty. A warning with Fix it (for example Merge the repeats or Even out the weights) fixes the card in place; Fix all runs every one. Ask to fix asks the assistant for a corrected card."
},
{
"title": "What it can change",
"text": "- Create wildcards (with tag groups, negatives, an empty option, a fallback and references), or change one: add, remove, rewrite, retag, re-weight and reorder options, set negatives, pick the fallback, rename its variable.\n- Create or change fixed values, combines and prompt templates.\n- Create derivations, and add, replace, move or remove rules, including AND / OR tests.\n- Create constraints, or change their rules, their reach, or the wildcard on either side.\n- Create bundles in the right running order with per-use settings, and group a pack into sub-bundles.\n- Change descriptions, tags, categories and ratings, find duplicates, and tell you what reads a variable before you rename it.\n- Make or rename categories and rename library tags everywhere. Each is a card with Undo.\n- Hand a whole new pack to the Pack builder with Open in Pack builder. It fills in the description and the settings the chat recommends, then waits for you to add images and press Plan it.\nIt only changes items you named, have open, or that its last answer showed you. Asked for something new, it makes a new item instead of rewriting yours.\nAdding a tag to options keeps the tags they have. A card that takes a tag off every option while a constraint still has rules for it says so, since those rules would stop working. Asked to draft options in a wildcard's panel, it passes on how many you asked for."
},
{
"title": "It can show you around and test its work",
"text": "- Show you around. It opens an editor, a docs page or a Settings section, and points at the part you asked about with an outline and a caption. Esc or a click closes it. On a list it can select rows for you.\n- Use the page's AI tools. It can open an item and ask its Describe or Draft with AI panel for you, open Organize with AI on the rows it means, or set up and run the Test Runner. You review the result there as usual.\n- Run a stack on the engine. Before it answers, it can run modules from your library and report what moves (\"wool coat 40% → 0%\"). Running changes nothing in your library. The run shows as a card with Open in Test Runner."
},
{
"title": "Images",
"text": "With a model that sees images (Gemma 3, Qwen2.5-VL, Llama 3.2 Vision, Claude, GPT-4o and others), attach up to 4 images: the image button, paste, or drop them on the chat. Try Make wildcards from this image: the assistant describes the subject, clothing, pose, setting, lighting, style and camera, and proposes wildcards from them.\nImages are shrunk to at most 1568 pixels on the long side and saved again as JPEG, which drops camera details and location. The model sees the newest 8 images of a chat. If your server says the model can't see images, the image button is off."
},
{
"title": "Tell it about your library",
"text": "Settings › AI assistant › About my library holds a short note, up to 600 characters, that the assistant reads before every request: the chat, every Draft with AI box and the Pack builder. Use it for what it can't guess from one request:\n- \"My wildcards use booru tags, all lowercase.\"\n- \"Variables are snake_case, like $hair_colour.\"\n- \"I write for Illustrious. Never add artist names.\"\nShort, concrete lines work best, above all on small local models. The app's own rules come first, so a note can't turn off a safety check or a permission.\nSuggest asks the model for a first draft from your library summary (names, variables, tags, how you write options) and puts it in the box. Nothing is saved until you press Save, so edit it first; Put back returns to the saved note. A chat keeps the note it started with, so start a new chat to use a new one."
},
{
"title": "Writing in another language",
"text": "Write in any language the model knows. The model always reads your own words, but some of the assistant's checks read English: whether a message is a question or a change, how many options it asks for, and whether the cards got the negatives, fallback or rules you asked for. So a message that isn't in English is first read into English by one short model call, and those checks read that. It takes a few seconds on a local model and happens only for messages that aren't in English. The chat, Draft with AI and the Pack builder all do this.\nSettings › AI assistant › Chat › Language you write in sets it: Automatic guesses per message, English never makes the call, and Another language makes it for every message (for a language the guess misses)."
},
{
"title": "Ask about a test run or the docs",
"text": "After a run in the Test Runner, the bar above the results offers Explain this run, Fix the warnings and Why do I never get…. The assistant runs the scenario again itself to check its answer and offers fixes as cards.\nIt answers questions about Wildcard Pipeline from these docs and names the page it used. It doesn't guess: if the docs don't cover something, it says so. On any docs page, Ask about this page (top right, it stays there as you scroll) puts a question naming the page in the chat box, so it still asks about that page if you move on before sending. In the help window over the canvas (the ? on a selected node) it opens the manager in a new tab on the same page, with the chat open and the question in its box.\nSelect text anywhere in the manager and a small bar appears: Explain sends it to the chat, Ask quotes it in the message box. Ctrl+Shift+X explains the selection directly. Turn the bar off with Settings › AI assistant › Show Explain on selected text."
},
{
"title": "Using the chat",
"text": "Stop\nEnds the answer.\nTry again\nAsks the same message again; the new answer replaces the old one. It waits until you undo changes you applied from that answer.\nEdit\nThe pencil on your last message puts it back in the box to change and send again (Esc cancels).\nCopy\nCopies an answer as Markdown.\nTime\nBeside Copy, how long the answer took. Time spent waiting on you (a question, a web search to allow) is left out.\nChats\nYour saved chats, newest first, with search. Rename, pin or delete each. ComfyUI keeps the newest 100 plus every pinned one.\nNew chat\nStarts over with an empty box.\nModel menu\nThe model name in the header: switch models from the next message on. If the new model can't carry on, start a new chat.\nWidth\nDrag the chat's left edge (or focus it and use the arrow keys). Double-click to reset.\nContext bar\nHow much of the model's memory the chat uses. Past three quarters, older messages are summarised into a short note; Compact does it now.\nWhile it works, a line shows what the model is doing (reading, thinking, writing) and for how long; the same line shows in the top bar on every page. What it writes between steps folds into a Working note, and a thinking model's reasoning folds into Thinking. To see only replies and cards, turn off Show thinking, Show working notes or Show what it did under Settings › AI assistant › Chat. The reply arrives whole when it is done; turn on Type out replies there to watch it appear word by word instead (and the Thinking box fill as the model thinks). When nothing has come back for ten seconds the line says so: the model may be busy with another request (LM Studio answers one at a time unless its model allows more in parallel), still loading, or reading a long prompt. The server can't tell which.\nA thanks or a 👍 gets an emoji reaction straight away, without asking the model. If you delete an item the answer mentioned, its chip is struck through.\nChat works best with a model that follows instructions well. A small model may need a second try or a more specific request; see Troubleshooting."
},
{
"title": "Good to know",
"text": "How it works\nReplies. Replies use Markdown: headings, lists, tables, quotes and code blocks with a copy button. Prompt syntax is coloured like in the editors: a variable in its own colour with .0 / .SHOES in amber and .neg in red, and a variable the run sets ($iteration, $model_family and the other WP Model Info ones) underlined dotted, since rules can read it but nothing writes it. Every item it mentions is a chip you can click, also when the model wrote the name in backticks, and a list of three or more items shows as an item list with each item's category. Lines starting \"Tip:\" or \"Warning:\" (or a GitHub-style [!NOTE] alert) stand out. Links show as text and are never opened. Code blocks over 20 lines are folded.\nFinding the right item. Before the model answers, the chat searches your library for what your message is about and hands it the clear matches, so \"make the outfit wildcard warmer\" starts from the right item. The search reads option texts, values and rules, not only names, and broad subjects widen (\"characters\" also finds hair and eye wildcards). It runs on a full-text index, so it stays quick with thousands of items. A how-to question also gets pointers to the matching docs sections.\nWhich items it may change. Items you named (\"add rain to my Weather\", a chip), the one you have open, ones its last answer showed you as a chip, or ones it read in full for your request. When several share a name, the one you named or it already read is meant. Options a constraint points at are never removed this way. If you edit an item after asking, its card says it's out of date. A card made before the item changed is blocked, so it can't undo the newer change.\nCards. A new item's card already carries its future id, so cards that point at it are linked from the start. Several changes to one item come as one Change card with one Apply and one Undo. Deletes, links and library-wide changes stay single cards. A card shows its first lines with \"+N more\"; the magnifier shows all of it. Grey notes only tell you something and have no button.\nKeeping it honest. The assistant tests module changes before it answers, so the reply says what the change does. An answer that claims something was made or tested with no call behind it, puts part of your request off until later, comes back empty or garbled, or names a setting or variable that nothing mentions is sent back once to fix it. A tool call written as text (\"I call: read …\") still runs, and the leftovers of a JSON step (\"calls: []\") never show in the reply. It doesn't ask \"should I go ahead?\", since the card is already the asking.\nReply against the cards. The reply is also checked against what its cards really do, and a grey note says where they differ: a weight or factor no card sets, a bundle child it says moved when the card moves nothing, or \"created successfully\" before you pressed Apply. Notes about missing cards skip delete and tidy answers. Tool names in a reply are said in plain words.\nDeleting. A delete card only names what you asked to delete. If the item you named doesn't exist, the assistant says so instead of offering to delete something similar, and \"delete the Weather category\" never deletes a Weather wildcard. \"Delete everything tagged wip\" still covers items you didn't name one by one. With deleting off, the reply says where to turn it on.\nAsking for less. \"Switch off\" in a bundle switches the child off and never removes it, and \"move Hair colour after Race\" moves it. A rewritten derivation rule keeps its ELSE unless you ask to drop it. Removing a misspelt tag adds the tag it meant. A new item, or a rename, that takes the name of one you have gets a warning. A test right after a change card runs with the change, plus the items that set what the tested item reads. Explain on selected text only explains.\nLimits. One answer reads about 6 items with a small model, 8 with a medium one and 12 with a large or cloud one before it must answer. A local model gets up to 20 minutes per answer, a cloud model 10. \"Cut off\" means the model hit its output limit; thinking models can spend it all before they answer.\nMemory. The chat keeps every search, read, test run and card, and is told what you did with each card and what changed in your library, so \"use the second one\" or \"undo that\" works. Each request starts with exactly what the last one sent, so the server can reuse what it already read: follow-ups are faster and, on a paid provider, cheaper. In a long chat, old tool results shrink and the oldest turns may be left out. The summary is written by your own model and kept with the chat. \"~\" on the context bar means an estimate, and \"about\" means a cloud model's usual size.\nQuestions. Number keys pick in the focused question, and some questions take several answers. It asks at most twice per message, then decides and says what it assumed."
},
{
"title": "Works with",
"text": "AI assistant Organize and import Web search Test Runner"
}
]
},
{
"id": "ai-pack-builder",
"title": "Pack builder",
"group": "ai",
"blurb": "Describe a pack; the assistant plans, builds, tests and installs it as one bundle.",
"keywords": [
"ai",
"pack",
"pack builder",
"plan",
"slot",
"axis",
"relation",
"pack graph",
"story",
"sweep",
"extend",
"sub-bundle"
],
"sections": [
{
"title": "What you get",
"text": "Pack builder in the sidebar (shown once the assistant is on) makes a complete pack from one description: wildcards, fixed values, rules, combines, constraints and a prompt template, filed into one bundle with a sub-bundle per section. A pack holds up to 60 items."
},
{
"title": "The five steps",
"text": "- Describe what the pack makes and the rules between its parts (\"elves never use guns, mages get robes\"). Add up to 4 reference images if your model sees them, and refer to them by number (\"the skirt in image 2\").\n- Settings. Next: pack settings shows the options below. They're kept in this browser, so next time Plan it is the only click. Above them, Recommended for this pack lists the settings that fit your request, each with why (Flux reads natural phrases and ignores negatives; two image models want Per model family). Use recommended takes them; nothing changes until you press it.\n- Plan. The assistant lists every item it will build, grouped by section, and asks what it can't guess. Answer, edit items, or ask for changes until the plan looks right.\n- Build & check. Each item is built, then the pack runs 200 seeds on the real engine. Items with problems are rebuilt, up to twice.\n- Install adds everything, tagged with the pack's name, and tells you how to wire it: the bundle in a WP Context, the template in a WP Prompt Assembler, an Injector row per input. Undo removes all of it.\nBuilding takes one request per item, so a local model needs a few minutes. Use a builder model. If an item fails, you can retry it, rebuild it or leave it out."
},
{
"title": "Pack settings",
"text": "Planner\nPack graph (the default): the model plans the picks and how they relate, and the code writes every rule and the template from that. Items one by one: the older planner, where the model writes each rule itself.\nPlan drafts\nWhen needed (the default) drafts the plan once, and once more only when the code sees a problem in the first, keeping the better one. Best of three drafts the first plan three times and keeps the one that fits your request best; about three times as long, helps small local models most. One: a single draft.\nYour library\nNew, in its style (the default), Reuse what fits (your existing wildcards and fixed values, marked yours), or Everything new.\nNegatives\nStanding + per option, Template only, or None.\nStructure\nSub-bundle per section, or One bundle.\nWritten as\nTags, Natural phrases, or let the planner decide.\nRating\nSafe or NSFW, or let the planner decide.\nOptions per wildcard\nHow many options each wildcard gets. Auto sizes each one.\nModel\nAny model, or Per model family: quality and negative tags per model (Pony score tags, Illustrious / NoobAI tags), chosen from $model_variant set by WP Model Info.\nThese are preferences for any pack. What the pack is about (its subject, LoRAs, a story) comes from your description."
},
{
"title": "Working on the plan",
"text": "- Questions. Answer by clicking, type your own under Other, or press Decide for me (Decide the rest for all open ones). Then Send answers. Your answers show under You decided and the model's guesses under The assistant assumed; click either to change it.\n- Edit an item. Click it to change it by hand, ask the AI to change just that item, or remove it. Add an item, Add a slot or Add a relation adds your own. What you edit by hand is marked \"yours\" and kept through every update.\n- Change the plan. Type what to change and press Update plan. Each item then says whether it's new or what changed. Plan 2 of 3 flips between versions, and nothing is overwritten.\n- Runs as under the title: Single images, Story over a Loop (a WP Context Loop plays a story beat per frame from $iteration, with the character and outfit held) or Sweep sheet (2 or 3 short wildcards as Loop sweep axes). The planner reads it from your description, and you can change it.\n- Inputs. A subject you give it (\"my OC\") or a LoRA you name becomes an input you wire from a WP Context Injector. A character LoRA gets a rule that swaps it in for the rolled character when set.\n- How it fits together under the title is the planner's own account of which pick decides which, and why each rule is there.\n- Web search. If it's set up, the planner may ask to search first (a franchise's characters, an era's clothing). Search the web shows what was found; then Use what fits or Use N ticked updates the plan with it, and None of these keeps the plan as it is. Skip the search doesn't search."
},
{
"title": "Plan review and fixes",
"text": "The plan is checked as you edit it: a variable nothing sets, a rule on a tag that doesn't exist, two constraints that loop, a story without beats, a LoRA input nothing reads. Each finding has Apply (when there's a ready fix) and Dismiss. Tick findings and press Fix with AI, or Fix all with AI. Review with AI asks the model for a second opinion on the design.\nAfter the build, each problem on an item has Fix with AI (rebuild it with the problem as a note), Explain and Dismiss. The test run also shows each variable's most common values, options that never came up, and a few sample prompts. A story pack plays every frame of its Loop. An item the code wrote from the plan (a constraint, derivation or template on a graph plan) has Change the plan instead of Fix with AI, since a rebuild would write the same thing. A problem marked still after a fix says why under it. Fix all with AI under the test run rebuilds every item with a problem at once, each with its problems as the note (wildcards first, then the rules on them), and tests again.\nThe quick checks under the plan only look at names: an empty list means every rule names slots and values the plan has, not that the rules do what you meant. The test run is what checks them on real prompts."
},
{
"title": "More ways to start",
"text": "- Start from a prompt you like. Paste a prompt and the planner splits it into parts, one wildcard each, to make more like it.\n- Extend with AI in a bundle's editor opens the Pack builder on that bundle: describe what to add (\"a weather wildcard the outfits follow\"). New items are installed where they belong, and Undo puts the bundle back. Add to the bundle waits until at least one new item (not just the template) has built.\n- Make sub-bundles in a bundle's editor groups its children by a name you type, pick or let Suggest with AI choose. The AI only groups children that sit next to each other, so the run order stays the same.\n- Open in Pack builder on a chat card hands a pack the chat suggested to the Pack builder, with the settings the chat recommends. It doesn't plan yet, so you can add reference images and check the settings first.\nThe planner first reads your library's style (tags or sentences, your tags, variables and standing negatives) so the pack matches what you have."
},
{
"title": "Good to know",
"text": "How the pack graph planner works\nWith Pack graph, the model writes a small graph instead of the items: a slot per choice the image makes (one wildcard each, with how many options, whether it prints or only feeds rules, \"pick 2\", hold, a share of \"none\"), each slot's axes (the ways its options differ, like Season: winter, summer), the relations between slots (never gets, only gets, favours, rarely gets, agrees with on an axis, accepts, sets a variable, adds to a variable, draws from) and the template order. The code then writes every constraint, derivation and the template from the graph, so a rule can only name a slot and value that exist.\n- A relation naming something the graph doesn't have is dropped and listed as \"Left out a rule\"; the model is asked once to write it again. Fix with AI on the note asks for just that rule.\n- Each relation names the words it's about (\"sandals\" for \"no sandals in winter\"), so the target wildcard gets options with those words and the test run checks the prompts for them.\n- What the run and settings need is wired by the code, not asked of the model: the story beat derivation from $iteration, sweep axes, holds, and the per-model quality rule. \"Wired by the code\" above the plan lists what it did. Those rules can't be changed with AI; change the run or the setting instead.\n- Settings land on the plan without an AI call: Options per wildcard caps every slot, Style and Rating replace the planner's, One bundle folds the sections. Runs as stays as you set it.\n- The code asks its own questions when the graph has an open fork, marked \"applied by the code\": Story or single and Sweep axes. Their answers apply at once.\n- \"Change with AI\" on an item changes just that slot, value, rule or the template. An image added after the plan is read on its own. Fix all with AI sends each finding about one item as a change to that item.\n- The template reads every printed slot, fixed value and derived variable. Defect tags written in its lead (ugly, bad anatomy) move to the negative, and \"1girl, solo\" is taken out when the request names no person.\n- Things your request names that the plan says nowhere (\"sandals\", \"parka\") become a finding, sent back to the planner once; if there's still no place, a note to add by hand.\n- What your request asks for is held to the plan. \"A hat 30% of the time\" leaves the hat out of 70% of runs, \"the same hero in every image\" holds the hero in a story, and the thing the pack is about (\"Outfits and shoes: …\") is shown in the prompt; the code sets these itself. A rule between two picks it names (\"winter never gets pastel colours\") with no relation between them is a finding for the planner, and a rule keyed on a shared list instead of what was drawn from it is a tip.\n- An option tagged with two axis values (a mountain that is cold and temperate) rolls one of them each run, so two rules never empty the pool together. The test run names rules that left a slot empty in three runs out of ten or more.\n- A rule writing a bare number (\"1.5\" for \"long styles more common\") is a weight on the tagged options, not text. A Quality slot becomes fixed values; a Negative slot becomes the template's negative. The per-model quality rule gets an \"otherwise\" with the pack's own quality words, so $quality is set even without WP Model Info, and only has rows for the model families the pack runs on.\n- An \"adds to\" rule joins its words with a comma (\"scarf, mittens\"), never glued onto the last word.\n- A slot nothing reads is shown in the prompt, and the template never prints loop or model variables ($iteration, $model_family); those only feed rules. Rating tags follow the pack's rating and style (no Pony rating_* tags on a pack that isn't just for Pony), and negatives drop words that are the pack's own options or repeat.\n- A library wildcard is reused only when it fits what the slot is about, and the planner sees only library items that match the request unless you ask for the whole library. A couple pack asks who the two people are.\n- \"Drop the hoods\" in Change with AI keeps hoods out of that slot's options, and a change can't add a rule against what it asked for (\"more scarves\" never adds a rule that keeps scarves out).\nHow building and checking work\n- Each item is built in its own request, wildcards first so rules can use their tags. Every item's id is reserved in the plan, so references point at items before they're built. On a graph plan only wildcards ask the model; everything else is written by the code.\n- Each option must carry one of its slot's axis values. When a value a rule needs is missing, the model is asked only to add options with it.\n- Before each test, words the prompt says twice are removed from the later item, but only when every test run reads the same without them.\n- The test run checks every relation on real runs (\"never gets\" that still happened, a rule that never fired) and says how many held. It also reads the prompt text, so a rule that held on the picks but whose banned words still show up (through another item) is broken, with what the prompt showed. A constraint with no rules, or a rule that prints a second pick of a one-pick slot, is a problem too. An item with a problem (a rule that never applies, a variable nothing sets, a prompt showing what a rule keeps out, like snow in a desert) is rebuilt with the problem as a note, up to twice.\n- An item or review cut off at the output limit is asked once more for a shorter answer.\n- An item that takes longer than 4 minutes is stopped; Skip this item moves on. If the template fails twice, a plain one that reads every pick is written for you.\n- Findings are known by what they're about, not their wording, so a dismissal holds. One the AI hands back unchanged twice is marked as something it couldn't change and isn't sent again unless you tick it. After Fix all, Review with AI adds at most two new findings.\n- Images go with the first plan, and again only when you add or remove one, mention them, or tick Show the images again; each costs about 1,000 tokens a request. What the planner took from them is listed under From your images.\n- A meter under the request shows how much context the last plan request took; every update sends the whole conversation again. A thinking model that spends its whole output reasoning is asked once more with thinking off, and the plan, item and Draft with AI calls stay that way for that model. The plan and each item have a folded Thinking, so you can see whether the model misread the request.\n- Pack builder suggestions use your tag list and LoRA files when their autocomplete is on; tags your list doesn't have get a note, never an error."
},
{
"title": "Works with",
"text": "AI assistant Recommended pack structure Bundles WP Context Loop\nFor adult packs, read Adult (NSFW) packs first."
}
]
},
{
"id": "ai-library",
"title": "Organize and import",
"group": "ai",
"blurb": "Sort the library with AI, tidy an imported pack, and the delete permission.",
"keywords": [
"ai",
"organize",
"category",
"tags",
"rating",
"import",
"delete",
"permissions",
"automatically"
],
"sections": [
{
"title": "Organize the library",
"text": "- Select up to 60 modules or bundles in any library list.\n- Press Organize with AI in the selection bar. Type a note first to steer it if you like (\"one category per subject\").\n- Review the suggestions: a category, tags to add or remove, and a content rating change (Safe or NSFW) where the content calls for it. Each comes with a short reason.\n- Click a chip to leave that change out, or untick an item. Apply saves right away, and the toast that follows has Undo.\nIt reuses your categories and tags where they fit; new ones are dashed and created when you apply. Bundles have no category. Only items that would change are listed.\nIn the chat you can also ask for this directly: \"make a Weather category\", \"rename Clothes to Outfits\", \"rename #warm to #cozy\". Renaming a tag to one you already have merges the two."
},
{
"title": "Tidy up an imported wildcard pack",
"text": "Packs imported in Import / Export → Wildcard packs arrive as flat lists. After the import, press Tag and connect with AI. The chat opens and asks the assistant to tag the options, group the tags, link the lists with constraints (with a fallback where a rule could leave a list empty), gather them into a bundle and run a test. Every change is a card; the option text stays as it was."
},
{
"title": "Permissions",
"text": "Both switches are in Settings › AI assistant › Permissions, off by default. The assistant can't change them.\n- Let the assistant delete items. The chat can then propose deleting modules, bundles, templates, categories and tags, up to 10 per card. The card says what uses each item and asks you to confirm. Undo brings everything back at the same ids, so references work again. Saved workflows keep their own copy of a deleted module.\n- Apply small changes automatically. New descriptions, tags and categories are applied without a click; the card says Applied automatically and keeps its Undo. Ratings, content changes, renames and deletes always wait for you.\nDescribe on an item's Identity card writes its description and tags one item at a time. See Draft with AI."
},
{
"title": "Works with",
"text": "Chat Draft with AI Wildcard"
}
]
},
{
"id": "ai-web",
"title": "Web search and tag lookup",
"group": "ai",
"blurb": "Let the assistant search the web or Danbooru, and use your tag list and LoRAs.",
"keywords": [
"ai",
"web",
"search",
"searxng",
"brave",
"tavily",
"kagi",
"crawl4ai",
"firecrawl",
"danbooru",
"lora",
"embedding"
],
"sections": [
{
"title": "Set up web search",
"text": "Open Settings › AI assistant › Web search and pick an engine:\n- SearXNG, your own server. Add json to search.formats in its settings.yml, or it refuses the chat's searches.\n- Brave Search, Tavily or Kagi with a key. The key stays on the ComfyUI machine and only goes to that engine.\nOnce set up, the assistant searches when references would help (a character's outfit, an art style, a new model's tags), not only when you ask. It takes effect in open chats from the next message."
},
{
"title": "It asks first",
"text": "Every search shows the exact words it wants to search for, with Allow once, Allow for this chat (no more asking in this chat for the next 8 hours) and Deny. Only the search words leave your PC. Five short results come back, and the assistant cites them as [1], [2] with the link.\nWhen a result's snippet isn't enough, it can ask to read that page. It only reads links its own allowed searches returned, never another address, and asks first.\nBuilt in\nThe default. ComfyUI fetches the page and keeps its visible text (a few thousand characters). It never opens an address on your own network. Pages that need JavaScript, a login or a browser check give little.\nCrawl4AI / Firecrawl\nYour own server (or Firecrawl's hosted API with a key) renders pages in a real browser first.\nOff\nNo page reading.\nDon't ask before web calls skips the Allow card for searches, page reads and Danbooru lookups. You still see each one above the answer, but a web page or an item could then steer what the chat sends out.\nTurning a web feature on shows a short note of what leaves your machine. Got it hides it, and Show dismissed notices again brings it back."
},
{
"title": "Danbooru tag lookup",
"text": "Off by default. When on, the chat can ask danbooru.donmai.us about one tag: post count, category, alias, what it implies, its wiki text and the tags used with it most. It reads tags and wiki pages only, never posts or images, waits a second between requests and asks you each time."
},
{
"title": "Your tags, LoRAs and embeddings",
"text": "While an autocomplete source is on (Settings › Autocomplete), the chat and the pack builder can look in your downloaded Danbooru tag list (real spellings and post counts), your LoRA files and your embeddings. This stays on your PC, apart from what the model reads. For spellings and post counts the chat uses this list instead of the Danbooru lookup.\nThe assistant also knows how the nodes work (the Loop's $iteration, sweeps and holds, Injector rows, WP Model Info's $model_family, $model_variant and $model_name, the Image Filter). With the canvas open in another tab, it knows which variables your canvas sets at run time, so a pack reading them isn't flagged."
},
{
"title": "Works with",
"text": "Chat Privacy and safety"
}
]
},
{
"id": "ai-privacy",
"title": "Privacy and safety",
"group": "ai",
"blurb": "What leaves your PC and what the assistant can and can't do.",
"keywords": [
"ai",
"privacy",
"safety",
"security",
"data",
"allowed hosts",
"dns rebinding",
"minor"
],
"sections": [
{
"title": "The short version",
"text": "- With a local server (Ollama, LM Studio, llama.cpp), nothing leaves your PC.\n- With Claude or OpenAI, what you ask about goes to that provider under your account. Requests to OpenAI ask it not to store the response.\n- Chats are stored only in ComfyUI's own database on your PC.\n- Web search, page reading and Danbooru lookups only run after you set them up, and ask each time unless you turn that off.\nComfyUI has no login, so anyone who can reach its port can use the assistant while it is on, which spends your credits on a paid provider. Keep it off on a ComfyUI you share."
},
{
"title": "What each feature sends",
"text": "Wildcard drafts\nYour request and the wildcard's name, options and tags.\nRules\nDerivations: the variables your library sets, with the first options of each wildcard. Constraints: the two wildcards' options and tags.\nValues, combines, templates\nThe item and the variables your library sets.\nDescribe / Organize\nWhat each item holds and the tags your library uses; Organize adds your category names.\nChat\nA list of your items (names, types, variables), the conversation so far, the items and docs it reads, results of stacks it runs (they run on your PC), and a short note of what your page shows.\nPack builder\nYour description, settings and answers, a sample prompt if given, a summary of your library's style, the bundle's modules when extending, and what the pack has built so far.\nSub-bundle suggestions\nThe children's names, types and variables.\nImages\nGo to the model with their message, shrunk and re-saved first, which drops camera details and location.\nMeaning search\nEach item's name, variable, tags, description and first options, and your search phrases, go to the embedding server.\nWeb\nA search sends only its words to your search engine; a page read fetches that page; a Danbooru lookup sends one tag.\nEach chat message sends the chat so far again; the provider may cache it for a few minutes to charge less. The full list of outbound requests is in docs/network-access.md in the repository."
},
{
"title": "What it can and can't do",
"text": "- It can search and read your library and these docs, run test stacks in memory, open pages and point at things, and propose changes as cards.\n- It can't touch files, change its own settings, install or publish packs, or open web pages outside the web search you allow.\n- Nothing is saved until you press Apply, unless you turned on Apply small changes automatically for descriptions, tags and categories (still with Undo).\n- It can only propose deleting when you allow it, and every delete waits for you.\n- Its replies can't contain links, images or HTML, so showing a reply can't send your data anywhere."
},
{
"title": "Content rules",
"text": "Two rules are enforced in every AI feature: the assistant never writes a minor in a sexual context, and never sex without consent. Options, values, rules or templates that pair a minor word or a non-consent word with a sexual one are left out, and a request that asks for it is refused. Negated words (\"no minors\") don't count, and adult body words (petite, slim) are never touched. Your own edits are never checked or changed. See Adult (NSFW) packs for the full safeguards."
},
{
"title": "Good to know",
"text": "How the assistant is protected\nText in your library is treated as data. A pack you downloaded could hide instructions in an option (\"ignore your rules and remove everything\"); the assistant is told such text is never an instruction, and at worst it would propose a card you'd see and dismiss. Text inside images, web results and pages is treated the same way.\nInvisible characters that can hide text (Unicode tag characters, right-to-left overrides, zero-width spaces) are removed from everything the model reads and writes, so a card shows exactly what it would save.\nThe assistant's routes check every request: requests from other websites are refused, and so is any request whose address isn't local (an IP address, localhost, a name with no dot, or a .local / .lan / .home.arpa name). That stops a website reaching your ComfyUI through a look-alike domain (DNS rebinding). If you reach ComfyUI through your own domain name, add it to the WP_AI_ALLOWED_HOSTS environment variable (comma-separated) and restart ComfyUI.\nStarted with --listen, ComfyUI is open to your network, and anyone who can reach it can use the assistant and its keys. Settings › AI shows a warning when that is the case.\nAPI keys are only sent to the address they were saved for. Each answer stops after its time limit, and Stop ends the model's work straight away."
},
{
"title": "Works with",
"text": "Set up a model Web search Adult (NSFW) packs"
}
]
},
{
"id": "ai-troubleshooting",
"title": "Troubleshooting",
"group": "ai",
"blurb": "Fixes for common assistant problems, and how it helps small local models.",
"keywords": [
"ai",
"troubleshooting",
"problem",
"slow",
"cut off",
"small model",
"9b",
"connect",
"error"
],
"sections": [
{
"title": "Common problems",
"text": "It can't connect\nPress Test connection in Settings › AI assistant. Check the server is running and the address and port are right. A saved key is only sent to the address it was saved for, so paste it again after changing the address.\nRefused when using a domain\nRequests that don't come from a local address are refused. If you reach ComfyUI through your own domain name, add it to WP_AI_ALLOWED_HOSTS and restart ComfyUI.\nTool calls show as text\nIn Ollama, Qwen 3.5 / 3.6 models pulled from Hugging Face miss the tool-call reader. Make a Modelfile copy as described on Set up a model.\nIt forgets or ignores instructions\nThe model probably has too little context. Load it with at least 16k, or 32k for chats that take several steps (LM Studio: Context Length in the load settings). Ollama gets the Most context to use setting. Test connection warns when it is too small.\nThe reply ends with “Cut off”\nThe model reached its output limit. Thinking models can spend it all before they answer: the assistant then asks once more with thinking off and keeps it off for that model. Set Thinking to Off or Low to skip the first try.\nA thinking level does nothing\nMany models have only on and off (Qwen 3 under LM Studio, most Ollama models), so Low, Medium and High all mean On. Settings › AI assistant › Thinking lists what the chosen model takes and says so under the picker.\nIt's slow\nTurn Thinking off, use a smaller model, or keep the model loaded longer with Unload after. A pack takes one request per item, so a few minutes with a local model is normal.\nAnswers are poor or it gets stuck\nUse a builder model for chat, rules and packs. Check Model size matches your model. Try Choose the tool first under Advanced. If you also have a cloud provider set up, the chat says once when switching would likely help.\nThe image button is off\nYour server says the model can't see images. Pick a vision model.\nA card says it's out of date\nThe item changed after you asked. Ask again for a fresh card.\nTry again is unavailable\nUndo the changes you applied from that answer first.\nTracing a wrong answer\nTurn on Debug log in Settings › AI assistant › Advanced and ask again. The Debug button in the top bar then lists the request with every model call it made; Copy debug gives the prompt as sent, the reply, thinking and tool calls to paste into a bug report.\nExplicit content is refused\nClaude and OpenAI don't allow explicit writing. Run an uncensored model yourself or use an unmoderated OpenRouter model."
},
{
"title": "Small local models",
"text": "A 9B model on a 16 GB card can run the chat. With Model size on small or medium, the assistant works differently so a small model makes fewer wrong turns. You don't need to do anything for this.\nWhat changes for small and medium models\n- Fewer tools at a time. The model starts with the tools for the page you're on (medium models also get the wildcard, rule, constraint and test tools everywhere) and loads more one group at a time. On a local server each step also offers a shortlist of tools that fit your message, plus the tools that usually come next (after a search, read; after reading a wildcard, its edit tools; after a card, the test). A question gets the docs and the reading tools but no tools that change things, so it gets an answer, not a card. Every tool says when to use it, which look-alike to use instead, and shows a sample call.\n- Only real items. With JSON steps, a tool call can only name items that exist, and an edit can only name items you pointed at in the chat (or ones it read when you asked for a change), so the model can't invent an id or change the wrong item. A server that can't take these lists gets the plain format.\n- Decide, then write. Each step first decides what to call next, then writes the reply with the tools out of reach, so it can't stop halfway to ask. Tool steps run with thinking off.\n- One item per step. A request for several items is listed first and then made one per step, with the code keeping count.\n- Worked examples. The model gets up to three calls that worked for similar requests: from cards you applied (kept in ai-examples.json beside ai.json) plus a few built-in ones.\n- Recipes. Common asks (an option never comes up, fixing warnings, negatives, weights, how-to questions) come with a short list of steps, and a docs question can be answered with one explain call.\n- Feature notes. When your message touches a feature (refs, tag filters, constraints, rules, negatives, weights, loops and more), a few lines on how that feature works come with it, at most three at a time. The first time a change tool is used or fails, its note comes with the result. Each note is sent once while the model can still see it.\n- Checked against your request. Before an answer with cards ends, the code checks that the cards have what you asked for (a constraint, an \"only\" rule, a fallback, negatives, a tag filter like @{Palette:warm}). If one is missing, the model is asked once to add it or say why not, and its next step offers only the tools that can add it. If the answer still talks about it, the reply says no card has it yet. A tool the model repeats twice in one answer is taken away for the rest of that answer, so it moves on instead of making the same card again.\n- A progress note. Each later step gets one line saying what the answer has done so far.\n- Shorter memory. The chat is summarised sooner (at half the window for a small model).\n- JSON steps. Tool format Automatic uses JSON steps, where the server also checks each call's arguments as the model writes them.\nTest connection tries one small-model tool step and says whether it worked."
},
{
"title": "Works with",
"text": "Set up a model Chat"
}
]
},
{
"id": "pack-structure",
"title": "Recommended pack structure",
"group": "guides",
"blurb": "Plan a pack, pick the right feature for each job, and copy a blueprint.",
"keywords": [
"structure",
"layout",
"pack",
"blueprint",
"recipe",
"decision",
"driver",
"when to use",
"which feature",
"best practice",
"anti-pattern",
"first pack",
"plan",
"story",
"sweep",
"season",
"two characters",
"accepts",
"outfit"
],
"sections": [
{
"title": "Plan your first pack",
"text": "- Write one prompt you like by hand, such as \"a woman with red hair in a raincoat, rainy street at night, film photo\".\n- Mark what should change. Hair colour, outfit, place, time of day: each becomes a wildcard. What stays (film photo) becomes a fixed value.\n- Mark what must fit together. A raincoat goes with rain, not a beach. That's a constraint, or a derivation when it only adds words.\n- Write the template with the variables in place: a woman with $hair hair in $outfit, $place, $style.\n- Test it in the Test Runner over a few hundred seeds and fix what looks wrong. Then save the modules as a bundle.\nThe rest of this page is the longer version, with blueprints to copy."
},
{
"title": "The idea in one minute",
"text": "A pack works best when each feature does one kind of job. Sort every part of your prompt into one of these roles and the right module follows from the role:\nDecision\nA choice something else reacts to. Make it its own wildcard with a variable, in the Context's stack. Anything a constraint, derivation, Match variable, sweep or other module reads must be one. A pick made only inside an @{} reference sets no variable, so nothing can read it, and a constraint whose source is only rolled inside a reference never applies.\nDetail\nVariety nothing reads. Put it in @{} references inside the decision's options, so each option draws its own parts: an Outfit option \"@{Tops:formal} with @{Bottoms:formal}\" next to \"@{Dresses:casual}\". This is a recipe wildcard. Lock the outfit and its parts still vary.\nAgreement\nTwo picks that must fit together. Use a constraint: a tag matrix for whole families, exceptions for single pairs, an Only rule for allow-lists, and a fallback option on the target so it never comes out empty.\nConsequence\nText that follows from a pick, like a title from a class. Use a derivation. Append and Prepend add exactly what you type, so start the text with its own separator (\", with a scarf\"). Never set a variable a wildcard already sets: the last write wins and the pick is thrown away.\nDriver\nA variable that decides things but should not print, like $season. Turn on Hide from prompt for it. Constraints, combines and derivations still read it; the Assembler leaves it out.\nPhrase\nA combine joins variables into one phrase: $look = \"wearing $outfit and $shoes\". The usual way to print a driver is through a phrase like this.\nSkeleton\nThe Assembler template is the prompt's order, written the way your model family wants it. It only fills in $variables: {a|b} prints exactly as typed and @{} prints nothing there.\nConstant\nFixed values hold always-on style and quality tags and standing negatives. They are not for variety: {a|b} in a fixed value has no weights, tags, constraints, fallback or Test Runner counts.\nFrames\nUnder a WP Context Loop: per-frame overrides for a few named beats, a rule on $iteration for something that runs over all frames, Hold across run for a character that stays the same, and a sweep for side-by-side comparisons (swept picks ignore constraints).\nModel and rating\nWP Model Info's $model_family and $model_variant drive the quality header through derivations or constraints. A $rating driver plus constraints decides how explicit a pack can get. The library's content rating is only a label.\nTwo rules explain most surprises. $var is the same value everywhere you read it. An @{ref} is a fresh, independent pick every time it appears. So \"$color jacket and $color jeans\" always match, and \"@{Colors} jacket and @{Colors} jeans\" match only by chance."
},
{
"title": "How a stack runs",
"text": "- Top to bottom, once, last write wins. A later module that writes the same variable replaces the earlier value without a warning (WP Debug's trace marks it). A chained Context adds to the one above and overrides it.\n- Only a chain wildcard makes a pick. Its pick is recorded, so it can be a constraint source, be read with $var.AXIS, be counted per wildcard by the Test Runner and be swept. Text from a reference, an inline pick, a fixed value, a combine or a derivation is just text.\n- Constraints sit between their source and their target: source, then constraint, then target. A target used only inside references is reached too.\n- Each text field reads its own part of the syntax. See the grid below.\n- What crosses a socket: variables (hidden ones too), picks, tag groups, constraints, negatives and loop details, so chained Contexts keep working together. Reference pools come from the node's own rows first, then from the library on the machine that runs the graph."
},
{
"title": "Layers in running order",
"text": "Most packs fall into these layers. Put each in its own sub-bundle, in this order:\n# | Layer | Typical modules | Variables | Printed?\n0 | Inputs (on the canvas) | Injector rows, WP Model Info, WP Context Loop | $subject, $character_lora, $model_variant, $iteration | Loop and Model Info variables start hidden; Injector rows as needed\n1 | Drivers | Anchor wildcards, Match variable wildcards (Frame, Model, Rating), Hold where needed | $_class, $_season, $_frame | Hidden\n2 | Subject | Wildcards, or a derivation \"a LoRA replaces the character\" | $character, $species | Parts hidden, a combine prints them\n3 | Appearance | Wildcards with @{Colors:…} details; constraints from drivers | $hair, $eyes, $body | Printed\n4 | Outfit | Recipe wildcard with accepts groups; shoes and accessories as their own wildcards, constrained by it | $outfit, $shoes, $accessories | Printed\n5 | Pose and expression | One tagged pose list plus a constraint from Frame or class | $pose, $expression | Printed\n6 | Scene | Place and time of day; constraints from drivers | $place, $time_of_day | Often hidden, printed through a phrase\n7 | Light, weather, camera | Wildcards whose accepts groups agree with the scene | $light, $weather, $camera | Printed\n8 | Style and quality | Fixed values with negatives; a per-model derivation or Match variable wildcard | $style, $quality | Printed\n9 | Composition | Combines that join layers; derivations that edit text (story beats, LoRA swaps, negatives) | $look, $scene, $beat | Printed\n10 | Template | One Assembler template, plus a negative template with $negatives | |\nThe engine sets three ordering rules: a constraint after its source and before its target, a derivation after what it reads, and a combine after its parts. Vocabularies are not in the stack at all: they live in the library (or as a row on the node) and are reached through references.\nModules\nPack · Layer · Thing in the library (\"Noir · Outfit · Coats\"). A → B for constraints (\"Frame → Pose\"). A short verb phrase for derivations (\"LoRA replaces character\", \"Story beat\").\nVariables\nLowercase snake_case, one concept each, singular ($pose, not $poses). Multi-picks plural ($accessories). Drivers start with an underscore ($_frame) and stay hidden. Injector rows can't start with an underscore, so inputs use plain names.\nVocabularies\nA plural noun with no variable (Colors, Fabrics, Flora), used only through references like @{Colors:warm}.\nTags\nLowercase single words for what rules test, the same word on both sides of a constraint. Classify groups lowercase (season, formality); accepts groups UPPERCASE (SHOES, LIGHT) so $outfit.SHOES reads as an accessor. When no tag fits a new option, add a new tag: never force \"battle axe\" into #sword.\nFallback\nOn every constraint target and every Match variable wildcard. Pick the most neutral option."
},
{
"title": "Which feature for which job",
"text": "Find the job in the first column. The second column is the feature to reach for, the third is the one people often try first, and the last is what to keep in mind.\nMaking a choice\nYou want | Use | Not | Watch out\nA list of alternatives for one concept | A wildcard with a variable | A fixed value with {a|b|c}; one wildcard per case ($winter_top, $summer_top) | One concept per wildcard. Pose, camera and hair in one list can no longer vary or be constrained apart.\nSometimes nothing (a hat in 30% of runs) | The null option; its weight is the share of empty runs | An option with empty text | A multi-pick never draws the null option: for \"maybe none\" set the use to Pick 0-2.\nNever come out empty | A fallback option (weight 0 if it should only stand in) | Hoping the weights cover it | Option toggles and the category filter run first and can remove the fallback for that use.\nCommon and rare options | Weights (2-3 for common, 0.5 for rare; 0 turns one off without deleting it) | Typing the same option twice | If every weight ends up 0, the wildcard gives nothing unless it has a fallback.\nSeveral at once (2-3 accessories) | Pick 2-3 on the use, with a separator; read one with $accessories.0 | Several wildcards with the same list | Constraints from a multi-pick source apply once per pick.\nA tiny cosmetic change inside one option | An inline pick: {red|crimson} scarf | A second wildcard | Nothing can test an inline pick: it has no tags and no pick record, and Debug shows only the result. {solo} with no pipe stays literal.\nParts that each option chooses for itself | @{} references inside the options (a recipe wildcard) | Two wildcards and a combine, when nothing reads the parts | Each reference is a fresh pick and sets no variable.\nOne vocabulary used in many places | A wildcard with no variable (Colors), drawn with @{Colors:warm} | Copying the list into every wildcard | It must be on the node or in the library of the machine that runs the graph.\nSeveral picks from a vocabulary | {2-3$$, $$@{Props:indoor}} inside an option | Separate Props wildcards | The picks are distinct and constraints on Props still apply.\nA shared workflow that keeps working elsewhere | Keep the vocabulary as a row on the node (it may be disabled) | Relying on the other person's library | A row on the node wins over the library copy, so later library edits don't reach it. WP Debug shows where each pool came from.\nBring in an existing wildcard collection | Import / Export → Wildcard packs. Merge files that are variants of one slot (the folder names become tags); keep separate files that are different slots | Pasting lists by hand | Importing the same pack again updates it instead of adding copies.\nNarrowing and making picks agree\nInside one roll the order is: filter and toggles, then weights, then constraints, then the fallback. So pick the first tool that says what you mean, and give any pool that can be emptied a fallback.\nYou want | Use | Not | Watch out\nOne pick to change another's odds | A constraint | A derivation that rewrites the target | \"Rarely, never, only, more often\" is a constraint. \"Add, say, replace\" is a derivation.\nA whole family of rules (winter rules out summer clothes) | The constraint's tag matrix | One exception per option | Use the same tag words on both wildcards.\nOne special pair | A constraint exception | A new tag for one option | Exceptions are keyed by option text: renaming the option orphans the rule.\nAn allow-list (the fight beat allows only fight poses) | An Only rule | Excluding everything else by hand | Options the row doesn't mention drop out, so give the target a fallback.\nTwo picks that agree on one attribute, and you print it | An accepts group on the source (SHOES) plus a constraint with a diagonal Only (sneakers → sneakers, boots → boots); print $outfit.SHOES | Classify tags: they allow sneakers and boots together | The accepted tag is rolled evenly, not by weight. Every option needs at least one tag in the group.\nA fixed narrowing for one use | The category filter on the use, or @{X:tag} in text | A constraint | A filter can't read a variable.\nHand-picked options for one use | Option toggles: tick only the ones you want | Deleting options | Even one ticked option still obeys constraints and can come out empty. Keep an enabled fallback.\nSofter odds without forbidding | Per-use weights | Exclude | Per-use changes don't show in the library editor.\nA variable decides the pick ($iteration, $model_variant, an Injector value) | Match variable on a wildcard whose options spell those values; it then works as a constraint source | A 20-branch derivation | It compares the option's raw text (case and outer spaces ignored), so options with {a|b} or @{} never match. It skips filters, toggles and constraints. Give it a fallback.\nA many-to-one mapping, or text, from a variable | A derivation | Match variable | A derivation's result is text, not a pick.\nOnly the first or next N copies of a target | The constraint's Target reach | Copying the constraint | Reach counts down the stack from the constraint.\nTo check the odds | The constraint's Preview card, then the Test Runner | Guessing | Preview leaves out other constraints, filters and toggles.\nText that follows, and constants\nYou want | Use | Not | Watch out\nText that depends on picks (if winter, add a scarf) | A derivation | A constraint | Append and Prepend need a separator in the text.\nA pick only in some cases | A derivation action holding @{Accessories:winter} | A wildcard that is mostly the null option | That pick sets no variable.\nAnother source replaces a pick (a LoRA replaces the rolled character) | A derivation: IF $character_lora is set, Replace $character | | The one good reason to overwrite a wildcard's variable.\nA lookup from one pick to another (mage → robe, 20 times) | A constraint on a tagged list | 20 equals → Replace branches | Story text keyed on $iteration is the exception: there is no pick to constrain from.\nSeveral picks joined into one phrase | A combine | Retyping it in each template | A combine drops @{}. Use it for phrases, not picks.\nA tag group printed as text | A combine: wearing $outfit with $outfit.SHOES | | Only accepts groups can be read. $outfit.STYLE on a classify group renders nothing.\nAlways-on text (quality, style preset) | Fixed values | A one-option wildcard | Never a wildcard for the same variable as well: the later one wins without a word.\nA prompt skeleton you reuse | The Templates library (Save and Load on the Assembler) | Fixed values | A template holds no variables and rolls nothing.\nNegative words an option invites (\"strawberry blonde\" invites fruit) | The option's negative | The standing negative | Reaches only an Assembler that rendered that variable, and only matters at CFG above 1.\nA standing negative (lowres, watermark) | The Assembler's negative template, with $negatives where the collected words go | A negative on every option |\nText from outside the stack (a typed subject, an LLM's caption) | WP Context Injector | A wildcard | Injected values are not picks. To make one a constraint source, match it with a wildcard.\nOutside text plus a light template on the canvas | Injector template rows | A combine | Use a combine for reusable library logic that rolls.\nHolding, hiding and frames\nYou want | Use | Not | Watch out\nA driver that should not print | Hide from prompt | Just leaving it out of the template | A later write without Hide from prompt makes it print again. In a template it renders nothing and warns \"unknown variable\".\nThe same pick for a whole Loop run | Hold across run | Lock pick | A held derivation still checks its conditions every frame; only the {a|b} and @{} rolls in each branch are frozen.\nThe same pick every time you press Generate | Lock pick (Lock seed on other modules) | Hold across run | Frozen across every run and every frame.\nNo roll at all | Fixed values | A locked wildcard |\nOptions compared side by side | Sweep combinations on the Loop (64 frames by default, up to 999) | Queueing runs by hand | Swept wildcards are pinned, so constraints don't re-weight them: a sweep can show pairs the pack forbids. Hold other picks keeps everything else still.\nA few frames that differ, on the canvas | Per-frame overrides (the edit frame chips) | Pack logic | Canvas only: not saved in bundles and not run by the Test Runner.\nA rule over all frames that ships in a pack | A Frame wildcard matching $iteration plus a constraint, or a derivation on $iteration | One wildcard per frame | Test it by pinning iteration in the Test Runner.\nOne module on or off for some frames | Per-frame enable | Bypassing the frame | Works both ways: on everywhere but some frames, or only on some.\nWhole frames skipped | Frame bypass (Ctrl-click a frame chip) | Turning every module off | Kept frames keep their numbers.\nKeeper frames you can reproduce | Override seed plus per-iteration seed locks on the Loop | Locks without Override seed (they are ignored) | WP Image Filter's Send to Loop writes both for you.\nA different sampler seed per frame | WP Seed List | Changing the Loop seed | Seed List only feeds the sampler. It never changes a prompt.\nSeeds you can read out | The Sequential strategy | | The strategy doesn't make results more or less varied.\nNodes around the stack\nYou want | Use | Not | Watch out\nDifferent wording per checkpoint | WP Model Info plus a derivation, or a Match variable wildcard | An Injector | Pin the variant when detection gets a checkpoint wrong.\nA pick that drives a number or switch ($steps, $cfg, a LoRA strength) | WP Var → Int / Float / Bool reading a hidden variable | A number in the prompt | A derivation can write the hidden $cfg that the Var node reads.\nA human choice before an expensive pass | WP Image Filter (pause) | Running it unattended | The queue waits. Unattended, use pass all, or a timeout set to keep all.\nDuplicates removed, weights merged, LoRA tags spaced, words blocked | WP Prompt Cleaner after the Assembler | The Assembler's tidy | The tidy only closes gaps left by empty variables. It never dedupes and leaves verbs (\"a girl wearing, sitting\").\nClean prose prompts | The Cleaner in text mode, Gentle | Tags mode | Tags mode splits sentences.\nWords kept out for safety | Constraints at the source, then negatives, then a Cleaner blocklist | Any one of them alone | The blocklist matches whole words: list every variant, or use a regex.\nWhy one run came out like that | WP Debug | The Test Runner |\nHow often each option shows, and what an edit changed | The Test Runner (many seeds; baselines to compare) | WP Debug | It runs frame 0 only. Pin $iteration to test a frame.\nOrganising and sharing\nYou want | Use | Not | Watch out\nA set of modules you reuse | A bundle, with sub-bundles for its sections | | A bundle groups modules. It does not keep their variables apart.\nTwo copies of one character | The bundle inserted twice, with the second copy's variables renamed | Two loose copies of one wildcard | A constraint inside each copy reads its own copy's source pick.\nThe same module again under a new variable | A second use with its own Variable binding | A fork |\nA canvas copy with its own variable | Duplicate ($hair becomes $hair_2) | |\nA library copy that will go its own way | Fork (\"(copy)\") | Editing the shared one |\nCanvas edits sent to the library | Push to library | | Library bundles that hold the module are updated too. Canvas copies elsewhere show drift.\nA newer library version pulled in | Refresh from library (a row) or Reset to library snapshot (a bundle) | |\nBundle children as stand-alone library modules | Extract to library, in the bundle editor | | It makes new ids (a fork) and leaves per-use overrides behind.\nTo try a pack without something | Disable it | Delete it | Delete through the manager: it fixes every reference in one step you can undo.\nA safe / nsfw label for lists and publishing | Content rating | Expecting it to filter anything | It has no effect on a run. Use a $rating driver.\nMany options pasted at once | Bulk add: value #tag *N -- negative, one per line | |\nMany options changed at once | The selection toolbar (tag, weight, delete) | |\nGetting AI help\nYou want | Use | Not | Watch out\nHelp with the item you have open | That editor's Draft with AI panels | The chat |\nAnswers, edits across the library, test runs | The chat | | Every change is a card you apply.\nA new pack of 5 to 60 items, or a bundle extended | The Pack builder | The chat |"
},
{
"title": "Which syntax works where",
"text": "Each field reads only part of the grammar; the rest stays as text or is dropped. The full grammar is on The $variable pipeline.\nField | $var | $var.K, .AXIS, .neg | @{ref} | {a|b}, {N$$sep$$…} | Notes\nWildcard option | no (stays as text) | no | yes | yes | Weights like (tag:1.2) pass through as text.\nOption negative | no | no | yes (no pick recorded) | yes | Rolls on its own stream, so it never moves a pick.\nFixed value and its negative | no | no | no | yes | No weights, tags or constraints on the inline pick.\nCombine template and its negative | yes | yes | dropped | yes |\nDerivation condition | names the variable it tests | yes | no | no | The value it compares to is plain text.\nDerivation action (all modes) | yes | yes | yes | yes | A fresh pick each time the branch fires.\nCategory filter, :filter | no | no | no | no | Tag names with and, or, not and parentheses.\nAssembler template | yes | yes | dropped | printed as typed | No seed. Hidden variables render nothing.\nAssembler negative template | yes (its text only) | yes | dropped | printed as typed | $negatives marks where the collected words go.\nA wildcard option can't read a variable. \"A $color dress\" is either a combine ($color dress, the same colour as elsewhere) or an option @{Colors} dress (a new colour). The two escapes $$ and @@ give a literal $ and @."
},
{
"title": "Blueprint: character",
"text": "A fantasy adventurer for a tag model. The class is a hidden driver that steers the expression and prints through a phrase, so a LoRA can replace the whole subject. Modules in running order:\n# | Module | Kind | Writes | Options and settings\nLibrary | Colors | Vocabulary | | Tags natural, vivid, dark, metal\n1 | Class | Wildcard | $_class | knight, ranger, mage, rogue, cleric; tags martial, caster, stealth. Hide from prompt, Hold across run\n2 | Species | Wildcard | $species | human (3), elf, dwarf, halfling (1 each)\n3 | Character | Combine | $character | $species $_class\n4 | LoRA replaces character | Derivation | $character | IF $character_lora is set → Replace $character_lora\n5 | Hair | Wildcard | $hair | long @{Colors:natural} hair, short @{Colors:natural} hair, undercut, …\n6 | Eyes | Wildcard | $eyes | @{Colors:vivid} eyes, heterochromia, …\n7 | Class → Expression | Constraint | | caster → serene boost 2; stealth → smirk boost 2\n8 | Expression | Wildcard | $expression | determined, smirk, serene, …; null option weight 3\n9 | Style | Fixed values | $style, $quality | fantasy illustration, painterly (negative photo, 3d render); your model's quality tags\nTemplate:\n$quality, $character, $hair, $eyes, $expression, $style\nNegative template:\nlowres, bad hands, watermark, $negatives\n- $_class is a decision (a constraint reads it), so it is a chain wildcard. Its text reaches the prompt only through $character.\n- Hair and eyes draw colours from one vocabulary as fresh picks: they don't need to match. If they should, make $color a wildcard and read it in a combine.\n- $character_lora comes from an optional Injector row. With no LoRA wired, the derivation does nothing."
},
{
"title": "Blueprint: outfit and shoes (accepts)",
"text": "A wardrobe where each outfit offers the shoes it goes with, the shoes always agree, and the season rules out the wrong clothes. Modules in running order:\n# | Module | Kind | Writes | Options and settings\nLibrary | Colors, Fabrics, Patterns | Vocabularies | | Colors: neutral, warm, cool, dark, metal. Fabrics: casual, formal, winter, summer. Patterns: subtle, bold, null weight 2\n1 | Season | Wildcard | $_season | winter, summer (tagged the same). Hide from prompt\n2 | Season → Outfit | Constraint | | winter → summer exclude; summer → winter reduce 0.2\n3 | Outfit | Wildcard | $outfit | @{Colors:neutral or dark} @{Fabrics:formal} suit [formal, winter, summer; SHOES loafers, boots]; @{Patterns} @{Fabrics:summer} sundress [casual, summer; SHOES sandals, sneakers]; @{Colors} hoodie and @{Colors:dark} jeans [casual, winter, summer; SHOES sneakers] fallback. Groups: style and season classify, SHOES accepts\n4 | Outfit → Shoes | Constraint | | Only on the diagonal: loafers → loafers, boots → boots, sandals → sandals, sneakers → sneakers\n5 | Shoes | Wildcard | $shoes | black loafers [loafers], ankle boots [boots], leather sandals [sandals], white sneakers [sneakers] fallback\n6 | Outfit → Accessories | Constraint | | formal → casual reduce 0.3\n7 | Accessories | Wildcard | $accessories | a @{Colors:metal} watch, a silk scarf, …; Pick 0-2, separator ,\n8 | Look | Combine | $look | wearing $outfit and $shoes, $accessories\nTemplate:\n$subject, $look, $style\n- Colours, fabrics and patterns are details: each outfit chooses its own and nothing reads them. Shoes and accessories are decisions because constraints act on them.\n- If the rolled accepted tag is all you need, skip the Shoes wildcard and write `wearing $outfit with $outfit.SHOES`.\n- Don't print $outfit.style: style is a classify group, and classify groups render nothing."
},
{
"title": "Blueprint: two characters from the same wildcards",
"text": "Two people built from one character bundle, without one overwriting the other. Modules in running order:\n# | Module | Kind | Writes | Options and settings\n1 | Character bundle, copy A | Bundle | $character, $hair, $eyes, $outfit | As built\n2 | Character bundle, copy B | Bundle | $character_b, $hair_b, $eyes_b, $outfit_b | Inserted again; each child's Variable binding renamed\n3 | Person A | Combine | $char_a | $character, $hair, $eyes, $outfit\n4 | Person B | Combine | $char_b | $character_b, $hair_b, $eyes_b, $outfit_b\n5 | Place, light, camera, style | Wildcards and fixed values | $place, $light, $camera, $style | Shared, once\nTemplate:\n$quality, 2girls, $char_a, $char_b, $place, $light, $camera, $style\n- Rename the second copy's variables. Two copies writing $hair just overwrite each other (last write wins). Duplicate on the canvas does this for you ($hair_2).\n- Use two bundle copies, not two loose rows of one wildcard: a constraint inside each copy reads the source pick of its own copy.\n- Attributes bleed between people. On tag models give them clearly different traits and a count tag. On prose models write \"On the left, $char_a. On the right, $char_b.\" For the strongest split, use one Assembler per region (regional prompting) reading the same Context."
},
{
"title": "Blueprint: landscape",
"text": "Wild places where the biome and season decide the weather, and the light agrees with the weather. The drivers print only through one phrase. Modules in running order:\n# | Module | Kind | Writes | Options and settings\nLibrary | Flora | Vocabulary | | Tags cold, temperate, arid, tropical (pine, birch, saguaro, palms, …)\n1 | Biome | Wildcard | $_biome | boreal forest of @{Flora:cold} [cold]; red desert canyon with @{Flora:arid} [arid]; misty jungle valley, @{Flora:tropical} [tropical]; alpine meadow [cold, temperate]. Hidden\n2 | Biome → Season | Constraint | | tropical: Only wet season and dry season; arid → wet season reduce 0.2\n3 | Season | Wildcard | $_season | winter, spring, summer, autumn, wet season, dry season. Hidden\n4 | Season → Weather | Constraint | | summer → snow exclude; winter → snow boost 3\n5 | Biome → Weather | Constraint | | arid → fog reduce 0.2; arid → snow exclude\n6 | Weather | Wildcard | $weather | heavy snowfall [snow; LIGHT overcast, dusk]; clear blue sky [clear; LIGHT noon, golden]; morning fog [fog; LIGHT dawn, overcast]; thunderstorm [storm; LIGHT overcast, night]. LIGHT is an accepts group. Option negatives: clear → clouds, snow → green grass\n7 | Weather → Light | Constraint | | Diagonal Only on LIGHT\n8 | Light | Wildcard | $light | soft dawn light [dawn], harsh noon sun [noon], golden hour glow [golden], … fallback soft overcast light\n9 | Camera | Wildcard | $camera | wide establishing shot, low angle, aerial view, …\n10 | Scene | Combine | $scene | $_biome in $_season, $weather, $light\n11 | Style | Fixed values | $style | landscape photography, 35mm (negative people, text)\nTemplate:\n$scene, $camera, $style\nNegative template:\n$negatives, lowres, oversaturated\n- Two constraints on Weather multiply. An exclude in either one wins.\n- Option negatives follow usage: \"clouds\" reaches the negative only when the sky is clear.\n- On anime tag models add `no humans, scenery`, or the model adds a person."
},
{
"title": "Blueprint: story over a Loop",
"text": "Six frames of one story. The cast stays the same, while place, pose and camera follow each frame's beat, and the whole thing ships in a pack. Modules in running order:\n# | Module | Kind | Writes | Options and settings\nCanvas | WP Context Loop, count 6 | Node | $iteration = 1 to 6 | Feeds the Context that holds the bundles below\n1 | Character and Wardrobe bundles | Bundles | $character, $look | Every use: Hold across run\n2 | Frame | Wildcard | $_frame | \"1\" [arrive], \"2\" [search], \"3\" [search], \"4\" [fight], \"5\" [fall], \"6\" [rest] fallback. Match variable iteration, hidden\n3 | Frame → Place | Constraint | | One Only cell per beat: arrive → arrive, search → search, …\n4 | Place | Wildcard | $place | train station [arrive], old library [search], rooftops [fight, fall], inn [rest], …\n5 | Frame → Pose | Constraint | | One Only cell per beat\n6 | Pose | Wildcard | $pose | About 30 poses, each tagged with every beat it suits\n7 | Frame → Camera | Constraint | | arrive → establish boost 3; fight → action boost 3; rest → emotion boost 2\n8 | Pose → Camera | Constraint | | lying → from above boost 2\n9 | Camera | Wildcard | $camera | wide shot [establish], close-up [emotion], dutch angle [action], …\n10 | Story beat | Derivation | $beat | IF $iteration equals \"1\" → \"stepping off the night train\"; ELIF matches ^[2-3]$ → \"searching dusty shelves for the letter\"; … ELSE \"asleep by the fire, letter in hand\"\nTemplate:\n$character, $look, $pose, $beat, $place, $camera, $style\n- One Pose list plus a Frame wildcard and an Only constraint beats one pose wildcard per frame: $pose stays random inside its beat, is a real pick that can drive Pose → Camera, and a new frame is one option plus one cell.\n- $iteration is text (\"1\" to \"6\"). Ranges are a matches test like ^[3-4]$. Frames past 6 take the fallback.\n- Test each frame in the Test Runner by pinning iteration. Hold and per-frame overrides only show on the canvas.\n- Per-frame overrides (the edit frame chips) are fine for a one-off tweak, but they live in the workflow and don't travel with the pack."
},
{
"title": "Blueprint: sweep comparison",
"text": "A contact sheet of every style against every light, with the rest of the prompt held still. Modules in running order:\n# | Module | Kind | Writes | Options and settings\nCanvas | WP Context Loop | Node | | Sweep combinations on: Style (4 options) × Light (3) = 12 frames. Hold other picks on. Keep the KSampler on one fixed seed\n1 | Subject | Wildcard | $subject | Held by Hold other picks\n2 | Style | Wildcard | $style | Swept (the first wildcard changes slowest, so it gives the rows)\n3 | Light | Wildcard | $light | Swept (the columns)\n4 | Quality | Fixed values | $quality |\nTemplate:\n$quality, $subject, $light, $style\n- Swept wildcards are pinned, so constraints don't re-weight them. Don't sweep a constraint target expecting the pack's rules: you will see pairs the pack forbids.\n- Turn Hold other picks off when the rest should vary too.\n- WP Image Filter lays a sweep out as a grid, and Send to Loop keeps the frames you pick."
},
{
"title": "Blueprint: seasonal pack",
"text": "One hidden season decides clothes, colours, extras and weather, and prints only inside a phrase. Modules in running order:\n# | Module | Kind | Writes | Options and settings\nLibrary | Colors, Accessories | Vocabularies | | Tagged spring, summer, autumn, winter\n1 | Season | Wildcard | $season | spring, summer, autumn, winter (tagged the same). Hide from prompt\n2 | Season → Colors | Constraint | | Matrix: each season boosts its colours. It reaches @{Colors} inside the outfits too\n3 | Season → Outfit | Constraint | | Only by season\n4 | Outfit | Wildcard | $outfit | @{Colors} wool coat [autumn, winter], @{Colors} linen dress [spring, summer], … fallback\n5 | Seasonal extras | Derivation | $outfit | IF $season equals winter → Append , @{Accessories:winter}; IF summer → Append , @{Accessories:summer}\n6 | Season → Weather | Constraint | | Only by season\n7 | Weather | Wildcard | $weather | light snow [winter], spring rain [spring], … fallback\n8 | Place | Wildcard | $place |\n9 | Scene | Combine | $scene | $place in $season, $weather\nTemplate:\n$subject, $outfit, $scene, $style\n- The extras are a conditional draw: the derivation adds a fresh accessory only in winter or summer. Its pick sets no variable, which is fine because nothing reads it.\n- The Append text starts with \", \". Without it the words run together.\n- For a fixed season on one node, untick the other options on the Season use instead of deleting them."
},
{
"title": "Blueprint: photography",
"text": "Portrait photos where the framing chooses a fitting lens, written as prose for Flux-style models or as short phrases for realistic SDXL models. Modules in running order:\n# | Module | Kind | Writes | Options and settings\n1 | Shot | Wildcard | $shot | close-up portrait [close], head and shoulders [close], half-body [mid], full-length [wide]\n2 | Shot → Lens | Constraint | | close → 85mm boost 3; wide → 35mm boost 2\n3 | Lens | Wildcard | $lens | 85mm lens, f/1.8, shallow depth of field [85mm]; 50mm lens, natural perspective [50mm]; 35mm lens, environmental portrait [35mm]\n4 | Subject | Wildcard or Injector | $subject | Adults with an age: a woman in her 30s with freckles and short auburn hair, …\n5 | Shot → Outfit | Constraint | | close → options tagged footwear exclude\n6 | Outfit | Wildcard | $outfit | One coherent outfit per option\n7 | Light | Wildcard | $light | Rembrandt lighting, butterfly lighting, soft window light from the side, rim light\n8 | Film | Wildcard | $film | Kodak Portra 400, Ilford HP5 black and white (negative vivid colors), …\n9 | Texture | Fixed values | $texture | visible skin texture, natural makeup\n10 | Photo | Combine | $photo | $shot of $subject wearing $outfit, $light, $lens, shot on $film\nTemplate:\n$photo. $texture.\nNegative template:\nlowres, blurry, watermark, plastic skin, $negatives\n- Name the light setup and the lens instead of \"beautiful, perfect, 8k\": that is what moves a photo prompt.\n- Shoes in a close-up waste words and leak colour, so the shot rules them out.\n- On Flux the negative does nothing (CFG 1). See Writing prompts for your model."
},
{
"title": "Blueprint: product shot",
"text": "A product typed on the canvas, shot in a few setups with matching light. Modules in running order:\n# | Module | Kind | Writes | Options and settings\nCanvas | WP Context Injector | Node | $product, $label | a ceramic pour-over coffee dripper; label text, short\n1 | Setup | Wildcard | $setup | hero shot on a seamless backdrop [studio]; flat lay from overhead [flat]; on a marble counter by a window [lifestyle]\n2 | Setup → Light | Constraint | | studio → softbox boost 3; lifestyle: Only window\n3 | Light | Wildcard | $light | studio softbox lighting with a rim light [softbox] fallback; soft natural window light from the side [window]; hard light with long shadows [hard]\n4 | Surface | Wildcard | $surface | reflection on black acrylic, on travertine, on washed linen\n5 | Lens | Wildcard | $lens | macro detail, 50mm at eye level, 45-degree angle\n6 | Shot | Combine | $shot | $product, $setup, $surface, $light, $lens\nTemplate:\nProduct photograph. $shot. The label reads \"$label\".\nNegative template:\ntext, watermark, people, $negatives\n- Quote the label and keep it short. Only text-capable models (Flux, Qwen-Image, Z-Image) spell it well.\n- Keep brand names and trademarks out of the options."
},
{
"title": "Blueprint: rating-aware pack",
"text": "One pack that can render safe or suggestive images, with the register decided once and every slot following it. Modules in running order:\n# | Module | Kind | Writes | Options and settings\n1 | Rating | Wildcard | $_rating | safe (fallback), suggestive. Hidden. Untick the options a node shouldn't use\n2 | Rating tag | Derivation | $rating_tag | IF $_rating equals safe → rating_safe (Pony) or safe (NoobAI, Anima); ELSE → rating_questionable or sensitive\n3 | Rating → Outfit | Constraint | | safe: Only options tagged safe\n4 | Outfit | Wildcard | $outfit | Tagged safe or suggestive\n5 | Rating → Pose | Constraint | | safe: Only options tagged safe\n6 | Pose | Wildcard | $pose | Tagged the same way\n7 | Safe guard | Derivation | $rating_tag | IF $_rating equals safe → Add to negative nsfw, nude, rating_explicit\nTemplate:\n$quality, $rating_tag, $subject, $outfit, $pose, $style\n- The rating tag comes from the same driver as the slots, so they never disagree.\n- The library's content rating (safe / nsfw) is a label for lists and publishing. It changes nothing in a run.\n- On CFG 1 models the negative is ignored, so the constraints and positive wording have to do the work.\n- A pack that can reach explicit content must follow Adult (NSFW) packs, safeguards included."
},
{
"title": "Blueprint: model-retargeting header",
"text": "One pack that writes the right quality and negative tags for whichever checkpoint is loaded. Modules in running order:\n# | Module | Kind | Writes | Options and settings\nCanvas | WP Model Info | Node | $model_family, $model_variant | First in the chain, its model input from the checkpoint loader\n1 | Quality header | Derivation | $quality | IF $model_variant equals pony → score_9, score_8_up, score_7_up, score_6_up, score_5_up, score_4_up, and Add to negative score_4, score_5, score_6. ELIF noobai → masterpiece, best quality, newest, absurdres, highres, negative worst quality, old, early, low quality, lowres. ELIF illustrious → masterpiece, best quality, absurdres, negative worst quality, low quality, bad anatomy. ELIF $model_family equals flux → nothing. ELSE high quality\n2 | Model | Wildcard | $_model | pony, illustrious, noobai, other (fallback). Match variable model_variant, hidden\n3 | Model → Style | Constraint | | pony → photo styles exclude, …\n4 | Style | Wildcard | $style | Tagged by what each model handles\nTemplate:\n$quality, $subject, $outfit, $pose, $style\nNegative template:\n$negatives, watermark\n- The header's negatives ride on $quality, so they reach the negative output only when the template renders $quality.\n- Equals is exact and case-sensitive; Match variable ignores case. The shipped variant names are lowercase.\n- Model Info reads Flux.2 as flux, and newer models (Anima, Z-Image and others) may read under another family name. Pin the variant, or add a variant rule for the file name.\n- Tag models and prose models want different skeletons. Keep one template per family in the Templates library."
},
{
"title": "Blueprint: recipe wildcards",
"text": "The pattern behind most of the blueprints above: a decision whose options are recipes built from vocabularies. Modules in running order:\n# | Module | Kind | Writes | Options and settings\nLibrary | Tops, Bottoms, Dresses, Colors, Props, Accessories | Vocabularies | | Tagged by style (casual, formal), season, place (indoor, outdoor)\n1 | Outfit | Wildcard | $outfit | @{Colors:neutral} @{Tops:formal} with @{Bottoms:formal} [formal]; @{Dresses:casual} [casual]; @{Tops:casual} and @{Bottoms:casual} [casual] fallback\n2 | Props | Wildcard | $props | {2-3$$, $$@{Props:indoor}} [indoor]; {1-2$$, $$@{Props:outdoor}} [outdoor]\n3 | Winter extras | Derivation | $outfit | IF $season equals winter → Append , @{Accessories:winter}\nTemplate:\n$subject, $outfit, $props\n- Lock the recipe (or tick only one option) and its parts still vary, because each reference is a fresh pick.\n- The parts set no variables, so nothing can read or test them. The moment something must (a constraint source, a derivation test, a second template), promote that part to its own wildcard.\n- Constraints still reach the parts: a Season → Tops constraint shapes the @{Tops} inside every outfit. For a narrowing that never changes, a filtered reference like @{Tops:formal} does the same job without a constraint.\n- $var means the same value everywhere. @{ref} is a new pick every time. Write $color twice to match; write @{Colors} twice for two colours."
},
{
"title": "Anti-patterns",
"text": "The traps people hit most, and what to do instead:\n- Reading a pick made inside a reference. A derivation testing $color can't see the colour inside \"@{Colors} dress\". Make it its own wildcard.\n- A constraint whose source only appears inside a reference. It never applies, and WP Debug warns unknown_constraint_source.\n- Expecting two references to match. \"@{Colors} jacket and @{Colors} jeans\" match only by chance.\n- @{} or {a|b} in the Assembler template. The reference prints nothing and the inline pick prints as typed.\n- A fixed value and a wildcard writing the same variable. Whichever runs later wins without a word.\n- {watercolor|oil painting} in a fixed value for real variety. That choice has no weights, tags, constraints, fallback or counts. Make it a wildcard.\n- A derivation that Replaces a variable a constrained wildcard sets (\"if mage, weapon = staff\" next to a Class → Weapon constraint). The constrained pick is thrown away. Use the constraint, with Only if needed.\n- One wildcard per case: $winter_top and $summer_top, or pose_1 to pose_6 for six frames. Use one tagged list and a constraint.\n- A lookup derivation with many equals → Replace branches. That is a constraint on a tagged list (story text on $iteration is the exception).\n- Printing a classify group. $outfit.STYLE renders nothing; only accepts groups can be read.\n- Forcing an option into the wrong tag. Add a new tag instead.\n- A hidden variable in the template, expected to print. Print it through a combine.\n- Append or Prepend text without a separator.\n- Two characters writing the same variable names. Rename the second copy's variables.\n- Match variable on options that hold {a|b} or @{}. They never match.\n- Expecting a sweep to follow the pack's constraints. Swept picks ignore them.\n- Per-frame overrides in a pack you share. They stay in your workflow.\n- Loop seed locks without Override seed. They are ignored.\n- Another family's tags: Pony score tags on Illustrious, or made-up ones like score_9.5. See Writing prompts for your model.\n- Relying on negatives on a CFG 1 model, or on the content rating to filter anything."
},
{
"title": "Works with",
"text": "Writing prompts for your model Adult (NSFW) packs The $variable pipeline Constraints in depth Test Runner"
}
]
},
{
"id": "prompt-writing",
"title": "Writing prompts for your model",
"group": "guides",
"blurb": "How each model family wants its prompt: order, quality tags, negatives and CFG, subjects and prompt types.",
"keywords": [
"prompt",
"model",
"illustrious",
"pony",
"noobai",
"flux",
"z-image",
"qwen",
"anima",
"wan",
"sdxl",
"sd1.5",
"krea",
"ltx",
"chroma",
"cfg",
"break",
"token",
"weights",
"lora",
"trigger",
"inpaint",
"img2img",
"hires",
"adetailer",
"controlnet",
"ip-adapter",
"regional",
"video",
"quality tags",
"score tags"
],
"sections": [
{
"title": "Pick your model first",
"text": "Find your model, then write your options in its style:\n- Tags, comma-separated (1girl, red dress, smiling): Illustrious, NoobAI, Pony V6, Animagine and SD 1.5 anime models.\n- Sentences (a woman in a flowing red dress, smiling): Flux, Krea 2, Chroma, Qwen-Image, Z-Image, SD 3.5, HiDream and video models like Wan and LTX.\n- A bit of both: SDXL base, Pony V7 and Anima.\nTag models also want quality tags (Pony's score tags, Illustrious's \"masterpiece, best quality\") and usually a negative. Most sentence models skip the negative. The sections below go family by family; WP Model Info lets one pack switch between them."
},
{
"title": "Read this first",
"text": "A wildcard pack is a prompt writer. The same pack can render tags for an anime model or sentences for a prose model, but only if you build it with the target family in mind. Most of the work is in three choices: the dialect (tags or sentences), the order of the parts, and whether the model uses a negative at all.\nModel popularity below was gathered on 2026-10-15 from ComfyUI's official template usage, Civitai ecosystem counts and one Hugging Face download report. Civitai and Hugging Face couldn't be read directly, so the numbers are rough. This field moves fast: recheck them before you rely on them.\nRank | Family | Why | Depth here\n1 | Illustrious XL (v0.1 to v3.5, and merges) | The largest LoRA ecosystem on Civitai (201K+) | In depth\n2 | Pony Diffusion V6 (V7 on AuraFlow) | 103K+ models, 2.37B+ images on Civitai | In depth\n3 | Flux.1 and Flux.2 (Dev, Klein) | 23 official ComfyUI templates, many LoRAs | In depth\n4 | Z-Image Turbo and Base | The second most opened single ComfyUI template | In depth\n5 | Qwen-Image and Qwen-Image 2.1 (and Edit) | The most opened open-weights template family | In depth\n6 | NoobAI-XL (eps and v-pred) | 8K+ resources, the strongest Danbooru coverage | In depth\n7 | Wan 2.2 (video) | 12K+ template opens | In depth\n8 | SDXL base and realistic SDXL fine-tunes | 2.27M Hugging Face downloads a month | Short\n9 | Krea 2 | 10.6K template opens, released June 2026 | Short\n10 | LTX-2.x (video) | 26K template opens across versions | Short\n11 | Anima | New and small (733 template opens) but rising among anime users | In depth\n12 | SD 1.5 | About 1.6M Hugging Face downloads a month, a legacy LoRA base | Short\n13 | Chroma, SD 3.5, HiDream, Animagine, Kolors, Neta Lumina, HunyuanImage, Playground, PixArt, Stable Cascade | Little data | One table row each\nFor your pack: decide your target family before you write options. Tag models want options like \"red dress\"; prose models want \"a flowing red silk dress\". If you serve both, keep options as short phrases and let the template and the model header do the rest."
},
{
"title": "How the text encoder reads your prompt",
"text": "Chunks\nCLIP models read 75 tokens at a time. A longer prompt is cut into 75-token chunks that are encoded apart and joined. Attributes that land in different chunks bind loosely, so a long tag prompt quietly loses structure. 40 to 70 tags is already two or three chunks; most good Illustrious prompts are 20 to 40 tags. T5 and LLM encoders don't chunk.\nEarly words\nEvery family gives early words more weight. Put the subject first.\nBREAK\nIn A1111 and Forge, BREAK starts a new chunk, which keeps one subject's tags together. It is not core ComfyUI syntax: the plain CLIP Text Encode node treats it as a word. Some node packs support it; the core way is two encodes joined with Conditioning (Concat). It means nothing to T5 or LLM encoders.\nWeights\n\"(word:1.2)\" raises a word, \"(word:0.8)\" lowers it. ComfyUI blends weighted words against an empty prompt, so the same number can feel different from A1111. 1.05 to 1.4 is the useful range; above 1.5 it tends to burn. Qwen-Image, Z-Image and Flux.2 switch weights off, so the brackets reach the model as text. Escape real parentheses: \"name \\(series\\)\".\nBinding\nModels mix up which words go with which noun. Write each colour next to its noun (\"red dress\"; on tag models the compound tag), use two or three colours per image, and never repeat a tag: a duplicate acts like an uncontrolled weight.\nLoRA triggers\nA trigger word wakes a LoRA. Take it from the model page and keep it exactly as written. Start at strength 0.7 to 0.8; stack two or three at 0.4 to 0.8 each, under about 2 in total. A LoRA only works on the base family it was trained for.\nEmbeddings\n\"embedding:name\" (or \"(embedding:name:1.2)\") in the text. Like LoRAs, they belong to one family. Negative embeddings such as EasyNegative go in a negative. With the Embeddings and LoRAs autocomplete on (Settings › Autocomplete), typing part of a name, \"embedding:\", \"<lora:\" or just \"<\" and picking a file writes the whole reference, \"<lora:path:1.0>\" with both brackets.\nFamily | Text encoder | Length | 75-token chunks | Weights\nSD 1.5 | CLIP ViT-L | 75 tokens a chunk | yes | yes\nSDXL, Pony V6, Illustrious, NoobAI, Animagine | CLIP ViT-L + OpenCLIP bigG | 75 tokens a chunk | yes | yes\nFlux.1, SD 3.5, Chroma | T5-XXL (with CLIP on Flux.1 and SD 3.5) | long (Flux.1 up to 512 tokens) | no | parsed, weak\nHiDream | CLIP + T5 + Llama 3.1 | about 128 tokens | no | parsed, untested\nFlux.2 | Mistral-family LLM | long | no | off (literal text)\nQwen-Image | Qwen2.5-VL | long, keep under 200 words | no | off (literal text)\nZ-Image | Qwen3-4B | long | no | off (literal text)\nAnima | Qwen3 0.6B | long | no | untested\nWan 2.x | umT5-XXL | 512 tokens | no | parsed, untested\nFor your pack:\n- Keep weight brackets out of option text in a pack meant for several families. Write emphasis in words, or weight in a family-specific fixed value.\n- On CLIP models, a whole prompt of 20 to 40 tags fits the budget. Use the variation budget below to keep it there.\n- Put a LoRA's trigger inside the option that stands for the LoRA, so they always come together. Never let the Cleaner's blocklist or fuzzy dedupe touch trigger words."
},
{
"title": "Negatives and CFG",
"text": "A negative prompt only works when the sampler runs real classifier-free guidance, which means CFG above 1. Distilled and few-step models run at CFG 1, where the negative is not used at all.\nFamily | Negative used? | Settings | Notes\nSD 1.5, SDXL, Pony, Illustrious, NoobAI, Animagine | yes | CFG about 4 to 7 | Short standing negative plus option negatives.\nFlux.1 dev and schnell | no | CFG 1; Flux guidance (about 3.5) is a different knob | Use positive wording, or NAG.\nFlux.2 (Dev, Klein) | no | no negative prompt | Use positive wording.\nZ-Image Turbo | no | guidance 0 to 2 | Put exclusions in positive wording or constraints.\nZ-Image Base | yes | CFG 3 to 5 |\nQwen-Image, Qwen-Image 2.1 | yes, at true CFG above 1 | true_cfg 4; the default negative is a single space | Lightning setups at CFG 1 ignore it.\nAnima Base / Turbo | yes / no | CFG 4 to 5 / CFG 1 |\nChroma | yes | guidance about 3 | De-distilled, so real negatives work.\nHiDream Full / Dev and Fast | yes / no | CFG about 5 / CFG 1 |\nSD 3.5 Large and Medium / Large Turbo | yes / no | CFG 3.5 to 7 / guidance 0 to 1 |\nWan 2.2 | yes at CFG above 1 | 4 to 8 step accelerated setups run CFG 1 | Then the anti-stillness negative does nothing.\nKolors, Playground v2.5, HunyuanImage 2.1 | yes | CFG about 3 to 6.5 |\nLightning, Hyper, LCM, Turbo variants | no at CFG 1 | CFG-preserving Hyper variants run CFG 5 to 8; SDXL Flash 2.5 to 3.5 | Check the variant's card.\nKrea 2 | not documented | Raw and 8-step Turbo | Test it; assume Turbo ignores it.\nNegatives work best on a concept the prompt actually invites (\"hat\" when the outfit tends to bring one). A long generic negative dilutes itself and can cancel things you asked for. To move the camera, say it in the prompt: a negative is not a composition tool. The built-in NAGuidance node brings negatives back on some distilled models.\nFor your pack:\n- Every pack feature that relies on negatives (option negatives, $negatives, a safe guard, the negative template) needs CFG above 1. Say so in the pack's description.\n- For CFG 1 models, put exclusions into positive wording (\"an empty street\", \"plain background\") and into constraints.\n- Option negatives follow usage: they reach the negative output only when the template renders that variable. That is the right home for a word one option invites."
},
{
"title": "Illustrious XL",
"text": "An SDXL fine-tune by OnomaAI, trained on Danbooru images with Danbooru tags. It is the default choice for anime characters without a LoRA, because it knows thousands of characters by their tag. v2.0 handles 512 to 1536 px and reads short sentences better; v3.5 is a v-prediction model.\nPrompt form\nDanbooru tags, comma separated, with spaces instead of underscores (\"long hair\", not long_hair). Parentheses are weight syntax, so escape them in names: \"name \\(series\\)\". Settings › Autocomplete can write picked tags this way for you (Write tags with spaces, Escape brackets in tags). Characters work by tag (\"hatsune miku, vocaloid\"). Matching canon colours reinforce a character; contradicting them makes the two fight.\nOrder\nQuality, count (1girl, solo), character, series, artist, appearance, outfit, pose, expression, setting, light, camera, style. NoobAI's template (below) is the order the whole family follows.\nQuality tags\n\"masterpiece, best quality, amazing quality, very aesthetic, absurdres\", or the shorter \"masterpiece, best quality, absurdres\". These are community habits, not an OnomaAI spec. Some merges repeat them at the end.\nRating\nDanbooru rating words (general, sensitive, questionable, explicit) or the merge's own form. Read the merge's page.\nNegative\n\"worst quality, low quality, lowres, bad anatomy, bad hands, extra digits, jpeg artifacts, signature, watermark, username\". For more push, weight a tag (\"(bad hands:1.2)\") instead of repeating it.\nSettings\nEuler a or DPM++ 2M, CFG about 4 to 7, 20 to 30 steps, SDXL sizes (1024×1024, 832×1216, 1216×832). v-pred versions need v-pred sampling settings.\nMistakes\nPony score tags (they do nothing here); five artist tags at once (the styles mush together); tags renamed on Danbooru after the model was trained; 60 or more tags; a colour on its own (\"red, dress\") instead of next to its noun (\"red dress\").\nFor your pack:\n- Write options as tags. Keep a whole prompt at 20 to 40 tags, which is one or two 75-token chunks.\n- Put the count tag first in the template, before $character.\n- Keep the quality header in a fixed value, or in the model header derivation from Recommended pack structure.\n- Prefer one named outfit (\"school uniform\", \"sundress\") per option to a pile of garment tags.\n- Give each colour its noun inside the option (\"red dress\", \"blue eyes\") and keep two or three colours per image."
},
{
"title": "Pony Diffusion V6 XL",
"text": "An SDXL fine-tune trained on about 2.6M images, balanced across anime, cartoon, furry and pony sources and across safe, questionable and explicit ratings. Artist names were removed from training. About half the images had natural-language captions, so short sentences work, but tags are more reliable for specifics.\nScore string\nStart every prompt with the full string \"score_9, score_8_up, score_7_up, score_6_up, score_5_up, score_4_up\". The model card says a bare score_9 is much weaker. Never invent scores like score_9.5.\nSource tags\nsource_anime, source_cartoon, source_furry, source_pony, right after the scores. They steer the style. For human anime, put \"source_furry, source_pony\" in the negative.\nRating tags\nrating_safe, rating_questionable, rating_explicit, after the source tag. Exactly one per prompt.\nOrder\nScores, source, rating, count, character, appearance, outfit, pose, expression, setting, light and camera, style.\nSettings\nClip skip 2 (CLIP Set Last Layer at -2 in ComfyUI). Without it you get low-quality blobs.\nNegative\nNot needed for good results. Common additions: \"score_4, score_5, score_6, source_furry, source_pony, watermark, signature, artist name\". It sometimes draws fake signatures that negatives can't fully remove.\nRealism\nV6 itself is weak at photos. Realism comes from merges and LoRAs, which often want their own tags (such as a photo source tag). Follow the merge's page. For safe images on a mixed merge, put rating_safe in the prompt and rating_explicit in the negative.\nPony V7\nBuilt on AuraFlow, so Model Info reads it as auraflow. Treat it as a sentence model with an optional tag tail: special tags, then a plain description of the subject, then style, then extra tags. Its special tags are much weaker than V6's, so don't reuse the V6 score string.\nMistakes\nA shortened score string; score tags in the middle; artist names; clip skip 1; Pony LoRAs on an Illustrious checkpoint (different bases, they don't transfer cleanly).\nFor your pack:\n- The score string is a constant: a fixed value, or the pony branch of the model header derivation.\n- Make the rating tag come from a $rating driver so it never disagrees with the outfit and pose (see Adult (NSFW) packs).\n- A source tag can be a small wildcard that a style driver constrains, so \"source_anime\" and \"source_cartoon\" never mix.\n- Model Info detects V6 by \"pony\" or \"pdxl\" in the file name. Name your merges that way, or add a variant rule."
},
{
"title": "NoobAI-XL",
"text": "An Illustrious-based model by Laxhar, trained on Danbooru and e621. It comes in eps and v-prediction versions. Its card gives the most explicit tag order in the anime family.\nTemplate\n\"<1girl/1boy/1other>, <character>, <series>, <artists>, <special tags>, <general tags>, <other tags>\". Special tags are quality, aesthetic and date.\nQuality tags\nRanked by percentile: masterpiece above the 95th, best quality 85 to 95, good quality 60 to 85, normal quality 30 to 60, worst quality 30 and below. \"very awa\" is the top 5% by aesthetic score; \"worst aesthetic\" the bottom 5%.\nDate tags\n\"year 2021\", or a period: newest (2021 to 2024), recent, mid, early, old.\nDefault prefix\n\"masterpiece, best quality, newest, absurdres, highres, safe,\". Some merges add \"very aesthetic, amazing quality, year 2024, very awa\".\nDefault negative\n\"nsfw, worst quality, old, early, low quality, lowres, signature, username, logo, bad hands, mutated hands, mammal, anthro, furry, ambiguous form, feral, semi-anthro\". The e621 words keep furry styles out.\nRating\nsafe in the prompt and nsfw in the negative by default. Adult work flips them (see Adult (NSFW) packs). Other levels: sensitive, explicit.\nSettings (v-pred)\nEuler only (\"other samplers will not work properly\"), CFG 4 to 5, 28 to 35 steps, about 1 MP. A v-pred checkpoint needs v-prediction sampling (ModelSamplingDiscrete set to v_prediction with zsnr) unless the loader sets it.\nMistakes\nAsking for a retro look (year 2005) while the negative holds \"old, early\"; animal words pulling anthro styles; Pony score tags.\nFor your pack:\n- Model Info names it noobai when \"noob\" is in the file name. The variant rule sits above Illustrious because NoobAI files often mention both.\n- Put the prefix in the noobai branch of the model header, and the default negative as that branch's Add to negative.\n- If an era wildcard rolls date tags, take \"old, early\" out of the negative, or the two cancel.\n- For real animals, add \"no humans, animal focus\" and keep the e621 words in the negative."
},
{
"title": "Flux.1 and Flux.2",
"text": "Black Forest Labs' models read full sentences. Flux.1 dev and schnell use CLIP-L plus T5-XXL and are guidance-distilled, so they run at CFG 1. Flux.2 (Dev and Klein) uses a Mistral-family text encoder.\nPrompt form\nFull sentences, not tag lists. BFL's frame is Subject, Action, Style, Context (setting, light, time, mood). The model pays more attention to early words, so the subject goes first. 30 to 80 words suits most scenes.\nNegative\nNone. At CFG 1 the negative has no effect; wire an empty encode or ConditioningZeroOut. Say what you want instead (\"an empty street\"). If you need a real negative, the built-in NAGuidance node restores it.\nWeights\nFlux.1 parses \"(word:1.2)\" but the effect is weak; rephrase (\"a vivid red dress, the brightest thing in the frame\"). Flux.2 turns weights off, so the brackets reach the model as text.\nText in the image\nIn double quotes, short: a sign that reads \"OPEN LATE\".\nTwo boxes\nCLIPTextEncodeFlux has clip_l and t5xxl boxes. The same text in both is fine; some put a short tag summary in clip_l.\nFlux.2 extras\nHex colours tied to an object work (\"a #1E90FF umbrella\"). Structured JSON prompts are supported; the field names in community guides are conventions, not a fixed schema.\nMistakes\nTag soup; weight stacks; negative boxes that do nothing; \"masterpiece, 8k\" (mostly ignored); \"beautiful, perfect\" instead of describing light and texture, which gives the plastic-skin look.\nAdult content\nBase Flux.1 lacks explicit anatomy. It is a poor default for an explicit pack.\nFor your pack:\n- Write options as phrases (\"a weathered leather jacket\"), not tags, and join them with a combine into sentences.\n- A prose skeleton: \"$subject, $action. $outfit. $place, $light. $camera, $style.\"\n- Option negatives and $negatives do nothing here. Put exclusions into positive wording and constraints.\n- Keep weights out of option text for Flux.2.\n- A Flux.2 JSON prompt can be a combine that writes the object from variables. Keep the keys stable and keep \"|\" out of it, since {a|b} is inline-pick syntax.\n- Model Info reads both Flux.1 and Flux.2 as flux. Use $model_variant (a rule on the file name) to tell them apart."
},
{
"title": "Z-Image (Turbo and Base)",
"text": "Alibaba Tongyi's model with a Qwen3-4B text encoder (loaded with the Lumina2 clip type in ComfyUI). Turbo is the popular one: about 9 steps, and no real CFG.\nPrompt form\nLong, detailed natural language works best. It renders text well; quote it.\nTurbo\nOfficial guidance is 0; ComfyUI users run CFG 1 to 2. The negative does nothing.\nBase\nCFG 3 to 5, and negatives work.\nWeights\nOff. \"(word:1.2)\" reaches the model as literal text. Write emphasis in words.\nMistakes\nTag lists; weight brackets; relying on a negative on Turbo.\nFor your pack:\n- Long rolled phrases are fine here, so recipe wildcards with several @{} parts suit it.\n- On Turbo, say \"a clean plain background\" or \"plain clothing\" in the prompt, or use a constraint, instead of a negative.\n- Model Info may read it as lumina2 or under its own class name. Check the Model Info node's output once and pin the variant."
},
{
"title": "Qwen-Image and Qwen-Image 2.1",
"text": "Alibaba's models use a Qwen vision-language encoder. ComfyUI wraps your prompt in a system line: \"Describe the image by detailing the color, shape, size, texture, quantity, text, spatial relationships of the objects and background\". That line is a good hint at the order it wants. Qwen-Image 2.1 (2026-09-20, about 7B) is one model for generation and editing, takes up to 10 reference images and outputs 2K.\nPrompt form\nSentences, under 200 words. The style can lead (\"Art poster design: ...\"), then the subject, background, palette and finish. Spatial wording works: \"on the left ..., on the right ..., facing each other\".\nText\nIts strength, including Chinese and Japanese. Quote the text, give its position and style, and never translate it.\nSuffix\nThe official examples end with \", Ultra HD, 4K, cinematic composition.\"\nNegative\nThe default is a single space. It works at true CFG above 1 (the official setting is true_cfg 4, 50 steps). Lightning setups at CFG 1 ignore it.\nWeights\nOff: brackets reach the model as text.\nEditing\nSay what to change and what to keep: \"change the jacket to red leather, keep the face, pose and background\". In 2.1 a transparency (RGBA) cue gives cut-outs.\nLicence\n2.1 is under a research licence. Check the card before commercial use.\nFor your pack:\n- A prose skeleton with the caption in quotes: $style: $subject wearing $outfit, $place. A sign reads \"$caption\". $light.\n- Keep the suffix as a fixed value, so a model header can drop it for other families.\n- For edit workflows, a wildcard fills the \"change\" slot and the \"keep\" part is fixed text in the template.\n- Model Info reads Qwen-Image as qwen_image. Check 2.1 once on the node and pin the variant if it reads differently."
},
{
"title": "Anima",
"text": "A 2B anime model by CircleStone Labs with Comfy Org, built on NVIDIA Cosmos-Predict2, with a Qwen3 0.6B text encoder and the Qwen-Image VAE (about 7 GB of VRAM). Base v1.0 came out on 2026-05-15; Turbo and Aesthetic v1.0 in July 2026.\nPrompt form\nTags and sentences mixed, in any order. Write at least two sentences. Name the character first, then describe them.\nPrefix\nThe base is not aesthetic-tuned, so start with \"masterpiece, best quality, score_7, safe,\" and change the rating tag as needed. Score tags use underscores, like Pony V7.\nNegative\n\"worst quality, low quality, score_1, score_2, score_3, artist name\".\nBase settings\nCFG 4 to 5, 30 to 50 steps, the er_sde sampler.\nTurbo settings\nCFG 1 (the negative is ignored), 8 to 12 steps.\nWeak spots\nPhotorealism and long text in the image.\nLicence\nNon-commercial. Check the card before you share or sell output.\nFor your pack:\n- The model header's anima branch writes the prefix. The rating word comes from $rating, not typed into the prefix.\n- Options can be tags; the template can add a sentence of context around them.\n- Model Info probably reads it as cosmos. Pin the variant to anima, or add a rule matching \"anima\" in the file name.\n- On Turbo, the safe guard and every exclusion must be positive wording or constraints."
},
{
"title": "Wan 2.2 (video)",
"text": "Wan uses a umT5-XXL encoder with room for 512 tokens. A video prompt needs things an image prompt never has: motion, a camera move, and a negative against stillness.\nPrompt form\nProse, 80 to 100 words. Simple, direct verbs. One clear action per clip; several moves muddy the result.\nOrder\nStyle or medium, subject, appearance and outfit, expression, pose and motion, background, texture, then shot size and angle, then the camera move.\nCamera\nOne move per clip: slow push-in, pan left, tracking shot, orbit, crane up, handheld. Say \"static camera\" when you want none.\nNegative\nThe official default is a long Chinese list that fights garish colour, overexposure and, unlike any image negative, stillness (\"static\", \"motionless\"). Copy it as is. 4 to 8 step accelerated setups run CFG 1 and ignore it.\nImage to video\nThe image already supplies the look. Prompt the motion, not the appearance: subject action, then one camera move, then pace.\nLanguage\nThe first-last-frame model was trained mainly on Chinese pairs, so Chinese prompts are recommended there.\nFor your pack:\n- Add an $action wildcard and a $camera_move wildcard that image packs don't have.\n- Hold $character across a Loop run and let $action and $camera_move roll per clip, so each frame is one shot of a shot list.\n- Keep the official negative in a fixed value, and only count on it at CFG above 1.\n- Model Info reads it as wan."
},
{
"title": "SDXL base and realistic SDXL fine-tunes",
"text": "Two CLIP encoders, 75-token chunks, about 1 MP sizes (1024×1024, 832×1216, 1216×832).\nPrompt form\nShort natural sentences plus style keywords. Keep the subject in the first chunk.\nOrder\nSubject and action, appearance, setting, light, camera, style.\nNegative\nShort: \"lowres, blurry, watermark, text, deformed\". Don't paste a whole SD 1.5 negative.\nTwo boxes\nCLIPTextEncodeSDXL has text_g and text_l. Use the same text in both; the labels differ between tools, so don't rely on a style box and a content box.\nFor your pack: Phrase options (\"a linen summer dress\") work on both realistic fine-tunes and base. Base SDXL is weak at explicit anatomy; adult work happens on fine-tunes."
},
{
"title": "SD 1.5",
"text": "One CLIP encoder, 512 px native. Most real use is through fine-tunes: anime ones trained on Danbooru tags, photo ones on captions.\nPrompt form\nComma-separated keyword phrases: subject, medium, style, details, colour, light. You don't need every kind.\nNegative\nBenefits most from a standing negative (\"lowres, bad anatomy, bad hands, extra digits, worst quality, low quality, jpeg artifacts, watermark, signature, text\") or a negative embedding such as EasyNegative.\nSettings\n512×512 or 512×768, then hires fix. Anime fine-tunes use clip skip 2.\nMistakes\n\"trending on artstation, 8k, octane render\" filler eating the short window; more than one subject; sizes far from 512.\nFor your pack: Keep option text short; the whole prompt should fit one or two chunks. A negative embedding goes in the standing negative or an option's negative, so it follows usage."
},
{
"title": "Krea 2",
"text": "A 12.9B model with Raw and 8-step Turbo versions, released 2026-06-22 and native in ComfyUI. No official prompting guide was found, so this is inferred.\nPrompt form\nTreat it as a natural-language model: Flux-style sentences.\nNegative\nTest it. Assume the Turbo version ignores it.\nFor your pack: Build it like a Flux pack. Model Info has no rule for it yet, so check what the node reads and pin the variant. Revisit this section as guides appear."
},
{
"title": "LTX-2.x (video)",
"text": "Lightricks' video models (2.3 and 2.5 are among the most opened ComfyUI templates). No primary prompting source was checked for this guide, so this follows the Wan rules.\nPrompt form\nProse that states the motion and one camera move, as for Wan.\nNegative\nCheck the template's CFG. Distilled setups at CFG 1 ignore it.\nFor your pack: Use the same $action and $camera_move slots as a Wan pack. Model Info reads it as ltxv."
},
{
"title": "Chroma",
"text": "An 8.9B model derived from Flux schnell, using only T5-XXL. It is de-distilled, so it runs with real CFG.\nPrompt form\nLong natural-language descriptions; a short tag tail also works (\"cold color palette, muted colors, detailed\").\nNegative\nWorks: \"low quality, blurry, deformed, ugly\" plus per-concept words. The card's example runs guidance 3 and 40 steps.\nAdult content\nReported to be trained without a safety filter. Check the card.\nFor your pack: Write it like Flux, but option negatives and $negatives do work here. Model Info reads it as chroma."
},
{
"title": "Other families",
"text": "Family | Prompt | Negative | Notes\nSD 3.5 Large / Medium | Sentences; composition terms (bird's eye view, close-up, wide-angle shot); text in double quotes, short | Start with none, add 5 to 15 words when a defect shows; Large Turbo has none | Medium: CFG 3.5 to 7, 28 to 40 steps. Large Turbo: 4 steps, guidance 0 to 1. Filtered base, little explicit capability\nHiDream-I1 (Full, Dev, Fast) | Sentences, front-loaded; about 128 tokens, the tail is lost | Full only | Full: CFG about 5, 50 steps. Dev and Fast are distilled: CFG 1\nAnimagine XL 3.1 | Tags: count, character, series, then the rest; prefix \"masterpiece, best quality, very aesthetic, absurdres\" | Its default holds \"oldest, early\", which fights year tags | Rating words safe, sensitive, nsfw, \"explicit, nsfw\". Model Info variant animagine\nAnimagine XL 4.0 | Tags: count, character, series, rating, the rest, then quality LAST (\"masterpiece, high score, great score, absurdres\") | \"lowres, bad anatomy, bad hands, worst quality, low score, ...\" | CFG 4 to 7 (5), 25 to 28 steps, Euler a. The one popular tag model with quality at the end\nKolors | Chinese or English sentences (ChatGLM3 encoder) | Yes | Guidance about 5 to 6.5, 25 steps\nNeta Lumina / NetaYume | Needs a system prompt (Danbooru, natural-language or structured variant); later NetaYume data is tags only | Yes | res_multistep, linear_quadratic, 30+ steps, CFG 4 to 5.5. Keep the system prompt in the Templates library\nHunyuanImage 2.1 | Concise sentences; a refiner stage and a prompt enhancer | For artefacts |\nPlayground v2.5 | SDXL-like sentences and keywords | Yes | Guidance about 3\nPixArt, Stable Cascade | SDXL-era prompting, untested | Untested | No data found\nHunyuanVideo | Sentences in its encoder's order: theme and subject, attributes and positions, actions and motion, background, light, style, then camera angle and moves | Short artefact list | One clear action per clip\nFor your pack: for any family not listed, start from its model card's example prompts, copy their order into an Assembler template, and save it in the Templates library. Fixed strings a model needs (a system prompt, a suffix) are fixed values or templates, never wildcards."
},
{
"title": "Section order per family",
"text": "Family | Dialect | Order | Quality tags\nSD 1.5 (base and photo) | keywords | count, character, appearance, outfit, pose, expression, setting, light, camera, style, quality | end\nSD 1.5 anime fine-tunes | tags | quality, count, character, appearance, outfit, pose, expression, setting, light, camera, style | start\nSDXL base | short sentences | subject and action, appearance, outfit, expression, setting, light, camera, style | optional, end\nPony V6 | tags | score string, source, rating, count, character, appearance, outfit, pose, expression, setting, light, camera, style | start, full string\nPony V7 | sentences plus tags | special tags, plain description, style, extra tags | start (weak)\nIllustrious | tags | quality, count, character, artist, appearance, outfit, pose, expression, setting, light, camera, style | start\nNoobAI | tags | quality and date prefix, count, character, series, artist, general tags, other | start\nAnimagine 4.0 | tags | count, character, series, rating, everything else, quality | END\nAnima | tags and sentences | prefix with score and rating, character, then description | start\nFlux.1, Flux.2, Krea 2, Chroma | sentences | subject, action, style, context (setting, light, camera); quoted text | none\nQwen-Image | sentences | style (may lead), subject, appearance, outfit, pose, positions, setting, palette and light, camera, quoted text, suffix | suffix\nZ-Image | long sentences | subject first, then the scene in detail | none\nWan 2.2, LTX | sentences | style, subject, appearance, expression, motion, background, shot, camera move | none\nOne slot order serves every family. Only the head and the tail change, and the sentence families join the slots into clauses instead of a tag list:\n# | Slot | Notes\nHead | Quality or score tags, source, rating | Family adapter: a fixed value or the model header derivation\n1 | Count and framing | 1girl, solo or \"a woman\"\n2 | Identity | Named character and series, or an original character's fixed traits\n3 | Appearance | Face, eyes, hair, skin, body, an adult marker\n4 | Outfit | One coherent outfit, or a clothing state\n5 | Pose or action | Plus motion for video\n6 | Expression and gaze |\n7 | Setting | Plus props\n8 | Light, time, weather |\n9 | Camera and framing | Plus the camera move for video\n10 | Style, medium, artist |\nTail | Quality last (Animagine 4.0), the Qwen-Image suffix | Family adapter\nFor your pack: make one variable per slot, in this order, and write one Assembler template per dialect (one tag skeleton, one prose skeleton). Save both in the Templates library. The head and tail come from the model header (see Recommended pack structure), driven by WP Model Info."
},
{
"title": "What WP Model Info reports",
"text": "WP Model Info writes $model_family from the model's class and $model_variant from rules on the file name. The family list was written before some newer models, so check what the node reads for yours once:\nModel | $model_family | $model_variant\nSDXL and its fine-tunes | sdxl | noobai, pony, illustrious or animagine from the file name; others empty\nSD 1.5 | sd15 |\nSD 3.5 | sd3 |\nFlux.1 and Flux.2 | flux (both) | Add a rule to tell them apart\nChroma | chroma |\nPony V7 | auraflow |\nHiDream | hidream |\nQwen-Image | qwen_image | Check 2.1 once\nZ-Image | probably lumina2 | Check once and pin\nAnima | probably cosmos | Pin, or add a rule for \"anima\"\nWan, LTX, HunyuanVideo | wan, ltxv, hunyuan_video |\nKrea 2, Kolors, HunyuanImage 2.1 | the model's class name in snake_case | Check once and pin\nFor your pack: drive the header from $model_variant where you can, with $model_family as the fallback. A merge without \"noob\", \"pony\" or \"illustrious\" in its file name is not detected: rename it, add a rule, or pin the variant on the node."
},
{
"title": "Subjects",
"text": "Subject | What to name, in order | In your pack\nPortraits (realistic) | Framing first (close-up, head and shoulders, half-body), then identity with an adult age (\"a woman in her 30s\"), skin texture, one outfit, expression and gaze, the light setup, then lens and aperture | Constrain outfit by framing: no shoes in a close-up. \"looking at viewer\" is a very strong default; vary it on purpose\nAnime characters | Count tag first; character plus series tag; for an original character, a fixed bundle of hair colour, length, style, eye colour and one accessory | Hold the identity bundle across a run. Matching canon traits help; contradicting them fights the character\nSeveral characters | A count tag and clearly different traits each; on sentence models \"on the left ..., on the right ...\" | Two bundle copies with renamed variables; for a strong split, one Assembler per region. Never draw two people's outfits from one shared pick\nFashion | One named outfit, or up to 3 or 4 layers from the base outward, one colour each, accessories last; say open or closed for outerwear | Constraints for season, formality and era. One top and one bottom, or one piece. Footwear only when the frame shows legs\nLandscapes | Foreground, midground, background, sky; time of day and weather are the strongest mood levers; wide lens for vast scenes, telephoto for stacked ridges | On anime tag models add no humans, scenery, or a person appears. One season per image\nArchitecture | Style, city, hour; two-point perspective or \"vertical lines straight\"; named materials (board-formed concrete, weathered copper) | \"tilt-shift\" gives the toy-miniature look. Negative people, cars, text for clean renders\nInteriors | Room, style, materials, palette, light, mood; wide angle at eye level | A material wildcard tagged by style, and a Style → Material constraint\nAnimals | Species, breed, coat, age (puppy or adult), action, setting; wildlife framing (telephoto, eye level) | On Pony and NoobAI, animal words can pull anthro styles: add no humans, animal focus and keep the furry words in the negative\nFood | Dish, plating, garnish, surface and props, light direction, palette, angle (45-degree or overhead flat lay) | Name light by direction and colour temperature (\"window light from the side\", \"warm 3200K tungsten\")\nProducts | Product, material, surface, light setup (softbox, rim), background, lens (macro, 85mm) | Keep brand names and trademarks out. Quoted label text on text-capable models only\nPhotography vocabulary | Shot size, angle, lens (35, 50, 85, 135 mm, macro), aperture and depth of field, light setup (Rembrandt, butterfly, rim, softbox), film stock | Make lens and light their own wildcards and let a Shot → Lens constraint pair them\nSci-fi and fantasy | One or two strong world nouns, then specifics; one material language for armour | Don't stack genres (\"steampunk cyberpunk solarpunk\"). Keep real nouns and invented nouns in separate vocabularies\nPixel art | \"pixel art style\", a pixel-art LoRA at 0.6 to 0.8, about 128 px, then a nearest-neighbour downscale | Keep option text short; fine detail words get lost\nManga and comics | monochrome, greyscale, screentone, spot colour, 4koma for strips | A fixed value for the manga look plus an exclude constraint against colour wildcards\nVehicles | Make or type, era, colour and finish, angle (three-quarter front), setting, motion blur | An Era → Setting constraint\nAbstract, patterns, textures | Medium, palette, motif, symmetry; \"seamless tile\" for patterns | No subject wildcard at all\nLogos and icons | Flat, vector, limited palette, centred, plain background | Text-capable models only; quote any text\nCreatures | Body plan, skin, features, a scale reference, habitat | A Habitat → Body plan constraint keeps them believable\nMecha and hardware | Frame type, armour style, weapons, markings, pose, setting | Tag models know mecha, science fiction\nHorror | Atmosphere (fog, low key), subject, setting, palette | Gore is an adult register: label the pack nsfw and gate it behind a $rating driver\nConcept art | \"concept art, matte painting\", composition terms (establishing shot), mood | Sentences on prose models\nChildren's book illustration | An art style, not a subject age: watercolour or gouache, soft palette, simple shapes; animal or adult characters by default | Never mix it with adult vocabulary. Add a hard exclude constraint from this style to any adult register\nHistorical | Era, region, clothing by era, architecture | An Era → Clothing constraint. No named real people\nCosplay | \"cosplay\" plus the character name, adult anchors | No real cosplayers or other real people by name or likeness\nTypography | The text in quotes, then font style, placement and background | \"$caption\" in a template works. Keep captions short: long rolled text turns to gibberish on SDXL-family models. Qwen is strong for CJK\nFor your pack: the subject decides which slots exist. A landscape pack has no outfit slot; a product pack has no expression. Build only the slots the subject needs and constrain the ones that depend on framing."
},
{
"title": "Prompt types beyond text to image",
"text": "Type | How to prompt | With Wildcard Pipeline\nimg2img | Describe the target image, not the change. Denoise 0.3 to 0.5 keeps the composition; 0.6 to 0.8 restyles | Roll style and light; keep the subject in fixed values or held picks\nInpainting | Start near denoise 0.5. With \"only masked\", describe only what goes in the mask, but keep the full image's style tags so it blends. Inpaint checkpoints take higher denoise | WP Image Filter: paint a mask and edit the prompt in the picker; the edited prompt is re-encoded when its clip input is wired\nOutpainting | Keep at least 20% overlap. Describe the new scene and background, not the subject again. Flux Fill runs at denoise 1 | A second Assembler that renders only $place, $light and $style\nHires fix (two passes) | Same prompt as the first pass, denoise 0.3 to 0.5 (up to 0.6), latent upscale at most 2× | WP Var → Bool on a $hires variable switches it per pick; WP Image Filter can gate it on your pick\nADetailer and FaceDetailer | A blank detailer prompt uses the main one. [SEP] gives one prompt per detected object, in no fixed order; [SKIP] skips one; [PROMPT] inserts the main prompt | Feed it from a second Assembler that renders only face and expression variables. With several people the order can swap, so prefer one shared face prompt. In adult packs it must carry the adult anchors\nControlNet | Start at weight 0.5 to 0.7; pick balanced or \"my prompt is more important\" | The pose comes from the control image: hide $pose, or switch it off with a derivation on an injected $controlnet value\nIP-Adapter and style transfer | Pick the style transfer or precise weight type; the text should still name the subject. Face models mostly ignore text: CFG 3 to 4 | With a style reference, hide the $style wildcard so text and image don't fight\nRegional prompting | A base prompt plus one prompt per region (Set Area or Set Mask with Combine; FluxRegionAttention for Flux) | One Assembler per region, all reading the same Context, so the picks agree\nLoRA triggers and stacking | Take the trigger from the model page. Start at 0.7 to 0.8; stack 2 or 3 at 0.4 to 0.8, under about 2 in total; the base family must match | Put the trigger in the wildcard option that is the LoRA, so pick and trigger travel together. WP Var → Float can feed the strength. Never blocklist or fuzzy-dedupe trigger words in the Cleaner\nEmbeddings | embedding:name in the text, optionally weighted; they only work on the family they were trained for | Negative embeddings go in an option's negative, so they follow usage\nVideo (Wan i2v) | Prompt the motion: subject action, one camera move, pace. \"static camera\" for none | Hold $character; roll $action and $camera_move per frame\nFor your pack: second passes usually want a smaller prompt than the first. A second WP Prompt Assembler reading the same Context renders just the slots that pass needs, and its picks match the first pass because they come from the same run."
},
{
"title": "Variation without incoherence",
"text": "Each slot that varies on its own multiplies the chance of a bad combination. A good budget is 2 to 4 varying slots per image (say pose, expression, camera and one prop), with everything else fixed or held.\n- Fixed for a whole set: the family head, the subject count, the character's identity (face, hair, eyes, body, the adult marker), the style.\n- Fixed within a scene: the outfit, the setting, the light.\n- Varies per image or frame: pose, expression, camera, small props.\n- Vary inside a slot with curated options of one kind (ten poses that all fit the framing), not mixed lists.\n- A fixed seed doesn't keep a character the same once the prompt changes. Hold the picks instead.\nFor your pack: use Hold across run for identity and style under a WP Context Loop, weights for how often each option shows, constraints to forbid bad pairs and a fallback for when a constraint leaves nothing."
},
{
"title": "Common mistakes",
"text": "Mistake | Symptom | Fix\nContradicting tags (smile and crying, nude and jacket, from behind and looking at viewer) | A muddled, random result | Constrain or derive the slots that depend on each other\nTag soup (60+ tags, many colours) | Colour leak; tags past the second chunk ignored | About 40 tags at most, 3 colours at most, colours next to their nouns\nDuplicates | Uncontrolled emphasis | WP Prompt Cleaner after the Assembler (the Assembler never dedupes)\nToo many subjects | One vanishes or two fuse | Two at most without regional tools\nAnother family's special tags | Wasted tokens, odd style | A model header per family\nWeight brackets on Qwen, Z-Image or Flux.2 | Literal \"(…:1.2)\" in the prompt | Write emphasis in words\nNegatives on CFG 1 models | Nothing happens | Positive wording, constraints, or NAG\nA huge generic negative | Cancels things you asked for | A short standing negative plus option negatives\nNo count tag | Extra people | 1girl, solo or \"a single woman\" first\nDetail the frame can't show (shoes in a portrait) | Changed crops, colour bleed | Constrain outfit and pose by framing\nA video prompt without motion or camera | A static clip | Motion and camera-move slots\nPony's score string cut to score_9 | Weak quality | The full string\nFor your pack: run the Test Runner over many seeds and read a handful of full prompts. Most of these mistakes are visible in the text before you render a single image."
},
{
"title": "Works with",
"text": "Recommended pack structure Adult (NSFW) packs WP Model Info WP Prompt Cleaner"
}
]
},
{
"id": "adult-packs",
"title": "Adult (NSFW) packs",
"group": "guides",
"blurb": "Structure, tag norms and the safeguards every adult pack must carry.",
"keywords": [
"nsfw",
"adult",
"explicit",
"rating",
"suggestive",
"safeguard",
"clothing state",
"rating_explicit"
],
"sections": [
{
"title": "Safeguards",
"text": "Two rules are enforced: the AI assistant never writes a minor in a sexual context, and never sex without consent. The rest of this list is our strong recommendation for any pack that can reach suggestive or explicit content. Your own edits are never checked or changed.\nNo minors, ever\nNever make sexual or suggestive content of minors or of characters presented as minors, in any form, and never loli or shota content. This holds whatever the request, whatever a canon age claim says (\"she's really 1000 years old\") and whatever the art style.\nAdult anchors\nEvery character in a pack that can reach suggestive or explicit is introduced as an adult: \"adult woman\" or \"1woman\" on tag models, \"an adult woman in her 30s\" on sentence models. \"mature female\" also works, but it pushes the look older, so use it when you want that. Adult body words (petite, slim, small breasts, curvy) are fine.\nYouth negative block\nEvery such render carries the youth block in its standing negative: \"child, loli, shota, toddler, aged down, young\" on tag models, \"child, minor, teenager, young girl, young boy, childlike\" on sentence models.\nNo youth-coded content\nSchool settings, childlike props and age numbers under 18 are safe-only options; a school uniform on an adult is your call. Named characters whose canon age is under 18 stay out of adult registers, even drawn \"aged up\".\nConsent\nEverything explicit is between consenting adults. Rough or kinky play is fine; rape, sex with someone unconscious or drugged, and \"against her will\" are not.\nNo real people\nNo real person's name, likeness or lookalike description, including real cosplayers.\nBlocklist backstop\nA WP Prompt Cleaner blocklist after the Assembler catches what slips through. It matches whole words, so list every variant (below).\nThe safeguards work in layers, and no single layer is enough: constraints keep youth-coded options out of adult registers at the source, the adult anchor steers every character in the prompt, the youth block pushes away in the negative (at CFG above 1), and the blocklist removes what slipped through.\nWhat the assistant does. In every AI feature (chat, Draft with AI, the Pack builder), an option, value, rule or template that puts a minor word (child, loli, shota, an age under 18) next to a sexual or nude one is left out. Words that name a young person but can be an adult too (teen, schoolgirl, young girl, younger sister) count as minor words in that context: \"schoolgirl, nude\" is not a costume. In a Pack builder pack rated NSFW, the whole pack counts as that context, so those words are left out wherever they are, in the items the model writes and the ones the pack graph wires: a dropped option takes its tags with it, a rule keyed on such a word goes too, and a word that pushes the image away from adults (\"mature\", \"adult woman\") is taken out of every negative, since the youth block and \"mature\" cannot both stand. A request whose own words ask for that (\"an NSFW pack of underage girls\") is refused before anything is planned, in English and in German, Spanish, French, Portuguese, Italian, Russian, Japanese, Chinese and Korean. A word that is negated doesn't count: \"adult women, no children, no minors\" names no minor, while \"no older than 15\" still does. Adult body and look words (petite, small breasts, flat chest, innocent look, cute) are never left out or flagged.\nConsent. The same goes for sex without consent: rape, non-consensual, against her will, without consent, unwilling, molested, sexual assault, violation, drugged, unconscious or coerced next to a sexual word (or anywhere in an NSFW pack) is left out, and a request for it is refused. Bondage, rough play and other kink between adults are not touched.\nRatings. An item the chat makes is rated NSFW when it is tagged #nsfw (or #explicit, #adult…) or its text is explicit. The chat and Organize with AI never lower an NSFW item to Safe or take #nsfw off unless your request asks (\"mark these safe\", \"fix the ratings\"). The chat's search can list items by rating. In a pack, \"rating explicit\" is written as Pony's rating_explicit, and a sample prompt that says two ratings at once is a problem to fix. A template draft whose request asks for a negative \"against anything youthful\" gets the youth words if the model left them out.\nA costume an adult wears (school uniform) is kept, with a Check warning on the item so you can judge it. Settings › Youth negative (on by default) adds the youth block to the template's negative; turn it off to write your own.\nFor your pack: build the safeguards first, before any content. They are a few fixed values, one negative template and one Cleaner node."
},
{
"title": "Register is a variable, not a label",
"text": "The library's content rating (safe or nsfw) is a label for lists and publishing. It changes nothing in a run. What decides how explicit an image gets is a hidden rating driver: one wildcard, picked once, that every adult slot is constrained by. The same driver picks the model's rating tag, so the tag and the content never disagree.\n- To cap a node at safe or suggestive, untick the higher levels on that use of the Rating wildcard. Nothing downstream can reach them then.\n- For one model family, a Rating tag wildcard plus a diagonal Only constraint maps the level to the right word. For a pack that serves several families, a derivation with AND conditions on $model_variant and $_rating writes it instead.\n- On a mixed model, the safe level also needs a guard: Pony's rating_safe in the prompt and rating_explicit in the negative; NoobAI's and Anima's safe prefix word. On CFG 1 models the guard must be positive wording.\nFor your pack: one rating driver per pack, first in the stack, hidden. Never roll the rating tag on its own."
},
{
"title": "Blueprint: rating-aware adult pack",
"text": "Modules in running order, for a Danbooru-tag model:\n# | Module | Kind | Writes | Options and settings\n1 | Rating | Wildcard | $_rating | safe (fallback), suggestive, explicit. Hide from prompt. Untick the levels a node must never reach\n2 | Rating → Rating tag | Constraint | | Only on the diagonal: safe → safe, suggestive → suggestive, explicit → explicit\n3 | Rating tag | Wildcard | $rating_tag | Your family's words (table below), tagged safe, suggestive, explicit\n4 | Character bundle | Bundle | $character, $body, $hair, $eyes | $character starts with the person's count tag (1girl, 1boy). Every option of $body carries the adult anchor (mature female, curvy, mature male, muscular). Hold across run\n5 | Rating → Clothing state | Constraint | | Matrix with Only cells: safe allows fully clothed; suggestive adds revealing and underwear; explicit allows the whole ladder\n6 | Clothing state | Wildcard | $clothing_state | The ladder below, each option tagged with its register and with what it needs (top, bottom, none). Option negatives by state\n7 | Clothing state → Outfit | Constraint | | topless → Only bottoms; bottomless → Only tops; underwear only → Only underwear; nude → Only the outfit's null option\n8 | Outfit | Wildcard | $outfit | Tagged top, bottom, one piece, underwear. A null option. School uniforms tagged safe only, and excluded from the other registers\n9 | Rating → Pose | Constraint | | Only the poses tagged for that register\n10 | Pose | Wildcard | $pose | Tagged by register and by framing\n11 | Pose → Camera | Constraint | | Framing that fits the pose (\"on back\" with \"from above\"; \"from behind\" without face detail)\n12 | Camera | Wildcard | $camera | Framing tags: portrait, upper body, cowboy shot, full body\n13 | Rating → Act | Constraint | | safe and suggestive: Only the act's null option\n14 | Act | Wildcard | $act | Explicit only. One act per option, each starting with the partner's count tag and the pairing tag (1boy, hetero, ...), so the counts never disagree with the act\n15 | Solo when alone | Derivation | $count | IF $act has no value (NOT with \"has value\") → solo\n16 | Clothing state → Place | Constraint | | Undressed states boost private places (bedroom, onsen, shower); public places with nudity only as a deliberate theme\n17 | Censorship | Fixed values | $censor | uncensored with negative censored, mosaic censoring, bar censor, or the reverse. Switch it by hand; never roll it\nTemplate:\n$quality, $rating_tag, $character, $count, $act, $body, $hair, $eyes, $clothing_state, $outfit, $pose, $expression, $place, $camera, $censor, $style\nNegative template:\n$negatives, child, loli, shota, toddler, aged down, young, bad anatomy, extra arms, extra legs, extra fingers, watermark\n- The count tags come first: the character's own, then solo or the partner's from the act. Below explicit the act is empty, so the derivation writes solo.\n- An Only rule pointing at a null option is how a constraint says \"nothing here\": for a nude state the outfit prints nothing, and below explicit the act prints nothing.\n- On a sentence model, keep the same modules and write the options as phrases. A combine builds each person's sentence with the adult anchor in it.\n- For two adults, insert the character bundle twice with the second copy's variables renamed, give each an anchor, and hold both. Regional prompting or one Assembler per person keeps their traits apart."
},
{
"title": "The clothing-state ladder",
"text": "A graded wildcard of clothing states, tagged by the registers that may use each and by what each needs from the outfit:\nState | Registers | Outfit allowed | Option negative\nfully clothed | safe, suggestive, explicit | top and bottom, or one piece | nude, nipples (in a suggestive register)\nrevealing clothes | suggestive, explicit | top and bottom, or one piece | nude\nopen shirt, shirt lift, clothes pull, clothing aside | explicit | the garment the state acts on (a shirt for open shirt, a skirt for skirt lift) |\ntopless | explicit | bottoms only | bra, shirt\nbottomless | explicit | tops only | skirt, pants, panties\nunderwear only, lingerie | suggestive, explicit | underwear only | dress, shirt, pants\nnude, completely nude | explicit | no garments (accessories only if the template says so) | clothes, underwear, panties, bra\nFor your pack: the state is a decision (constraints read it), so it is its own wildcard. Its option negatives follow usage, so each state pushes away exactly the garments it rules out. Under a Loop, a planned sequence can step along the ladder with per-frame overrides or a Frame wildcard and a constraint, while the character and outfit stay held."
},
{
"title": "Coherence rules",
"text": "- One clothing state per character, picked first. Everything else follows it: the outfit only draws garments the state allows, and \"open shirt\" needs a shirt, \"skirt lift\" a skirt.\n- The negative follows the state: nude puts clothes in the negative, topless puts a bra and shirt there, fully clothed in a suggestive register puts nudity there. This is the best use of option negatives in an adult pack.\n- Pose decides framing and what is visible. Constrain the camera by the pose rather than rolling them apart. Framing is also the main lever for keeping explicit detail where you want it.\n- One act per image, with the people counted: \"1girl, 1boy, hetero\" for a couple, \"2girls, yuri\", \"solo\" for one person. Several acts at once fuse bodies.\n- Body description is identity, not variety. Hold it across a run. In adult packs it also carries the adult anchor, so it is never optional.\n- The expression fits the act and register. Avoid pairs that fight (smile and crying) unless you mean it.\n- Standing anatomy negatives: \"bad anatomy, extra arms, extra legs, extra fingers, fused fingers, missing limbs, deformed\". Add \"extra people\" when the count is small.\n- The rating tag must agree with the content. rating_safe with nude fights itself; rating_explicit on a clothed café scene drifts toward nudity. That is why the rating tag comes from the same driver as the slots.\nFor your pack: every rule here is a constraint between two wildcards, or an option negative. Write the rule once and the pack keeps it on every seed."
},
{
"title": "Rating tags by model family",
"text": "Danbooru rates every post general, sensitive, questionable or explicit. Each model family maps those four levels to its own words:\nFamily | Rating words | Notes\nPony V6 | rating_safe, rating_questionable, rating_explicit, after the score and source tags | Trained on equal thirds of each rating. For safe output on a mixed merge: rating_safe in the prompt and rating_explicit in the negative\nPony V7 | rating_* special tags | Much weaker than V6's special tags; lean on the description\nIllustrious | Danbooru's levels: general, sensitive, questionable, explicit, or the merge's own form (nsfw, rating:explicit) | Read the merge's page\nNoobAI | safe, sensitive, nsfw, explicit | The default prefix has safe and the default negative has nsfw. For adult work swap the prefix word and take nsfw out of the negative\nAnimagine 3.1 / 4.0 | safe, sensitive, nsfw, explicit, nsfw | 4.0 puts the rating right after the series\nAnima | safe in the default prefix; the same four levels as NoobAI | Turbo runs at CFG 1, so the negative is ignored. Non-commercial licence\nChroma | Plain description | Reported to be trained without a safety filter; real negatives work\nSDXL base, SD 3.5, Flux.1, Qwen-Image, Z-Image | None | Weak or absent explicit anatomy. Adult work needs a fine-tune or LoRA; Flux is a poor default\nRealistic merges | The merge's own | Follow the merge's page\nFor your pack: exactly one rating word per prompt, in the family's head (see Writing prompts for your model for where the head goes)."
},
{
"title": "Tag vocabulary norms",
"text": "Use common tags\nDanbooru tags with fewer than about 5,000 posts are learned weakly. Prefer the high-count synonym (\"nude\" over a rare variant).\nClothing state\nThe ladder above uses real Danbooru tags: open clothes, open shirt, clothes pull, shirt lift, skirt lift, clothing aside, topless, bottomless, underwear only, lingerie, nude, completely nude, naked towel.\nAdult body\nmature female, mature male, curvy, wide hips, toned, muscular female, abs. These are also the standard steer toward clearly adult depictions on anime models.\nCount and pairing\n1girl, 1boy, 2girls, solo; hetero, yuri, yaoi. They come first, before the character.\nActs and positions\nDanbooru keeps these in its \"sexual positions\" and \"sex acts\" tag groups. Draw one per image from a short, curated list, never from a full group dump.\nCensorship\nAbout a third of Danbooru's explicit art is censored (\"censored\" 456K posts against \"uncensored\" 134K), so Danbooru models add mosaics or bars by default. \"uncensored\" in the prompt plus \"censored, mosaic censoring, bar censor\" in the negative turns that off; the reverse turns it on.\nSentence models\nDescribe plainly and clinically, with the adult anchor in the same sentence as the person (\"an adult woman in her 30s, nude, lying on a bed in a dim bedroom\").\nCFG 1 models\nThe negative is ignored, so a safe guard must be positive wording (\"fully clothed in a buttoned wool coat\") plus constraints.\nFor your pack: curate short lists of well-known tags. A small list of coherent options beats a large one that can contradict itself."
},
{
"title": "Setting up the blocklist",
"text": "WP Prompt Cleaner's blocklist in list mode matches each entry as a whole word, ignoring case. In tags mode it drops the whole tag that contains the word; in text mode it removes just the word. Because it matches whole words, blocking child does not touch children or childlike. List every form:\nEntry | Also list\nchild | children, childlike, childish, child's\nkid | kids, kiddo\nloli | lolis, lolicon\nshota | shotas, shotacon\ntoddler | toddlers\nminor | minors (this also removes \"minor\" in other senses)\nunderage |\nteen | teens, teenage, teenager, teenagers, preteen\nschoolgirl | schoolgirls, schoolboy, schoolboys\nyoung girl | little girl, young boy, little boy\naged down |\nOr switch the blocklist to regex mode, one pattern per line, such as \\bchild\\w*, \\bkids?\\b, \\b(loli|shota)(s|con)?\\b, \\btoddlers?\\b, \\b(pre)?teen(s|age|ager|agers)?\\b, \\bschool ?(girl|boy)s?\\b, \\b(little|young) (girl|boy)s?\\b and \\baged down\\b.\nDon't block young on its own in the positive: it would also cut \"young woman\" style wording without making anything safer. Keep it in the negative block. When the Cleaner also cleans the negative, leave the blocklist off in its negative column (the default there), or it would remove the youth block.\nFor your pack: the blocklist is the last layer, not the first. If it ever removes something, fix the option that produced it."
},
{
"title": "Second passes and edits",
"text": "- ADetailer and FaceDetailer reuse the main prompt when their prompt is blank. A custom detailer prompt must carry the adult anchors.\n- An inpaint edit, or a prompt edited in WP Image Filter's picker, is a new prompt. It needs the anchors and, at CFG above 1, the youth block.\n- Artistic nude and explicit are different registers. Artistic nude uses photography and fine-art words (light, pose, medium) at the questionable level.\n- Horror gore is an adult register too: label it nsfw and gate it behind the rating driver.\nFor your pack: a second Assembler for the detailer, reading the same Context, renders the face slots plus the anchor variable, so the anchor can't be forgotten."
},
{
"title": "Before you share",
"text": "- The library's content rating is set to nsfw. It is only a label for lists and publishing, but it tells others what the pack is.\n- Every character's identity option carries an adult anchor, and Hold across run keeps it for the whole Loop.\n- The youth block is in the Assembler's negative template itself, not only in option negatives, so it is there on every render.\n- No option reachable from suggestive or explicit holds a youth term, a school item or a real person. Pin the Rating wildcard to each level in the Test Runner and read the prompts.\n- The Cleaner's blocklist lists every variant, and stays off in the Cleaner's negative column (its default there), where it would strip the youth block.\n- Detailer, inpaint and Image Filter prompts carry the adult anchors too. A blank ADetailer prompt reuses the main prompt; a custom one or an edited prompt in the picker is a new prompt.\n- The pack says it needs CFG above 1 if it relies on negatives.\n- The model's licence and the host's rules allow the use. Some models restrict adult or commercial use (Anima's licence is non-commercial).\n- No imported tag dumps. Third-party adult wildcard files and raw tag-group files include prohibited categories; curate by hand.\n- Non-consent themes and categories that sharing sites ban (incest, self-harm, bodily excretion and similar) are not in the pack."
},
{
"title": "Works with",
"text": "Recommended pack structure Writing prompts for your model WP Prompt Cleaner Constraints in depth Negatives"
}
]
},
{
"id": "variable-pipeline",
"title": "The $variable pipeline",
"group": "concepts",
"blurb": "How modules fill $variables, and which syntax works where.",
"keywords": [
"$var",
"@{uuid}",
"{a|b|c}",
"syntax",
"where it works",
"naming",
"escape"
],
"sections": [
{
"title": "How it works",
"text": "A WP Context runs its modules from top to bottom. Each one writes a value into a $variable: the wildcard picks \"a fox\" for $subject, the fixed value sets $style to \"watercolor\". The finished set of variables goes to the next node. The WP Prompt Assembler then swaps every $name in your template for its value."
},
{
"title": "What you can write",
"text": "Write | What it does\n$name | The variable's value. If the wildcard picked several options, all of them, joined by its separator.\n$name.0 | One of several picks, counting from 0: $colors.0 is the first. A number past the end prints nothing.\n$name.GROUP | The tag rolled from an accepts tag group: $outfit.SHOES is the shoe that goes with this run's outfit. It's rolled once, so it's the same everywhere. With several picks, $outfit.0.SHOES is the first one's. A group that isn't accepts prints nothing and gets a warning.\n$name.neg | The variable's negative words. See Negatives.\n$name.bool | A WP Toggles variable's switch, true or false, even when the toggle writes its own Value.\n@{…} | A fresh pick from another wildcard. Type @ to insert one; it shows as a chip. It can narrow the pick to some tags (warm or cool) or leave out the null option. Written out it looks like @{abcd1234#Mood:calm or intense!null}: the 8-letter id, then an optional name, filter and !null. More in Chips & references.\n{a|b|c} | Picks one part each run. {3::cat|dog} makes cat three times as likely.\n{2$$a|b|c} | Picks two different parts, joined by a comma. {2$$ and $$a|b|c} joins them with \" and \"; {1-3$$a|b|c} picks between one and three. Add ~ ({2~$$a|b|c}) to allow the same part twice.\n$$ · @@ | Print a plain $ or @. Braces need no escape: only a closed {…} with a | (or a {2$$…} count) is a pick, so {word} and {{word}} print as written.\nThese nest: a part of {…} can hold a reference or another choice, such as {a @{abcd1234} cat|{2$$, $$red|blue|green} sky}.\nNaming a variable: letters, digits and underscores, starting with a letter or underscore, up to 64 characters. Names starting with two underscores are kept for the engine."
},
{
"title": "Which syntax works where",
"text": "Not every field reads everything. Anything a field doesn't read prints as typed, except where the table says \"nothing\" (it prints nothing and WP Debug notes it).\nField | $var | @{…} | {a|b}\nWildcard option | — | ✓ | ✓\nFixed value | — | — | ✓\nCombine template | ✓ | nothing | ✓\nDerivation \"then\" value | ✓ | ✓ | ✓\nDerivation \"if\" value | — | — | —\nAssembler template | ✓ | nothing | as typed\nWildcard options and fixed values can't read a $variable, wherever they sit. To build text from earlier picks, use a Combine or a Derivation below them.\nThe Assembler doesn't roll anything, so put choices in a wildcard or combine and print its variable. A derivation's \"if\" compares one variable ($name.0, $name.GROUP and $name.neg work too) with plain text."
},
{
"title": "The flow",
"text": "Figure: Modules produce $vars inside a Context; the Assembler fills the template. Diagram of the variable pipeline. Across the top, labelled modules inside the Context, sit five module kinds: Wildcard, Fixed Values, Combine, Derivation and Constraint, with a down arrow into the flow below. The flow runs left to right: WP Context (rolls + resolves), an arrow labelled emits, a group of resolved $vars chips ($subject, $style, $lighting), an arrow labelled fills, Prompt Assembler (with a template beginning 'A $style portrait…'), an arrow labelled prompt, and CLIP Encode (conditioning). Caption: modules produce $vars inside a Context, and the Assembler fills the template."
},
{
"title": "Works with",
"text": "WP Context WP Prompt Assembler Wildcard"
}
]
},
{
"id": "chips-and-references",
"title": "Chips & references",
"group": "concepts",
"blurb": "The chips in text fields, what a red chip means, @{…} references and how to write a tag filter.",
"keywords": [
"chip",
"chips",
"placeholder",
"broken",
"@{uuid}",
"@name",
"reference",
"filter",
"sub-category",
"not",
"and",
"or",
"negation",
"!null",
"expression"
],
"sections": [
{
"title": "Variables and chips",
"text": "Text fields that understand pipeline syntax colour what they recognise. Two kinds of token matter here:\n- Variables ($mood, $outfit.SHOES) read a variable from the Context. They are ordinary text in their own colour, wherever variables can be read: combine and derivation text and the Prompt Assembler template. You edit them like any other word. Each name keeps the same colour everywhere, so $mood in a combine matches the one in the Assembler's variable strip. Hover one to see which module writes it, or that it binds at runtime.\n- Ref chips (@{abcd1234#Mood}) embed a fresh pick from another module. They appear where references work: wildcard options and derivation actions. A chip shows the target's name, not its id, and it is one unit: the caret steps over it in one move and Backspace deletes it whole, so you can't half-delete a reference and leave broken syntax behind.\nUnder the chip the field still stores plain text. Copying a chip copies its syntax, and pasting that syntax anywhere turns back into the same chip."
},
{
"title": "The three states",
"text": "Resolved\nThe chip points at something real. A ref chip is tinted with the colour of the module it points at (violet ✦ for a wildcard) and hovering it shows the target's id and option count.\nFiltered\nA resolved ref chip that narrows its pool. A funnel icon means it carries a sub-category filter, a ban icon means it excludes the null option. Hover shows the filter as it reads and how many options still match, e.g. \"3 of 8 options match\". Click the chip to edit the filter.\nBroken\nA red chip with a ? icon: the reference points at a module id that isn't in your library (a placeholder, a deleted module, or one never imported here). Hover shows the name it carries, when the reference stored one. Click it to point it at another module. The editors also outline the field and mark its row (an option, a derivation branch) in red, so you can find it in a long table. At run time it produces nothing and the run reports a \"Reference not found\" warning.\nIn the library a variable never turns red just because nothing sets it yet: an Injector, a loop or an earlier Context may set it when it runs. Its hover says binds at runtime. On the canvas, where the wiring is known, a name nothing before it sets gets a red wavy underline (hover: no upstream producer). So does $outfit.SHOES when SHOES isn't an accepts group, because it would print nothing."
},
{
"title": "Inserting variables and chips (and placeholders)",
"text": "Type $ to open the variable list, or @ to open the module list. Keep typing to narrow the list, then pick with Enter or a click.\nPicking a wildcard that has sub-categories (or a null option) opens a second step, the filter picker, where you can type a filter expression and tick Exclude null. Press Enter to insert the chip, or leave the expression empty for an unfiltered reference. You can reopen the same picker later by clicking the chip.\nPlaceholders. To use a variable nothing produces yet, type $ and the name, e.g. $lighting, then a space or a comma (or press Enter when the list is empty). The name is filled in by whatever module later writes $lighting. Until then it prints nothing and the Assembler marks it as missing, which is a handy to-do marker while you build a template ahead of its modules.\nModule placeholders. The same works for a module you haven't built yet. Type @ and the name, e.g. @castle. When no module has that name, the last row of the list is Placeholder @castle. Pick it to insert a red broken chip under that name. The row it sits in is marked red too, and the list view shows its warning triangle, so it can't be forgotten. Once the module exists, click the chip and point it at the module."
},
{
"title": "@{uuid#name:filter} versus plain @name",
"text": "A reference is always written with braces and the target's 8-character id. Typing @name does not make a reference: it stays literal text and goes into the prompt as written. Use the @ list to insert the chip and the editor writes the full form for you. The long form has four segments, and only the id is required:\nSegment | Example | What it does\nid | @{abcd1234} | The module's 8-character id. The reference follows the id, so renaming the wildcard never breaks it.\n#name | @{abcd1234#Mood} | A remembered label. Only used for display, and to tell you what a broken chip used to point at.\n:filter | @{abcd1234#Mood:warm or calm} | Only options whose sub-categories satisfy the expression can be picked. Grammar below.\n!null | @{abcd1234#Mood:warm!null} | Drops the wildcard's null option from the pool. Without it, the null option always stays in, whatever the filter says.\nWhere a plain name is enough, reach for a variable instead: $mood reads the value a wildcard above already picked, while @{…} rolls the referenced wildcard again, on the spot, from its (optionally filtered) pool."
},
{
"title": "Writing a filter",
"text": "A filter is a short sentence of tag names joined by not, and and or. Each option is checked against its own tags and stays in when the sentence is true.\nWrite | Keeps options that\nwarm | carry the warm tag\nnot warm | don't carry warm (options with no tags at all count)\nwarm and bright | carry both\nwarm or cool · warm, cool | carry either (a comma is short for or)\nnot warm and (low or high) | don't carry warm, and carry low or high\nnot (warm or cool) | carry neither\n$model_variant | carry the tag the variable holds when the prompt runs (e.g. pony), plus options with no tag from that tag's group\n- Order: not goes first, then and, then or. So a or b and c means a or (b and c). Use parentheses whenever you mean something else; they nest to any depth.\n- Operators are lowercase words. Symbols like !, & or | are not operators, and ! is reserved for the !null flag. Write not warm, never !warm.\n- Tag names match exactly, including case. A name can't contain spaces or any of ( ) ! , # : { } @ $, and and, or, not and null are reserved.\n- A $variable is read when the prompt runs. Its value is compared with the tags like a tag name, ignoring case and with spaces as _ (\"Light rain\" finds light_rain). Options with no tag from the value's group stay, so untagged options work for every value. When the variable isn't set, or its value is no tag here, that part of the filter is left out and the run notes it. A multi-pick list keeps the options tagged with any of its picks. The match count is worked out before the variables.\n- Null is a flag, not a term. To keep the null option out, tick Exclude null (the !null segment); writing not null in the expression is an error.\nThe filter picker checks the expression as you type. It shows how the expression reads as (fully parenthesised where it matters), how many options match, and flags an unknown sub-category with a did you mean fix. If a valid filter matches no option, the reference prints nothing when you run and raises a Filter matched no options warning that lists the tags the wildcard actually has (see Warning & conflict types)."
},
{
"title": "Works with",
"text": "The $variable pipeline Wildcard WP Prompt Assembler Warning & conflict types"
}
]
},
{
"id": "context-chaining",
"title": "Context chaining",
"group": "concepts",
"blurb": "Wire one WP Context into another; the later one wins.",
"keywords": [
"upstream",
"chain",
"override",
"last write wins"
],
"sections": [
{
"title": "How it works",
"text": "Wire a WP Context's context output into another Context's upstream input. The second Context starts with every variable the first one made, then runs its own modules. If it sets a variable that already exists, its value wins. Everything it doesn't touch passes through as it was.\nFigure: Each Context starts with the variables before it; the last one to set a variable wins. Steps: WP Context A (rolls $vars) → feeds → WP Context B (adds / replaces) → merged $vars → Prompt Assembler (fills template). Flow diagram of three nodes left to right: WP Context A (rolls $vars), an arrow labelled feeds, WP Context B (adds / overrides), an arrow labelled merged $vars, and Prompt Assembler (fills template). Caption: each Context inherits the upstream map, and the last write for any $var wins.\nA common setup: one Context for the character and the place, a second that only sets $style. Swap or bypass the second to try another style while the rest stays the same. Chain as many as you like.\nFigure: Two chained WP Contexts: the first sets $subject and $mood, the second adds $hair and $pose, and one Assembler reads all four. Screenshot of two chained WP Context nodes feeding one WP Prompt Assembler. The first Context holds Starter subject and Starter mood; its context output goes into the upstream input of the second Context, which holds Hair and Pose. The Assembler's template is 'a $mood $subject with $hair, $pose' with four upstream chips, and the preview reads 'a fierce wolf with black hair, standing'. Variables from the first Context flow through the second one.\nTo add a single value from the canvas, such as typed text or a caption, a WP Context Injector is lighter than a whole Context."
},
{
"title": "Good to know",
"text": "- Hidden variables stay hidden down the whole chain, and later Contexts can still read them.\n- Constraints work across Contexts: a constraint in one Context can change the odds of a wildcard in a later one, as long as the source wildcard comes before the constraint. You can keep all your constraints in a Context of their own, between the one with the sources and the one with the targets.\n- WP Debug shows which Context set each variable."
},
{
"title": "Works with",
"text": "WP Context Hidden variables Constraints in depth"
}
]
},
{
"id": "seeds-and-loops",
"title": "Seeds & loops",
"group": "concepts",
"blurb": "How seeds decide picks, how to keep a pick, and how loops make variations.",
"keywords": [
"seed",
"loop",
"iteration",
"rng"
],
"sections": [
{
"title": "The seed decides the picks",
"text": "Every WP Context has a seed. The seed decides what each wildcard picks. Same seed, same picks; a new seed, new picks. With control after generate on randomize (ComfyUI's default), every Generate gives you a fresh set.\nEach module rolls on its own, so moving modules around, adding an option to one wildcard or adding a new module never changes what the others picked. To get back a prompt you liked, set the seed back to the number it used (WP Debug shows it)."
},
{
"title": "Keep something steady",
"text": "Open a module's editor (or right-click its row) and use the Runtime buttons:\nNothing (default)\nThe module rolls again on every Generate and on every loop frame.\nHold across run\nThe module rolls once per Generate and keeps that result on every loop frame. The next Generate rolls again. Good for keeping one character or outfit through a batch.\nLock pick / Lock seed\nThe module keeps the same result on every frame and every Generate until you unlock it. Good for an art style you've settled on. Wildcards call it Lock pick, other modules Lock seed.\nLock the style and leave the subject free to see how one style looks across many subjects.\nHow Hold across run works\nHold keeps the finished text, not just the seed. A wildcard holds its option even when a constraint changes the odds later in the batch, and a reference inside it is held too: an outfit of @{color} jeans that lands on \"green jeans\" stays \"green jeans\" for the whole run. It is worked out with the first frame's inputs, so a constraint that follows $iteration doesn't move it on later frames.\nA held derivation still picks its rule from the current inputs on every frame. What it holds is the random part of each rule, so a rule that fires again gives the same text it gave on the first frame."
},
{
"title": "Many variations in one click",
"text": "Put a WP Context Loop in front of your first WP Context and set its count. One Generate then runs the workflow that many times, each frame with its own picks.\nFigure: The loop runs the chain N times and you get one prompt per frame. Steps: WP Context Loop (count N) → frame 1…n → WP Context (new picks each frame) → $vars → Prompt Assembler (N prompts). Flow diagram of three nodes left to right: WP Context Loop (count N), an arrow labelled frame 1…N, WP Context (new picks each frame), an arrow labelled $vars, and Prompt Assembler (N prompts). Caption: the loop runs the chain N times and you get one prompt per frame.\nInside a loop you can use $iteration (the frame number, from 1) and $iteration_total (the count). Both are hidden from the prompt at first. To print them, switch off the globe button (for $iteration) and the Σ button (for $iteration_total) next to Iteration var on the loop."
},
{
"title": "Give each image its own sampler seed",
"text": "A plain KSampler uses one seed for every frame. If two frames roll the same prompt, you get the same image twice. Add a WP Seed List and wire the loop's loop_config into it: it gives the sampler one seed per frame and matches the loop's count by itself.\nFigure: Prompt 1 goes with seed 1, prompt 2 with seed 2, and so on. Steps: WP Context Loop (N prompts) → loop_config → WP Seed List (N seeds) → paired in order → KSampler (N different images). Flow diagram of three nodes left to right: WP Context Loop (N prompts), an arrow labelled loop_config, WP Seed List (N seeds), an arrow labelled paired in order, and KSampler (N different images). Caption: prompt 1 goes with seed 1, prompt 2 with seed 2, and so on, so every frame gets its own sampler seed.\nSimpler options: put $iteration in the prompt so no two prompts match, or send $iteration through WP Var → Int into the sampler's seed."
},
{
"title": "Make a batch you can repeat",
"text": "Turn on Override Context seed on the loop. The loop then works out every frame's seed from its own seed, so the same loop seed always gives the same batch. The strategy buttons choose how those seeds are spread. It only changes which numbers are used; every strategy gives each frame its own picks.\nhash (default)\nEach frame gets a seed worked out from the base seed. The seeds look random.\nsequential\nSeeds count up: base, base+1, base+2 … Easy to read out and type back in.\nstride\nSeeds take large even jumps from the base.\nWith it off (the default), each WP Context uses its own seed and the loop changes it a little per frame."
},
{
"title": "Keep the frames you liked",
"text": "Per-iteration seeds (on the loop and on WP Seed List) lists the seed each frame will use. Lock a frame's seed to keep it while the rest keep changing. Lock all, Copy and Paste (as #N: seed lines) work on the whole list.\nAfter a run you liked, use Prev. The list shows the seeds for the next run, and with randomize on those have already moved. Prev locks the seed the frame used last time. On the loop it needs Override Context seed on."
},
{
"title": "Good to know",
"text": "- Count 1 or the loop's Bypass loop switch runs a single frame without unwiring anything.\n- To drive the seed from another node, right-click the node and convert seed to an input.\n- To compare chosen options instead of random ones, use the loop's Sweep combinations (see WP Context Loop).\n- To change one frame on its own, see Per-iteration overrides."
},
{
"title": "Works with",
"text": "WP Context Loop WP Seed List WP Context Per-iteration overrides"
}
]
},
{
"id": "iteration-overrides",
"title": "Per-iteration overrides",
"group": "concepts",
"blurb": "Give one loop frame its own settings: a fixed pick, a locked seed, a module switched off.",
"keywords": [
"override",
"iteration",
"frame",
"keyframe",
"per-frame",
"edit frame",
"enable",
"disable"
],
"sections": [
{
"title": "What it does",
"text": "A WP Context Loop runs your Contexts several times, one frame each. Normally every frame uses the same settings, called the base. An override changes something for one frame only. Think of it as a keyframe: most frames follow the base, and you change just the ones that need it.\nFigure: Each frame starts from the base, then that frame's overrides are applied on top. Steps: Base settings (apply to all frames) → default → Frame #k override (changes just frame k) → per-frame change → Each frame (base, except where overridden). Flow diagram of three stages left to right: Base settings (apply to all frames), an arrow labelled default, Frame #k override (patches just frame k), an arrow labelled per-frame patch, and Resolved frames (base, except where overridden). Caption: each frame resolves from the base, then any override recorded for that frame is layered on top, like a keyframe that changes only the frames you override."
},
{
"title": "Editing a frame",
"text": "The loop has an Edit frame row of chips: base, then #1 #2 #3 …. Click a frame chip and every row editor now edits that frame: what you change is saved for that frame only. Click base to go back to editing all frames.\nFigure: The loop's Edit frame chips (base, #1 to #4) above the Context it drives. Screenshot of a WP Context Loop (seed 42, fixed, count 4) wired into a WP Context (Starter subject and Hair) and a WP Prompt Assembler ('a $subject with $hair', preview 'a wolf with black hair'). On the loop, the Edit frame row shows the chips base (selected), #1, #2, #3 and #4, with the hint 'Alt-click locks a seed · Ctrl-click bypasses'. Below are Override Context seed (off), the iteration var $iteration with its globe and Σ buttons, and Bypass loop (off).\nA frame keeps only what you changed. Rows with a change show a badge, and Revert to base clears a frame's changes. Adding or removing options, or anything else that changes the module itself, always edits the base."
},
{
"title": "What you can change per frame",
"text": "Wildcard options\nWhich options a frame may pick, their weights, or the tag filter. Tick only \"red\" on frame #3 and that frame is red while the others stay random. A constraint can still rule a ticked option out, so keep a fallback.\nCombine template or fixed value\nA frame's own text, such as a different caption on the last frame.\nConstraint\nIts reach and rules for one frame.\nLock pick\nLock a pick on one frame only. See below.\nOn / off\nRun a module on some frames only. See below.\nFrame changes live in the workflow. They aren't saved in bundles, and the Test Runner doesn't use them. For a frame rule you can share, use a wildcard with Match variable on iteration plus a constraint, or a derivation that tests $iteration. See Recommended pack structure.\nGood for\n- A few named beats on one canvas (frame #1 is the establishing shot)\nBetter with something else\n- A pattern over all frames: a derivation on $iteration\n- Anything you'll share: frame changes aren't saved in bundles"
},
{
"title": "Switch a module on or off per frame",
"text": "The tick box on a row works per frame too. A frame uses its own setting if it has one, otherwise the base. So both of these work:\n- On in the base, off on a frame: the module runs everywhere except that frame.\n- Off in the base, on on a frame: the module only runs on that frame, so an extra element shows up just there.\nA frame with a module off still makes an image; the module just adds nothing. WP Debug shows whether a module was off for that frame or off everywhere, and a module that is off in the base but on for some frames says where it runs (off · on #4).\nThe WP Prompt Assembler knows about this. A variable that only some frames set is not missing: its chip is blue with a dashed edge and a frame tag (#4), the variables header counts it as on some frames, its underline in the template is a blue dotted line, and pointing at it says which frames set it and which render it empty. The preview shows one frame at a time: the one picked under Edit frame (frame 1 while base is picked), or step through them with the ‹ 1/4 › arrows above it, which move the Edit frame pick too. On a frame that doesn't set the variable, the preview strikes it through and says it is empty there. Run only warns about a variable that no frame sets."
},
{
"title": "Lock a pick on one frame",
"text": "Lock a row while a frame chip is active and only that frame keeps its pick; the others still change. Lock it on base to keep it on every frame. Unlocking on one frame while the base is locked frees just that frame.\nHide from prompt and Hold across run apply to every frame, so they're greyed out while a frame chip is active. Switch back to base to change them."
},
{
"title": "Works with",
"text": "WP Context Loop Seeds & loops WP Context"
}
]
},
{
"id": "constraints",
"title": "Constraints in depth",
"group": "concepts",
"blurb": "How constraints combine, which uses they reach, and how they work across Contexts.",
"keywords": [
"pairing",
"matrix",
"reach",
"combine",
"across contexts"
],
"sections": [
{
"title": "Before you read this",
"text": "This page builds on the Constraint page, which covers making a constraint, the rule modes, Only, the preview and the order in the stack. You only need what's below when one wildcard is used several times, constraints overlap, or you use tag axes."
},
{
"title": "Several constraints on one target",
"text": "Every constraint that reaches a target applies, and their effects multiply: a ×3 boost and a ×0.5 reduce give ×1.5. An Exclude from any of them wins. You can add several constraints for the same source and target, one per rule, each with its own reach."
},
{
"title": "How far a constraint reaches",
"text": "A constraint isn't used up after one pick. By default it changes the odds of every use of its target below it in the run, including uses inside references. Each use of a constraint in a Context has a Target reach setting to narrow that:\nAll\nThe default. Every use of the target below the constraint.\nFirst\nOnly the first use below it; later ones roll freely.\nNext N\nThe next N uses below it (you set N); the rest roll freely.\nPick\nOnly the uses you tick from a list of the ones it can reach.\nReach only looks downwards: First and Next N count from the constraint's own position. Pick names each use by its row, or, for a use inside a reference, by the row and option that hold the reference.\nIf a constraint reaches nothing (its only target sits above it, the count is larger than what's below, or a picked row is gone), the canvas flags it so a dead pairing doesn't go unnoticed. If it reaches fewer uses than Next N or Pick asked for, WP Debug notes a partial reach after the run."
},
{
"title": "Targets used inside references",
"text": "If the target wildcard only appears inside another wildcard's option text, as an @{…} reference, the constraint still applies: it follows the reference to find the target. Each such use counts once for the reach setting, even when several references lead to it."
},
{
"title": "Tag axes on either side",
"text": "A tag group marked accepts (an axis, read as $var.AXIS) lists alternatives: one of its tags is rolled per pick. Constraints treat it so the tag you read always agrees with the tag the constraint acts on.\n- Accepts source. The constraint uses the one tag that was rolled, not every tag on the option. An outfit tagged sneakers · heels · sandals that rolls sandals makes a “same tag wins” matrix pin the target to sandals, so $outfit.SHOES and the shoe that gets picked match.\n- Classify source (the default kind): all the option's tags apply together. With a strict diagonal matrix, an option carrying several tags can rule out the whole target.\n- Accepts target. A target option stays in the pool if the source allows any of its axis tags, and its own axis roll is then limited to the allowed tag. A shoe tagged sandals · heels isn't dropped because heels is excluded; it stays and rolls sandals.\nAn accepts source, an accepts target and a diagonal matrix together make $source.AXIS equal $target.AXIS on every run."
},
{
"title": "Constraints across chained Contexts",
"text": "A constraint in one Context also applies to its target in a later, chained Context. You can keep all your pairing rules in a separate \"constraints\" Context placed before the one with the wildcards."
},
{
"title": "Works with",
"text": "Constraint Wildcard Context chaining"
}
]
},
{
"id": "internal-variables",
"title": "Hidden variables",
"group": "concepts",
"blurb": "Hide a $variable from the prompt while other modules still use it.",
"keywords": [
"internal",
"hide from prompt",
"hidden",
"globe",
"mark internal",
"strip"
],
"sections": [
{
"title": "What it does",
"text": "Click the globe on a row (or switch on Hide from prompt in its editor) and the row's variables print nothing in the prompt. Combines, derivations, constraints and later Contexts can still read them. The docs and older messages also call this internal.\nAn example: a hidden $season wildcard picks \"winter\". A combine below it writes $coat as \"a $season coat\", and a derivation sets $weather to \"snowing\" when the season is winter. The prompt gets the coat and the weather, but never the bare word \"winter\".\nGood for\n- A pick that decides other picks but shouldn't print, like a season, a class or a rating level\n- Numbers a WP Var node reads\nBetter with something else\n- Leaving a variable out of one template: just don't write it in that template"
},
{
"title": "Good to know",
"text": "- A hidden variable stays hidden through chained Contexts. Only the Assembler drops it, so a later Context can still read it.\n- Hiding belongs to the row that wrote the value. If a later module writes the same variable without hiding it, it prints again.\n- Written in an Assembler template, a hidden variable prints nothing and shows as missing. To print something built from it, use a combine and print the combine's variable.\n- A hidden variable adds nothing to the Assembler's negative output.\n- WP Context Loop's $iteration and $iteration_total are hidden by default. Click their globe buttons on the loop to print them, for example to caption a frame \"1 of 4\".\n- WP Model Info's variables are hidden by default too; switch them on the same way."
},
{
"title": "Works with",
"text": "Context chaining WP Prompt Assembler Combine"
}
]
},
{
"id": "negatives",
"title": "Negatives",
"group": "concepts",
"blurb": "Send words to the negative prompt from the option, value or rule that needs them.",
"keywords": [
"negative",
"negative prompt",
"send to negative",
"$negatives",
"add to negative",
"--",
"neg",
"$name.neg",
"replace negative"
],
"sections": [
{
"title": "The idea",
"text": "A negative belongs to the $variable it came with, and only reaches an Assembler that prints that variable. The main Assembler that prints $hair, $mood and $face gets all three negatives; a face detailer Assembler that prints only $face gets only the face words.\nFigure: Negative words only reach an Assembler that printed their variable. Steps: Wildcard option (negative: fruit) → picked → $hair (carries it) → used → Assembler (prints $hair) → collected → negative output (fruit). Flow diagram of four stages left to right: a Wildcard option carrying 'negative: fruit', an arrow labelled picked, the variable $hair (carries it), an arrow labelled used, the Assembler (renders $hair), an arrow labelled collected, and the negative output (fruit). Caption: negatives follow usage, so words only reach an Assembler's negative output when their variable was rendered by its template."
},
{
"title": "Where negatives come from",
"text": "Wildcard option\nEach option has a Negative line under its value. When the option is picked, its words go with the wildcard's variable. With several picks, each pick brings its own. The null option has none.\nFixed value\nSame Negative line. A fixed value always applies, so its negative always comes along: a $style of \"oil painting\" can bring \"photo, 3d render\". On a Context node each row shows its negative under the value; change it there for that node only, and the reset button brings back the library's.\nCombine\nA combine brings along the negatives of every variable it reads, with no setup. It can also have a Negative of its own for the phrase as a whole, which you can change per node too.\nDerivation\nTwo extra actions: Add to negative keeps the variable's text and adds words to its negative (\"when $mood is gloomy, add smiling\"). Replace negative swaps its negative for new words. Use + And to change the text and the negative in one rule.\nWP Context Injector row\nA row can have a Negative, which can use the row's input. It replaces any negative the variable had before."
},
{
"title": "What you can write in a negative",
"text": "A negative takes the same syntax as the value next to it.\nField | Accepts\nWildcard option negative | text, {a|b}, @{ref} (no $vars)\nFixed value negative | text, {a|b} (no refs, no $vars)\nCombine negative | $vars, {a|b}\nDerivation “Add to negative” value | $vars, @{ref}, {a|b}\nInjector row negative | the row's input, like the row template\nAssembler negative template | $vars and $negatives (where the collected words go)\nNegatives never change your picks. Adding or editing one leaves every seed's prompt exactly as it was. A reference inside a negative rolls on its own and isn't counted as a pick: it doesn't show in the Test Runner, doesn't trigger constraints, and brings no negative of its own."
},
{
"title": "How they follow the variable",
"text": "Setting a variable drops its old negative\nWhen a module writes a variable again, the old negative goes with the old value. Append and Prepend keep it and add the new one.\nOne pick, one negative\n$props.0 brings only the first pick's negative. $props and $props.GROUP bring every pick's.\nHidden variables add nothing\nA hidden variable never prints, so its negative never reaches the negative output. Later modules that read it still get it.\nWords you type in the negative template\nA $variable you write in the Assembler's negative template adds its text, not its negative.\nReading a negative: $name.neg\n$pose.neg is the negative $pose has at that point, and $props.1.neg the second pick's. Use it like any variable: in a combine, in the Assembler's negative template, in a derivation's \"if\", or to copy one variable's negative to another with Add to negative. Reading .neg doesn't count as printing $pose, so its words don't come in twice."
},
{
"title": "The Assembler's negative output",
"text": "WP Prompt Assembler has a second output, negative. Wire it into your negative CLIP Text Encode. Its negative template sits in a fold-out section under the preview:\nFigure: The Assembler's negative preview and output next to the prompt. Screenshot of a WP Context (seed 3) holding Hair, Weather and Quality, wired into a WP Prompt Assembler whose prompt and negative outputs each feed a Preview as Text node. The template is '$quality, a woman with $hair, $weather'; the preview reads 'masterpiece, best quality, a woman with black hair, clear sky'. The negative template is empty ('just the variables' negatives'), and the negative preview, marked '1 from variables', reads 'lowres, bad anatomy', which came from the Quality fixed value. The negative Preview as Text prints 'lowres, bad anatomy'.\n- $negatives marks where the collected words go, e.g. lowres, bad anatomy, $negatives. New Assemblers start with it.\n- Without $negatives the collected words are left out: the negative is only your own text, and an empty template is an empty negative.\n- Workflows and saved templates from before negatives were opt-in get $negatives added when they load.\nRepeated words are dropped, also when written differently: (blurry:1.2) counts as blurry, and so does one you already typed in the template."
},
{
"title": "Cleaning both prompts",
"text": "WP Prompt Cleaner has an optional negative input and a cleaned negative output. Its rule list has a second column for the negative, which follows the prompt's preset except fuzzy dedupe and the blocklist. Switch on Drop negative tags also in prompt to remove a word from the negative when the prompt asks for it; the report names the overlap either way."
},
{
"title": "Bulk add",
"text": "In a wildcard's bulk add, -- starts an option's negative. Everything after the first -- is the negative, and #tag / *N still work at the end of either side:\nModule | Line\nWildcard | strawberry blonde #warm *2 -- strawberry, fruit\nFixed values | style = oil painting -- photo, 3d render\nWith options checked, bulk edit's Negative menu adds words to each checked option, replaces their negative, or clears it."
},
{
"title": "Seeing where each word came from",
"text": "WP Debug lists each variable's negative under its value, and names the module when the words came from somewhere else (a derivation, or a combine reading $hair). The Test Runner shows a NEG line under each output, coloured by the variable each word came from, and a Negative column in Samples."
},
{
"title": "Sharing",
"text": "A pack that uses negatives needs an extension version that knows them. An older version asks you to update instead of dropping the words."
},
{
"title": "Works with",
"text": "WP Prompt Assembler WP Prompt Cleaner Wildcard Derivation"
}
]
},
{
"id": "conflicts-and-warnings",
"title": "Conflicts & warnings",
"group": "concepts",
"blurb": "Where warnings show, what the colours mean. Nothing here stops a run.",
"keywords": [
"scanner",
"advisory",
"warning"
],
"sections": [
{
"title": "Where you'll see them",
"text": "- On the node, while you build. A WP Context shows a small coloured dot and a short label on the row, such as MISSING VAR. Hover it for the message. A subgraph with WP nodes inside gets one badge with the worst issue, so you don't have to open it. The Validation strictness setting decides what both show: everything (Strict), no blue info notes (Relaxed), or nothing (Permissive).\n- In WP Debug, after a run. The Warnings tab lists what came up during the run, one line each.\nFigure: The Starter scene combine reads $mood, which nothing sets, so its row shows MISSING VAR. The run still goes ahead. Screenshot of a WP Context (modules 3 / 3) wired into a WP Prompt Assembler. The Context holds Starter subject, Starter scene and Starter style; the Starter scene combine row is outlined in amber with a MISSING VAR badge, because it reads $mood and nothing upstream sets it. The Assembler's template is '$scene, $style, masterpiece, highly detailed' and the preview still resolves to 'wolf, oil painting, masterpiece, highly detailed'. Warnings are advisory; the run carries on."
},
{
"title": "What the colours mean",
"text": "- Blue: for your information, usually nothing to fix.\n- Amber: worth a look; something probably isn't doing what you meant.\n- Red: something points nowhere, such as a reference to a deleted wildcard. The run still finishes; that piece just does nothing.\nNone of these ever stops a run. Setting a variable twice is fine, for example; the later one wins and the dot just shows it."
},
{
"title": "Common things it flags",
"text": "Shadowed variable\nAn earlier WP Context already sets this variable and this one replaces it. Normal when you chain Contexts; shown in blue so you know it's happening.\nDuplicate variable\nTwo modules on the same Context set the same variable, and the lower one wins. Rename one, or reorder them if the wrong value comes through.\nMissing variable\nThe Assembler template uses a $variable nothing sets (or a hidden one), so it prints nothing. The token is underlined in the template. Add a module that sets it, or remove the token.\nConstraint can't reach\nA constraint can't find one of its wildcards, or they're in the wrong order. It must sit after the source wildcard and before the target.\nConstraint never applied\nThe constraint's target never ran after it, so it changed nothing. Often fine; it's just telling you.\nThe full list is on Warning & conflict types."
},
{
"title": "Works with",
"text": "Warning & conflict types WP Debug Constraints in depth"
}
]
},
{
"id": "wp-context",
"title": "WP Context",
"group": "nodes",
"blurb": "Holds your modules, rolls them every run and passes the $variables on.",
"keywords": [
"context",
"modules",
"seed"
],
"sections": [
{
"title": "What it does",
"text": "Add a wildcard and the node rolls it into $subject; add a fixed value and you get $style. On every Generate the node runs its modules from top to bottom and passes the finished list of variables to the next node.\nGood for\n- One per pack or per character\n- A second, chained Context that adds to or replaces the first\nBetter with something else\n- Text typed onto the canvas or an LLM caption: use WP Context Injector\nFigure: Two WP Contexts from the One image example: Character (its bundles open, each row with its lock, globe and bin) feeds Scene. Screenshot of two WP Context nodes, each with seed 42. WP Context · Character (node maple-marmot, modules 4 / 5) holds the Character bundle with a Hero wildcard, a Look sub-bundle (Hair, Eyes), an Outfit sub-bundle (Outfit) and a switched-off Shared lists bundle; each row has a lock, a globe and a bin button. Its context output feeds the upstream input of WP Context · Scene, which holds the Scene bundle: a constraint Hero fits the place, wildcards Setting and Time of day, a derivation Lighting from time, a combine Scene line and a fixed value Style. Both end with Add module and Add Bundle.\n- upstream (in)Optional.\n- context (out)The variables.\n- seedDecides every random pick in this node.\n- Lock / globe / binThe row buttons: lock the pick, hide the variable from the prompt, remove the row."
},
{
"title": "Use it",
"text": "- Click Add module and pick from your library, or Add Bundle for a saved group.\n- Mind the order: modules run top to bottom. A combine that reads $mood goes below the wildcard that picks it, and a constraint goes between its two wildcards.\n- Wire context into a WP Prompt Assembler and write a template such as a photo of $subject.\nDrag a row by its handle to reorder it or move it to another Context. Untick a row to switch it off. Right-click a row for Edit, Duplicate, Hold across run, Save to library and more."
},
{
"title": "Inputs and outputs",
"text": "upstream (in)\nOptional. Another WP Context, an Injector, Model Info or a Loop. This node starts with their variables and can add to them or replace them.\nseed\nDecides every random pick in this node. See Seeds & loops.\ncontext (out)\nThe variables. Wire it into a WP Prompt Assembler, a WP Debug or another WP Context."
},
{
"title": "What the row markers mean",
"text": "Coloured dot or label\nSomething to look at, such as MISSING VAR or a variable set twice. Blue is for your information, amber is worth a look. Hover for the message. Nothing here stops a run.\ndrift\nThe library entry changed after you added it. Right-click → Refresh from library to take the change. On a bundle it reads library updated; right-click the bundle → Reset to library snapshot.\nmissing\nThe entry isn't in your library (deleted, or the workflow came from someone else). The row still works from its own copy; right-click → Save to library to add it.\nmod\nYou changed this row on the node, so it differs from the library.\nheld\nHold across run is on.\nLock / globe / bin\nThe row buttons: lock the pick, hide the variable from the prompt, remove the row. Set Row buttons to On hover (ComfyUI Settings or the Display Playground) to show them only when the pointer is on the row; a pressed lock or globe always shows."
},
{
"title": "Good to know",
"text": "- Chaining: a later Context can set the same variable again, and the later value wins. See Context chaining.\n- Keep a pick steady: Lock pick / Lock seed keeps it for good, Hold across run for one Generate. See Seeds & loops.\n- Duplicate renames the copy's variable ($hair becomes $hair_2) so the copy rolls on its own.\n- Rows are copies. Editing a module in the library doesn't change the workflow until you Refresh the row, so a saved workflow keeps giving the same results.\nWhere @{} references look\nA reference such as @{color} looks for its wildcard among this node's rows first (even a switched-off row), then in the library of the machine that runs the workflow. The same goes for a reference inside a referenced wildcard, at every level. Keep the wildcards a reference needs on the node and a workflow you share works on someone else's machine too. In the editor, hover the chip: its card says pool: this node or pool: library. WP Debug shows where each pick came from.\nWire a WP Debug to the output to see exactly what each module picked."
},
{
"title": "Works with",
"text": "WP Prompt Assembler WP Context Loop Per-iteration overrides WP Context Injector Wildcard Constraint"
}
]
},
{
"id": "wp-context-loop",
"title": "WP Context Loop",
"group": "nodes",
"blurb": "Make several variations in one Generate.",
"keywords": [
"loop",
"iterate",
"batch"
],
"sections": [
{
"title": "What it does",
"text": "Set the count to 8, press Generate once, and you get eight prompts (and eight images from your sampler), each with its own picks. No queueing the same workflow by hand.\nGood for\n- A batch of variations from one click\n- A story told over several frames\n- Comparing chosen options side by side (Sweep)\nBetter with something else\n- Just giving the sampler a new seed per frame: add WP Seed List\nFigure: WP Context Loop sweeping 9 frames into the Character and Scene Contexts and an Assembler; Preview as Text shows the prompt of the run. Screenshot after a run. A WP Context Loop (count 9, strategy hash, Per-iteration seeds and a Sweep combinations button marked 9 frames, Edit frame chips base and #1 to #9, Override Context seed off, iteration var $iteration, Bypass loop off) has its context output wired through folded WP Context · Character and WP Context · Scene nodes into a WP Prompt Assembler with the template '$hero, $hair, $eyes, wearing $outfit, $scene, $style'. Its prompt output feeds a Preview as Text that shows the prompt the run made; the Assembler's own preview rolls frame 1's seeds.\n- context (out)One Context per frame.\n- loop_config (out)Wire it into a WP Seed List so each frame also gets its own sampler seed.\n- seedThe loop's own seed.\n- countHow many frames one Generate makes (1–999).\n- Strategyhash, sequential or stride: how the frame seeds are worked out when Override Context seed is on.\n- Per-iteration seedsThe seed each frame will use.\n- Sweep combinationsRun every combination of the options you pick instead of random rolls.\n- Edit frameThe base / #1 #2 … chips.\n- Override Context seedOn: the loop sets every frame's seed from its own seed, so the batch can be repeated.\n- valuesOptional text from another node: one frame per line (or per list entry), each writing its line to $iteration_value.\n- Iteration varThe name of the frame-number variable ($iteration).\n- Bypass loopRuns a single frame while the node stays wired."
},
{
"title": "Set it up",
"text": "- Add WP Context Loop and wire its context output into your first WP Context's upstream.\n- Set count.\n- Optional: add a WP Seed List on loop_config so each image also gets its own sampler seed.\nWithout a Seed List, a plain KSampler uses one seed for every frame. Two frames that roll the same prompt then give the same image."
},
{
"title": "The controls",
"text": "seed\nThe loop's own seed. Only used when Override Context seed is on.\ncount\nHow many frames one Generate makes (1–999). 1 switches looping off without unwiring the node.\nvalues\nOptional text from another node: one frame per line (or per list entry), each writing its line to $iteration_value. The values set the frame count and count follows the last run; with a sweep on, every value runs every combination. Its dot sits beside the iteration variable (beside the values note once wired) and shows when you hover it.\nStrategy\nhash, sequential or stride: how the frame seeds are worked out when Override Context seed is on. See Seeds & loops.\nPer-iteration seeds\nThe seed each frame will use. Lock single frames, Lock all, Copy / Paste, or Prev to keep the seed a frame used last run. Locks need Override Context seed on.\nSweep combinations\nRun every combination of the options you pick instead of random rolls. See below.\nEdit frame\nThe base / #1 #2 … chips. Pick a frame to change just that frame (see Per-iteration overrides). Ctrl-click skips a frame, Alt-click locks its seed.\nOverride Context seed\nOn: the loop sets every frame's seed from its own seed, so the batch can be repeated. Off (default): each WP Context keeps its own seed and the loop changes it a little per frame.\nIteration var\nThe name of the frame-number variable ($iteration). The globe and Σ buttons hide or show $iteration and $iteration_total in the prompt.\nBypass loop\nRuns a single frame while the node stays wired.\ncontext (out)\nOne Context per frame. ComfyUI runs the nodes after it once per frame.\nloop_config (out)\nWire it into a WP Seed List so each frame also gets its own sampler seed."
},
{
"title": "Sweep combinations",
"text": "Instead of random rolls, run every combination of the options you choose. Open Sweep combinations, switch it on and add wildcards from the WP Contexts after the loop. Sweep $hair (3 options) against $mood (2 options) and one Generate makes 6 frames: red + calm, red + sad, blue + calm, and so on. The window shows the total and lists every frame before you run.\nFigure: The Sweep window: $subject (4 options) × $hair (4 options) = 16 combinations, each listed as a frame. Screenshot of the WP Context Loop Sweep window ('Run every combination of the wildcards you pick, one frame each'). Sweep combinations is on and reads 4 × 4 = 16 combinations. Two swept wildcards are listed: $subject (4/4, its options cat, tiger, dog and wolf all ticked, with all / none / enabled only links) and $hair (4/4). Limit is 64 and Hold other picks is on. The Frames list shows #1 cat · strawberry blonde hair through #16 wolf · black hair, the first wildcard changing slowest. Behind the window the loop node shows 16 frame chips and a 16 FRAMES badge on Sweep combinations.\nall / none / enabled only\nQuick ways to tick a wildcard's options. A wildcard with nothing ticked isn't swept.\nOrder\nThe first wildcard changes slowest, so two wildcards read like a grid. Use the arrows to reorder.\nLimit\nThe most frames a sweep runs (64 by default, up to 999). Past it, the first combinations run and the window says so.\nHold other picks\nOn by default: everything you aren't sweeping keeps one pick on every frame, so only the swept part changes.\nKeep the sampler on one fixed seed for a clean comparison, so the image only changes where the prompt does.\nHow it works\nA swept wildcard's pick is set by the sweep, so constraints don't change its odds. That means sweeping a constraint's target can show pairs your pack normally rules out. Constraints it drives still apply to the wildcards after it, and its tags still roll.\nSkipped frames and seed locks move with their combination when you change the sweep, and are dropped when that combination no longer runs."
},
{
"title": "Frame numbers in the prompt",
"text": "$iteration is the frame number (from 1) and $iteration_total the count. A template of frame $iteration of $iteration_total, $subject gives \"frame 1 of 4, a fox\", \"frame 2 of 4, a cat\" and so on. Both are hidden from the prompt until you switch off the globe and Σ buttons; while hidden you can still use them in derivation rules.\nFigure: A full looped workflow: the loop's loop_config feeds a WP Seed List on the sampler, so each of the four frames gets its own prompt and its own seed. Screenshot of a full looped workflow and its output. Top row: WP Context Loop (seed 42, fixed, count 4, hash, with Per-iteration seeds and Sweep combinations rows and Edit frame chips base and #1 to #4) feeds a WP Context holding Starter subject and Starter style, which feeds a WP Prompt Assembler with the template 'iteration $iteration of $iteration_total: a picture of a $subject, in the style of $style'. A Preview as Text node lists the four prompts: a dog, a cat, a dog and a wolf, each in the style of oil painting. Bottom row: Load Checkpoint, a WP Seed List fed by the loop's loop_config whose seed goes to the KSampler, two CLIP Text Encode nodes (prompt and negative from the Assembler), Empty Latent Image, VAE Decode and a Preview Image showing a 2 by 2 grid of painted animal portraits captioned iteration 1 of 4 to iteration 4 of 4: a dog, a cat, a dog and a wolf. One image per frame, each with its own prompt and seed."
},
{
"title": "Good to know",
"text": "- A module on Lock pick / Lock seed keeps its pick on every frame. Hold across run keeps it for one Generate. See Seeds & loops.\n- Per-frame changes stay on the canvas: bundles don't save them and the Test Runner doesn't run them."
},
{
"title": "Works with",
"text": "Seeds & loops Per-iteration overrides WP Seed List WP Context WP Prompt Assembler"
}
]
},
{
"id": "wp-seed-list",
"title": "WP Seed List",
"group": "nodes",
"blurb": "One sampler seed per loop frame, so each frame makes its own image.",
"keywords": [
"seed",
"list",
"ksampler",
"loop",
"batch",
"unique"
],
"sections": [
{
"title": "What it does",
"text": "A plain KSampler takes one seed and uses it for every frame of a loop. Two frames that roll the same prompt then make the same image. WP Seed List gives the sampler a list of seeds instead, one per frame: frame 1 gets seed 1, frame 2 gets seed 2, and so on.\nGood for\n- Giving each loop frame its own sampler seed\n- Keeping an image you liked reproducible (lock its seed)\nBetter with something else\n- Prompt variety: it never changes a prompt. The Context seed and the Loop do that"
},
{
"title": "Set it up",
"text": "Figure: Wire the loop's loop_config into the Seed List and its seed output into the sampler's seed. Steps: WP Context Loop (N prompts) → loop_config → WP Seed List (N seeds) → paired in order → KSampler (N different images). Flow diagram of three nodes left to right: WP Context Loop (N prompts), an arrow labelled loop_config, WP Seed List (N seeds), an arrow labelled paired in order, and KSampler (N different images). Caption: wire the loop's loop_config into the Seed List and its seed output into the sampler's seed.\n- Wire the loop's loop_config output into the Seed List.\n- Wire the Seed List's seed output into your sampler's seed input (right-click the sampler and convert seed to an input if needed).\nThe count now follows the loop, so the two lists always line up.\nFigure: WP Context Loop's loop_config wired into WP Seed List: the count and strategy follow the loop, and Preview as Text shows the seeds the run gave each frame. Screenshot after a run. A WP Context Loop (count 9, strategy hash) has its loop_config output wired into a WP Seed List. The Seed List shows a base_seed, a greyed count of 9 (taken from the loop), strategy hash, a Per-iteration seeds button, seed chips #1 to #9 ('Click a frame to lock its seed'), Override base seed from loop off and Override count from loop and Override strategy from loop on. Its seed output feeds a Preview as Text that lists the seeds of the run.\n- loop_config (in)Optional.\n- seed (out)The list of seeds, one per frame.\n- base_seedWhere the seed list starts.\n- countHow many seeds it gives (1–999).\n- Strategyhash, sequential (base, base+1 …) or stride.\n- Per-iteration seedsLock single seeds, Lock all, Copy / Paste, or Prev to keep the seed a frame used last run.\n- Override … from loopThree switches: take the base seed, the count and the strategy from the wired loop."
},
{
"title": "The controls",
"text": "base_seed\nWhere the seed list starts. Same base, same seeds.\ncount\nHow many seeds it gives (1–999). Follows the loop's count while Override count from loop is on.\nStrategy\nhash, sequential (base, base+1 …) or stride. It changes which numbers you get, not how varied the images are.\nPer-iteration seeds\nLock single seeds, Lock all, Copy / Paste, or Prev to keep the seed a frame used last run.\nOverride … from loop\nThree switches: take the base seed, the count and the strategy from the wired loop. Count and strategy switch on by themselves when you connect loop_config; base seed stays yours unless you switch it on.\nloop_config (in)\nOptional. A WP Context Loop's loop_config output, so the seeds follow its frames.\nseed (out)\nThe list of seeds, one per frame. ComfyUI runs the node after it once per seed: wire it into a KSampler's seed.\nPrev is the one to use after a run you liked: the list shows the seeds for the next run, and Prev locks the seed the frame actually used."
},
{
"title": "Good to know",
"text": "- Without a loop it still gives count seeds, so the sampler runs that many times. Set count to 1 for a single seed.\n- Unplugging loop_config leaves the switches as they were; the node falls back to its own values.\n- Seeds stay in the range ComfyUI's own randomize button uses, so you can copy one into a plain sampler and get the same image.\n- More on seeds: Seeds & loops."
},
{
"title": "Works with",
"text": "Seeds & loops WP Context Loop Per-iteration overrides WP Context"
}
]
},
{
"id": "wp-context-injector",
"title": "WP Context Injector",
"group": "nodes",
"blurb": "Turn any value on the canvas into a $variable.",
"keywords": [
"inject",
"override",
"rows"
],
"sections": [
{
"title": "An example",
"text": "You have a text box with red hoodie, ripped jeans and another with rooftop at night. Wire both into an Injector, name the rows $outfit and $place, and a template of a woman wearing $outfit, $place prints \"a woman wearing red hoodie, ripped jeans, rooftop at night\".\nFigure: Three text boxes (the LoRA trigger words) wired into a WP Context Injector, each becoming a variable; WP Debug after the run lists them with the picks from the Contexts above. Screenshot after a run. Folded WP Context · Character and WP Context · Scene nodes feed the upstream input of WP Context Injector · LoRA words. Three Text nodes (your character's trigger words, your character's outfit words, your style's trigger words) wire into its input_0, input_1 and input_2; a free input_3 waits below. Its injected variables list (3 / 3) has three string rows named lora_hero_words, lora_outfit_words and lora_style_words, each with a globe and a bin, and an Add template row button. The Injector's context goes into a WP Debug whose Variables tab lists $hero, $hair, $eyes, $outfit, $setting and $time with the module that set each.\n- upstream (in)Optional.\n- context (out)The variables from before, plus one per row.\n- input_0 … input_9 (in)Wire any ComfyUI output (text, a LoRA name, a number) into the next free input_N.\n- NameThe $variable the value becomes.\n- Hide from prompt (globe)The variable still reaches combines and derivations, but prints nothing in the prompt.\nGood for\n- Text from outside your library: something you type, an LLM caption, a LoRA name\n- A number from a slider or another node\nBetter with something else\n- Choices: an injected value isn't a pick, so nothing can constrain it. Use a wildcard with Match variable\n- Reusable text that rolls: a Combine in the library"
},
{
"title": "Use it",
"text": "- Wire any output into the next free input_N. A row appears for it.\n- Type the variable's name in the row.\n- Wire context into a WP Context, an Assembler or a Debug. Wire an existing Context into upstream to add to it; its variables pass through.\nFigure: The Injector gives an outside value a name the rest of the workflow can use. Steps: Any node (any output) → wire → WP Context Injector (names it $var) → $variable → WP Context (merged in). Flow diagram of three stages left to right: Any node (any output), an arrow labelled wire, WP Context Injector (names it $var), an arrow labelled $variable, and WP Context (merged in). Caption: the Injector gives an outside value a name the rest of the workflow can use."
},
{
"title": "Inputs and outputs",
"text": "upstream (in)\nOptional. Another WP Context, Injector, Model Info or Loop to add to. Only nodes after the injector see its variables.\ninput_0 … input_9 (in)\nWire any ComfyUI output (text, a LoRA name, a number) into the next free input_N. Each wired input becomes a row; a new free one appears as you wire, up to 10.\ncontext (out)\nThe variables from before, plus one per row."
},
{
"title": "Row options",
"text": "Right-click a row → Edit for its template and negative.\nName\nThe $variable the value becomes. Letters, digits and underscores, starting with a letter.\nTemplate\nOptional. Wrap the value in text, e.g. wearing $input_0. Leave empty to use the value as it is. $$ prints a dollar sign. Inline {a|b} picks need a seed, so they print as written (the editor warns).\nNegative\nOptional negative words for this variable (it can use $input_0 too, so another node can write it). Only reaches an Assembler that prints the variable. Rows with one show a NEG badge.\nHide from prompt (globe)\nThe variable still reaches combines and derivations, but prints nothing in the prompt."
},
{
"title": "Join several inputs",
"text": "Click Add template row and write a template like $input_0 by $input_1. A template row isn't tied to one input, stays when you rewire, and runs after the other rows, so it can also use the variables they made ($outfit).\nHow it works\nA row made for a wired input can only use its own input in its template. It disappears when you unplug that input. Up to 10 inputs per Injector; chain a second one for more. Text works best: images and latents turn into their text description, which is rarely useful in a prompt.\nSetting a variable here clears the negatives it had from earlier nodes; the row's own negative takes their place. A template row also carries the negatives of the variables it reads, before its own: $outfit = \"$cloak with a silver clasp\" brings $cloak's \"torn fabric\". A hidden row, or one that only feeds a derivation, never adds to a negative."
},
{
"title": "Works with",
"text": "WP Context WP Prompt Assembler Negatives"
}
]
},
{
"id": "wp-model-info",
"title": "WP Model Info",
"group": "nodes",
"blurb": "Name the loaded checkpoint so rules can branch on it.",
"keywords": [
"model",
"checkpoint",
"pony",
"illustrious",
"lora",
"variant"
],
"sections": [
{
"title": "What it's for",
"text": "One prompt setup rarely suits every checkpoint. Pony wants its score tags, Illustrious wants different LoRAs, Flux reads plain sentences. WP Model Info looks at the model you loaded and writes what it found into the Context, so the rest of the pipeline can react to it.\n$model_family\nThe architecture, read from the loaded model itself: sd15, sdxl, flux, sd3, chroma and so on. Needs the model input.\n$model_variant\nThe fine-tune line, read from the checkpoint's file name by the variant rules: pony, illustrious, noobai, animagine, or empty when no rule matches.\n$model_name\nThe checkpoint's file name without folders or extension, e.g. ponyDiffusionV6XL.\nPony and Illustrious are both SDXL underneath, so $model_family reads sdxl for both. $model_variant tells them apart, and it can only come from the file name.\nSome models read in ways you might not expect:\nFlux.1 and Flux.2\nBoth read as flux.\nAnima\nProbably reads as cosmos.\nZ-Image\nProbably reads as lumina2.\nModels with no entry\nKrea 2, Kolors and others read as their class name, in lower case with underscores.\nMerges\nA file name without \"noob\", \"pony\" or \"illustrious\" in it gets no variant.\nCheck once what the node reads for your checkpoint, then pin the variant or add a rule for its file name. Notes per model are on Writing prompts for your model.\nGood for\n- One pack that writes different quality tags, LoRAs or styles per checkpoint\nBetter with something else\n- A workflow that only ever runs one model: a fixed value is simpler"
},
{
"title": "Where it goes",
"text": "Figure: Model Info heads the chain, so every Context after it can read the model variables. Steps: Load Checkpoint (MODEL) → model → WP Model Info ($model_variant) → context → WP Context (rules branch on it). Flow diagram of three stages left to right: Load Checkpoint (MODEL), an arrow labelled MODEL, WP Model Info ($model_variant), an arrow labelled context, and WP Context (rules branch on it). Caption: Model Info heads the chain, so every Context after it can read the model variables.\nIf a node that loads LoRAs from the prompt sits on the model, wire Model Info to the MODEL from before that node. Otherwise the prompt needs the model and the model needs the prompt, and ComfyUI refuses to run the loop.\nFigure: WP Model Info with the family and the checkpoint file name given as text: the family is sdxl, the name is ponyDiffusionV6XL and the pony rule matches it, so the variant is pony. Screenshot after a run. A Model family text ('sdxl') goes into the family input of WP Model Info and a Checkpoint file text ('ponyDiffusionV6XL.safetensors') into its name input; the model input is free. The Detected section lists family sdxl (wired), variant pony (rule 2) and name ponyDiffusionV6XL (wired), each with a globe and a pin button. Variant rules map noobai → noob, pony → pony|pdxl (lit), illustrious → illustrious|ilxl and animagine → animagine, with Add rule below. Its context output feeds a folded WP Context · Render, then Width and a Preview as Text.\n- upstream (in)An existing Context to extend.\n- context (out)The upstream Context plus the three variables.\n- model (in)The MODEL from your checkpoint loader.\n- GlobeWhether the variable can print.\n- family, variant, name (in)Optional text from another node, such as a switch that names the model you're testing.\n- DetectedThe three values and where each comes from.\n- PinUse your own value for a row instead of the detected one: a file name when no model is wired, or a variant to try your Pony rules without switching checkpoint.\n- Variant rulesRows of variant and pattern."
},
{
"title": "Inputs, outputs and controls",
"text": "upstream (in)\nAn existing Context to extend. Only nodes after Model Info see its variables, so it usually goes first.\nmodel (in)\nThe MODEL from your checkpoint loader. Gives the family, and the node follows the wire back to the loader for the file name, stepping over LoRA loaders and other model patches.\nfamily, variant, name (in)\nOptional text from another node, such as a switch that names the model you're testing. A wired value beats the pin and detection (an empty one is ignored); a wired name goes through the variant rules like the loader's. The row shows wired, and its value after a run. Each input's dot sits beside its row, like a built-in widget's, and shows when you hover the row or wire it.\nDetected\nThe three values and where each comes from. Variant and name update as soon as you change the loader's checkpoint; the family shows after the first run.\nGlobe\nWhether the variable can print. All three start hidden: rules and constraints read them, but the Assembler leaves them out. Click the globe to print one.\nPin\nUse your own value for a row instead of the detected one: a file name when no model is wired, or a variant to try your Pony rules without switching checkpoint. Click it again to go back to detection.\nVariant rules\nRows of variant and pattern. The first pattern found in the file name sets $model_variant, and its row lights up. Capitals don't matter; | separates alternatives (pony|pdxl). A broken row is outlined in red and skipped. Reset brings back the built-in rules.\ncontext (out)\nThe upstream Context plus the three variables."
},
{
"title": "LoRAs per model",
"text": "In the derivation editor, LoRA by model adds a ready-made rule: if $model_variant is pony, set $loras to a Pony LoRA tag; if illustrious, an Illustrious one; otherwise leave it empty. Swap the placeholder LoRA names for yours, add branches for other variants, and put $loras in your Assembler template. The same pattern works for quality tags, negatives or anything else a derivation can write."
},
{
"title": "Options per model in one wildcard",
"text": "When one wildcard should offer different options per model, you don't need a constraint. Tag its options with the variants (pony, illustrious) and pick $model_variant under By variable in its Pool. Each run keeps the options tagged with the loaded model, plus options with no model tag. Constraints on that wildcard still apply on top."
},
{
"title": "Constraints from the model",
"text": "Constraints react to a wildcard's pick, not to a variable, so the model reaches them through a wildcard. Make a wildcard (say Model) with options pony and illustrious, tag them, add it to your Context, and in its node settings set Match variable to model_variant. Instead of rolling, it picks the option whose text equals the detected variant, and any constraint can use it as its source, for example “if Model is pony, exclude photo styles”.\nMark one option of that wildcard as the fallback (for example “other”), so checkpoints your rules don't recognise still get a predictable pick. Without a fallback the wildcard rolls as usual and WP Debug notes that nothing matched."
},
{
"title": "Good to know",
"text": "The canvas previews $model_variant and $model_name from the loader's file name straight away. $model_family needs the loaded model, so it previews as a placeholder until the first run (or pin it).\nModel Info lives in the workflow, not your library, so packs that branch on $model_variant work on any version that has the node."
},
{
"title": "Works with",
"text": "WP Context Derivation Constraint WP Context Injector"
}
]
},
{
"id": "wp-toggles",
"title": "WP Toggles",
"group": "nodes",
"blurb": "Several on/off switches in one node, as $variables and BOOLEAN outputs, that can also mute or bypass nodes.",
"keywords": [
"toggle",
"boolean",
"switch",
"on",
"off",
"lora",
"merge",
"mute",
"bypass",
"pick one"
],
"sections": [
{
"title": "What it's for",
"text": "WP Toggles replaces a stack of Boolean nodes. Each toggle writes $its_variable as true or false, so a derivation can branch on it without a Context Injector, and can show a BOOLEAN output at the right of its row for a switch node (off for a new toggle: turn on BOOLEAN output in its window).\nFigure: Hires fix is on and Quick preview off: their BOOLEAN outputs feed ComfyUI's If/Else Switch and two Preview as Text nodes, which print what the run sent (True, False, and the switch's pick). Detailer is off, so the text node wired into its dot is muted. Screenshot after a run. A WP Toggles node in its clean view with a Render label and three toggles: Hires fix (a switch, on, $hires), Quick preview (off, $quick_preview) and Detailer (off, $detailer, with a mute icon by its name). A Detailer prompt text node is wired into the dot at the left of the Detailer row and shows Muted. Hires fix and Quick preview each have a BOOLEAN output at the right of their row. Hires fix feeds the switch input of an If/Else Switch whose on_true is 'upscaled 2x, then refined' and on_false 'base size only'; the switch's output goes to a Preview as Text that prints 'upscaled 2x, then refined'. Two more Preview as Text nodes print True (Hires fix) and False (Quick preview).\n- upstream (in)Optional Context to extend.\n- context (out)The Context with every toggle variable added.\n- toggle outputsOne BOOLEAN per toggle that has an output, at the right of its row.\n- control dots (in)A toggle that mutes or bypasses has a dot at the left of its row: wire the nodes it drives into it."
},
{
"title": "When to use it",
"text": "Good for\n- Switches you flip by hand: use a LoRA, add a detailer, day or night\n- Paths a rule picks between: $use_lora_character in a derivation\nBetter with something else\n- A switch a random pick should flip: a wildcard and WP Var → Bool"
},
{
"title": "Controls",
"text": "upstream (in)\nOptional Context to extend. Only nodes after this one see the toggle variables. It can also set toggles (see below).\nPencil (toolbar)\nSelect the node and the indigo pencil in ComfyUI's selection toolbar opens the edit view: the mode, the Pick one options, the grips to reorder, the row buttons, Add toggle and Add label. Done goes back to the clean view, which shows only the switches.\nAny on / Pick one\nAny on: every toggle is on or off by itself. Pick one: at most one is on; tick Always one on to keep exactly one on, like radio buttons.\nwrites (Pick one)\nOptional variable that also gets the picked toggle's value (or its name), so one rule can branch on it.\nGlobe (row)\nEdit view only. On (the default) keeps the toggle's variable internal: rules read it, the Assembler doesn't offer it and it never prints. Turn it off to use the variable in a prompt. Same in Any on and Pick one.\nAlways one on (Pick one)\nLike radio buttons: one toggle is always on. Clicking the picked one does nothing; pick another to switch. With none on, the first toggle is picked. Off (the default), the picked toggle can be switched off so none is on.\nShow in Assembler (Pick one)\nThe picked variable shows in the Assembler and can print in the prompt. Off, it is internal.\nStyle (Pick one)\nHow every row looks in Pick one: Radio (a round dot, the default), Chip (the name across the row, lit when picked), Switch or Check. Turning one on turns the others off.\nLayout\nEdit view: Rows (one row per toggle, the default) or Grid (each toggle in a tile, the tiles wrapping to the node's width), in both modes and every style. Each label has a grid button for its own group; setting it back to the node's layout makes the group follow the node again. Under a grid's tiles, thin lines carry the dots: mute/bypass dots stack on the left with their toggle's name, BOOLEAN outputs on the right with theirs, each side packed on its own (so Dawn | Dawn, Sunset | Night). The arrow on the first line merges the group's lines into one that carries every dot and says how many go in and out (the names on hover), and the arrow on the merged line opens them again.\nPencil, globe, trash (row)\nEach row in the edit view has three buttons, like a module on a Context node: the pencil opens the toggle's own settings in a window (name, variable, looks, style, needs, what off mutes or bypasses, group and its output) with the row drawn on top as the node will show it; the globe keeps its variable internal; the trash removes it.\nAdd toggle / Add label\nA new toggle, or a label that groups the toggles under it. Click a label's arrow to fold it; its icon and colour buttons pick any icon and any colour. Drag a toggle's grip under a label to move it into that group; a dragged label takes its toggles along.\ncontrol dots (in)\nA toggle that mutes or bypasses has a dot at the left of its row: wire the nodes it drives into it.\ncontext (out)\nThe Context with every toggle variable added. Internal ones (the globe) are for rules only; the rest show in the Assembler.\ntoggle outputs\nOne BOOLEAN per toggle that has an output, at the right of its row. Up to 16."
},
{
"title": "Editing a toggle",
"text": "Name\nShown on the row and on its output. A Chip shows Name · $variable (the variable while Show is on). A name colour also colours its variable.\nVariable\nGets true or false, or the toggle's Value while on and nothing while off. $name.bool is always true or false. It follows the name until you change it. Internal until you turn off the row's globe. Show puts it under the name, or after it on a Chip.\nLooks\nAn icon before the name, and colours for the icon, the name and the control (switch, check, radio, chip or buttons), so Day can be yellow and Night blue. None keeps the theme's colour.\nStyle\nSwitch, Check, Buttons (two words of your own, like Day / Night; the On side lights up in the control colour while picked, and a click on the lit side flips it too) or Chip (a small chip with the name). Any on only: in Pick one the Style in the top bar sets every row's look.\nValue\nWhat the toggle's variable gets while it is on, instead of true; off, it gets its Off value (nothing when that is empty), so $rain can print \"heavy rain\" or nothing. $rain.bool still reads true or false, for rules. In Pick one the picked variable gets it too, instead of the toggle's name. A text box like the Assembler's: $ lists every variable upstream (Context Injector ones included) and the toggles' own, and tag, LoRA and embedding autocomplete work when they are on. A $name nothing upstream sets is flagged under the box and in WP Debug. Inline {a|b} picks need a seed, so they print as written; roll them in a wildcard instead. Drag the corner to make it taller.\nNegative\nWords to keep out of the image while this toggle is on. They go with its variable (and, in Pick one, with the picked variable), so an Assembler that prints it sends them to its negative output, like a wildcard option's negative. It can read $variables; off, or without one, the variable carries none.\nOff value (Any on)\nWhat the variable gets while the toggle is off, with its own Off negative. Empty prints nothing. With both, one toggle writes either side: a Buttons toggle Anime / Real can write \"anime style\" or \"photorealistic\", each keeping the other's look out of the image. Either one makes the variable hold text, not true or false; $name.bool still reads the switch.\nNeeds\nAny on only. Another toggle this one needs. While that one is off, this one is off too; it keeps your choice for when it comes back on.\nWhen off\nNothing, Mute or Bypass: what happens to the nodes wired into the row's dot (and to the canvas groups you pick) while the toggle is off. On, they run. When several toggles drive one node, off wins: it runs only while all of them let it (Mute beats Bypass).\nInvert\nMute or bypass while the toggle is ON instead, for a node used only as a switchboard.\nCanvas groups\nGroups on the canvas whose nodes When off also covers, by title. No wires needed.\nUpstream can set it\nWhen the upstream Context holds this toggle's variable as a yes/no word (true, yes, on, 1, false, no, off, 0), that value sets the switch for the run.\nGroup\nWhich label it sits under. You can also drag the toggle's grip under another label, or above the first one.\nBOOLEAN output\nShow an output for switch nodes. Off for a new toggle; leave it off for a toggle only rules read.\nDrag the grip to reorder: the row you drop beside opens a gap with a line. An indented line in a group's colour puts the toggle in that group (a folded one opens); a full-width line, just above a label or below the last row, puts it below the group, in no group. A label moves with its toggles and lands only between groups. Moving a row never re-points a wire: each output stays with its toggle."
},
{
"title": "Icons and colours",
"text": "Give a toggle an icon and its own colours under Looks in its window: one for the icon, one for the name and one for the control itself, so Day can glow yellow and Night blue. On Buttons the colour lights up the On side while it's picked, and clicking the lit side flips the toggle too. A label's icon and colour buttons do the same for the group, and its toggles wear that colour on their left edge. The row at the top of the toggle's window shows each change before you press Done."
},
{
"title": "Use a toggle in the prompt",
"text": "A toggle's variable is internal by default: rules read it, but the Assembler doesn't offer it, since true or false rarely belongs in a prompt. To print it, open the edit view and turn off the globe on its row. In Pick one, Show in Assembler does the same for the picked variable, so $time can print the picked toggle's Value.\nGive a toggle a Value and its variable holds that text while it is on and nothing while it is off: a Rain toggle with the Value \"heavy rain\" makes $rain print \"heavy rain\" or nothing. The switch itself is still there as $rain.bool, true or false, for rules and conditions. Its Negative goes with the variable while it is on, so an Assembler that prints $rain also sends \"sunny, dry\" to its negative output. A Value that reads a $variable brings that variable's negatives along too.\nIn Any on a toggle can also say what to write while it is off: its Off value and Off negative. A Buttons toggle Anime / Real with the Value \"anime style\" and the Off value \"photorealistic\" writes one or the other, and each side's negative keeps the other look out. Leave them empty and off prints nothing, as before."
},
{
"title": "Mute or bypass nodes",
"text": "Set a toggle's When off to Mute or Bypass and its row gets a dot on the left. Wire any node's output into it, or a WP Node Group to drive several. Turning the toggle off mutes or bypasses them; turning it on lets them run. Needs and Pick one count: a toggle that is off because of them drives its nodes off too. A toggle can do all of it at once: write $its_variable, send its BOOLEAN and mute or bypass. Once something is wired, an icon next to the name shows it: coloured while it is muting or bypassing, dim while not; hover it for the nodes.\nFigure: Two nodes in a group, wired into Detailer's dot. Detailer is off, so both are muted. Two nodes, Detailer decode (a VAE Decode) and Detail upscale (an Upscale Image By), are wired into a WP Node Group, which lists them as muted rows with Run / Mute / Bypass buttons at the top and a free node_3 dot. Its nodes output is wired into the dot at the left of the Detailer row of a WP Toggles node; Detailer is set to Off (On / Off buttons) and shows a mute icon by its name. Upscale is a switch, on.\nThis happens on the canvas, the moment the toggle flips, so the saved workflow keeps the modes. The wires to the dots never reach the run. An upstream value that sets a toggle changes its variable and output, not the modes."
},
{
"title": "Pick one",
"text": "One toggle is on. Which one, first match wins: the toggle the upstream picked variable names (with Upstream can pick), then the first toggle upstream set to a yes word, then the first one you switched on. The picked variable gets the toggle's Value, or its name; it is internal unless Show in Assembler is on. A toggle's Negative, under its Value, goes with the picked variable: an Assembler that prints it sends those words to its negative output. All rows share one look here: the Style next to those boxes picks Radio (the default), Chip, Switch or Check, so a toggle's own Style and Needs are hidden.\nBy default you can also switch the picked toggle off, so none is on. Tick Always one on to make the group work like radio buttons: one toggle is always on, clicking it again does nothing, and you switch by picking another. With none on (or when you tick it), the first toggle is picked."
},
{
"title": "Rows or a grid",
"text": "The Layout switch in the edit bar draws the node as Rows (one row per toggle, the default) or as a Grid: each toggle in a tile, the tiles wrapping to the node's width, in both modes and every style. Each label has a grid button for its own group, so one group can be a grid while another stays rows. Under a grid's tiles, thin lines carry the dots: mute/bypass dots stack on the left with their toggle's name and BOOLEAN outputs on the right with theirs, each side packed on its own, so one line can hold two toggles. The arrow on the first line merges the group's lines into one that carries every dot and says how many go in and out (the names on hover), like the Context Injector's merged wires. Rows keep their dots on the row, and the edit view always shows rows so you can reorder them."
},
{
"title": "Merge the Boolean nodes you have",
"text": "Select Boolean nodes, right-click one and pick Merge into WP Toggles. Each becomes a toggle named after its title, keeps its value, and its wires move to the matching output. A node can merge when its only output is BOOLEAN, nothing is wired into it and it has one on/off value, so ComfyUI's Boolean and most pack toggles work. Ctrl+Z undoes the merge."
},
{
"title": "Works with",
"text": "WP Context Injector WP Var → Bool WP Node Group WP Mode Relay WP Model Info"
}
]
},
{
"id": "wp-node-group",
"title": "WP Node Group",
"group": "nodes",
"blurb": "Gather nodes so one WP Toggles switch mutes or bypasses them all.",
"keywords": [
"mute",
"bypass",
"group",
"collector",
"repeater",
"toggle",
"rgthree"
],
"sections": [
{
"title": "What it's for",
"text": "WP Node Group collects nodes for a WP Toggles control. Wire the nodes in on the left and its output into a toggle's dot: when the toggle is off, every node in the group is muted or bypassed. Groups can go into groups. It never runs and is left out of the prompt.\nWith nothing wired in, put it inside a canvas group instead: it then stands for every node of that group. Muting or bypassing it, or a toggle or relay driving it, mutes or bypasses the whole group.\nFigure: Two nodes in a group, wired into Detailer's dot. Detailer is off, so both are muted. Two nodes, Detailer decode (a VAE Decode) and Detail upscale (an Upscale Image By), are wired into a WP Node Group, which lists them as muted rows with Run / Mute / Bypass buttons at the top and a free node_3 dot. Its nodes output is wired into the dot at the left of the Detailer row of a WP Toggles node; Detailer is set to Off (On / Off buttons) and shows a mute icon by its name. Upscale is a switch, on.\n- node dots (in)Wire any node's output in.\n- nodes (out)Wire it into a WP Toggles control dot: that toggle then mutes or bypasses every node here.\n- Run / Mute / BypassSet every node in the group at once.\n- Mode chipEach row's chip shows its node's mode."
},
{
"title": "When to use it",
"text": "Good for\n- One switch for a whole branch: a detailer, an upscale pass, a LoRA stack\n- Muting or bypassing by hand from one place\n- A mute or bypass switch that lives inside a canvas group, with no wires\nBetter with something else\n- A WP Toggles row driving canvas groups it sits outside: pick the groups by title in the toggle"
},
{
"title": "Controls",
"text": "node dots (in)\nWire any node's output in. A new free dot appears as you wire; a freed one goes. Each shows its node's title.\nnodes (out)\nWire it into a WP Toggles control dot: that toggle then mutes or bypasses every node here.\nWhole group\nWith nothing wired into its dots (a WP Mode Relay doesn't count), the node gathers every node of the canvas group it sits in, the smallest one when groups nest, and the bar names that group. Nodes a wired-in relay watches stay out. Wire any node in and it goes back to only the wired ones.\nCovers\nShown while it stands for a canvas group. Its group: the smallest group it sits in, inner groups included. Not inner: that group, leaving out the nodes of groups inside it. Every group: every group it sits in, outer ones too.\nRun / Mute / Bypass\nSet every node in the group at once.\nMode chip\nEach row's chip shows its node's mode. Click it to go Runs → Muted → Bypassed.\nMuting or bypassing the group node itself does the same to its nodes, and setting it back lets them run."
},
{
"title": "Whole canvas group",
"text": "A WP Node Group with nothing wired into its dots looks for the canvas group it sits in and gathers every node of it, so you don't wire each one. The bar then reads Whole group: <title> with the count. When groups nest it takes the smallest one around it, inner groups included; Covers changes that to Not inner (leave out the nodes of groups inside it) or Every group (outer groups too). A WP Mode Relay wired in doesn't count as a wired node, and the nodes that relay watches stay out, so the relay never changes what it reads. Wire any other node in and the group node goes back to only its wired nodes.\nNotes, bookmarks and other frontend-only nodes in the group are left alone. A WP Toggles node driving the group node never mutes itself, even when it sits in the same group.\nWith a group node in an outer group and another in an inner one, the outer node covers the inner group's nodes too. When toggles of one WP Toggles node both reach a node, off wins: it runs only while every one of them lets it, whichever you clicked last."
},
{
"title": "Works with",
"text": "WP Toggles WP Mode Relay"
}
]
},
{
"id": "wp-mode-relay",
"title": "WP Mode Relay",
"group": "nodes",
"blurb": "When the nodes it watches get muted or bypassed, mute, bypass or run a WP Node Group.",
"keywords": [
"mute",
"bypass",
"relay",
"chain",
"rgthree",
"group"
],
"sections": [
{
"title": "What it's for",
"text": "WP Mode Relay chains mutes and bypasses. It watches the nodes wired into it and, when what they do changes, sets the nodes of the WP Node Group its output goes into. So muting a sampler can bypass its whole save branch, or bypassing one LoRA can let a different one run. It never runs and is left out of the prompt.\nFigure: WP Model Info is muted, so the relay watching it sends Mute and its WP Node Group mutes the Render settings nodes. A folded Load Checkpoint and a muted, folded WP Model Info; Model Info is wired into the watch dot of a WP Mode Relay, whose table maps All muted to Mute (the current case), All bypassed to Bypass and Any runs to Run, with the line 'Watching 1 node: muted · drives 3 nodes'. The relay's drives output goes into a WP Node Group (model settings) whose rows WP Context · Render, Width and Height show Muted; the folded nodes below are muted too.\n- watch dots (in)Wire the nodes to watch.\n- drives (out)Wire it into a WP Node Group: the relay sets that group's nodes.\n- All mutedWhat to send when every watched node is muted: Run, Mute, Bypass or nothing (—).\n- All bypassedWhat to send when every watched node is bypassed.\n- Any runsWhat to send when at least one watched node runs."
},
{
"title": "When to use it",
"text": "Good for\n- Turning one branch off when another is off\n- Swapping: bypass A when B runs\nBetter with something else\n- One switch for several nodes: a WP Toggles row with a WP Node Group"
},
{
"title": "Controls",
"text": "watch dots (in)\nWire the nodes to watch. A new free dot appears as you wire.\ndrives (out)\nWire it into a WP Node Group: the relay sets that group's nodes.\nAll muted\nWhat to send when every watched node is muted: Run, Mute, Bypass or nothing (—).\nAll bypassed\nWhat to send when every watched node is bypassed.\nAny runs\nWhat to send when at least one watched node runs.\nA mix of muted and bypassed sends nothing. The relay acts at once when the watched nodes change and when you change one of its choices, never when a workflow loads."
},
{
"title": "Works with",
"text": "WP Node Group WP Toggles"
}
]
},
{
"id": "wp-bookmark",
"title": "WP Bookmark",
"group": "nodes",
"blurb": "Mark a spot on the canvas and jump back to it with a shortcut or from the Bookmarks dock.",
"keywords": [
"bookmark",
"jump",
"navigate",
"shortcut",
"zoom",
"dock",
"rgthree",
"hotkey"
],
"sections": [
{
"title": "What it's for",
"text": "WP Bookmark marks a place in a big workflow. Press its shortcut, or click it in the Bookmarks dock, and the canvas glides there: the bookmark lands at the corner, edge or centre of the screen you picked, at the zoom you picked. It never runs and is left out of the prompt; its settings are saved with the workflow.\nFigure: A bookmark open in its edit view (select it and press the pencil in ComfyUI's selection toolbar), next to the KSampler it marks. Screenshot of a KSampler next to a WP Bookmark in its edit view: an icon button, a colour (none) and the name The image; Lands at with a 3×3 grid of spots and Your view (the view you set, at its zoom) with a Use this view button; Zoom Set / Keep current with 0.97×; Shortcut Alt + 6 with a bolt button and a clear button; Show on canvas on and Pinned off; Go and Done buttons at the bottom.\n- Lands atA 3×3 grid: the corner, edge or centre of the free part of the screen (what ComfyUI's toolbar, the open side panel and the bottom panel leave, following them as they open and close), 24 px in.\n- Use this viewLands on the view on screen now, at its zoom, instead of a grid spot.\n- ZoomSet jumps to that zoom (0.1–10×; the magnifier takes the zoom you're at).\n- ShortcutRecord, then press the keys (Esc cancels, Backspace clears).\n- Show on canvasOff hides the node when you press Done: not drawn, not clickable, not selectable.\n- PinnedComfyUI's own pin: a pinned bookmark can't be moved by accident.\n- GoTries the jump."
},
{
"title": "Notes as bookmarks",
"text": "A WP Note can be a bookmark too: turn on Also a bookmark in its editor. It gets the same Lands at, Zoom and Shortcut settings and shows in the dock in the note's colour, but it never hides on the canvas, and muting it turns its shortcut off without fading it."
},
{
"title": "On the canvas",
"text": "A bookmark has no node frame and can't be resized: it is a small pill with its icon, name and shortcut (a warning mark when something else uses the key, a pin when it is pinned). Hover it for where it lands. A double-click, or the indigo pencil in ComfyUI's selection toolbar while it is selected, opens the editor, and Done closes it. A new bookmark opens in the editor. Drag the editor's background (anywhere that isn't a control) to move it while it is open."
},
{
"title": "When to use it",
"text": "Good for\n- Jumping between the prompt, the sampler and the outputs of a large graph\n- A tour of a shared workflow: step through it with Next\n- Getting back to the same view and zoom every time\nBetter with something else\n- Explaining a step to someone: a WP Note"
},
{
"title": "Settings",
"text": "Icon, colour, name\nThe icon opens the full icon list. The colour marks the pill and its dock row.\nLands at\nA 3×3 grid: the corner, edge or centre of the free part of the screen (what ComfyUI's toolbar, the open side panel and the bottom panel leave, following them as they open and close), 24 px in. A jump that would land under the dock, the minimap or the zoom controls moves just enough to clear them all (the dock only while its Land clear of the dock switch is on). It lands in the same place however often you press the key, selected or not. Only the layout counts: what pops up on its own (the job progress panel while a workflow runs, the error card, toasts, menus, tooltips, dialogs) never moves it.\nUse this view\nLands on the view on screen now, at its zoom, instead of a grid spot.\nZoom\nSet jumps to that zoom (0.1–10×; the magnifier takes the zoom you're at). Keep current leaves it alone.\nShortcut\nRecord, then press the keys (Esc cancels, Backspace clears). The bolt picks a free one, a new one on each press.\nShow on canvas\nOff hides the node when you press Done: not drawn, not clickable, not selectable. The dock's eye shows it again.\nPinned\nComfyUI's own pin: a pinned bookmark can't be moved by accident. New bookmarks start unpinned; a saved workflow keeps each bookmark's pin.\nGo\nTries the jump.\nMuting or bypassing a bookmark turns it off: its shortcut does nothing and the dock greys it."
},
{
"title": "Shortcut clashes",
"text": "While you set a shortcut, the node lists who else uses it.\nRefused\nKeys the browser keeps: Ctrl+T, Ctrl+W, Ctrl+1–9, F5, F11 and the like.\nWarning\nA ComfyUI shortcut (yours from Settings › Keybinding included), another extension's, another WP Bookmark's, or an rgthree bookmark's in the same workflow.\nNote\nNo Ctrl or Alt: it fires whenever the canvas has focus, never while you type.\nExtensions that read keys themselves declare nothing a keymap could show, so the node also asks the page: when you record a key or press the bolt, it sends a pretend key press and watches for anything that claims it. A key something answers is listed as taken and the bolt skips it. The pretend press stops a handler at the moment it claims the key, so nothing runs. It carries the same key codes a real press does, so extensions that only read those (like ComfyUI-Easy-Use's Alt+0–9 node template keys) are found too.\nA handler that acts without claiming the key can't be seen this way. If an extension still takes the key first, the bookmark doesn't jump and a toast names the combo once, so you can pick another. The key is remembered: the clash check marks it from then on and the bolt never suggests it."
},
{
"title": "The Bookmarks dock",
"text": "The bookmark button in the top bar shows or hides the dock. It also opens by itself in a workflow with bookmarks, unless you closed it. It sits in the free part of the canvas, clear of the side panel, the bottom panel, the minimap and the zoom controls, and moves when they open or close. What pops up on its own (the job progress panel while a workflow runs, the error card, toasts, menus, tooltips) never moves it or where bookmarks land; it floats over them. It is see-through until you point at it (more so when folded). The grid button in its header shows the bookmarks as icon tiles instead of rows, and the list button goes back; the dock remembers which you picked.\nFigure: The dock in view mode: click a row to jump. Its pencil opens edit mode, where the eye brings back a hidden bookmark. Screenshot of the Bookmarks dock, in view mode, at the top left of the canvas, slightly see-through. Its header reads Bookmarks, 4 · 1 hidden, with Back, Previous, Next, a pencil to edit and Close. The rows show a number, a coloured icon, a name and a shortcut: 1 Prompts (Alt + 1), 2 Sampler (Alt + 2, highlighted as the bookmark just jumped to), 3 Upscale in grey italics with a small crossed-out eye because it is hidden on the canvas (Alt + 3), and 4 Save and share marked OFF because the node is muted. On the canvas, the Sampler bookmark's pill (Alt + 2) sits by the KSampler and VAE Decode, the muted Save and share pill is greyed and marked OFF, and the prompt and latent nodes are below; the hidden Upscale bookmark is not drawn.\nRow\nClick to jump. Number (a Step note's in its circle), icon, name and shortcut; a mark for one hidden on the canvas or inside a subgraph, OFF for a muted one.\nGrid / List\nShows the bookmarks as icon tiles (number, icon in its colour, name, shortcut) that wrap three to a line, or as rows. The dock remembers your choice; edit mode always shows rows.\nBack\nReturn to where you were before the last jump.\n↑ / ↓\nStep through the bookmarks that are on, in dock order.\nPencil\nEdit mode: drag the dock by its header and it snaps to the nearest spot of a 3×3 grid on the free canvas (a dashed outline shows where); drag the grips to reorder, the eye shows or hides a bookmark on the canvas, and Bookmark this view adds one at the zoom you're at: the small 3×3 grid beside it picks where on screen the new bookmark sits, and it lands there, so jumping to it brings back that exact view. Land clear of the dock (on by default) moves a jump out from under the dock; off, bookmarks land where they would without it, behind the see-through dock. Done goes back. The dock follows the layout (side panels, the bottom panel, the top bar, the minimap and zoom controls, a floating run bar) and stays put when the job progress panel, the error card, a toast or a menu pops up; those float over it.\nNext, Previous, Back, Bookmark this view and the dock toggle are commands without keys: bind them in Settings › Keybinding."
},
{
"title": "Works with",
"text": "WP Note WP Node Group WP Toggles"
}
]
},
{
"id": "wp-prompt-assembler",
"title": "WP Prompt Assembler",
"group": "nodes",
"blurb": "Write a template with $variables and get the prompt and its negative.",
"keywords": [
"template",
"prompt",
"assemble"
],
"sections": [
{
"title": "What it does",
"text": "Write a $style portrait of $subject, $lighting and the Assembler puts each variable's value in its place, every run. The result is plain text for CLIP Text Encode. Variables you set to Hide from prompt print nothing.\nGood for\n- One per prompt you need: the main prompt, a face detailer, one per region\n- Each reads the same Context, so their picks agree\nBetter with something else\n- Rolling anything: it has no seed. Pick in a wildcard and use its $variable\nFigure: The Assembler with a template, its variable chips and its preview. The preview inside the node rolls each Context on its own seed (or the Int or seed node wired into it), so it is what Run prints and changes with the seed; the two Preview as Text nodes show what the run printed. Screenshot after a run. Folded WP Context · Character and WP Context · Scene feed a WP Prompt Assembler with the template '$hero, $hair, $eyes, wearing $outfit, $scene, $style' and the negative template 'lowres, bad anatomy, watermark, $negatives'. Its variables strip shows chips for $hero, $hair, $eyes, $outfit, $setting, $time, $lighting, $scene and $style (9 upstream); below are the resolved preview and the negative preview with 1 from variables. The prompt and negative outputs feed two Preview as Text nodes that print the run's prompt and negative.\n- context (in)The variables from a WP Context (or anything that ends in one).\n- prompt (out)The finished prompt.\n- negative (out)The negative words of the variables this template printed, plus your negative template.\n- templateYour prompt with $variables in it.\n- negative_templateOptional, in the collapsible negative section."
},
{
"title": "Write a template",
"text": "- Type $ and pick a variable, or click a chip in the variables strip to insert it at the cursor.\n- A variable nothing sets is underlined in amber and shows as a dashed chip, and prints nothing when you run. Click the chip to remove it from the template.\n- With a WP Context Loop upstream, a variable that only some frames set (a module switched on or off per frame) is not missing: its chip is blue with a frame tag such as #4, the header counts it as on some frames, and pointing at it says which frames render it empty. The preview shows one frame, the one picked under the Loop's Edit frame; the ‹ 1/4 › arrows above it step through them (see Per-iteration overrides).\n- $$ prints a single dollar sign.\n- Save and Load in the toolbar keep templates in your library (see Templates).\nThe Assembler doesn't roll. {a|b|c} prints exactly as typed and @{…} prints nothing. Put choices in a wildcard or combine and print its variable.\nHow empty variables are tidied\nWhen a variable comes out empty, the commas around it and a joining word left with nothing after it (\"with\", \"and\") are removed. Your own words stay: with $hat, $scarf and $place empty, a girl wearing $hat with $scarf, $pose, in the $place gives \"a girl wearing, sitting\". Write templates that still read well when something is empty (put the verb inside a combine), and use WP Prompt Cleaner to remove duplicates. Emoticon tags such as :d are kept."
},
{
"title": "Hide the variables you don't use",
"text": "A big Context offers many variables, and most of them feed other variables rather than the prompt. The eye button in the variables toolbar (beside Load, Save and Clear) marks every variable that neither this template nor the negative template reads as internal, so the chip strip and the $ list only offer what you print.\n- Internal only hides a variable from Assemblers. Combines, rules, constraints and the Var nodes still read it, so $hero used inside $scene keeps working.\n- Every other Assembler on the same context is checked first: a variable one of them prints ($x.0, $x.AXIS and $x.neg count) is kept.\n- A Context module has one internal switch for all the variables it writes, so a fixed values module that also writes a variable in use is left as it is.\n- It flips the same switches you would: the globe on a module, an Injector row, a toggle, a Model Info row or a Loop variable. The message says what it marked and what it kept and why; Undo in it puts everything back.\n- An Assembler whose template comes from a wire can't tell what it uses, so nothing it can see is marked."
},
{
"title": "The negative output",
"text": "Wildcard options, fixed values, combines and derivations can carry negative words. The negative output collects them, but only for variables this template printed: an Assembler for a face detailer that prints just $face never gets the hair negatives.\nFigure: Hair, Weather and Quality feed an Assembler with both outputs wired. The negative preview shows the words collected from the variables the template printed. Screenshot of a WP Context (seed 3) holding Hair, Weather and Quality, wired into a WP Prompt Assembler whose prompt and negative outputs each feed a Preview as Text node. The template is '$quality, a woman with $hair, $weather'; the preview reads 'masterpiece, best quality, a woman with black hair, clear sky'. The negative template is empty ('just the variables' negatives'), and the negative preview, marked '1 from variables', reads 'lowres, bad anatomy', which came from the Quality fixed value. The negative Preview as Text prints 'lowres, bad anatomy'.\n- A new Assembler starts with $negatives: just the collected words. Add your standing negative around it, e.g. lowres, bad anatomy, $negatives.\n- Without $negatives the negative is only what you write, and an empty box is an empty negative. Workflows saved before this get $negatives added when they load, so they print the same.\n- Repeated tags are dropped, also when written with a weight like (blurry:1.2).\n- Clear template empties the negative template too.\nMore in Negatives."
},
{
"title": "In a subgraph",
"text": "Inside a subgraph, drag the template or negative_template dot to the subgraph's input to edit it from outside. The subgraph node then shows the same editor, with the $ list of the variables upstream of the Assembler inside. What you type there is what the Assembler runs with, and it is saved with the workflow. While a wire feeds that input on the outside, the box is read-only."
},
{
"title": "Inputs and outputs",
"text": "context (in)\nThe variables from a WP Context (or anything that ends in one).\ntemplate\nYour prompt with $variables in it. Wire another node's text into it to use that text as the template instead: the box then says \"From the template input\", and the $variables in the wired text still fill in.\nnegative_template\nOptional, in the collapsible negative section. Your standing negative, with $negatives where the collected negative words go. New Assemblers start with $negatives; without it the negative is only your own text (empty = no negative). It takes a wired text the same way the template does.\nprompt (out)\nThe finished prompt. Wire it into CLIP Text Encode or a WP Prompt Cleaner.\nnegative (out)\nThe negative words of the variables this template printed, plus your negative template. Wire it into your negative CLIP Text Encode."
},
{
"title": "Works with",
"text": "The $variable pipeline Negatives WP Context WP Prompt Cleaner"
}
]
},
{
"id": "wp-prompt-cleaner",
"title": "WP Prompt Cleaner",
"group": "nodes",
"blurb": "Tidy a prompt: duplicates, stray commas, brackets, LoRA tags, blocked words.",
"keywords": [
"clean",
"dedupe",
"blocklist"
],
"sections": [
{
"title": "What it's for",
"text": "Wire a prompt, usually from a WP Prompt Assembler, into the Cleaner. It tidies spaces, empty brackets, stacked weights and LoRA tags, drops duplicate tags, and removes words on your blocklist. The clean text goes on to CLIP Text Encode. It needs no Context.\nGood for\n- Removing duplicates and near-duplicates after the Assembler\n- Merging weights, spacing LoRA tags, blocking words\nBetter with something else\n- Fixing a pack: if it often removes something, fix the option that wrote it\nNever blocklist or fuzzy-dedupe a LoRA's trigger words, or the LoRA stops working. The blocklist matches whole words, so list every form of a word you block (see Adult (NSFW) packs). For prose prompts use text mode on Gentle: tags mode splits on commas, so it would split sentences.\nFigure: A messy tag list and negative go into WP Prompt Cleaner and come out without the doubled tags, empty brackets and stray commas. Each rule counts its fixes, and Last run sums them up; click it for the before and after. Screenshot after a run. A Prompt text node ('masterpiece, best quality, , 1girl, red hair, Red Hair, ((smile)), (, ), solo,, < lora : style : 0.8 >') and a Negative text node ('lowres, bad hands, , lowres, red hair') wire into WP Prompt Cleaner, in tags mode, intensity balanced, with its rules (whitespace, punctuation, empty groups, merge weights, LoRA spacing, tag dedupe, fuzzy dedupe, blocklist) and their counts, prompt and negative columns, and drop negative tags also in prompt. Last run reads prompt: 1 duplicate; negative: 1 duplicate, 1 tag also in prompt 'red hair'. Two Preview as Text nodes print 'masterpiece, best quality, 1girl, red hair, ((smile)), solo, <lora:style:0.8>' and 'lowres, bad hands, red hair'.\n- prompt (out)The cleaned prompt, for CLIP Text Encode.\n- negative (out)The cleaned negative, using the negative column's rules.\n- prompt (in)The text to clean, usually from a WP Prompt Assembler.\n- negative (in)Optional.\n- cleaner widgetPick a mode and a preset, switch single rules on or off, and edit the blocklist.\n- blocklist (in)Optional text from another node, such as one shared word list feeding several cleaners."
},
{
"title": "Inputs and outputs",
"text": "prompt (in)\nThe text to clean, usually from a WP Prompt Assembler. Any text works.\ncleaner widget\nPick a mode and a preset, switch single rules on or off, and edit the blocklist. After a run it shows how many fixes each rule made, and the Last run strip opens the run's before and after with what each rule changed and why.\nnegative (in)\nOptional. A negative to clean too: type it or wire in the Assembler's negative. Left empty, the negative output is empty.\nprompt (out)\nThe cleaned prompt, for CLIP Text Encode.\nblocklist (in)\nOptional text from another node, such as one shared word list feeding several cleaners. Its entries join the panel's blocklist (comma- or newline-separated in list mode, one per line in regex mode) and turn the blocklist on; the Blocklist button reads + input. Its dot sits beside that button and shows when you hover it or wire it.\nnegative (out)\nThe cleaned negative, using the negative column's rules. Wire it into your negative CLIP Text Encode."
},
{
"title": "Mode",
"text": "Tags is for comma-separated tag lists, the usual SD style: the Cleaner works tag by tag. Text is for sentences: tag rules such as duplicate removal are off, and the blocklist removes just the word, not the whole phrase."
},
{
"title": "Presets",
"text": "Gentle\nOnly tidies spaces and commas: doubled spaces, spaces at the ends and before a comma, doubled commas and empty slots between them (blurry, , lowres becomes blurry, lowres). The safest choice.\nBalanced\nAlso removes empty brackets, tidies LoRA tags and stray punctuation, and drops exact duplicate tags. None of these change what the prompt means. A good default.\nAggressive\nAlso merges stacked weights and drops near-duplicates: the same words up to plurals and a leading a, an or the (\"a knight\", \"knight\" and \"knights\" keep the first), or tags 90% alike letter by letter, which includes the same tag at another weight. Now and then it removes something you meant to keep.\nCustom\nShown when you've switched single rules away from the preset."
},
{
"title": "Prompt syntax rules",
"text": "Three rules read brackets and tags the way ComfyUI does. They work in both modes and run first, so anything they leave behind is tidied in the same run.\nEmpty groups\nRemoves brackets with nothing inside: (), [], ( , ), (:1.2), and groups left empty once those go. red (), dress becomes red, dress. The usual cause is a weighted reference that printed nothing: (@{hat}:1.2) becomes (:1.2). On from Balanced.\nMerge weights\nFolds stacked weights into one group by multiplying them, as ComfyUI does (a bare ( ) counts as 1.1): ((a:1.1):1.2) becomes (a:1.32), (((a))) becomes (a:1.33), and (a:1.0) becomes plain a. Only merges when the inner group is everything inside the outer one, rounds to 2 decimals, and never touches [ ]. On in Aggressive.\nLoRA spacing\nTidies <lora:…> tags (and lyco, locon, hypernet, <embedding:…>): < lora : x : 0.8 > becomes <lora:x:0.8>, and girl<lora:x:1> becomes girl <lora:x:1>. Spaces inside a file name stay. On from Balanced.\nEscaped \\( \\) stay literal, LoRA and embedding names are never edited, and if the brackets don't balance the bracket rules leave the prompt exactly as written."
},
{
"title": "Cleaning the negative",
"text": "With a negative wired in, one Cleaner cleans both. The rule list gets a second column of tick boxes for the negative. It follows the prompt column, except near-duplicate removal and the blocklist are off: near-duplicates in a negative are usually on purpose, and blocked words often belong in a negative.\nDrop negative tags also in prompt (off by default) removes a tag from the negative when the prompt asks for it too, since the model can't both draw it and avoid it. Either way, the Last run strip names the tags the two share on an amber line of their own."
},
{
"title": "Last run",
"text": "After a run, the Last run strip at the bottom of the node says how many rules changed the prompt and what they dropped. Click it to open the last run in a window:\n- Before and After side by side, with removed tags struck through in red and added ones in green. In tags mode whole tags are compared, so ((silver hair)) → (silver hair:1.21) reads as one tag out and one in.\n- One line per rule that changed something, in the order they ran. Open a line to see that rule's own before and after, and why it fired: a dropped duplicate beside the tag it repeats, a near-duplicate beside the tag it is close to (and how close), a blocked tag beside the blocklist entry it matched. The cause is marked in blue in the text.\n- Prompt / Negative switches sides when the run cleaned a negative too; the negative side also lists tags the prompt has as well. Copy cleaned text copies the side you are on.\nEach rule runs once per run, in the order of the list (bracket and LoRA rules first, then whitespace, punctuation, tag dedupe, fuzzy dedupe, blocklist), and each works on what the one before left. So a rule can count 0 because an earlier one already fixed it: the empty slot in blurry, , lowres goes with whitespace, leaving punctuation nothing. Duplicate removal never touches BREAK or AND, and punctuation keeps emoticon tags such as :3 and -_-.\nIt's the quickest way to check a pack: if a rule keeps removing the same thing, fix the option that wrote it."
},
{
"title": "Blocklist",
"text": "Add words or phrases you always want removed. Two modes:\n- List: words separated by commas or new lines; capitals don't matter. In Tags mode the whole tag with the word goes; in Text mode just the word.\n- Regex: one pattern per line; capitals don't matter. A broken pattern is skipped and named in the run stats.\nHover a rule for what it does. After a run, the count next to each rule shows what it removed."
},
{
"title": "Works with",
"text": "WP Prompt Assembler Negatives"
}
]
},
{
"id": "wp-image-filter",
"title": "WP Image Filter",
"group": "nodes",
"blurb": "Pause the run and pick which images (and their prompts) go on.",
"keywords": [
"image",
"filter",
"pick",
"select",
"upscale",
"batch",
"pause"
],
"sections": [
{
"title": "What it's for",
"text": "Upscaling every image of a batch costs time on the ones you'd throw away. Put WP Image Filter between the first KSampler and the upscaler: the run stops there, a picker shows every image, and only the ones you pick continue. Everything that belongs to an image travels with it, so the upscaler gets each picked image's own latent, conditioning and prompt.\nGood for\n- A human pick before an expensive second pass (hires, upscale, detailer, video)\n- Curating a sweep grid\n- Inpainting a picked image in the same run\nBetter with something else\n- Unattended runs: the queue waits while the picker is open. Set it to pass all, or give it a timeout"
},
{
"title": "Where it goes",
"text": "Figure: Wire the same conditioning and prompt text through the filter that the upscaler would otherwise take from the start. Steps: KSampler + VAE Decode (images, latent) → images + prompts → WP Image Filter (you pick) → picks → Upscaler (picked only). Flow diagram of three stages left to right: KSampler + VAE Decode (images, latent), an arrow labelled images + prompts, WP Image Filter (you pick), an arrow labelled picks, and Upscaler (picked only). Caption: wire the same conditioning and prompt text through the filter that the upscaler would otherwise take from the start, so only the picked images and their matching data go on.\nWith a Context Loop, every iteration is a frame. The picker groups images by frame, and the prompt, conditioning and Context of a frame are kept only when one of its images is."
},
{
"title": "Inputs & outputs",
"text": "Every input has a matching output. Connect only what you need.\nimages\nThe images to pick from. Required: this is what the picker shows.\nlatent, masks\nSliced to the picked images. A mask you paint in the picker replaces that image's mask.\npositive, negative\nCONDITIONING. A batched conditioning is sliced with the images; a single one is kept for every frame that keeps an image.\npositive_text, negative_text\nThe prompt strings, kept or dropped with their frame.\ncontext\nEach frame's WP Context, kept or dropped with it.\nclip (in)\nOptional. Re-encodes a prompt you edit in the picker, so the positive/negative conditioning follows the edit. Without it, an edit changes only the text outputs.\nextra_1, extra_2\nAny type. The output takes the type of what you connect. Batched values are sliced, anything else follows its frame.\npicks (out)\nWhat was kept, as frame:image (1-based), for example 1:2, 3:1.\nlabels (out)\nOne label per item, in step with images: #3 · red · 2 by default, or the name you gave the image in the picker. An item with several images has one line per image."
},
{
"title": "The picker",
"text": "The picker has three modes, switched at the top or with G, D and Z. The window keeps its size in every mode, the pick count and the Keep buttons stay in the footer, and the picker opens in the mode you left it in.\nFigure: Grid: six loop frames of two images each, at tile size M. Three images are picked. Screenshot of the WP Image Filter picker, marked Waiting, in Grid mode (the Grid / Detail / Zoom switch is top right) with Tile size M: 6 frames of 2 images each, every frame a card labelled #1 to #6 with an all button. Three images are picked (green border and tick). The footer reads 3 of 12 picked from 3 frames, with Stop branch, Keep all 12 and Keep 3 picked.\nGrid G\nPick fast. Click an image or press Space over it; Ctrl+A picks everything; a frame says how many of its images are picked, and its Pick frame button picks them all. Tile size S, M, L or Fit (F, as big as the picker allows). Double-click an image or click its frame to open it in Detail. Each image's label sits under it; click it to rename.\nDetail D\nOne image next to its size, seed and label (click to rename), with Pick / Picked (P), Compare (C) and Pick frame at the top of the side panel and three tabs under them: Values, Prompt and Mask. Each value is marked in the prompt in its own colour (the same colour in Values); hover a mark to see which variable set it. Prompt and negative have copy buttons. Only what is wired shows. ←/→ step; the strip under the picture groups a loop's images by frame and jumps to any image. C compares two images with a slider, M paints a mask.\nZoom Z\nOnly the picture, at Fit, 100%, 200% or any level between (− / +, keys 0 1 2). Scroll zooms toward the mouse, drag moves, double-click goes to 100% at that spot. Zoomed in, a mini-map shows where you are (click or drag it to move); the bar fades while the mouse is still. F (or the fullscreen button) fills the screen, with the frame's images and Keep picked along the bottom. ←/→ keep the zoom and the spot on the next image. The label says Picked once picked. C pins the image and the next one shows beside it, locked to the same zoom and spot.\nFigure: Zoom on the hand in frame #5 at 200%. The mini-map shows where you are and the label says the image is picked. Step with the arrow keys and the next image opens on the same spot. Screenshot of the WP Image Filter picker in Zoom mode. The picture fills the window, zoomed to 164% on a figure's hand labelled 5 fingers. A bar on top holds ‹, −, Fit, 100%, 200%, +, the level 164%, a Pick button and ›. A tag at the bottom left reads Frame #5 · image 1 · 9 / 12, and a hint reads Drag to move · scroll to zoom · Esc back.\nKeep Enter\nKeep the picks and let the run go on.\nStep back Esc\nStep back a mode: Zoom to Detail, Detail to Grid. From Grid it tucks the picker away; a waiting pill at the bottom of the screen opens it again, picks intact.\nKeep all sends everything on. Stop branch stops the nodes after this one; the rest of the run finishes normally. Cancelling the run closes the picker.\nA short chime plays when the picker opens, and the tab title starts with ● Pick images while something waits, so you notice from another tab. Turn the chime off with Image Filter sound in Settings; turn on Image Filter desktop notification to also get a system notification while the tab is in the background."
},
{
"title": "Labels, sweep grid and Send to Loop",
"text": "With a Context Loop, each frame is labelled with its number and the options its sweep pinned (for example #3 · red · pencil). Hover a label to see the frame's seed and prompt. A sweep of two or more wildcards opens in Grid as a table. The bar above it picks which wildcard goes on Rows and which on Columns (⇄ swaps them); any others Split by, one small grid per value. Click a row, column or group name to pick all of it. Layout › Frames switches back to the list.\nFigure: A sweep of $time × $medium × $mood as a table: rows and columns, split by $mood. Screenshot of the WP Image Filter picker in Grid mode for a sweep of $time, $medium and $mood, laid out as a table. The bar reads Tile size S M L, Layout Table / Frames, Rows $time, Columns $medium, Split by $mood. Two tables sit side by side, $mood calm (2 of 6 picked) and $mood stormy (1 of 6 picked), each with rows dawn and night and columns oil, watercolor and pencil; the picked cells have a green border and tick.\nSend to Loop writes your picks back to the loop: the picked frames' seeds are locked (when the loop overrides seeds) and every other frame is bypassed, so the next run repeats only what you kept."
},
{
"title": "Label each image",
"text": "Every image has a label, and the labels output sends them in step with images, one per item. By default a label is the frame number (the loop's number with a Context Loop), the options its sweep pinned, then the image number when the frame has several: #3 · red · pencil · 2.\n- Rename an image under its tile in Grid, or under its title in Detail. Clear the name to get the default back.\n- With Same shape an item holds a frame's picks together, so its label has one line per image.\n- Pass all labels images #3 · 2 (no picker, so no option names), and Reuse last picks keeps the names from the last pause.\nFigure: Grid with a label under every image: the frame number, the swept option and the image number, and one image renamed. Screenshot of the WP Image Filter picker in Grid mode at tile size L: three loop frames of two images each, headed #1 · oil, #2 · watercolor and #3 · pencil, each with 1 of 2 and a Pick frame button. Under every image is its label: #1 · oil · 1, #1 · oil · 2, then hero shot (an image that was renamed), #2 · watercolor · 2, #3 · pencil · 1 and #3 · pencil · 2. Three images are picked. The footer reads 3 of 6 picked from 3 frames, with Send to Loop, Stop branch, Keep all 6 and Keep 3 picked."
},
{
"title": "Refine before it goes on",
"text": "In Detail, the side panel edits the frame's positive and negative prompt and paints a mask for a refine pass. Edits travel with the picks; tiles show ✎ for an edited prompt and ◐ for a painted mask.\n- An edited prompt replaces the text output. Wire a CLIP into the filter's clip input and the conditioning is re-encoded to match; without one the conditioning stays as it was, and the panel says so.\n- With Same shape a frame's picks go on together, so a prompt edit covers the whole frame. With One per image it covers just that image.\n- A painted mask goes to the masks output, resized to the image. Images you don't paint keep the incoming mask, or an empty one when nothing is wired in. Once painted, the Mask tab previews it: Over image tints the masked part, Mask only shows it white on black, as the output sends it.\nFigure: Detail on frame 3: Pick, Compare and Pick frame at the top, then the Prompt tab with each value marked in its own colour (hovering one says which variable set it) and the negative. The strip below groups the images by frame. Screenshot of the WP Image Filter picker in Detail mode on frame #3, image 1. The picture sits between ‹ and › buttons, with a strip of all 12 images under it. The side panel reads Frame #3 · image 1, 1024 × 1024, seed 41814, 5 / 12, with a Pick button, Compare and Zoom; Values lists $hair silver hair, $place desert and the seed, with Pick whole frame (2); the Prompt box marks silver hair and desert in the prompt; the Negative box reads blurry, extra fingers; Mask has a Paint button.\nFigure: How the picks carry over. Diagram titled How the picks carry over: 3 frames of 2 images from a Context Loop; frame 1 image 1 and both frame 2 images are picked, frame 3 none. Rows compare IN with OUT (same shape): images, latent and masks keep only the picked items of frames 1 and 2 (frame 2 gets a PAINTED mask); positive / negative keep 'cond red' and turn 'cond blonde' into 'cond blonde, freckles' marked RE-ENCODED; positive text becomes 'blonde hair, freckles…' marked EDITED. Notes: per-frame items follow their frame, batched ones are sliced; an edited prompt is re-encoded when a CLIP is wired; One per image emits single-image items."
},
{
"title": "Paint a mask",
"text": "M or Paint on the Mask tab opens the painter over the image. A mask is not just on or off: each pixel can be part-way masked, and inpainting changes those parts less, so soft edges blend in without a seam.\n- Brush paints where you drag. Lasso: drag around an area and let go to fill it. Fill: click to fill the area under the pointer, up to the lines you painted (paint an outline, then fill its inside), or click a painted area to take all of it.\n- Paint adds to the mask and Erase takes it away, with any tool (X swaps).\n- Size is the brush ([ and ] change it). Hardness softens the edge of the brush, lasso and fill; at 0% the edge fades over half the brush size. Strength masks part-way: one stroke at 50% stays 50% where it crosses itself, and a second stroke adds on top.\n- A ring under the mouse is the brush at its real size (dashed when erasing, with a dotted inner ring where a soft edge starts).\n- Undo and Redo (Ctrl+Z, Ctrl+Shift+Z) step through strokes, Invert and Clear while the painter is open.\nWire the masks output into a Set Latent Noise Mask on the filter's own latent, not behind an Upscale Latent. The sampler keeps every part you didn't paint exactly as it gets it, and an upscaled latent left as it is decodes blocky. To refine and upscale, upscale after the masked pass.\nFigure: The painter: a soft brush stroke at the top, an outline filled with Fill, and a soft-edged Lasso on the right. The side panel has the tools, Paint / Erase, Size, Hardness and Strength, then Undo, Redo, Invert and Clear. Screenshot of the WP Image Filter picker in Detail with the mask painter open on frame #1 · oil, image 1. Over the picture, painted in a red tint: a soft brush stroke across the top, a solid oval filled with the Fill tool over the lower left, and a soft-edged lasso shape on the right that fades out. The side panel reads Mask painted with a Done button, then the tool switch Brush / Lasso (selected) / Fill, the switch Paint (selected) / Erase, sliders Size 80 px, Hardness 0% and Strength 100%, buttons Undo, Redo, Invert and Clear, and a hint about hardness and strength."
},
{
"title": "Settings on the node",
"text": "Figure: WP Image Filter after a run of four frames with two images each: three were picked, and only those reach the Preview Image. Screenshot after a run. Folded WP Context Loop, Character and Scene Contexts, WP Prompt Assembler and WP Prompt Cleaner; the cleaned prompt goes into a stand-in image node (two 384 × 384 images per frame) whose images go into WP Image Filter, with the cleaner's texts in positive_text and negative_text and the Scene's context in context. The node lists its inputs and matching outputs (images, latent, masks, positive, negative, positive_text, negative_text, context, extra_1, extra_2, clip in; picks and labels out). Under When it runs: Mode Pause & pick, Send picks as Same shape, Timeout 600 s then keep all. Last run reads 3 of 8 kept · frames 1, 2, 4 with three thumbnails, and the images output feeds a Preview Image showing the three landscapes.\n- imagesThe images to pick from.\n- latent, masksSliced to the picked images.\n- positive, negativeCONDITIONING.\n- positive_text, negative_textThe prompt strings, kept or dropped with their frame.\n- contextEach frame's WP Context, kept or dropped with it.\n- extra_1, extra_2Any type.\n- clip (in)Optional.\n- picks (out)What was kept, as frame:image (1-based), for example 1:2, 3:1.\n- labels (out)One label per item, in step with images: #3 · red · 2 by default, or the name you gave the image in the picker.\n- ModePause & pick asks every run.\n- Send picks asSame shape keeps each frame's picks together as one batch.\n- TimeoutSeconds to wait; 0 waits until you answer.\nMode\nPause & pick asks every run. Reuse last picks keeps the same picks without asking, and asks again when they no longer fit. Pass all lets everything through.\nSend picks as\nSame shape keeps each frame's picks together as one batch. One per image sends every pick as its own item, with its own copy of its frame's prompt, conditioning and Context.\nTimeout\nSeconds to wait; 0 waits until you answer. After a timeout it keeps all, keeps the first image, or stops the branch.\nThe Last run strip under the settings shows what the last run kept."
},
{
"title": "Works with",
"text": "WP Context Loop WP Prompt Assembler WP Seed List"
}
]
},
{
"id": "wp-label-grid",
"title": "WP Label & Grid",
"group": "nodes",
"blurb": "Put labels on images and lay them out in a labelled grid or XY plot.",
"keywords": [
"label",
"grid",
"xy",
"plot",
"compare",
"caption",
"text",
"contact sheet"
],
"sections": [
{
"title": "What it's for",
"text": "Comparing runs side by side is easier when every image says what it is. WP Label & Grid writes a label on each image and, if you like, lays them all out in one grid with row and column headers. It takes whatever a workflow produces: one image, a batch, a list, or a Context Loop's frames of batches.\nGood for\n- Turning a sweep into an XY plot with headers\n- Stamping each image with its frame, seed or settings\n- One contact sheet of a whole loop\nBetter with something else\n- Picking images to keep: that is the WP Image Filter"
},
{
"title": "Set it up",
"text": "Figure: Wire the Image Filter's images and labels into the node. The labels name each frame's sweep options, so the grid can sort by them. Steps: WP Context Loop (a sweep) → frames → WP Image Filter (images + labels) → images, labels → WP Label & Grid (labelled grid). Flow diagram of three nodes left to right: WP Context Loop (a sweep), an arrow labelled frames, WP Image Filter (images + labels), an arrow labelled images, labels, and WP Label & Grid (labelled grid). Caption: wire the Image Filter's images and labels into the node; the labels name each frame's sweep options, so the grid can sort by them.\n- Wire your images into images. Labels are optional.\n- Pick where the label goes in the node's panel.\n- Run once. The panel says what came in and marks the grid preset that fits it; click Use it, or pick another preset.\nFigure: A 3 × 3 sweep (three outfits × three times of day) with the XY plot preset: rows from label part 1, columns from label part 2, the title from the run. The stand-in node draws an image per prompt. Screenshot after a run. Folded WP Context Loop, WP Context · Character, WP Context · Scene, WP Prompt Assembler and WP Prompt Assembler · grid labels nodes; the prompt goes into a stand-in image node (Your images, 384 × 384) whose images, and the grid labels, go into WP Label & Grid. Its panel reads 9 images · labelled · grid 2016×1702; under LABEL the text is {label} with place None; under GRID the switch is on and XY plot is picked, Rows from Label part 1 (the outfits), Columns from Label part 2 (dawn, golden hour, night), the title 'Same hero and place: three outfits × three times', spacing 8 gap, 16 edge, 512 px cells, white background and black headers. A preview shows the 3 × 3 grid of landscapes with dawn, golden hour and night columns.\n- imagesOne image, a batch, a list, or a list of batches (a Context Loop's frames).\n- images (out)The labelled images, in the shape they came in.\n- labelsOptional text, one entry per item and one line per image of its batch.\n- grid (out)Every image in one labelled grid.\n- label_textOptional text from another node for what each image shows; {label}, {index} and the other tokens still work.\n- TextWhat each image shows.\n- PlaceAbove, below, left or right adds a strip beside the image.\n- LayoutFit the run, one row or column per frame, fill rows (with a column count), all in one row, all in one column.\n- row_headers, column_headersOptional text from another node, one header per line (a list gives one header per entry).\n- Rows, ColumnsNo headers, typed (one header per line in the box that opens), or a label part.\n- titleOptional text from another node for the grid title.\n- Title, SpacingA line of text above the grid; the gap between cells, the edge around the grid, and the cell width (empty keeps image sizes, a number scales every cell to it).\n- ColoursThe grid's background and the colour of headers and title."
},
{
"title": "Ports",
"text": "images\nOne image, a batch, a list, or a list of batches (a Context Loop's frames). Required.\nlabels\nOptional text, one entry per item and one line per image of its batch. One line covers the whole batch, and a single one-line label covers every image of every item. The Image Filter's labels output fits straight in. One typed text with a line per image works too, spread over the images in order. When the lines and images don't match, a warning on the node says how many images got no line or how many lines were left over.\nlabel_text\nOptional text from another node for what each image shows; {label}, {index} and the other tokens still work. A list gives one text per item, the last one covering any item past it.\ntitle\nOptional text from another node for the grid title. Once wired, the panel's Title box shows that it comes from the wire.\nrow_headers, column_headers\nOptional text from another node, one header per line (a list gives one header per entry). Wiring one sets that side to typed headers and replaces its dropdown on the panel. Like a built-in widget's, each text input's dot sits beside the field it feeds and shows when you hover that field or wire it.\nimages (out)\nThe labelled images, in the shape they came in. A batch stays a batch.\ngrid (out)\nEvery image in one labelled grid. The node also shows it on the canvas, so it can end a branch."
},
{
"title": "Labels",
"text": "Text\nWhat each image shows. {label} is the incoming label (the default), {frame} and {image} count from 1 per input item, {index} counts across all images, {count} is how many there are. \\n breaks a line.\nPlace\nAbove, below, left or right adds a strip beside the image. Over image draws a see-through band over that edge instead and keeps the size, on any side. The last button draws no label.\nText direction\nLeft and right only: horizontal text, or vertical text turned along the edge (reading upwards on the left, downwards on the right), which keeps the strip thin.\nFont and size\nYour system fonts plus any you drop into the extension's fonts folder; type to search. An empty size is automatic: a share of the image width, so labels read the same at 512 px and at 4K.\nColours\nClick a swatch for the colour square and a hex box. The strip or band also has an opacity, and None drops it.\nWithout a labels input the template still works: #{index} numbers every image."
},
{
"title": "Grid presets",
"text": "Images can come as one batch, a list, or a list of batches, and each wants a different grid. A preset sets the layout and headers in one click; change anything after.\nFit the run\nThe default. Follows what comes in: one batch fills a sheet, a few single images sit in one row, a list of batches gets one row per frame.\nSheet\nEvery image in turn, filled into a near-square sheet, or the columns you set.\nSide by side\nEvery image in one row, for comparing a few runs.\nFrame rows / Frame columns\nOne row (or column) per frame of a loop, its batch across. With the Image Filter's labels they are headed #1, #2, …\nXY plot\nRows and columns from the two label parts that vary, each part's values as headers.\nNo grid\nOnly label the images."
},
{
"title": "The grid",
"text": "Layout\nFit the run, one row or column per frame, fill rows (with a column count), all in one row, all in one column. Label-part headers set the layout themselves.\nRows, Columns\nNo headers, typed (one header per line in the box that opens), or a label part. The list shows what each part held on the last run. An image whose label lacks the part goes to a last row or column headed (no label), and the node warns when typed headers are fewer or more than the rows or columns.\nTitle, Spacing\nA line of text above the grid; the gap between cells, the edge around the grid, and the cell width (empty keeps image sizes, a number scales every cell to it).\nColours\nThe grid's background and the colour of headers and title. Reset puts spacing and these back.\nA label can hold parts joined by · . The Image Filter writes #3 · red · pencil: the frame, then the sweep options in the loop's order, then the image number when a frame has more than one. With Rows from label part 2 and Columns from label part 3 (what the XY plot preset picks here), every image lands under its options whatever order it came in, the options become the headers, and the cells keep just #3. Two images with the same options (another seed) repeat the row.\nAn image with no label line (four images, three lines), or one whose label lacks a part, goes to a last row or column headed (no label), so the grid stays a grid. A warning under the run line says when label lines, row headers or column headers are fewer or more than the images, rows or columns they are for."
},
{
"title": "Good to know",
"text": "- Long labels wrap to the image width; past three lines they end with \"…\".\n- Images of different sizes are centred in the largest cell.\n- A font the machine doesn't have (a workflow from someone else) falls back to the default.\n- Grids are capped at 16384 px on the longest side. Use the cell width to keep them small.\n- Wire grid into a Save Image node to keep it."
}
]
},
{
"id": "wp-debug",
"title": "WP Debug",
"group": "nodes",
"blurb": "See every variable, pick and warning after a run.",
"keywords": [
"debug",
"inspect",
"trace"
],
"sections": [
{
"title": "What it's for",
"text": "Wire a Context's output into a WP Debug. After each Generate it shows everything that reached that point. It has no output and changes nothing.\nFigure: The Scene Context feeding WP Debug after a run. The Variables tab lists each variable, its value, its tags and which module set it. Screenshot after a run. A folded WP Context · Character feeds WP Context · Scene (seed 42, its Scene bundle open: constraint, Setting, Time of day, Lighting from time, Scene line, Style), whose context goes into a WP Debug. The Debug header reads seed 42, 11 steps, 2 nodes, clean run, with tabs Variables (9), Trace (11), Warnings and Raw, a negatives count and a Filter variables box. The Variables tab lists $hero, $hair, $eyes, $outfit, $setting and $time with their values, tags and the module that set each.\n- context (in)Any WP node's context output.\n- VariablesEvery $variable and its value at this point, with the module or node that set it (its icon and colour on the left edge say which kind: wildcard, derivation, Toggles, Model Info, Context Loop, Injector …).\n- TraceEvery module and node row in the order it ran, grouped by the node it ran in.\n- WarningsWhat came up during the run, such as missing variables or a constraint that changed nothing.\n- RawEverything as JSON, with a download button."
},
{
"title": "When to use it",
"text": "Good for\n- Seeing why one run came out as it did: every variable, who wrote it, each pick and warning\nBetter with something else\n- How often options come up over many seeds: use the Test Runner"
},
{
"title": "Input",
"text": "context (in)\nAny WP node's context output. The node has no output and changes nothing; drag its corner to resize."
},
{
"title": "The tabs",
"text": "Variables\nEvery $variable and its value at this point, with the module or node that set it (its icon and colour on the left edge say which kind: wildcard, derivation, Toggles, Model Info, Context Loop, Injector …). The quickest way to check what your wildcards picked. Negative words show under the value in red, with the module they came from when it isn't the variable's own.\nTrace\nEvery module and node row in the order it ran, grouped by the node it ran in. Open one to see why it did what it did: a wildcard's odds and the constraints that changed them, which derivation branch ran, the negative each one left, and for rows from other nodes where the value came from (a Loop frame and its seed, a Model Info detection, a Toggles switch set upstream).\nWarnings\nWhat came up during the run, such as missing variables or a constraint that changed nothing. Look here first when something seems off.\nRaw\nEverything as JSON, with a download button. Handy for bug reports.\nThe filter box at the top of each tab searches by variable name or warning."
},
{
"title": "What a trace step tells you",
"text": "Click a step in the Trace tab to see why it came out the way it did. Each kind says what matters for it:\n- Wildcard: the odds of the pick, the pool after each step (options → after the pool filter → after the node's option toggles → drawable), a locked or held seed, a multi-pick's count, whether repeats were allowed and what joins the picks, the value Match variable read, and the constraints that re-weighted it. Options lists every option with its weight (the node's override beside the library's), its chance, and why an option can't come up: filtered out, toggled off, excluded by a constraint, or weight 0. It opens by itself when nothing could be drawn.\n- Derivation: each rule, which branch fired, every test with the value it read, and each action.\n- Fixed values: whether the node's own rows or the library's were used, rows turned off, and a value's text before it was filled in.\n- Combine: the template and what each $variable in it read, with one nothing set flagged.\n- Constraint: its reach, how often it applied and which steps it re-weighted (click one to go there).\n- WP Context Loop: the frame, its seed and seed strategy, skipped frames and the sweep pins.\n- WP Model Info: the model class the family came from, the rule that matched the variant (or that none did) and the file name.\n- WP Toggles: the switch on the node, what an upstream variable said, a parent it needs that is off, and for Pick one which row was picked and why.\n- WP Context Injector: the input a value came from, and a template with what each $name filled with."
},
{
"title": "Rows from other nodes",
"text": "Besides the modules in your Context nodes, WP Debug shows what the other context nodes wrote. The Variables tab groups the variables by the node that set them, under the node's title (and a Context's codename), each in its own card even when it is the only node, and each node's rows show what matters for that node. The Trace tab stays the run itself: every row is a numbered step in the order it ran, and these nodes differ only by their icon, colour and a short note on where the value came from:\n- WP Context Loop (orange): $iteration, $iteration_total and $iteration_value, under a line saying which frame this is, the frame's seed when the Loop sets seeds (click to copy), the sweep combination and the value line.\n- WP Model Info (lime): Family, Variant and Name, each saying where it came from: the model, a file-name rule, a pin, a wire or the loader's file.\n- WP Toggles (indigo): each toggle's variable as true / false, or its value text when the toggle has one (empty while off), with its .bool state and negative under it. In Pick one, each pick still has its own on/off variable (true is the picked one) and the Pick one variable holds the picked value. The source says when an upstream variable set a toggle.\n- WP Context Injector (blue): each wired value with its type (STR, INT, FLOAT, BOOL, or TPL for a template).\nShow node (in a step's details, a row's right-click menu, or the arrow on a node row) selects the node on the canvas and makes the module or field that row came from blink: a Context's module, a Model Info field, a toggle, an Injector row, or the Loop's iteration field. A collapsed node, a folded bundle, a folded Toggles group or the Injector's folded row list opens first so you can see it. A node inside a subgraph opens that subgraph; the subgraph itself is left as it is.\nA long value shows its first three lines (a long list its first three items, a long negative one line) until you press Show all under it; Show less folds it again.\nTheir warnings (an unknown $variable in a Toggles value, a Model Info rule that couldn't be read, a skipped Injector name) show in the Warnings tab and on that node's row. A wildcard pinned by a Loop sweep says swept, one set to Match variable says which variable it matched, and a pool filter that reads a $variable shows the value it read. With a Loop that skips frames, the frame picker shows the Loop's own frame number."
},
{
"title": "Where to put it",
"text": "After one Context to check that step, or after the last to see everything. Use as many as you like; each shows what it sees at its own spot.\nFigure: Two Context → Debug pairs. The second Debug shows a negative ('umbrella') riding along with $weather. Screenshot of two Context → Debug pairs stacked. The top WP Context holds Starter subject and Pose; its Debug lists $subject dog (canine) and $pose standing. The bottom WP Context holds Hair and Weather; its Debug shows a red '1 negative' badge and lists $hair black hair (cool) and $weather light rain with the negative '- umbrella' under it (tag wet). It shows that Debug lists each variable's value, tags and source, and the negative words a pick carries."
},
{
"title": "Works with",
"text": "Conflicts & warnings Warning & conflict types"
}
]
},
{
"id": "wp-var-to-int",
"title": "WP Var → Int",
"group": "nodes",
"blurb": "Turn a number in a $variable into a whole-number input.",
"keywords": [
"convert",
"int",
"number"
],
"sections": [
{
"title": "What it's for",
"text": "A pick can hold a number: a size like 1920x1080, a step count like 30 steps, or just 512. WP Var → Int takes the number out and gives it to any node that wants a whole number. If there's no number, it gives your default.\nFigure: WP Var → Int reads $size ('832x1216', the Render Context's pick for an sdxl model) at index 0 and sends 832 to Empty Latent Image's width. Screenshot after a run. A Model family text ('sdxl') goes into a folded WP Model Info, then a folded WP Context · Render, whose context feeds WP Var → Int titled Width, set to $size. Its last execute line shows → 832, parsed from '832x1216', with index 0 and default 1024. The value output feeds the width input of an Empty Latent Image and a Preview as Text that prints 832.\n- context (in)The Context that holds the $variable.\n- value (out)The whole number, for any INT input: width, steps, batch size and so on.\n- var_nameThe $variable to read.\n- indexWhich number to take when there are several: 0 is the first, 1 the second.\n- defaultThe value to use when the variable is missing, has no numbers in it, or the index is beyond what was found."
},
{
"title": "When to use it",
"text": "Good for\n- A pick that drives a whole number: $steps, a width or height, a seed\nBetter with something else\n- Text that should stay in the prompt\nHide the variable it reads (the globe on its row), so the value doesn't print in the prompt. The node re-runs only when that value changes."
},
{
"title": "Controls",
"text": "context (in)\nThe Context that holds the $variable.\nvar_name\nThe $variable to read. The list shows every variable the wired Context has.\nindex\nWhich number to take when there are several: 0 is the first, 1 the second. Usually 0.\ndefault\nThe value to use when the variable is missing, has no numbers in it, or the index is beyond what was found. The node never stops the run; it uses this instead.\nvalue (out)\nThe whole number, for any INT input: width, steps, batch size and so on."
},
{
"title": "How it finds the number",
"text": "The node finds every whole number in the text (a minus sign counts), in order, and index picks one:\n- 1920x1080: index 0 gives 1920, index 1 gives 1080\n- 30 steps: index 0 gives 30\n- hello: no number, so the default\nFor width and height from one $resolution, use two WP Var → Int nodes on the same variable: index 0 for width, index 1 for height."
},
{
"title": "Works with",
"text": "WP Var → Float WP Var → Bool WP Context"
}
]
},
{
"id": "wp-var-to-float",
"title": "WP Var → Float",
"group": "nodes",
"blurb": "Turn a number in a $variable into a decimal input.",
"keywords": [
"convert",
"float",
"number"
],
"sections": [
{
"title": "What it's for",
"text": "A pick can hold a number such as 0.75, cfg 7.5 or 8. WP Var → Float takes it out and gives it to any node that wants a decimal. Whole numbers work too. If there's no number, it gives your default.\nFigure: WP Var → Float reads $cfg (6.0) and sends it to the KSampler's cfg. Screenshot after a run. A Model family text into a folded WP Model Info and a folded WP Context · Render, whose context feeds WP Var → Float titled CFG, set to $cfg. Its last execute line shows → 6.0, with index 0 and default 6.00. The value output feeds the cfg input of a KSampler and a Preview as Text that prints 6.0.\n- context (in)The Context that holds the $variable.\n- value (out)The number, for any FLOAT input: CFG, denoise, a LoRA strength and so on.\n- var_nameThe $variable to read.\n- indexWhich number to take when there are several: 0 is the first, 1 the second.\n- defaultUsed when the variable is missing, has no number, or has fewer numbers than the index."
},
{
"title": "When to use it",
"text": "Good for\n- A pick that drives a decimal: $cfg, a LoRA strength, a denoise\nBetter with something else\n- Text that should stay in the prompt\nHide the variable it reads (the globe on its row), so the value doesn't print in the prompt. A derivation can write it from another pick (“if $model_family is flux, $cfg is 1”). The node re-runs only when that value changes."
},
{
"title": "Controls",
"text": "context (in)\nThe Context that holds the $variable.\nvar_name\nThe $variable to read. The list shows every variable the wired Context has.\nindex\nWhich number to take when there are several: 0 is the first, 1 the second. Usually 0.\ndefault\nUsed when the variable is missing, has no number, or has fewer numbers than the index. The node never stops the run; it uses this instead.\nvalue (out)\nThe number, for any FLOAT input: CFG, denoise, a LoRA strength and so on."
},
{
"title": "How it finds the number",
"text": "The node finds every number in the text, in order, and index picks one:\n- cfg 7.5: index 0 gives 7.5\n- 1920x1080: index 0 gives 1920.0, index 1 gives 1080.0\n- 1.5e-3: index 0 gives 0.0015\n- hello: no number, so the default"
},
{
"title": "Works with",
"text": "WP Var → Int WP Var → Bool WP Context"
}
]
},
{
"id": "wp-var-to-bool",
"title": "WP Var → Bool",
"group": "nodes",
"blurb": "Turn a yes/no in a $variable into a true/false input.",
"keywords": [
"convert",
"bool",
"toggle"
],
"sections": [
{
"title": "What it's for",
"text": "WP Var → Bool reads a $variable as true or false. true, yes, on and 1 are true; false, no, off and 0 are false. Wire it into a switch or toggle, and a wildcard pick decides whether it's on.\nFigure: WP Var → Bool reads $quick_preview from a Pick one WP Toggles (Full is picked, so 'false') and outputs False, which an If/Else Switch uses to pick the full-size latent. Screenshot after a run. A WP Toggles · steps node in Pick one mode with Quick ($quick_preview) and Full ($full_render, picked) feeds WP Var → Bool titled Quick preview?, set to $quick_preview. Its last execute line shows → False, parsed from 'false', with index 0 and default off. Its value output goes to a Preview as Text that prints False and to the switch input of an If/Else Switch titled Latent, whose on_true is a Quick size latent (512 × 768) and on_false a Full size latent (832 × 1216).\n- context (in)The Context that holds the $variable.\n- value (out)True or false, for any BOOLEAN input: a switch node, a toggle and so on.\n- var_nameThe $variable to read.\n- indexWhich yes/no word to use when there are several: 0 is the first, 1 the second.\n- defaultUsed when the variable is missing or has no yes/no word."
},
{
"title": "When to use it",
"text": "Good for\n- A pick that flips a switch in the graph: $hires into a switch node, a detailer on or off\nBetter with something else\n- Text that should stay in the prompt\nHide the variable it reads (the globe on its row), so the value doesn't print in the prompt. The node re-runs only when that value changes."
},
{
"title": "Controls",
"text": "context (in)\nThe Context that holds the $variable.\nvar_name\nThe $variable to read. The list shows every variable the wired Context has.\nindex\nWhich yes/no word to use when there are several: 0 is the first, 1 the second. Other words don't count.\ndefault\nUsed when the variable is missing or has no yes/no word. The node never stops the run; it uses this instead.\nvalue (out)\nTrue or false, for any BOOLEAN input: a switch node, a toggle and so on."
},
{
"title": "How it reads the text",
"text": "The node splits the text at spaces, commas, semicolons, pipes and slashes and checks each word (capitals don't matter). Other words are skipped and don't count for the index:\n- True: true, yes, on, 1\n- False: false, no, off, 0\n- Everything else (e.g. maybe, 1.5, hello) is ignored\nSo true maybe false has two: index 0 is true, index 1 is false.\nA WP Toggles toggle with a Value holds that value in its variable, not true or false. Read its switch with .bool: $rain.bool is true while the toggle is on and false while it's off.\nA wildcard with options on and off through WP Var → Bool switches any true/false input at random. Weights set how often."
},
{
"title": "Works with",
"text": "WP Var → Int WP Var → Float WP Context"
}
]
},
{
"id": "wp-var-to-choice",
"title": "WP Var → Choice",
"group": "nodes",
"blurb": "Send a $variable into a dropdown: checkpoint, sampler, scheduler, VAE, LoRA.",
"keywords": [
"convert",
"combo",
"dropdown",
"checkpoint",
"sampler",
"scheduler",
"model",
"choice"
],
"sections": [
{
"title": "What it's for",
"text": "WP Var → Choice sends a pick into any dropdown: the checkpoint, the sampler, the scheduler, the VAE or a LoRA name. It reads the real list of the dropdown it's wired into and finds the option your text names, so wai nsfw v14 finds illustrious/waiNSFW_v14.safetensors. A wildcard of model names can then pick the model per image, or per frame in a Context Loop.\nFigure: WP Var → Choice reads $sampler and sends the matching sampler to the KSampler's sampler_name. Screenshot after a run. A Model family text into a folded WP Model Info and a folded WP Context · Render, whose context feeds WP Var → Choice titled Sampler, set to $sampler, match loose, fallback euler, if_no_match use fallback. Its last execute line shows the sampler it sent, with how it matched and 'into KSampler › sampler_name · 45 options'. The choice output feeds the sampler_name input of a KSampler and a Preview as Text that prints it.\n- context (in)The Context that holds the $variable.\n- choice (out)Wire it into a dropdown input: ckpt_name, sampler_name, scheduler, vae_name, lora_name and so on.\n- var_nameThe $variable to read.\n- matchHow close the text must be to an option: exact, loose (the default) or closest.\n- fallbackA dropdown of the options of the input it's wired into, filled when you connect it.\n- if_no_matchuse fallback carries on with the fallback and warns you."
},
{
"title": "When to use it",
"text": "Good for\n- A pick that chooses the checkpoint, sampler, scheduler, VAE or LoRA\n- Trying several models in one loop: a wildcard of model names, one per frame\nBetter with something else\n- Text that should stay in the prompt\n- A number (steps, size): use WP Var → Int or Float\nHide the variable it reads (the globe on its row), so the model name doesn't print in the prompt. Add WP Model Info after the loader and your rules can see which model was picked."
},
{
"title": "Controls",
"text": "context (in)\nThe Context that holds the $variable.\nvar_name\nThe $variable to read. The list shows every variable the wired Context has.\nmatch\nHow close the text must be to an option: exact, loose (the default) or closest. See below.\nfallback\nA dropdown of the options of the input it's wired into, filled when you connect it. Used when nothing matches or the variable is empty. (first option) means the first option in the list.\nif_no_match\nuse fallback carries on with the fallback and warns you. stop the run stops here and lists some of the options: the reason shows in red on the node and in a message.\nchoice (out)\nWire it into a dropdown input: ckpt_name, sampler_name, scheduler, vae_name, lora_name and so on. The line under the picker names what it feeds and how many options that has."
},
{
"title": "How it matches",
"text": "- exact: the text must be an option, letter for letter.\n- loose: capitals, folders, the file extension, spaces, _, - and . don't matter, so Euler A finds euler_ancestral. If that finds nothing, it takes the shortest option that starts with your text, then one that contains it, so dpmpp 2m finds dpmpp_2m and not dpmpp_2m_sde.\n- closest: loose, and then the most similar option, so a typo like dreamshapr still lands.\nAfter a run the picker shows what it sent and how it found it. When it used the fallback, the line turns amber and a warning pops up. When stop the run stopped it, the line reads stopped the run in red with the reason under it (the variable was empty, or nothing matched, with some of the options), and a message says the same at the corner of the canvas. The next run that gets past the node clears it.\nWithout this node ComfyUI doesn't check text that comes in through a wire: a name that isn't in the list only fails inside the loader. WP Var → Choice checks it first.\nWired into a switch or another node without a list, the text passes through unchecked and the picker says so."
},
{
"title": "Works with",
"text": "WP Model Info WP Context Loop WP Var → Int WP Var → Bool"
}
]
},
{
"id": "wp-note",
"title": "WP Note",
"group": "nodes",
"blurb": "A note that explains a workflow: a step, a tip or a label, with a docs link.",
"keywords": [
"note",
"comment",
"label",
"sticky",
"step",
"tip",
"explain"
],
"sections": [
{
"title": "What it's for",
"text": "WP Note explains a workflow on the canvas. It looks like a panel, not a node: no title bar, no frame and no sockets, in the classic renderer and in Nodes 2.0, and its selection outline hugs the panel. It is left out of the prompt; its text is saved with the workflow.\nFigure: A Step card with docs buttons, a Warning card with its own icon, a Tip sticky, and a Label. Four WP Notes on the canvas with no node frame around them. A Step 1 card, Load your model, with a $model_family code span and two docs buttons (WP Model Info, Writing prompts for your model); an amber Warning card, Lock before you compare, with a triangle icon and a lock icon in its text; a green Tip sticky with a lightbulb, tinted with its colour; and a small purple Label pill, 2 Label each frame, with marks for its text and link."
},
{
"title": "When to use it",
"text": "Good for\n- Numbered steps through a shared workflow\n- A warning or tip next to the node it is about, with its docs page one click away\n- A small label over a group of nodes\nBetter with something else\n- A heading for a group of nodes: ComfyUI's own group title"
},
{
"title": "Looks",
"text": "Card\nA panel with a coloured edge on the left. The title and the text show on the canvas.\nSticky\nThe same panel, tinted with the colour.\nLabel\nA small pill with the title only, sized to it. Hover it to see its text and links in a card; move onto the card to click them. Marks on the pill show that it has text (lines) or a link (chain)."
},
{
"title": "Moving, sizing and editing",
"text": "Unpin a note to drag it like any node. A Card or Sticky resizes from its corner: the width is yours and the height follows the text. A Label sizes itself to its title. Double-click a note, press the indigo pencil in ComfyUI's selection toolbar while it is selected, or press Edit on a label's hover card to open the editor; Done goes back to reading. The editor on the node holds the note's settings: tone, look, colour, title and the bookmark settings. Drag the editor's background (anywhere that isn't a control) to move the note while it is open.\nThe text has its own window: press Edit text (Write text on an empty note). The Markdown editor is on the left and the note as it will read on the right, following what you type: the node's own card, with its look, tone, colour, title and width. A label shows its tag and, under it, the card that opens when you point at it. Done (or Ctrl+Enter) puts the text on the note; Cancel, Escape or × leaves the note as it was, and asks first when you changed something. The node shows the text's first line and how long it is.\nTone\nStep (a numbered circle), Icon (pick any icon from the list next to it) or Plain (no badge).\nColour\nPresets that follow the theme (Accent, Tip, Info, Warning, Danger, Teal, Violet, Neutral), or any colour from the picker.\nLook\nCard, Sticky or Label (above).\nPinned\nComfyUI's own pin: a pinned note can't be moved or resized by accident. New notes start unpinned; a saved workflow keeps each note's pin.\nTitle\nShown in bold at the top, or on the pill of a Label.\nText\nIts first line and length on the node; Edit text opens the text window, with the editor beside a live preview. Done (Ctrl+Enter) puts it on the note, Cancel or Escape leaves the note as it was. Markdown, with the formatting bar below over it: headings (# to ####), bold, italic, strikethrough, code, bulleted, numbered and nested lists, task lists (- [x] / - [ ]), quotes and callouts (> [!TIP], > [!WARNING]), tables with column alignment (:--, :-:, --:), rules (---) and code blocks.\nKeys you type in the editor stay in the editor: Delete, Ctrl+Z and ComfyUI's other shortcuts don't reach the canvas."
},
{
"title": "The text window and its formatting bar",
"text": "Figure: The text window: the Markdown on the left with the link panel open (a docs page found by typing), the note as it will read on the right. Screenshot of a WP Note's text window, titled Load your model with a NOTE TEXT chip. On the left, under Write, the formatting bar has its link button pressed and the link panel open: Docs is picked, the page is Seeds & loops (Whole page), with an optional Text box, the Button style with the Book icon, Colour none (None follows the note), Cancel and Insert link; under it is the note's Markdown. On the right, under Preview, is the note as the node draws it: a Card with the accent edge, the step badge 1 and the title Load your model, then Pick the checkpoint in the loader. $model_family follows it, so the prompt fits the model, a bullet, a ticked and an open task, a tip callout Lock the seed before you compare two settings, and a WP Model Info button. At the bottom: Ctrl+Enter Done, Cancel and Done.\nUndo and redo Ctrl+Z / Ctrl+Y\nStep back or forward through your changes, typing and the bar's own edits alike (Ctrl+Shift+Z and the right-click Undo and Redo work too). Typing counts as one step until you pause, move the caret or start a new line. The steps last while the text window is open.\nBold, italic, strikethrough Ctrl+B / Ctrl+I\nWraps the selection in **, * or ~~.\nCode\nWraps it in backticks, for a $variable.\nLists and quote\nPuts - , 1. or > before each selected line (press again to take it off).\nLink Ctrl+K\nA Docs page (type to find any page of these docs, then pick a section or the whole page), a Web address, a Library button (below) or a Community pack (owner/pack, or paste the pack's address), drawn as a Button with an icon or as a Text link, in its own colour or the note's. The selected text becomes the link's text. It goes where the caret is when you press Insert link, so you can click in the text after opening the panel.\nIcon\nAny PrimeIcons icon, searchable, in a colour or the text's own, inserted where the caret is.\nPictures\nAdd a picture from a file, or paste or drop one into the text; it goes on a line of its own where the caret is. Or pick one the extension ships (the node toolbar with its ? help button, or any docs screenshot), with a search box to find it. The panel stays open after adding and lists the note's pictures with a Width slider (a share of the note), Own size and Remove."
},
{
"title": "Links and icons in the text",
"text": "The bar writes these for you; you can also type them. A note can hold any number of links, anywhere in the text. Any other kind of link shows as plain text.\n[Read more](https://example.com)\nA link that opens in a new tab.\n[Seeds](docs:seeds-and-loops)\nA link to that page of these docs.\n[The grid](docs:wp-label-grid#s-the-grid)\nA link to one section of it.\n[Start here](docs:quick-start){button pi-play}\nThe link drawn as a button with an icon. {button} alone has no icon.\n{button pi-play #rrggbb} [Read more](https://example.com){#rrggbb}\nA button or a text link in its own colour, a hex code like the colour field shows. Without one it takes the note's.\n[Add the pack](wp:library){button pi-download}\nA button that adds this workflow's bundles and modules to your library, after a review. Always a button.\n[Add the Scene lists](wp:library#node-12){button}\nThe same for one WP Context only (its node id; inside a subgraph the editor writes the subgraph's id too).\n[Get the pack](wp:community/owner/pack)\nOpens that published pack in the manager's Community tab, where it installs.\n:pi-heart: :pi-heart:{#rrggbb}\nAn icon, optionally in a colour: a hex code like the custom colour field shows.\n![What it shows](wp:image/1a2b3c4d)\nA picture saved in the note: the bar's Pictures panel, a paste or a drop writes it and its id.\n![The ? button](wp:asset/toolbar-help)\nA picture the extension ships: the toolbar one, or any docs screenshot, named by its file.\n![What it shows](wp:image/1a2b3c4d){width=60%}\nThe picture as wide as 60% of the note (10 to 100). Without it, its own size, never wider than the note."
},
{
"title": "Pictures",
"text": "A note can show pictures: a screenshot of the step it explains, or one the extension ships, such as the node toolbar with its ? button or any screenshot from these docs. Open Pictures in the bar and press Add a picture…, paste or drop a picture straight into the text, or pick one under From the extension: type part of its name in the search box and click it in the grid that shows up. Each one goes on a line of its own where the caret is, named by what you type in the box (or the picture's name). The panel stays open after you add one, so you can set its width right away.\nFigure: The text window with the Pictures panel: the pictures the extension ships, the note's own pictures with a Width slider, and the preview showing it. Screenshot of a WP Note with pictures, and the same note open in its editor. The card on the left, Find the help page with a question-mark icon, reads Every WP node has a help page. Select the node and press the ? in its toolbar, then shows the picture the extension ships: ComfyUI's node toolbar over a WP Context · Character node, with the indigo ? button lit. Under it, The page opens next to the canvas, and a pasted screenshot of two chained Contexts at 70% of the note's width. On the right, the editor's formatting bar has its Pictures button pressed; the panel has a box for what the picture shows, Add a picture… (or paste or drop one into the text), From the extension with a search box for the 53 pictures it ships (its grid shows only what matches), and In this note: Node toolbar with the ? help button at Own size and Two Contexts chained at 70%, each with a Width slider and a remove button, then a hint that pictures are saved in the workflow, made smaller, and never make the note bigger. Below is the note's Markdown with ![Two Contexts chained](wp:image/…){width=70%}.\nA picture is never wider than the note. Its width is a share of the note's, so it grows and shrinks with the note and never makes the note bigger; set it with the panel's Width slider, or Own size for its own width up to the note's. Remove takes it out (Undo brings it back while the text window is open).\nA pasted or dropped picture is saved in the workflow, so a shared workflow shows it too. It is made smaller first: at most 1280 px on its long side and about 400 KB, which also drops camera and location data. A note holds up to six pictures and 2 MB. Pictures never load from a web address, so opening a shared workflow fetches nothing."
},
{
"title": "An add-to-library button",
"text": "A note can carry a button that puts the bundles and modules of the workflow into your library, so someone who opens a shared workflow can keep its lists in one press. In the link panel pick Library, then This workflow or One WP Context (a list of the Contexts on the canvas). The example workflows' Start here notes have one.\nFigure: The Start here note's button pressed: the review lists the workflow's six bundles and fifteen modules before anything is added. Screenshot of the example workflow One image with the review dialog open over its Start here note, whose button reads Add this workflow's lists to my library. The dialog is titled Add to your library, From this workflow, and lists under New: 6 bundles and 15 modules each item with its kind and a grey EXAMPLE chip: the bundles Character, Look, Outfit, Shared lists, Scene and LoRA swaps, then the wildcards Hero, Hair, Eyes, Outfit, Colors, Setting and Time of day and the fixed values Style. At the bottom are Cancel and a primary button Add 6 bundles and 15 modules.\nThe button's tooltip says what it would add (\"Add 4 bundles and 12 modules from this workflow to your library\"); when everything is there already it says so and stays off. Pressing it opens a review that lists every item. New ones go in under their own id. For an item your library already holds with other content you choose Use mine (the default: nothing changes), Replace or Keep both (it goes in as a copy and this workflow points at the copy). An id that belongs to another kind of item offers only Keep both or Leave out.\nAdd installs it all in one step, with the same checks as the Import tab: if one item is refused, nothing goes in. The EXAMPLE and MISSING marks clear on every Context, and the message that follows has Undo, which takes it all back out of the library.\nNothing is added until you press Add in the review, and the button only ever reads the workflow's own rows: no downloads, files or code. A shared workflow's button could be worded to mislead; the review lists exactly what it adds."
},
{
"title": "A note that is also a bookmark",
"text": "Turn on Also a bookmark at the bottom of the editor and the note joins the Bookmarks dock: pick where it lands on screen, its zoom and a shortcut, the same settings as a WP Bookmark, with the same clash check. A new one gets the next free shortcut. The dock shows it with the note's colour and icon, and a small bookmark mark on the note (its tooltip names the key) says it is one.\nFigure: A note open in its editor on the node with Also a bookmark on, a note card with its bookmark mark, and the dock listing both. Screenshot of WP Notes used as bookmarks. On the left, the Bookmarks dock lists three rows: 1 Load your model (its step number 1 in a blue circle, Alt + 1, highlighted after a jump), 2 Sampler (an amber lightning icon, Alt + 3) and 3 Label each frame (its step number 2 in a red circle, Alt + 2). In the middle, the note Label each frame is open in its editor (Step 2, Card, colour #ef4444, its title and Markdown text); at the bottom the Also a bookmark switch is on, with the hint Jump here from the Bookmarks dock or a shortcut, and under it Lands at with the centre of a 3 × 3 grid picked (Centre of the screen, 24 px in) and a Use this view button, Zoom set to 1 ×, and the shortcut Alt + 2, then Done. On the right, the note card Load your model (step 1, blue) shows a small bookmark mark in its head next to the pencil, and below it a WP Bookmark, Sampler, as a pill: a lightning icon, its name and Alt + 3.\nUnlike a WP Bookmark, a note can't be hidden on the canvas (it is there to be read), and muting it turns its shortcut off without fading it."
},
{
"title": "Works with",
"text": "Quick start WP Label & Grid WP Bookmark"
}
]
},
{
"id": "wildcard",
"title": "Wildcard",
"group": "modules",
"blurb": "A list of options; each run picks one into a $variable.",
"keywords": [
"random",
"weight",
"option",
"tag groups",
"fallback",
"multi-pick",
"match variable"
],
"sections": [
{
"title": "What it does",
"text": "A wildcard holds options such as \"a cat\", \"a dog\", \"a fox\" and picks one each run. The pick goes into a $variable you name, so your template and later modules can use it. Weights make some options more likely, and tags let constraints and filters work with groups of options.\nGood for\n- Any choice something else reacts to: a constraint, a derivation, Match variable, a sweep\n- Any list you want weights, tags, a fallback or Test Runner counts on\n- A shared vocabulary with no variable, drawn through @{} references\nBetter with something else\n- Always-on text: use Fixed Values\n- A tiny variant inside one option: an inline {a|b}\n- A phrase built from other picks: use a Combine\nFigure: The Starter subject wildcard: four options with weights, chances and categories, and the Runtime buttons (Lock pick, Hide from prompt, Hold across run, Match variable). Screenshot of the Starter subject wildcard editor opened from a WP Context row (WILDCARD, Library entry · weighted options resolved per pick). Identity shows Display name Starter subject and Variable binding $subject. The Pool section has Pick 1 – 1 and an Advanced button, and a table of four ticked options: cat and tiger (category FELINE) and dog and wolf (category CANINE), each with weight 1 and a 25% probability bar; 4 of 4 enabled. The Runtime row has Lock pick, Hide from prompt (on), Hold across run and Match variable. The footer has Edit library entry in SPA, Reset overrides, Save to library, Cancel and Save."
},
{
"title": "Make one",
"text": "- In the manager, use Quick create → Wildcard on the dashboard, or the new button on the Wildcards page.\n- Give it a name. The variable name follows it ($subject).\n- Add options, one per row, or paste a list with Bulk edit.\n- Save, then add it to a WP Context with Add module."
},
{
"title": "Option fields",
"text": "Value\nThe text that goes into the prompt, such as \"a cat\". It can hold {a|b} choices and @{} references to other wildcards.\nWeight\nHow likely the option is next to the others: weight 2 comes up twice as often as weight 1. 0 switches it off without deleting it.\nSub-categories (tags)\nLabels on an option, such as \"feline\" or \"warm\". An option can have several. Constraints and the category filter work with them, and you can sort them into groups (species, temperature).\nNull option\nAn option that prints nothing, for \"sometimes leave this out\". At most one per wildcard.\nFallback\nHover an option's probability and click \"set as fallback\". It rolls like any other option, and is also used when nothing else is left to pick (constraints ruled everything out, or every weight is 0). Give it weight 0 if it should only ever stand in. One per wildcard. WP Debug and the Test Runner say \"Fallback used\" when it kicks in."
},
{
"title": "Change it for one node",
"text": "Open the row's editor on the node to change how this one use behaves. The library entry stays as it is.\nVariable binding\nThe variable this use writes. It starts as the library's; change it to use the same wildcard twice ($hair and $hair_b) without one overwriting the other.\nOption toggles\nUntick options this use may not pick. Leaving one ticked fixes the value, though a constraint can still rule it out (keep a fallback for that).\nPer-use weights\nChange an option's weight for this use only. The library editor doesn't show them.\nCategory filter\nOnly pick options with certain tags, e.g. \"feline and warm, not lynx\" (a comma means or). Click the tag pills or type it under Advanced. Pick a variable under By variable (or type $model_variant) to filter by its value when the prompt runs. A wildcard whose options have no tags says so in the Pool, since there is nothing to filter by.\nPick, separator, Allow repeats\nPick several options at once. See below.\nExclude null\nLeaves the null option out for this use, so something always prints.\nFallback\nUntick the fallback to switch it off for this use; then nothing prints when nothing is left.\nMatch variable\nInstead of rolling, pick the option whose text equals a variable's value (case doesn't matter), e.g. $model_variant from WP Model Info. No match uses the fallback, or rolls as usual. This is how a variable can drive a constraint.\nLock pick\nKeeps the same pick on every Generate and loop frame until you unlock it. Good for an art style you've settled on.\nHold across run\nThe same pick on every frame of one loop run, a new one next Generate. Good for a character who stays the same through a story.\nHide from prompt\nThe variable prints nothing, but constraints, combines and derivations can still use it.\nTo fix one value on a node, untick every other option or use Lock pick."
},
{
"title": "Pick several at once",
"text": "Set Pick to a range, e.g. 2–3, and the wildcard picks that many options, joined by the separator. Allow repeats lets the same option come up twice. $colors prints them all (\"red, blue, green\"); $colors.0 is just the first (\"red\").\nWhen picking several, the null option is left out. Set the lower end to 0 for \"maybe none\"."
},
{
"title": "Wildcards inside wildcards",
"text": "An option can hold a reference to another wildcard: type @ and pick it. Picking \"@{color} jeans\" then also picks a color. A reference can narrow what it draws, such as only the warm colors. See Chips & references.\nOption text can't read a $variable: $mood in an option prints as typed. To build text from other picks, use a Combine."
},
{
"title": "Tags you can read as variables",
"text": "Each tag group has a kind, set on its header:\nclassify (default)\nThe tags describe what the option is: casual, t-shirt, skirt. All of them are true at once, and a constraint on the group uses them together.\naccepts\nThe tags are choices the option offers: an outfit that goes with sneakers, heels or sandals. One of them is rolled with the pick, you can print it as $outfit.SHOES, and a constraint reacts to that one tag.\nAsk what the tag says about the option. \"Is this outfit casual?\" is classify. \"Which shoes go with this outfit?\" is accepts.\nReading an accepts group\nIf $outfit has an accepts group named SHOES, then $outfit.SHOES is the shoe rolled with this run's outfit. It is rolled once, so it reads the same everywhere. With several picks, $outfit.0.SHOES is the first pick's. Only accepts groups can be read this way; anything else prints nothing and gets a warning. The group name must be letters, digits and underscores, and renaming it breaks templates that read it."
},
{
"title": "With constraints",
"text": "A Constraint can change this wildcard's odds based on another pick, such as making \"stormy\" likelier when the subject is \"ocean\". It must sit after the source wildcard and before this one.\nFigure: Source wildcard, then the constraint, then the target wildcard. The #1 badges link the constraint to its target. Screenshot of a WP Context (modules 3 / 3) wired into a WP Prompt Assembler. The Context rows, top to bottom: Starter subject ('$subject · 4 options'), Starter pairing, a constraint marked #1-all ('$subject → $mood · 2×2'), and Starter mood marked #1 ('$mood · 4 options'). The Assembler's template is 'a $mood $subject, masterpiece' and its preview reads 'a sleepy wolf, masterpiece'. It shows the order: source wildcard, then the constraint, then the target wildcard it changes.\nConstraints react to picks, not variables. To steer one from a variable (such as the loaded model), give a wildcard options that spell the variable's values and set Match variable on it. WP Model Info has an example."
},
{
"title": "One wildcard, different options per model",
"text": "To let a variable narrow a wildcard directly, tag its options with the variable's values and add the variable to the Pool's filter. For example, tag a Setting wildcard's options pony or illustrious (in a model tag group), then pick $model_variant under By variable in the Pool. Each run keeps the options tagged with the loaded model; options with no model tag stay for every model. Constraints on the wildcard still apply to what is left, so a Hero → Setting constraint keeps working.\nThe same works in a reference: @{Setting:$model_variant}. If the variable isn't set, or its value is no tag on the wildcard, that part of the filter is left out and WP Debug notes it. Match variable is still the way to make a variable the source of a constraint. See Chips & references for the filter rules."
},
{
"title": "Coming from A1111 or Forge?",
"text": "Your wildcard packs can be imported: see Import wildcard packs."
},
{
"title": "Try it",
"text": "Create a ready-made $subject wildcard with four options (cat, tiger, dog, wolf) in two categories. It's the first piece of the starter set."
},
{
"title": "Works with",
"text": "The $variable pipeline Constraint Import wildcard packs WP Context"
}
]
},
{
"id": "wildcard-import",
"title": "Import wildcard packs",
"group": "modules",
"blurb": "Bring in A1111, Forge, Dynamic Prompts, PPP or Impact Pack wildcards.",
"keywords": [
"import",
"dynamic prompts",
"ppp",
"yaml",
"txt",
"zip",
"a1111",
"forge",
"wildcard packs",
"impact pack",
"merge lists"
],
"sections": [
{
"title": "Import a pack",
"text": "- Open Import / Export → Wildcard packs.\n- Choose a folder, files or a .zip, or drop them on the page. It reads .txt lists and .yaml, .yml and .json files. Wildcard folders of extensions installed in ComfyUI (Impact Pack, Dynamic Prompts, Easy-Use, PPP, LoRA Manager) are listed below: click Convert to use one.\n- Review the plan: what each list becomes and how well it carries over.\n- Pick what to import (everything starts selected) and import.\nImporting the same pack again finds the wildcards you already have instead of adding copies.\nFigure: A small fantasy pack in review: 49 files become 11 wildcards, 43 lists merged into 5 tagged wildcards. Open here is characters, 11 lists merged into one wildcard; each list shows the tags that pick it, and Keep these lists separate undoes the merge. Screenshot of the wildcard import review page for a pack named fantasy-pack: 11 wildcards with 285 options from 49 files, where 43 lists were merged into 5 tagged wildcards. Tiles count 3 Entry point, 3 Composition, 5 Vocabulary and 0 Group, and a How it carries over bar reads Exact 10, Close 0, Needs a look 1. Domains on the left: All, prompts (with a red dot), characters, scenes and Loose files. The table lists each wildcard with its role, options, used by and carries over; prompts/landscape needs a look. The merged characters wildcard (11 lists) is open: one wildcard made from 11 lists under characters/, with tag groups and its lists such as hair + colors + red and races + elf, and a Keep these lists separate button. On the right are Import settings (pack name, library tag, category, Bundle the entry points, Merge related lists, Apply) and a conversion note that ${name} variables stay as plain text. A Continue to import button sits at the bottom."
},
{
"title": "What you get",
"text": "- Folders of small lists become one tagged wildcard. hair/colors/red.txt and hair/colors/blonde.txt become one hair/colors wildcard whose options are tagged red and blonde. Untick Merge related lists to keep every list separate.\n- Logic becomes stacks. A line that rolls variables, a template reference with arguments or an if-condition becomes a bundle of modules that run together: wildcards that roll each variable once, constraints that keep picks consistent, and a Combine that writes the prompt. Drop it on a Context.\n- You see what it makes. Each stack and entry point is rolled a few times on the real engine before you import, and the review shows those prompts.\n- References keep working. __name__ becomes an @{} chip pointing at the imported wildcard, and {a|b} choices work as before.\n- Everything gets the pack's tag and a category per top folder, so the pack stays together in your library.\nRows marked Needs a look list what changed. An if on something the pack never rolls once is removed (the choice stays), an if inside a {…} choice keeps only its else branch, a variable read inside a {…} choice rolls its own value, and a reference to a list the pack doesn't contain stays as text."
},
{
"title": "The details",
"text": "How each part of a pack is converted\nLists and their names\nEach list is named by its path the way __name__ finds it: animals/cats.txt is animals/cats, and a YAML key clothing: { tops: [...] } is clothing/tops. A list that isn't merged becomes its own wildcard, and its variable is the last part ($cats, $tops).\nMerging related lists\nWith Merge related lists ticked (the default), a folder of small lists becomes one wildcard, like one you'd build by hand: hair/colors/red.txt, hair/colors/blonde.txt and hair/colors/dark.txt become one hair/colors wildcard whose options are tagged red, blonde and dark. Each folder level is a tag group, and lists with the same name in sibling folders share a tag: female/footwear and male/footwear give a gender group (female, male) and a footwear tag, like the axes of a hand-built wildcard. A folder merges when it holds at least two lists and up to 1,000 options over at most three levels; a bigger folder is split into its subfolders. Entry points stay separate, and so does a list that points at another list in the same folder, because a wildcard can't reference itself.\nTables\nWhen the subfolders of a folder repeat the same list names (beings/elf/prompt.txt, beings/orc/prompt.txt, …) each list name becomes one table wildcard, tagged by the folders in between, so the folder becomes a tag group you can filter or steer. The row shows a table chip.\nTag groups\nOpen a merged row to see its tag groups. Each one is named after the folder level it came from; type a better name and the pack converts again with it.\nKeep separate\nMerged rows show a lists chip. Open one to see each list with its tag and the tag groups, and click Keep these lists separate to import that folder as one wildcard per list again; Merge again under the settings undoes it. Untick Merge related lists to keep every list separate.\nRoles\nThe review sorts every wildcard by what it does in the pack. Entry points build a prompt from other wildcards and nothing else uses them. Stacks are modules that run together as one bundle. Compositions build on other wildcards and are used by one. Vocabulary is a plain list. Groups are made for globs and folder references. Copies and stack parts are small wildcards the import built for template references and stacks.\nCarries over\nEach wildcard is marked Exact, Close (same outputs, but a setting or label didn't carry over) or Needs a look (some text no longer renders as it did). Open a row to see why.\nSample rolls\nThe import rolls each stack and entry point a few times on the real engine. Open a row to see what it makes. A warning from those rolls marks the row Needs a look.\nDomains\nThe top folders (or YAML top keys) of the pack. Pick one to review it on its own. Each wildcard goes in a category named after its domain, unless you type one category for all.\nLibrary tag\nEvery module gets the library tag, which starts as the pack's name, so the whole pack sits together on the Tags page.\nBundles\nStacks only (the default) makes one bundle per stack and nothing else. Stacks + entry points by folder also puts the entry points into one bundle per domain, inside one pack bundle. None makes no bundles. Lists always stay in the library, where references reach them.\nReferences\n__other__ references become @{} chips that point at the imported wildcard. A reference to a list that was merged points at the merged wildcard, filtered by that list's tags, so it still draws from that list only. A glob like __animals/*__, or a parent key like __clothing__ in Impact packs, becomes a group wildcard that picks one of the matching wildcards, weighted by how many options each has. PPP filters such as __colors'warm'__ become a category filter on the chip. A reference to a wildcard from an earlier import of the pack links to the one in your library.\nVariables\nA line that sets variables and then uses them (Dynamic Prompts ${season=!{winter|summer}}, PPP <ppp:set>) becomes a stack: a wildcard that rolls each variable once, then a Combine that writes the line into a variable. ${x=v} without ! rolls again at each use, as in Dynamic Prompts. Defaults like ${x:dry} become a derivation that fills the variable only when nothing set it. A variable nothing sets is left for you to fill upstream of the stack.\nTemplate arguments\n__outfit(season=winter)__ makes a copy of the outfit list with the variable filled in. When several entry points are copies of one template that differ by one value, Fold template copies (on by default) turns them into one wildcard plus a choice that picks the value, with constraints that keep each pair together. Pin the choice to get one of the originals back.\nIf-conditions\nA PPP <ppp:if> that tests a variable the line rolls becomes a constraint: the options behind each branch only show when the condition holds. A test on the model (_is_sdxl, _is_pony, …) becomes a model check stack: put it after a WP Model Info node and it leaves out what the pack rules out for the loaded model. Conditions on anything else are removed, and the choice stays.\nNegatives and LoRAs\nPPP <ppp:stn>…</ppp:stn> and Umi **text** go to the option's negative, so the Assembler's negative output carries them. PPP <ppp:ext lora …> becomes <lora:name:weight> text.\nUmi AI packs\nIn Umi YAML the key is the prompt and Tags: lists its tags. Tag queries like <[female][casual|--formal]> become a pick from the matching entries, weighted the same way. Yaml Tags.txt names the tag groups. {20%a|b} weights carry over.\nChoice syntax\n{a|b}, weights like {2::a|b} and multi-pick like {2$$a|b|c} keep working, and so does __2$$name__: it picks two different values of name. Open ranges like {-2$$a|b} get their missing end filled in, samplers like {~a|b} pick at random, and the default separator is a comma.\nWeights, labels and fallbacks\nWeighted lines (3::text), PPP labels ('summer, casual'::text) and PPP's else choice carry over as option weights, sub-categories and the wildcard's fallback. A line written twice becomes one option with twice the weight, so the odds stay the same.\nComments and encoding\nLines starting with # are skipped and kept as the module's description, as is the # text inside multi-line YAML prompts. Text after a # with a space before it is cut (so C# survives). Files saved as UTF-8 or Windows-1252 both read correctly."
},
{
"title": "Works with",
"text": "Wildcard Bundles Chips & references"
}
]
},
{
"id": "fixed-values",
"title": "Fixed Values",
"group": "modules",
"blurb": "Set $variables to text you type.",
"keywords": [
"fixed",
"constant"
],
"sections": [
{
"title": "What it does",
"text": "Fixed Values sets each $variable to the text you typed, every run. Nothing is picked at random unless you write a {a|b|c} choice in the value.\nUse it for text that stays the same: a style like \"oil painting\", quality tags like \"8k, masterpiece\", or words you always want in the negative. Put it at the top so every module below can read it.\nFigure: The Starter style fixed value: $style = oil painting, with a + negative button for negative words. Screenshot of the Starter style fixed value editor opened from a WP Context (FIXED, Library entry · static $var → value pairs). Identity shows Display name Starter style. The Values table has one ticked row: variable $style, value 'oil painting', with a + negative button under the value and an Add new $var for this instance link below; 1 of 1 enabled. The Runtime row has Lock seed, Hide from prompt and Hold across run, and the footer has Edit library entry in SPA, Reset overrides, Save to library, Cancel and Save.\nGood for\n- Style and quality tags that never change\n- A model's required prefix or a standing negative\n- A switch you flip by hand\nBetter with something else\n- Real variety: use a Wildcard (it has weights, tags, constraints and Test Runner counts)\n- Text built from other picks: use a Combine\nDon't let a fixed value and a wildcard write the same variable: whichever runs later wins, without a warning."
},
{
"title": "Row fields",
"text": "Name\nThe variable to set: \"style\" sets $style.\nValue\nThe text. A {a|b|c} choice adds a little variety; other $variables can't be read here (use a Combine).\nEnable / disable\nSwitch a row off without deleting it, such as an optional quality booster."
},
{
"title": "Change it for one node",
"text": "Value override\nChange a row's value, or its negative, on this node only. An empty negative clears it.\nRow toggles\nSwitch rows on or off on this node only.\nLock seed\nKeeps any {a|b|c} choice the same on every Generate.\nHold across run\nKeeps the choices the same for one loop run, and rolls again next Generate.\nHide from prompt\nThe variables print nothing; other modules can still read them.\nA fixed value can't read $variables other modules set. To build a value from earlier picks, use a Combine."
},
{
"title": "Try it",
"text": "Create a starter Fixed Values module that sets $style to \"oil painting\". The starter template prints it every run."
},
{
"title": "Works with",
"text": "Combine Derivation WP Context"
}
]
},
{
"id": "combine",
"title": "Combine",
"group": "modules",
"blurb": "Build a new $variable from text and other variables.",
"keywords": [
"template",
"merge"
],
"sections": [
{
"title": "What it does",
"text": "Write a template such as $style portrait of $subject, $mood lighting. The combine fills in the variables set above it and writes the result to one new variable, such as $shot, for the Assembler or later modules.\nYou can also sprinkle {a|b|c} inline picks directly inside the template for any on-the-fly variation that doesn't need its own wildcard.\nFigure: The Starter scene combine: '$mood $subject' resolves to 'serene cat' and is stored as $scene. The Negative field sends words to the Assembler's negative. Screenshot of the Starter scene combine editor opened from a WP Context (COMBINE, Library entry · template interpolates $vars into output binding). Variable binding is $scene. The template is '$mood $subject', Detected 2 vars lists $mood and $subject, the preview shows the template and Resolved shows 'serene cat'. A Negative field (NEG) says it goes to the Assembler's negative when its template uses $scene, and a note reads stored as $scene. The Runtime row has Lock seed, Hide from prompt and Hold across run.\nGood for\n- Joining picks into one phrase: “wearing $outfit and $shoes”\n- Printing a hidden driver inside a phrase\n- Reading an accepts group as text\nBetter with something else\n- Making a pick: a combine isn't a pick, so nothing can constrain it. Use a Wildcard\n- Text that depends on a condition: use a Derivation"
},
{
"title": "What you type in the template",
"text": "- $name prints what that variable holds at this point. A module above must set it.\n- {a|b|c} picks one part each run, for small variations that don't need a wildcard.\n- $$ prints a plain $.\n- $name.0, $outfit.SHOES and $name.neg read one pick of a multi-pick, an accepts group, or a variable's negatives.\n- @{} references are dropped here: they print nothing and WP Debug notes it. Put a reference in a wildcard option instead.\nThe variables your template reads show as chips under it. A chip marked missing means no module above sets that variable."
},
{
"title": "Change it for one node",
"text": "Template override\nChange the template on this node only.\nOutput variable\nWrite to a different $variable on this node.\nNegative override\nChange the combine's negative on this node. An empty one clears it.\nLock seed\nKeeps the template's {a|b} choices the same on every Generate.\nHold across run\nKeeps the result the same for one loop run.\nHide from prompt\nThe result prints nothing; other modules can still read it."
},
{
"title": "Try it",
"text": "Create a starter Combine that fills $mood $subject into a single $scene variable. It reads the starter subject and mood wildcards, so build those first, or use \"Build starter bundle\" to get the whole set."
},
{
"title": "Works with",
"text": "Wildcard Fixed Values The $variable pipeline WP Prompt Assembler"
}
]
},
{
"id": "derivation",
"title": "Derivation",
"group": "modules",
"blurb": "If/then rules: when a variable says one thing, change another.",
"keywords": [
"rule",
"condition",
"logic",
"and",
"or",
"group"
],
"sections": [
{
"title": "What it does",
"text": "A derivation checks variables that earlier modules set and changes others to match. For example: IF $subject equals \"ocean\", append \", with crashing waves\" to $mood.\nFigure: The Starter accent derivation: when $mood is dramatic, $accent becomes cinematic lighting. Screenshot of the Starter accent derivation editor opened from a WP Context (DERIVATION, Library entry · conditional rule-based variable rewrites). Identity shows Display name Starter accent. Under Rules there is one ticked rule, Rule 1: '$mood = dramatic → $accent = cinematic lighting'; the hint says click a rule to expand, grey text is the library and typing in a field overrides it on this node. The Runtime row has Lock seed, Hide from prompt and Hold across run.\nGood for\n- Text that follows from a pick (“if winter, add a scarf”)\n- A pick that only happens sometimes (an action holding @{Accessories:winter})\n- A LoRA or injected value replacing a rolled pick\n- Story text on $iteration, and negatives that depend on a pick\nBetter with something else\n- Changing the odds of another pick: use a Constraint\n- A long lookup from one pick to another: a constraint on a tagged list\n- Overwriting a variable a constrained wildcard sets: the pick is thrown away"
},
{
"title": "How rules run",
"text": "A derivation can hold several rules, and every rule is checked, top to bottom, even when an earlier one fired. Inside one rule only the first branch that matches runs: IF, then each ELIF, then ELSE. Several small rules are easier to read than one big one."
},
{
"title": "Tests",
"text": "equals\nThe value is exactly this text. Capitals count.\ncontains\nThe text appears somewhere in the value. Capitals count.\nmatches\nThe value fits a regular expression (a text pattern). A broken pattern never matches.\nexists\nThe variable is there at all. A second switch narrows it: any, is empty (there but empty, such as a wildcard that rolled its null option) or has value.\nis set / is unset\nIs set: there and not empty. Is unset: missing or empty.\nNOT\nThe NOT switch turns any test around: equals becomes not equals, contains does not contain, and so on. Does not contain and does not match need this version or newer.\nA test compares with plain text. $variable, @{} and {a|b} written there are not read. To compare two variables, test one and write the other's values as branches. You can test $colors.0, $outfit.SHOES or $hair.neg like any variable."
},
{
"title": "Several tests in one branch",
"text": "Click + Condition to add another test. The word between them reads AND (all must match); click it to make it OR (any may match). AND groups tighter than OR, the way it reads, and tests joined by AND are drawn in a box so you see what goes together.\n+ Group adds a box yourself: IF $time equals \"night\" AND ($weather equals \"rain\" OR $weather equals \"fog\"). Groups nest up to three deep."
},
{
"title": "Actions",
"text": "Replace\nThe variable becomes the action text.\nAppend\nAdds the text at the end. Nothing goes in between, so start it with its own comma (\", with a scarf\").\nPrepend\nAdds the text at the start. End it with its own comma (\"wet, \").\nAdd to negative\nLeaves the variable's text alone and adds the words to its negative, so they go wherever the variable is printed.\nReplace negative\nLike Add to negative, but the words replace whatever negative the variable had.\nClick + And under an action to add another. They run in order, and each sees what the one before wrote: IF $tier equals \"warmup\", set $pose to $pose_portrait AND add $pose_portrait.neg to its negative.\nAn action's text can hold $variables, a {a|b} choice or a @{} reference with a filter, so a rule can pull a fresh pick: cinematic lighting, @{Lighting:dramatic} writes \"cinematic lighting,\" plus a pick from the dramatic lights.\nWhat happens to negatives\nReplace swaps the variable's negative for those of the variables its new text read; Append and Prepend keep the old one and add. A reference inside Add to negative never changes a pick."
},
{
"title": "LoRA by model",
"text": "LoRA by model in the editor adds a ready-made rule for WP Model Info: if $model_variant is pony, $loras gets a Pony LoRA tag; if illustrious, an Illustrious one; otherwise nothing. Put your LoRA names in, add a branch per model you use, and write $loras in your template."
},
{
"title": "Change it for one node",
"text": "Switch off a rule or branch\nSkip one rule, or one IF, ELIF or ELSE, on this node only.\nChange a value\nChange what an action writes, or the text a test compares with, on this node only.\nRule order\nReorder the rules on this node.\nLock seed\nKeeps the {a|b} and @{} rolls inside the actions the same on every Generate.\nHold across run\nKeeps those rolls the same for one loop run. The tests still run every frame, so a frame can still take another branch.\nHide from prompt\nThe variables the rules set print nothing; other modules can still read them."
},
{
"title": "Try it",
"text": "Create a starter derivation that sets $accent from the mood: if $mood is \"dramatic\" it writes \"cinematic lighting\", otherwise \"soft lighting\"."
},
{
"title": "Works with",
"text": "Wildcard Combine The $variable pipeline Negatives WP Context"
}
]
},
{
"id": "constraint",
"title": "Constraint",
"group": "modules",
"blurb": "Let one pick change the odds of another.",
"keywords": [
"pairing",
"matrix",
"exclude"
],
"sections": [
{
"title": "What it does",
"text": "A constraint watches the pick of a source wildcard and changes the odds of a target wildcard below it, before the target rolls. The target still picks at random; you're only shifting the odds.\nExample: when $weather picks \"stormy\", boost \"dramatic\" and \"moody\" in $mood and reduce \"sunny\" and \"cheerful\".\nFigure: The Starter pairing constraint: feline subjects lean intense, canine subjects lean calm. Below are exceptions and the target reach (First, Next N, All, Pick). Screenshot of the Starter pairing constraint editor opened from a WP Context (CONSTRAINT, Library entry · source pick → modifies target option weights). The Rule matrix has source Starter subject and target Starter mood; rows feline and canine cross columns calm and intense: feline × calm ×0.3, feline × intense ×3.0, canine × calm ×3.0, canine × intense ×0.3, with a Legend button. Exceptions is empty with an Add extra exception button. Target reach has First, Next N, All (selected) and Pick, with the note 're-weights all 1 downstream target'.\nGood for\n- Two picks that must agree (winter rules out summer clothes)\n- Whole families of options with the tag matrix, single pairs with exceptions\n- Allow-lists with Only\nBetter with something else\n- Adding text: use a Derivation\n- A narrowing that never changes: a category filter or @{X:tag}"
},
{
"title": "Make one",
"text": "- Create a constraint and pick the Source wildcard (the one that decides) and the Target wildcard (the one that follows).\n- In the Rule matrix, set a rule for whole tag pairs: rows are the source's tags, columns the target's. For example, winter × summer clothes = Exclude.\n- Add Exceptions for single option pairs. An exception wins over the matrix.\n- Check the Preview, save, and add the constraint to your WP Context between the two wildcards.\nFigure: Order in the stack: source, then constraint, then target. Steps: Source wildcard (its pick) → if it lands on x → Constraint (reweights) → limits → Target wildcard (picks to match). Flow diagram of three stages left to right: Source wildcard (its pick), an arrow labelled if it lands on X, Constraint (reweights), an arrow labelled limits, and Target wildcard (picks to match). Caption: the constraint nudges the odds of its downstream target instances, every match by default or a narrower set you choose, and it sets no $vars itself.\nOrder matters: the source sits above the constraint, and the target below it. The source must be a wildcard in the stack; a pick made only inside a reference sets no variable, so a constraint on it never applies."
},
{
"title": "Rule modes",
"text": "Neutral\nNo rule. The pair rolls as if the constraint weren't there.\nBoost\nMakes the target options more likely, by the factor you set.\nReduce\nMakes them less likely, without removing them.\nExclude\nRemoves them from the pool for this source pick.\nOnly\nMakes the row a guest list. When the source picks this, the target can only pick options this row says something about: the ones set to Only, plus any you boosted or reduced (they keep those odds). Everything the row doesn't mention is off, including options you add later."
},
{
"title": "Linking picks with Only",
"text": "Only ties one wildcard's pick to another's. Add an exception maid → frilled apron in Only mode, and whenever the source picks \"maid\" the target can only pick \"frilled apron\". You don't list every outfit to rule out.\n- Several Only exceptions for the same source value make a short list: \"maid\" can pick \"frilled apron\" or \"kimono\" and nothing else.\n- In the matrix, Only works on tags: summer → open means a summer pick can only reach targets tagged open. The rest of that row shows a faint × so you can see what's shut out. A Neutral cell doesn't count as a rule.\n- Boost and Reduce in the same row stay on the guest list with their odds. Formal → Only heels, Boost loafers ×2: a formal outfit gets heels or loafers (loafers twice as often), never sneakers or boots.\n- Other source values are unaffected: a \"biker\" pick still rolls every outfit."
},
{
"title": "Preview",
"text": "The Preview card shows the constraint in numbers: one row per source option, one column per target option, and in each cell the chance of that target option when the source picked that row. A dash means ruled out. A row marked nothing left would leave the target empty, and fallback means the target's fallback option stands in. Other constraints, filters and toggles aren't included."
},
{
"title": "When it rules out everything",
"text": "If, for some source value, every target option is excluded or shut out, the target would come out empty. The constraint row then shows a rules out all badge before you run; hover it to see which source values do it. Allow an option for those values, or mark one of the target's options as its fallback."
},
{
"title": "A missing source or target",
"text": "If the constraint's source or target isn't in your library (you installed it without its wildcards, or deleted one), the editor shows the missing wildcard's name and locks the matrix. Reattach points it at a wildcard you have, or Download from community fetches the missing one. The rules are then mapped onto the new wildcard's tags; rules for tags it doesn't have are dropped, and the editor shows how many first."
},
{
"title": "Try it",
"text": "Create a starter constraint: feline subjects lean intense, canine subjects lean calm. It also creates the two wildcards it needs ($subject and $mood) if you don't have them yet."
},
{
"title": "Works with",
"text": "Several constraints on one target, reaching only some uses of a wildcard, tag axes and wildcards used inside references: see Constraints in depth.\nConstraints in depth Wildcard WP Context"
}
]
},
{
"id": "bundles",
"title": "Bundles",
"group": "modules",
"blurb": "Several modules saved together, added in one go.",
"keywords": [
"group",
"snapshot"
],
"sections": [
{
"title": "What a bundle is",
"text": "A bundle is a saved group of modules: a character, a wardrobe, a whole pack. Drop it into a WP Context and all its modules arrive at once, in order. In the stack it shows as one frame you can collapse to keep long setups tidy. A collapsed bundle still warns you: when a row inside has a problem (a missing variable, a conflict, a module gone from the library), its header shows that row's coloured dot, and hovering the dot names the rows. Hidden rows never make the node wider.\nWhen the pipeline runs, a bundle's modules run exactly as if they sat in the stack on their own. The bundle itself adds nothing; it only groups.\nFigure: A WP Context with the Starter set bundle expanded and a loose Pose module below it. Screenshot of a WP Context (seed 42, modules 7 / 7) wired into a WP Prompt Assembler. The Context holds the Starter set bundle (6 mods), expanded inside a frame: Starter subject, Starter pairing, Starter mood, Starter style, Starter scene and Starter accent. Below the bundle is a loose Pose wildcard. The Assembler's template is '$scene, $pose, $style, $accent' with 4 upstream chips, and the preview reads 'sleepy wolf, standing, oil painting, soft lighting'.\nGood for\n- A set of modules you reuse together: a character, a wardrobe, a whole pack\n- Sub-bundles for the sections of a pack\nBetter with something else\n- Keeping two copies apart: a bundle doesn't scope its variables. Insert it twice and rename the second copy's variables"
},
{
"title": "Make a bundle",
"text": "- On the canvas, select rows in a WP Context, right-click and choose Save as bundle.\n- In the manager, create one on the Bundles page and add modules to it.\n- Drop a saved bundle into a Context with Add Bundle.\n- Drag a row into a bundle on the canvas: a line in the bundle's colour shows where it lands. On a closed bundle, drop on the middle of its header to put the row inside (the bundle opens); its top and bottom edges drop the row beside it."
},
{
"title": "When you edit a module later",
"text": "A bundle keeps its own copy of each module. What happens when you edit the original:\n- Bundles in the library follow. Saving a module updates it in every library bundle that holds it, and each bundle keeps its own settings for it.\n- Bundles already on a canvas don't change by themselves. Their header shows library updated. Right-click the bundle and choose Reset to library snapshot, or Refresh from library on one row, to pull the change.\n- Push to library… goes the other way: it saves your canvas edits into the library.\nIf a module is deleted from the library, the bundle keeps working from its copy, and the row shows a MISSING badge. When you delete a wildcard, the manager lists the bundles that hold it and offers to clean up its other uses."
},
{
"title": "Two copies of one bundle",
"text": "Inserting a bundle twice gives two independent sets of rows, still linked to the library, and references and constraints inside it keep working. But both copies write the same $variables, and the later one wins. To keep both, rename the second copy's variables (each row's Variable binding), or use Duplicate on single rows, which renames for you ($hair becomes $hair_2).\nExtract to library in the bundle editor makes the modules into new stand-alone library modules (a copy you can change freely), points the references between them at the new copies, and leaves the per-use settings behind."
},
{
"title": "Bundles from an example workflow",
"text": "The example workflows (Workflow › Browse Templates › Wildcard Pipeline) keep their bundles on the canvas, so they run on an empty library. Their headers show a grey EXAMPLE chip instead of MISSING; sub-bundles and rows inside show nothing. Right-click the header and choose Push to library… with Restore missing references to keep the bundle, its sub-bundles and its rows; the chip goes away. Delete it from the library later and it reads as MISSING like any other. See Start from an example."
},
{
"title": "Switch a bundle off",
"text": "Each bundle has an enable toggle. Turning it off silences all its modules without touching their own settings; turn it back on and everything is as it was. Handy for testing."
},
{
"title": "Try it",
"text": "Build the whole starter set in one click: the subject, mood, style, scene, accent and pairing modules, plus a bundle that groups them, ready to drop into a Context. Anything you already created from the other pages is reused."
},
{
"title": "Works with",
"text": "WP Context Recommended pack structure"
}
]
},
{
"id": "templates",
"title": "Templates",
"group": "modules",
"blurb": "Save an Assembler template and load it anywhere.",
"keywords": [
"template",
"assembler",
"$var",
"prompt"
],
"sections": [
{
"title": "What a template is",
"text": "A template is the text you write in the WP Prompt Assembler: words and $variables, such as $scene, $style, $accent, masterpiece, highly detailed. The Assembler fills in each variable every run.\nSaved templates live in your library next to modules, but they don't go on a Context. A bundle reuses a set of modules; a template reuses the wording around their variables.\nGood for\n- A prompt skeleton you reuse, one per model family (tags or prose)\n- A fixed system prompt a model needs\nBetter with something else\n- Variety: a template rolls nothing, and {a|b} prints as typed in the Assembler. Put choices in wildcards\nSee Writing prompts for your model for the order each model family wants."
},
{
"title": "How it pairs with the Assembler",
"text": "The Assembler's toolbar has Save and Load. Save puts its template in your library; Load replaces the template with one from the library.\nA variable in the template needs a module before the Assembler that sets it. If none does, it prints nothing and is underlined in the preview; add the module or fix the name."
},
{
"title": "Try it",
"text": "Create the starter template, $scene, $style, $accent, masterpiece, highly detailed. Load it into a WP Prompt Assembler and the starter modules fill it in."
},
{
"title": "Works with",
"text": "WP Prompt Assembler The $variable pipeline Bundles"
}
]
},
{
"id": "warning-types",
"title": "Warning & conflict types",
"group": "reference",
"blurb": "Every warning, what it means and what to do.",
"keywords": [
"warning",
"error",
"conflict",
"reference"
],
"sections": [
{
"title": "As you build (canvas)",
"text": "These show on the nodes while you build:\nWhat it flags | Severity | What it means\nShadowed variable | Info | An earlier Context already sets this variable, and this one replaces it. Normal when chaining; shown so you know.\nDuplicate variable | Warning | Two modules on the same Context set the same variable; the lower one wins.\nMissing variable in template | Warning | The Assembler template uses a $variable nothing before it sets (or one that is hidden).\nConstraint source not found | Warning | A constraint points at a source wildcard that isn't in your library.\nConstraint target not found | Warning | A constraint points at a target wildcard that isn't in your library.\nConstraint source not upstream | Warning | The source wildcard isn't above the constraint, so there's no pick for it to react to.\nConstraint target not downstream | Warning | The target wildcard isn't below the constraint (or the constraint's reach covers none of its uses), so it can't change anything.\nRules out all (no fallback) | Warning | For some source picks, every option of the target is ruled out and there's no fallback, so it would print nothing. The hover message names those picks. Allow at least one option for them, or set a fallback.\nInjector binding unmatched | Warning | A WP Context Injector row names a variable that nothing before it sets.\nUnknown tag group | Warning | A $name.GROUP read names a group that isn't an accepts group on that wildcard, so it prints nothing. Make the tag group accepts, or fix the name.\nOption missing an accepts tag | Info | Some options have no tag from an accepts group, so $name.GROUP is empty when they're picked. Tag them, or leave it if empty is fine."
},
{
"title": "After a run (WP Debug → Warnings)",
"text": "These show up once you run. The labels match what WP Debug shows.\nWhat it flags | Severity | What it means\nUnknown variable | Warning | A template or combine used a $variable nothing set, so it printed nothing.\nReference not found | Warning | An @{…} reference points at a wildcard that wasn't found on the node or in the library.\nFilter matched no options | Warning | A reference's tag filter left no options, so it printed nothing.\nReference not allowed here | Warning | An @{…} reference sits somewhere that can't use one, such as a combine or the Assembler, so it printed nothing.\nVariable not allowed here | Warning | A $variable was written somewhere that can't read one, such as a wildcard option. It printed as typed.\nUnknown tag axis | Warning | A $name.GROUP read names a group that isn't an accepts group on that wildcard, so it printed nothing.\nPick has no tag on this axis | Info | The picked option has no tag from the accepts group being read, so $name.GROUP printed nothing.\nReferences nested too deep | Warning | References inside references went too many levels deep, so the deepest ones were left out.\nReference cycle | Warning | References pointed back at each other in a circle; the circle was cut so the run could finish.\nConstraint never applied | Info | The constraint's target never ran after it (or wasn't drawn this run), so it changed nothing.\nConstraint partial reach | Info | A constraint set to reach the next N (or picked) uses of its target found fewer than that.\nConstraint source missing | Warning | The constraint's source wildcard hadn't run yet when the constraint was reached.\nConstraint excluded every option | Warning | The constraint ruled out every option of the target and there was no fallback, so it printed nothing.\nFallback used | Info | Nothing was left to pick, so the wildcard's fallback option stood in.\nFilter variable left out | Info | A $variable in a pool or reference filter was unset, or its value is no tag on that wildcard, so that part of the filter narrowed nothing.\nNo option matches the variable | Info | A wildcard set to Match variable found no option equal to the variable's value and has no fallback, so it rolled as usual.\nPinned option missing | Warning | A sweep frame (or other pin) points at an option the wildcard no longer has, so that frame rolled instead.\nConstraint factor ignored (allow) | Info | A rule set to Allow also has a number; Allow ignores it.\nUnknown constraint mode | Warning | A constraint rule has a mode this version doesn't know (probably from a newer pack), so the rule was skipped.\nFixed-values overrides malformed | Warning | The node-only changes on a Fixed Values row couldn't be read, so the library values were used.\nConstraint failed to register | Warning | A constraint couldn't be set up, usually because its data is damaged. It was skipped.\nModule failed | Warning | A module hit an error and was skipped. The message says which.\nInvalid injector name | Warning | A WP Context Injector row used a variable name that isn't valid.\nReserved injector name | Warning | A WP Context Injector row name starts with _, which is kept for the engine.\nModel Info found nothing | Warning | WP Model Info has no model wired and nothing pinned, so $model_family, $model_variant and $model_name are empty.\nModel Info rule skipped | Warning | One of WP Model Info's variant rules couldn't be read (often a broken pattern), so it was skipped.\nToggles setup problem | Warning | Something in a WP Toggles node's setup doesn't add up, such as two rows writing the same variable. The message says what.\nUpstream turned on several | Warning | In Pick one with Upstream can pick, upstream variables turned on more than one row, so the first of them was picked."
},
{
"title": "See also",
"text": "Conflicts & warnings WP Debug Constraints in depth"
}
]
},
{
"id": "component-kit",
"title": "Component kit",
"group": "reference",
"blurb": "Every shared control, colour and size, shown live.",
"keywords": [
"kit",
"wp-kit",
"kit site",
"kit version",
"components",
"design",
"buttons",
"tokens",
"colours",
"contributors"
],
"sections": [
{
"title": "What's in it",
"text": "- Tokens: the colours, type sizes, spacing and corners, with the value each has in the palette you pick.\n- Controls: buttons, fields, dropdowns, checkboxes and switches, tabs, sliders, tag, date, colour and icon pickers.\n- Display and layout: cards, chips, badges, tables, lists, code blocks, diffs, image grids and split panels.\n- Feedback and overlays: toasts, banners, progress, popovers, menus, tooltips, dialogs and drawers.\nEach of these is one component on both sides: the search box in a node picker and on a manager page is the same box, and so are the number fields, tabs and toasts. The App pieces page here shows the 8 parts only the extension has, because they work with your library: the prompt box, the negative box, the selection bar and a few more."
},
{
"title": "Reuse first",
"text": "New screens are built from kit pieces. A new control is made only when nothing in the kit fits, and it goes into wp-kit with its demo on the site; the extension then brings in the new kit version. Two checks keep this honest: one fails when a piece has no demo, the other fails when a file adds a hand-made button, select, number box, dialog or colour.\nEvery control follows one standard: the same prop names (size, variant, tone), one height scale, colour tokens only, full keyboard use, Escape to close anything that floats, and focus back where it was. The full rules are in docs/component-standard.md in the repository.\nSettings › About says which kit version this copy of the extension uses, and Settings › Advanced › Component kit opens the kit site or the app pieces."
}
]
},
{
"id": "settings",
"title": "Settings",
"group": "settings",
"blurb": "Every setting in the manager's Settings page, by section.",
"keywords": [],
"sections": [
{
"title": "General",
"text": "Updates, what's new and privacy.\n- Check for updates on launch: Look for a newer release when the manager opens.\n- Check for updates now: Ask GitHub for the latest release.\n- Open What's new after an update: Show the release notes once, the first time the manager opens on a new version.\n- Share anonymous usage stats: Let the Community tab count page views, installs and publishes on the community site."
},
{
"title": "Appearance",
"text": "How the manager looks on this device.\n- Colours: The colour theme: Orbit, Native, Nebula, Tide, Ember, High contrast or your ComfyUI palette.\n- Look: Corners, type, borders and selection: Orbit, Crisp, Soft, Terminal, Editorial, Compact or TUI.\n- Mode: Dark, light, or follow the system.\n- Accent color: The highlight color used for buttons, links and selection. Theme's own follows the colour theme.\n- Density: Spacing and control height across the manager.\n- Sidebar: Show the navigation with labels, or as icons only.\n- Start page: What the manager opens to.\n- Reduce motion: Turn animations and transitions off in the manager."
},
{
"title": "Editing",
"text": "How the module editors behave.\n- Keep empty tag groups: Keep a tag group with no tags when saving a wildcard.\n- Sub-categories panel: Whether the sub-categories panel opens on its own in the wildcard editor."
},
{
"title": "Autocomplete",
"text": "Suggestions while you type option values.\n- Booru tags: Danbooru tag names while you type option values.\n- LoRAs: Installed LoRA files, inserted whole as <lora:path:1.0>, after a typed < or <lora: too.\n- Embeddings: Installed embedding files, inserted as embedding:path.\n- Write tags with spaces: Picked tags use spaces instead of underscores.\n- Escape brackets in tags: Picked tags write ( and ) as \\( and \\).\n- Append \", \" after a pick: Follow each completion with a comma and a space.\n- Max suggestions: How many tag suggestions the popup lists.\n- Minimum characters: Letters typed before tag suggestions appear."
},
{
"title": "Canvas",
"text": "The nodes on the ComfyUI canvas, and how runs resolve.\n- Ref recursion limit: How deep nested @{uuid} references resolve when a workflow runs.\n- Module density: Module spacing and chip sizes.\n- Decoration: Gradients & shadows. Off = flat (weak GPU / remote desktop).\n- Color intensity: How saturated accent / kind / status colors render. Muted reduces chroma for a calmer palette; vivid bumps it for pop.\n- Canvas colors: Which colors WP widgets use on a ComfyUI color palette other than Dark or Light (Nord, Solarized, an imported one…). Follow takes the palette's node, widget and text colors; Wildcard Pipeline keeps our own dark or light colors.\n- Type style: How the kind indicator shows on module rows AND bundle headers: chip text, icon glyph, or both.\n- State indicator style: How mod / missing / drift / conflict markers appear.\n- State border highlights: Color the module border by its state.\n- Collapse new modules by default: Modules added via the picker render with body hidden (header only). Bundles have their own setting below.\n- Collapse new bundles by default: Bundles inserted from the library render with the frame collapsed (header only). Independent from the modules collapse-default since bundles usually carry 3-8 children and users often want a different default there.\n- Collapse stack mode: Independent: each module collapses on its own. Accordion: expanding a module collapses all others.\n- Focus mode: Hover a module to dim the others.\n- Row buttons: When a module row shows its lock, internal and remove buttons. On hover keeps rows calmer; a pressed lock or internal button always shows.\n- New modules start disabled: When on, modules added from the picker start with their toggle off. Useful when configuring before letting them run.\n- Confirm destructive bundle actions: Show a confirm dialog before remove / reset-to-library / save-to-library on bundles. Turn off if you trust the Undo toast as the only safety net.\n- Bundle master toggle: clear behavior: What the bundle master ON->OFF click clears. Preserve manual: only revert rows the master itself turned on; rows the user marked internal / locked individually stay put. Cascade: clear every applicable row regardless of how it got set. Applicability (skip constraint for internal, skip non-lockable for lock) is hardcoded — the engine ignores the flag on those kinds, so writing it would be dead data.\n- Validation strictness: How aggressively the conflict scanner surfaces issues. Permissive turns it off — use only if you know what you're doing.\n- Toast lifetime: How long status toasts stay on screen before auto-dismissing.\n- Suppress info-severity toasts: When on, info toasts (status confirmations) are filtered out. Warnings + errors still show.\n- Image Filter sound: Play a short chime when WP Image Filter is waiting for your pick, so you hear it from another tab.\n- Image Filter desktop notification: Show a system notification when WP Image Filter is waiting and this tab is in the background. Your browser asks for permission the first time.\n- Reduce motion: Disables Wildcard Pipeline animations. Match system honors prefers-reduced-motion.\n- Contrast: Bumps borders + text contrast. Match system honors prefers-contrast."
},
{
"title": "Test Runner",
"text": "What a new Test Runner scenario starts with.\n- Seed mode: Consecutive seeds, or a fresh random set on every run.\n- First seed: Where a consecutive range starts.\n- Number of seeds: How many prompts a run makes."
},
{
"title": "AI assistant",
"text": "Use your own model (Claude, OpenAI or a local server) to draft modules.\n- Turn on the AI assistant: Adds the AI tools (editor panels, Describe, Organize, the chat and the Pack builder), using a model you already have.\n- Provider: Who runs the model.\n- Server address: Where the model server answers.\n- API key: Saved on the ComfyUI machine and never shown again.\n- Model: Which model the assistant uses.\n- Model size: How much the assistant asks of the model: shorter instructions for small ones.\n- Most context to use: The context the assistant asks Ollama for, in tokens. At least 16k; 32k for chats that take several steps.\n- Unload the model after: Free the GPU for ComfyUI after a short idle.\n- Thinking: Let a thinking model reason first, ask it to answer straight away, or pick how much it reasons. Shows only what the chosen model takes.\n- Embedding server key: A key for an embedding server other than the chat's, sent only there.\n- Choose the tool first: Small and medium local models name the tool, then write only its arguments.\n- Short tool list: Small local models get one line per tool; each tool's full description comes when a step first offers it.\n- Debug log: Record every AI request (prompt, reply, thinking, tool calls, tokens) and copy it from the top bar.\n- Sampling: Optional top K, top P, min P, repeat and presence penalty sent with each request; empty uses the server's own setting.\n- Reset to recommended: Puts every AI choice back to its recommended value; keeps provider, keys and models.\n- Tool format: How the chat calls its tools: native tool calls or the JSON step format.\n- Embedding model: Meaning search: lets the chat find items by what they mean, not only their words. Off when empty.\n- Embedding server type: Ollama or OpenAI-compatible, for an embedding server other than the chat's.\n- Embedding server: Where the embedding model runs, when it isn't the chat's server. Its models are listed from there.\n- Library index: The chat's search index of your library. Rebuild it if search seems off.\n- Show Explain on selected text: A small Explain / Ask bar over text you select; Ctrl+Shift+X explains it without the bar.\n- Show thinking: Show or hide what a thinking model reasoned, in the chat.\n- Show working notes: Show or hide the text the model writes between steps.\n- Show what it did: Show or hide the lines listing what the assistant read, searched and ran.\n- About my library: A short note every chat, Draft with AI and Pack builder request reads. Suggest drafts one from your library.\n- Language you write in: Read messages in another language into English for the assistant's checks.\n- Type out replies: Show the reply word by word as the model writes it, or all at once when it is done.\n- Let the assistant delete items: Lets the chat propose deleting items, categories and tags. Each delete still needs your confirm, and Undo brings it back.\n- Apply small changes automatically: Descriptions, tags and categories from the chat apply without a click, with Undo.\n- Web search: Let the chat search the web with SearXNG, Brave, Tavily or Kagi. Every search asks you first.\n- SearXNG address: Your SearXNG server, with JSON output turned on.\n- Search engine key: The Brave, Tavily or Kagi key, sent only to that engine.\n- Read result pages: Let the chat read one page its search returned, after you allow it: built in, or your Crawl4AI or Firecrawl server.\n- Page reader address: Your Crawl4AI or Firecrawl server.\n- Page reader key: The Firecrawl key or Crawl4AI token, sent only to that server.\n- Danbooru tag lookup: Let the chat look a tag up on Danbooru: wiki, aliases, implications and related tags. Each lookup asks first.\n- Don't ask before web calls: Run web searches, page reads and Danbooru lookups without the Allow card.\n- Test connection: Check the server answers and the model returns JSON, and see how much context a chat takes before you type."
},
{
"title": "Library & data",
"text": "Your module database, backups and maintenance.\n- Database: File, size and what is in it.\n- Automatic backups: Copy the database before every migration and once a day.\n- Backups to keep: The newest backups are kept, manual ones too; pinned ones are never deleted.\n- Backups: Back up now, pin, restore, or delete a backup.\n- Database location: Where the database file lives.\n- Maintenance: Vacuum, integrity check, analyze, re-run migrations."
},
{
"title": "Advanced",
"text": "Move settings between machines, start over, or open the component kit.\n- Export settings: Save every preference to a JSON file.\n- Import settings: Load a settings file from another machine.\n- Component kit: Every shared control with when to use it, on the kit site, plus the pieces only the extension has. For people building the extension.\n- Reset browser preferences: Clear this device's preferences. Your library is untouched."
},
{
"title": "About",
"text": "Version, who makes Wildcard Pipeline, and how to support it.\n- Wildcard Pipeline: Version, license and source repository.\n- Support the project: Sponsor on GitHub, buy a coffee or send a PayPal donation.\n- Other ways to help: Star the repo, join the Discord, share your modules."
}
]
}
]
}