Simple LLM Caption (Batch)
Caption a whole folder of images and get training-ready 1.png / 1.txt pairs
- pipeline
- STRING
The dataset-prep node
SimpleLLMCaptionBatch is why most people install this pack at all: it points at a folder of images, captions every one, and writes out chronologically numbered 1.png, 1.txt, 2.png, 2.txt... pairs ready to drop into Kohya, ai-toolkit, or whatever you train with. Same Joy Caption Alpha Two engine as the basic node, minus the UI - folder in, training folder out.
This is the node to reach for when you're building a dataset for an LLM-encoder model (Flux, Z-Image, Qwen-Image, that family). Those models want natural-language captions, which is exactly what the Descriptive and Training Prompt types produce. If you're captioning for Illustrious or Pony, this can hand you Danbooru tags via the Booru Tags type, but the community default for the tagged-anime lineage is still a WD14 tagger - keep that in your back pocket.
Inputs and behavior
Required: pipeline, input_directory, output_directory, caption_type, caption_length, temperature, max_new_tokens, and save_as_txt (default on).
The two directory fields are where beginners slip. input_directory must be a real path on disk - the node returns "Error: Input directory does not exist" if it isn't. output_directory is required, not optional; without it the node refuses to run with "Please specify an output directory for chronological naming." Both are plain STRING widgets rather than folder pickers, so you type the absolute path.
Supported formats: .jpg, .jpeg, .png, .bmp, .webp, .tiff. The node sorts the file list, re-saves each image as a numbered PNG (RGBA is flattened to RGB), and writes the matching .txt. Unlike the Advanced node there's no top_p input here - it's fixed at 0.9 - and the text-processing options (lora_trigger, the replacement fields, the remove flags, prefix/suffix) all carry over.
Output is a single STRING status report: images processed, errors, and the output path. This is an output node, so it won't feed anything downstream; it's the end of the line.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/Pixelailabs/Joy_Caption_Two_PixelaiLabs.git
cd Joy_Caption_Two_PixelaiLabs
pip install -r requirements.txt
Restart and find it under image/captioning. ComfyUI Manager search "Joy Caption Two" works as well.
The catch: it's not a fire-and-forget queue
Two things to know before you point this at 2,000 images. First, the VRAM management that makes the basic node safe applies here too: models unload to CPU after each image and reload before the next. That keeps an 8GB card from dying on a big folder, but the wall-clock time per image includes a reload - a few thousand images is an overnight job, not a coffee break.
Second, the numbering starts at 1 on every run and it overwrites. Rerun the same folder and the previous 1.png / 1.txt are replaced. Fine for a single pass, annoying if you thought you were resuming. If a model load or download fails mid-run, the error count climbs and the node keeps going - usually the right behavior, but check the console for what actually failed.
One more thing worth repeating because it bites everyone: the first run downloads ~6-8GB of models (SigLIP, the Joy Caption adapters, your chosen LLM). Run a single test image through the basic node first so the loader has everything cached, then let the batch rip.
Inputs (16)
| Name | Type | Default | Description |
|---|---|---|---|
| pipeline | LLM_PIPELINE | — | |
| input_directory | STRING | — | |
| output_directory | STRING | — | |
| caption_type | COMBO | 7 options: Descriptive, Descriptive (Informal), Training Prompt, MidJourney, Booru Tags, Art Critic, +1 | |
| caption_length | COMBO | medium | 6 options: any, very short, short, medium-length, long, very long |
| temperature | FLOAT | 0.70.1–2 | — |
| max_new_tokens | INT | 30050–1000 | — |
| save_as_txt | BOOLEAN | true | — |
| lora_triggeropt | STRING | — | |
| gender_age_replacementopt | STRING | — | |
| hair_replacementopt | STRING | — | |
| body_size_replacementopt | STRING | — | |
| remove_tattoosopt | BOOLEAN | false | — |
| remove_jewelryopt | BOOLEAN | false | — |
| prefixopt | STRING | — | |
| suffixopt | STRING | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| STRING | STRING | — |