Caption Dataset (ZenTrainer)
The captioning node you never actually configure
Captioning is the least glamorous part of training a LoRA and usually the part that decides whether it works. ZenTrainer does it with a vision-language model you already have installed, inside ComfyUI, on the same GPU as everything else - and the node you found is the slot that job runs in.
What it does
ZenCaptionSession takes one string, job_id, and returns nothing. It's an output node, so ComfyUI runs it; that's the point. The engine has already stashed the job - dataset folder, which caption set to write, which vision model, the instruction prompt, how many images - in memory, keyed by that id. The node's execute fetches the job and captions it.
So the answer to "what do I put in this field" is: nothing you type. The dataset page in the app builds the job and submits this node as a one-node prompt, exactly the way it submits a training run. Writing the id by hand gets you an error, not captions.
How it works
Each image is run through ComfyUI's own text generation path, the same one behind the core TextGenerate node: load a vision-language text encoder from models/text_encoders, tokenize the picture together with an instruction, and generate greedily. The instruction comes from one of three styles - natural-language paragraph, single sentence, or comma-separated tags - plus an optional subject line telling the model to call your subject by a specific word, which is how you get your trigger token into every caption instead of "a woman".
The output is cleaned before it's written: think blocks from reasoning-style templates get stripped, and a leading "Caption:" or "Tags:" prefix gets dropped. Then it's written as <image>.txt beside the file, atomically, so a crash can't leave you a half caption. If it overwrote something, the old text is kept so the job can be undone after a restart. A preview run writes nothing at all and just shows you what it would have said - use that before you let a model loose on 400 images.
Two details give away how much thought went into this. Captioning sets CLIPLoader's type to stable_diffusion on purpose, so a Qwen-VL file loads as the stock chat model rather than an image model's conditioning wrapper. And it disables ComfyUI's model compiler (CUDA graphs) while it runs, because token-by-token generation device-asserts on the second token under it and takes the server down with it.
What you actually set
All of it lives in the app, not in the node. The ones that matter:
- The vision model. Any text encoder in
models/text_encodersthat actually carries a vision tower - the pack reads the safetensors header to check, so it won't offer you T5. Qwen3-VL and Qwen2.5-VL are the usual picks; Gemma works too, and Gemma 4 variants can caption a video clip including its soundtrack. - Which images. Missing (only ones with no caption yet), all, or selected. The default is the safe one.
- The caption set. Long and short captions can live side by side, and training reads the
defaultset unless told otherwise. Rewriting existing captions in that set requires an explicitreplace_defaultflag - it will not happen by accident.
The node itself has one input, and one job: run.
Installing it
Same install as the rest of the pack - ComfyUI Manager, search ZenTrainer, or clone it:
cd ComfyUI/custom_nodes
git clone https://github.com/Nynxz/ZenTrainer
Restart, then open /zentrainer/. You need ComfyUI 0.41+, and a source clone needs the web app built once (cd web && pnpm install && pnpm build) - otherwise the app 404s and you're left staring at a node with a text field.
No extra dependencies: requirements.txt is empty on purpose, everything comes from ComfyUI. The vision model is your download, not the pack's. It'll also patch text-encoder LoRAs onto the captioner - the author publishes an abliterated Qwen3-VL one, which is the usual reason to bother.
When it doesn't caption
"…can't generate text; pick a vision-language model (Qwen-VL, Gemma)." You pointed it at something that isn't a chat model - a T5, a CLIP-L, or an image model's wrapper file. Pick a proper VLM.
"caption job cap-xxxx is gone (ComfyUI restarted?); start it again." The job list is in memory. Captions already written stay on disk and stay undoable, but the job itself doesn't survive a restart. Kick it off again in the app.
It captioned the wrong set, or nothing happened. An all run against the default set that would overwrite existing captions is refused with a count of how many it would replace. That's a feature; confirm it and run again.
The captions are decent but miss things. That's the ceiling of every VLM captioner, not this node - multi-subject attribution is where they all fall over. Auto-caption the big set, then read the ten that matter.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| job_id | STRING | — |
Outputs (0)
No outputs