Nodes/ComfyUI-Prompt-Formatter/πŸ“ Categorized Prompt Formatter
ComfyUI Node

πŸ“ Categorized Prompt Formatter

The node that sorts your tags for you

By xLegendeΒ·Created about a year agoΒ·Updated 4 months agoΒ· 7
πŸ“ Categorized Prompt Formatter
    • formatted_prompt
    • rejected_prompt
    β—„promptβ–Ί
    β—„category_definition_fileprompt_categories.yamlβ–Ί
    β—„output_template<|quality|>, <|character|>, <|clothing|>, <|setting|>β–Ί
    β—„input_delimiter,β–Ί
    β—„output_delimiter, β–Ί
    β—„strip_whitespacetrueβ–Ί
    β—„case_sensitive_matchingfalseβ–Ί
    β—„handle_weightstrueβ–Ί
    β—„match_underscores_spacestrueβ–Ί
    β—„disable_duplicatesfalseβ–Ί
    β—„unmatched_tag_handlingdiscardβ–Ί
    β—„seed-1β–Ί

    There's a specific chore that eats a stupid amount of time in tag-based workflows (anything Danbooru-style - Pony, Illustrious, and friends): a model loads with a prompt where the quality tags are in the middle, there are three competing styles, and the clothing tags you don't want are fighting the ones you do. You end up hand-reordering a comma soup. The πŸ“ Categorized Prompt Formatter is the node that does that reordering for you, on every run, against a category file you own.

    It's the pack's namesake and it does exactly what the name says: it takes an existing prompt, figures out which of your YAML categories each tag belongs to, then rebuilds the prompt from your template. The random formatter sibling generates new prompts; this one restructures the ones you already have, preserving the original tag spelling and weights.

    How it works

    You define categories in a YAML file (quality, character, clothing, style, whatever you like). The node splits your input prompt on its delimiter, strips off weight syntax like (tag:1.2) for matching purposes, checks each tag against every category - tolerating case differences and blue_eyes vs blue eyes by default - and buckets them.

    Then your output_template decides the structure. <|category|> emits all matched tags for that category. The interesting part is the limit syntax:

    • <|category:2|> keeps at most the first 2 matched tags.
    • <|category:-1|> keeps at most the last 1.
    • <|category:0|> includes none of that category, ever.

    Because the input prompt's order decides "first" vs "last," you can quietly control which tags win without touching the YAML: put your favorite style tag earlier in the input, keep it with :1, and the rest get dropped. unmatched_tag_handling decides the fate of tags the template never mentions - discard (default) drops them, append_end sticks them on the back, output_separately routes them to the rejected_prompt output.

    What to wire up

    The two outputs are formatted_prompt and rejected_prompt. The obvious move: send formatted_prompt to your positive CLIP Text Encode, and set unmatched_tag_handling to output_separately so rejected_prompt - the clutter you filtered out - becomes your negative prompt. One node turns a messy positive into a clean positive and a sensible negative. That's the setup I'd actually run this pack for.

    The seed input only affects {a|b|c} inline-choice resolution inside your template and tags (the same dynamic-prompt syntax Impact Pack uses). disable_duplicates and the delimiters are self-explanatory; handle_weights (on by default) is worth keeping on so (tag:1.1) still matches the category tag while the weight rides along into the output intact.

    Installing

    ComfyUI Manager (search "ComfyUI-Prompt-Formatter"), or:

    cd ComfyUI/custom_nodes
    git clone https://github.com/xLegende/ComfyUI-Prompt-Formatter
    

    Restart ComfyUI. Dependency is just PyYAML - no model downloads, runs instantly on CPU. The example prompt_categories.yaml in the pack is a good starting skeleton; edit it in any text editor.

    Common issues

    If nothing gets categorized, your prompt is probably using a different delimiter than the input_delimiter (default ,) - or your YAML category names don't match your template. Both fail quietly: output comes back near-empty with only a console warning. And remember that matching is case-insensitive and underscore/space-tolerant by default, so a "no match" is almost never a spelling problem - it's a category that doesn't exist in the file. Start from the included example YAML, verify against that, then expand. This is a fiddly node to configure but a genuine time-saver once the file's shaped right.

    Categorytext/filtering

    Inputs (12)

    NameTypeDefaultDescription
    promptSTRINGβ€”
    category_definition_fileSTRINGprompt_categories.yamlβ€”
    output_templateSTRING<|quality|>, <|character|>, <|clothing|>, <|setting|>β€”
    input_delimiteroptSTRING,β€”
    output_delimiteroptSTRING, β€”
    strip_whitespaceoptBOOLEANtrueβ€”
    case_sensitive_matchingoptBOOLEANfalseβ€”
    handle_weightsoptBOOLEANtrueβ€”
    match_underscores_spacesoptBOOLEANtrueβ€”
    disable_duplicatesoptBOOLEANfalseβ€”
    unmatched_tag_handlingoptCOMBOdiscard3 options: discard, append_end, output_separately
    seedoptINT-1-1–18446744073709550000β€”

    Outputs (2)

    NameTypeDescription
    formatted_promptSTRINGβ€”
    rejected_promptSTRINGβ€”