Booru Tag Pipeline
When your LLM writes 'twin tails' but the model trained on 'twintails'
- final_tags
- unknown_tags
- replacements_made
- implications_added
- debug_log
Anime checkpoints live and die by Danbooru's exact vocabulary. Illustrious, NoobAI, and Pony were trained on the board's crowd-tagged archive, and that means the model didn't learn "twin tails" - it learned twintails, one token-shaped word that maps straight to a training example. Now flip that around: you've got a local LLM rewriting your prompts into booru-ish tags, and it happily hands you "twin tails", "dark atmosphere", and other things that look like tags but aren't in the vocabulary the model was trained on. That's the gap Booru Tag Pipeline exists to close.
The name is accurate, for once - it's a pipeline, not an API call. It needs no key, no GPU, and no network at runtime after a one-time data download. It's the deterministic validation layer between your prompt rewriter and your CLIP text encoder: whatever tag-like string goes in, only real Danbooru tags come out, aliases resolved, implied tags expanded, unknowns reported instead of silently guessed.
How it works
Under the hood is a local SQLite snapshot of the live Danbooru tag tables - about 1.7 million tags, 59k aliases, and 49k implications at the time this snapshot was built. On first use the node auto-downloads it from Hugging Face (baton4ik/danbooru-tag-metadata-snapshot, a ~175MB file) and queries it in read-only mode from then on.
For each tag in your input it does the boring, reliable stuff an LLM can't be trusted to do: splits on commas, semicolons, and newlines; strips A1111-style (tag:1.2) weights; lowercases; tries a handful of candidate forms (twin tails → twin_tails); then looks up the alias table. An alias match wins and returns the canonical tag, so looking at viewer becomes looking_at_viewer and thigh highs becomes thighhighs. Anything that resolves to nothing gets reported as unknown rather than passed through - this is the part most "booru-style" generators skip, and it's the entire point.
The author's framing is worth quoting: deterministic on purpose, strong at canonicalization, aliases, and implications, and it does not try to semantically guess every unknown phrase. No hallucinated tags, no vibes. It either knows or it says so.
The inputs that matter
tags_text is the only required input - the raw comma-separated output from your LLM or prompt-rewriting node, or just text you paste in. Everything else is optional, and honestly only three of them are worth touching:
- include_implications (default on) with implication_depth (default 1, max 3) - expands implied tags breadth-first. A depth-1 expansion is usually plenty; past depth 2 you start pulling in generic meta tags you probably didn't want.
- drop_quality_boilerplate (default on) - strips
masterpiece,best_quality,newest,absurdresand friends. If you'd rather keep them for your own prompt template, flip it off. - prefer_underscores (default off) - outputs
hatsune_mikuinstead ofhatsune miku. Spaces are the sane default for most ComfyUI setups; flip this if a downstream tool expects underscore form.
sort_mode defaults to preserve and only matters if you want grouped (count tags and characters first, then the rest) or plain alphabetical.
Outputs
final_tags is the one you wire into a CLIP Text Encode node. The other four are diagnostic: unknown_tags tells you what it couldn't resolve, replacements_made logs alias rewrites, implications_added lists what got expanded, and debug_log is the whole run as one readable trace. Worth a glance when something comes out of the model looking wrong - nine times out of ten unknown_tags has the answer.
Installing it
Clone into custom_nodes and install requirements into ComfyUI's Python environment:
cd /path/to/ComfyUI/custom_nodes
git clone https://github.com/hlibr/ComfyUI-Booru-Tag-Pipeline.git comfyui_booru_tag_pipeline
/path/to/ComfyUI/.venv/bin/python -m pip install -r comfyui_booru_tag_pipeline/requirements.txt
Restart ComfyUI. It's also findable through ComfyUI Manager if you'd rather click - search "Booru Tag Pipeline". Dependencies are light (requests, huggingface_hub, pyarrow); there are no model weights to fetch. The heavy thing is the snapshot, and it downloads itself on first use, though you can grab it manually ahead of time with the bundled scripts/download_public_snapshot.py if you want the first run to be instant.
Where people get burned
The ~175MB snapshot download on first use is the classic moment of panic - the queue sits there, the log goes quiet, and you assume it hung. It didn't; it's pulling data. Also know that the package will not silently update an existing snapshot - that's deliberate, so your results stay reproducible, but it also means your tag data ages. And a final reality check: this node is built for tag-like LLM output, not raw prose. Feed it "a girl sitting on a bench in the rain" and you'll get mostly unknown_tags back, because that's not what it's for - that's what your prompt rewriter is for.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| tags_text | STRING | — | |
| include_implicationsopt | BOOLEAN | true | — |
| implication_depthopt | INT | 10–3 | — |
| include_retired_aliasesopt | BOOLEAN | true | — |
| include_deleted_aliasesopt | BOOLEAN | false | — |
| drop_quality_boilerplateopt | BOOLEAN | true | — |
| prefer_underscoresopt | BOOLEAN | false | — |
| sort_modeopt | COMBO | preserve | 3 options: preserve, grouped, alphabetical |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| final_tags | STRING | — |
| unknown_tags | STRING | — |
| replacements_made | STRING | — |
| implications_added | STRING | — |
| debug_log | STRING | — |