Nodes/comfyUI_probabilistic_tag_sampler/Probabilistic Tag Sampler (Text)
ComfyUI Node

Probabilistic Tag Sampler (Text)

A tag sampler where every pick remembers the last one

By Travers5·Created 7 months ago·Updated 6 months ago· 0
Probabilistic Tag Sampler (Text)
    • tags
    seed0
    input_text
    min_tags0
    max_tags-1
    separatorcomma_space
    boost_amount0.25
    suppress_amount0.25
    output_strengthsfalse
    rng_avg_count2
    normalise_strengthstrue
    Group 01 (Exclude)false
    Group 01 (Boost)false
    Group 01 (Suppress)false
    Group 02 (Exclude)false
    Group 02 (Boost)false
    Group 02 (Suppress)false
    Group 03 (Exclude)false
    Group 03 (Boost)false
    Group 03 (Suppress)false
    Group 04 (Exclude)false
    Group 04 (Boost)false
    Group 04 (Suppress)false
    Group 05 (Exclude)false
    Group 05 (Boost)false
    Group 05 (Suppress)false
    Group 06 (Exclude)false
    Group 06 (Boost)false
    Group 06 (Suppress)false
    Group 07 (Exclude)false
    Group 07 (Boost)false
    Group 07 (Suppress)false
    Group 08 (Exclude)false
    Group 08 (Boost)false
    Group 08 (Suppress)false
    Group 09 (Exclude)false
    Group 09 (Boost)false
    Group 09 (Suppress)false
    Group 10 (Exclude)false
    Group 10 (Boost)false
    Group 10 (Suppress)false

    Wildcards and {a|b|c} brackets give you prompt variety, but they're blind: every pick is independent, and nothing you choose affects what comes next. Probabilistic Tag Sampler is the version that treats your prompt like a system instead. Pick rain and the odds of night or wet clothes climb while sunny falls off a cliff. It's a small stochastic tag generator for the Danbooru-style tag models (Illustrious, NoobAI, Pony derivatives) where comma-separated vocabulary is the prompting language - batch a scene and get variations that stay coherent with each other, the same job people use LLM prompt nodes or the bracket trick for, done locally with zero API calls and no key.

    The name isn't lying about anything. It's pure Python, no dependencies, no model files to download.

    How it works

    Every tag in the model carries a base score in [-1, 1], which a logistic S-curve maps to a probability (-1 = hard 0%, +1 = hard 100%, 0 = coin flip). The node then runs a selection loop: for each remaining candidate it rolls the RNG (averaged rng_avg_count times for stability), computes probability − roll_avg, and picks the tag with the best positive margin. If nothing clears the bar and min_tags forces more picks, it reluctantly takes the least-bad negative candidate. No margins and the minimum's met? It stops - a tag at 3% doesn't get dragged in for fun.

    The interesting part is the influence matrix. Every time a tag gets chosen, its row is applied to the scores of everyone still standing, so night can push moon up and daylight down. That's the dependency model wildcards don't have. Chosen tags also apply their influence to any tags you passed in via input_text, which is how chaining works.

    Tags are grouped, and each of the 10 groups gets three toggles: Exclude hard-locks the group's tags to -1 (never pick them), while Boost and Suppress nudge their scores by boost_amount / suppress_amount. The group assignments themselves live in the model data as bitmasks - you define them.

    The inputs that matter

    Most of the schema is defaults you'll leave alone. The ones you'll actually touch:

    • seed - reproducibility. ComfyUI gives you the control-after-generate widget for free here, so re-rolls are one click.
    • input_text - pass in a base prompt or another sampler's output; its tags get pre-selected and their influence applied, so samplers can chain.
    • min_tags / max_tags - floor and ceiling. max_tags defaults to -1, which means "no cap" (all non-excluded tags); min_tags backfills the lowest-probability winners if the loop undershoots.
    • separator - comma_space, comma, space, or newline; you're feeding a booru model, so comma_space is where you'll live.
    • output_strengths - flip this on to emit weighted (tag:1.35) syntax instead of bare tags; normalise_strengths then rescales so the weights average 1.0.

    Output: a single tags STRING. Wire it straight into a CLIP Text Encode (or a prompt-concat node if you're merging with fixed tags).

    Installing it

    ComfyUI Manager is easiest - search "Probabilistic Tag Sampler" (the pack is comfyUI_probabilistic_tag_sampler) and install. Or do it by hand:

    cd ComfyUI/custom_nodes
    git clone https://github.com/Travers5/comfyUI_probabilistic_tag_sampler
    

    Restart ComfyUI and the node shows up under text/tags. No requirements.txt, no pip step, nothing heavy - just random, math, csv, and json from the stdlib.

    The catch nobody in the README underlines

    First run auto-creates tag_data.json and tag_data.csv in the node's folder, and out of the box they're placeholder city: six tags named tag_01 through tag_06, every base score 0.0, an all-zeros influence matrix, no group memberships. Until you edit that data, the node is literally rolling dice among six dummy labels. This is a framework more than a finished tool - the model is the product, and there's no GUI for authoring it. You edit the CSV in a spreadsheet (better for the matrix) or the JSON by hand, and if the CSV is newer than the JSON, the loader rebuilds from the CSV automatically. Budget the data-entry time before you build a workflow on it.

    Troubleshooting

    • Edits not showing up: the loader only rebuilds JSON from CSV when the CSV is newer. Touch the CSV again after an edit, or the older JSON wins.
    • Zero community signal: it's brand new - one commit, no Reddit presence, no impressions to speak of. You're an early adopter, so the repo's own docs (INTENDED_BEHAVIOR.md, PROGRAM_STRUCTURE_AND_IO.md) are your best map; there's nobody to ask yet.
    • Want to see what it's doing: the source has code-only debug settings (DEBUG_LOG_TO_FILE and friends) that write prob_tag_sampler_debug.log with the full per-iteration margin math. Handy when your influence matrix produces nonsense.
    Categorytext/tags

    Inputs (40)

    NameTypeDefaultDescription
    seedINT00–18446744073709550000
    input_textSTRING
    min_tagsINT00–9999
    max_tagsINT-1-1–9999
    separatorCOMBOcomma_space4 options: comma_space, comma, space, newline
    boost_amountFLOAT0.250–1
    suppress_amountFLOAT0.250–1
    output_strengthsBOOLEANfalse
    rng_avg_countINT21–32
    normalise_strengthsBOOLEANtrue
    Group 01 (Exclude)BOOLEANfalse
    Group 01 (Boost)BOOLEANfalse
    Group 01 (Suppress)BOOLEANfalse
    Group 02 (Exclude)BOOLEANfalse
    Group 02 (Boost)BOOLEANfalse
    Group 02 (Suppress)BOOLEANfalse
    Group 03 (Exclude)BOOLEANfalse
    Group 03 (Boost)BOOLEANfalse
    Group 03 (Suppress)BOOLEANfalse
    Group 04 (Exclude)BOOLEANfalse
    Group 04 (Boost)BOOLEANfalse
    Group 04 (Suppress)BOOLEANfalse
    Group 05 (Exclude)BOOLEANfalse
    Group 05 (Boost)BOOLEANfalse
    Group 05 (Suppress)BOOLEANfalse
    Group 06 (Exclude)BOOLEANfalse
    Group 06 (Boost)BOOLEANfalse
    Group 06 (Suppress)BOOLEANfalse
    Group 07 (Exclude)BOOLEANfalse
    Group 07 (Boost)BOOLEANfalse
    Group 07 (Suppress)BOOLEANfalse
    Group 08 (Exclude)BOOLEANfalse
    Group 08 (Boost)BOOLEANfalse
    Group 08 (Suppress)BOOLEANfalse
    Group 09 (Exclude)BOOLEANfalse
    Group 09 (Boost)BOOLEANfalse
    Group 09 (Suppress)BOOLEANfalse
    Group 10 (Exclude)BOOLEANfalse
    Group 10 (Boost)BOOLEANfalse
    Group 10 (Suppress)BOOLEANfalse

    Outputs (1)

    NameTypeDescription
    tagsSTRING