Nodes/ComfyUI-LZNodes/LZ Prompt Pick (Multi-source)
ComfyUI Node

LZ Prompt Pick (Multi-source)

LZ Prompt Pick

By liz-ils·Created 8 months ago·Updated 13 days ago· 2
LZ Prompt Pick (Multi-source)
    • result
    • picked
    ◄template►
    ◄placeholder_tag_►
    ◄source_file_a►
    ◄pick_count1►
    ◄uniquetrue►
    ◄separator, ►
    ◄seed-1►
    ◄source_file_b►
    ◄json_key►
    ◄candidates►
    ◄weight_min1.00►
    ◄weight_max1.00►

    When you're generating thirty images hunting for one keeper, the boring problem isn't retyping the prompt - it's that all thirty have the same prompt. LZPromptPick (displayed as LZ Prompt Pick (Multi-source)) pulls one or more tags out of plain text, CSV or JSON files you write yourself, joins them, and drops them into a template. Change the seed, get a different roll. Set a fixed seed, get the exact image back.

    How it works

    It builds a pool first, in order: source_file_a, then source_file_b, then whatever's in the multiline candidates box. Duplicates are removed without reordering, blank lines and lines starting with # are skipped, and the whole pool is capped - files over 2 MB are ignored, and names must be flat (no subfolders, no ..). Text files are one item per line, CSV files contribute every cell of every row, and JSON can be a list - or an object where json_key names the list you want (leave it blank and every list in the file is merged in sorted key order).

    Then the randomness. With seed at -1 it uses the ordinary Python random source and reports itself as changed on every execution, so it genuinely re-rolls each generation. With seed at 0 or above it builds a seeded RNG, which makes a prompt exactly reproducible. With unique on, picks come from rng.sample - no repeats - unless the pool is smaller than pick_count, in which case it relaxes to random choice with replacement and you may see the same tag twice. Weights wrap only when they aren't exactly 1.0, giving CLIP-style (tag:1.25); weight_min / weight_max set the range and swap silently if you type them backwards.

    Finally, substitution: the placeholder (default _tag_) is replaced inside template with the joined picks. If your template doesn't contain the placeholder anywhere, the template comes back untouched - the picks still appear on the second output. Leave placeholder empty and the picks are appended to the template with your separator instead.

    The inputs you'll actually touch

    template is the sentence your tags slot into, placeholder is the hole they fill, source_file_a (plus optional source_file_b) is the dropdown of files it found, and pick_count says how many tags to draw. seed and unique are the two you'll flip most. The rest - separator, json_key, candidates, weight_min, weight_max - are set-and-forget.

    Two outputs. result is the finished string: wire it to your encoder's positive prompt, into the pack's sampler, or into the template of a second pick node. picked is the bare joined tags - useful for filenames and logs when you want to know what that seed actually rolled.

    Installing it

    Same clone as the rest of the pack, then restart ComfyUI:

    cd ComfyUI/custom_nodes
    git clone https://github.com/liz-ils/ComfyUI-LZNodes
    

    No requirements.txt, no pip dependencies - stdlib and ComfyUI's own modules. Then make the folder the node reads from, under ComfyUI's output directory:

    mkdir -p ComfyUI/output/prompt_csv
    printf 'red hair\nblonde hair\nblack hair\n' > ComfyUI/output/prompt_csv/hair.txt
    

    Where people get burned

    The file location is the whole point and the README doesn't say it. Your tag files go in ComfyUI/output/prompt_csv/ - not input/, not the pack folder. The node creates that directory on first use and lists .txt, .csv and .json files from it. If you relocate ComfyUI's output directory with --output-directory, the folder moves with it.

    A new file won't show up until the dropdown refreshes. The list is built when node definitions are fetched, so refresh the page; if it's still missing, restart ComfyUI, and check the extension is one of the three it accepts.

    An empty pool fails silently. If nothing is found, the node returns your template as-is - meaning the literal _tag_ gets tokenized and paid for by the model. If you see underscores in your output, the file wasn't found, was over 2 MB, or lived in a subfolder.

    Unique only applies within one node. The author's own suggestion is to stack several of these for separate axes (hair, clothing, colour), and nothing stops two of them rolling the same tag. Across nodes, dedupe is on you.

    Weights only mean something on CLIP-era models. On SD 1.5, SDXL, Illustrious and Pony, (tag:1.25) does what you expect. On LLM-encoded models - Z-Image, Flux 2, Krea 2, Anima - attention syntax is discarded outright, and the parentheses get fed to the encoder as literal punctuation, which is worse than not using it. The tags themselves still work; the weighting doesn't. That's why half the wildcard advice on Reddit is quietly out of date.

    A fixed seed plus control_after_generate is the classic seed trap. If your picks never change, look at that little dropdown next to seed and at ComfyUI's widget-control-mode setting - it bites exactly the same way on a KSampler.

    CategoryMyCustomNodes/Text

    Inputs (12)

    NameTypeDefaultDescription
    templateSTRING—
    placeholderSTRING_tag_—
    source_file_aCOMBO1 options:
    pick_countINT11–20—
    uniqueBOOLEANtrue—
    separatorSTRING, —
    seedINT-1-1–18446744073709550000—
    source_file_boptCOMBO1 options:
    json_keyoptSTRING—
    candidatesoptSTRING—
    weight_minoptFLOAT1.000.1–4—
    weight_maxoptFLOAT1.000.1–4—

    Outputs (2)

    NameTypeDescription
    resultSTRING—
    pickedSTRING—