Nodes/ComfyUI-PromptRandomChoice/Runtime Prompt Random Choice Ex
ComfyUI Node

Runtime Prompt Random Choice Ex

Nested {options} that re-read your edits mid-queue

By ruminar·Created 3 months ago·Updated about 15 hours ago· 3
Runtime Prompt Random Choice Ex
    • selected_text
    • selected_text_safe
    options_texttown zoo{animals{birds|penguins}|aquarium,{fish|jellyfish}} coffee shop{cake|coffee cup} amusement park{ferris wheel|carousel|balloons} (){ white day wedding ceremony birthday party }
    change_every1
    state_key

    Same game as its sibling Runtime Prompt Random Choice - re-roll every execution, edit the candidate pool while the queue is still running - plus one big addition: nested {} candidate expansion. If you want a zoo candidate that sometimes drags along an animal sub-list, this is the node. And unlike ComfyUI's built-in syntax, the nesting goes as deep as you want and is resolved before the CLIP encoder, not inside it.

    What "Ex" adds

    The flat list syntax is identical to the base node - newline or | separated, # to comment a line out, () for an explicit empty pick. On top of that, any candidate can carry a {...} child group whose contents get attached to the winner with , . Example straight from the README:

    town|zoo{animals{birds|penguins}|aquarium,{fish|jellyfish}}
    

    Conceptually that's five leaf candidates - town, zoo, animals, birds, zoo, animals, penguins, zoo, aquarium, fish, zoo, aquarium, jellyfish - and one is picked. Nested groups keep nesting: {animals{birds|penguins}} works because each child group is itself a pool. The expansion is joined with commas at each level, which is why it plays so well with tag-based models.

    The mechanism that matters

    Selection is weighted by leaf count and never fully enumerated. Each level picks a branch in proportion to the number of leaves it leads to, so birds and penguins and town all end up exactly as likely. That's not a corner detail - it's why the runtime sync stays fast. The server re-parses your text on every keystroke, and a pool that expands to tens of thousands of combinations is still checked as a tree, not blown out into a list.

    The plumbing is the same as the base runtime node: frontend pushes your text to a server route, the backend validates it and stores the latest accepted revision keyed by state_key, and the Python node picks from that revision at execution. IS_CHANGED returns NaN, so it re-rolls every run and never gets cached.

    Inputs and outputs

    Identical to Runtime Prompt Random Choice: options_text (the candidate list, now with {} groups), change_every (hold a pick for N runs), and state_key (leave blank). Outputs are selected_text and selected_text_safe, the latter being the filename-safe version that returns empty when () wins. The grammar changed; the sockets didn't.

    Syntax rules worth knowing

    • # comments work at every level, not just the top.
    • ! force-selects a branch - Runtime-only, first match from the top wins, and the forced branch's children still get evaluated.
    • One direct child group per candidate. A{B|C}{D|E} is a hard error. The v0.6.0 release deliberately killed adjacent-child Cartesian products; independent axes go into separate Ex nodes and get combined with a string join.

    That last one matters because the runtime node distinguishes "I'm mid-typing a brace" from "I wrote something illegal." Unmatched braces are a transient EDITING / SYNTAX INCOMPLETE - generation quietly continues with the last good revision. A Cartesian product is a HARD ERROR: no silent fallback, the next execution stops until you fix it. That's the right call - silently generating with stale candidates is how you burn an overnight batch.

    Why you'd reach for it

    Parent/child relationships. The README ships ready-made Ex pools for this: backgrounds grouped as indoor{...}|Outdoor{...}|Nature{...}, fantasy settings, seasons. Drop in the "Fantasy" list and the fantasy sub-locations only attach when the fantasy parent wins. It is not a grab-bag for mashing every axis into one node - that's what several nodes plus a string join are for, and the author says so in so many words.

    Installing

    ComfyUI Manager (search "Prompt Random Choice"), or:

    cd ComfyUI/custom_nodes
    git clone https://github.com/ruminar/ComfyUI-PromptRandomChoice
    

    then restart. Zero Python dependencies, no models to fetch - just a folder in custom_nodes and a frontend extension.

    Gotchas

    • The A{B|C}{D|E} hard error is by design - the error message points at the offending brace, so read it before assuming the pack is broken.
    • Literal { or } as actual prompt text isn't supported. If a tag genuinely contains a brace, this isn't your node.
    • Weighted-by-leaf means equal leaf probability: a skinny one-child parent is as likely as a forty-child parent. If you want zoo itself to be rare, weight it by repetition or tuck it behind ().
    CategoryPrompt Random Choice

    Inputs (3)

    NameTypeDefaultDescription
    options_textSTRINGtown zoo{animals{birds|penguins}|aquarium,{fish|jellyfish}} coffee shop{cake|coffee cup} amusement park{ferris wheel|carousel|balloons} (){ white day wedding ceremony birthday party }
    change_everyINT11–999999
    state_keySTRING

    Outputs (2)

    NameTypeDescription
    selected_textSTRING
    selected_text_safeSTRING