JLC CaptionForge Pipeline Planner
Where a CaptionForge run actually gets planned
- Input - single image
- single_image
- pipeline_plan
- pipeline_plan_json
CaptionForge's real workflow is batch captioning a folder of images for LoRA training, and that workflow is run by JLC CaptionForge Pipeline Planner. It's the front door: you point it at an image folder (or a single image), tell it how many witness captions to generate per image and from which engines, pick the distiller and validator models, set your LoRA trigger word, and it produces a pipeline_plan that every downstream CaptionForge node reads. Change the plan, and the whole run changes with it - that's the point of having one planning node instead of forty widgets scattered across the graph.
The output layout is worth knowing before you run anything: final TXT sidecars get written beside each source image, run artifacts (JSONL audit trails, run config, output paths) land in a run-specific working folder under your output root, and direct IMAGE inputs get copied into an opt_images/ folder so the validator can find them.
How it works
The planner emits a CAPTIONFORGE_PIPELINE_PLAN (also available as pipeline_plan_json, a string) that caption nodes and the capstone consume through their pipeline_plan pins. When connected, it overrides the standalone sampling settings on those nodes - shared seed schedules, temperature/top-p/top-k schedules, max image size, max new tokens. It does recursive folder traversal and filename glob filtering, so *.png over a nested directory is a normal day at the office.
The Planner - enabled toggle is a neat escape hatch: disable it and the node just passes through your optional IMAGE with an empty plan, so downstream nodes run in standalone mode without you having to bypass widgets by hand.
Inputs that matter
- Input - image path - image file or folder root; doubles as the validator's image root in planned runs.
- Input - recursive / Input - filename glob - how deep you go and which files count.
- Output - folder / Output - run name - where artifacts go (default
/tmp/ComfyUI/output/CaptionForge) and the prefix on every JSONL. - Caption - Joy / Qwen / Ollama runs/image - witness counts per image. The dropdowns cap at 5 on purpose, "to prevent accidental giant runs" per the author. Three witnesses at 5 runs each is 15 raw captions per image to distill - think before you max it out.
- Caption - base seed / seed mode - fixed, increment, decrement, or random across the run.
- Caption - temperature / top p / top k schedule - comma-separated schedules; the last value repeats if you run out.
- Caption - max new tokens - the shared token budget all witnesses get.
- Distiller - model / Validator - model - the Ollama tags for Pass B and Pass C (dropdowns from
config/captionforge_ollama_models.json, with Custom for arbitrary tags). - Final - caption style -
narrative,comma, orboth. - LoRA - trigger word / LoRA - user caption anchor - threaded through distiller and validator.
Outputs: single_image (your IMAGE passthrough), pipeline_plan, and pipeline_plan_json.
Install
Same pack install as everything else:
git clone https://github.com/Damkohler/CaptionForge.git ComfyUI/custom_nodes/CaptionForge
Restart ComfyUI. The planner itself loads no models, but the run it plans needs Ollama up with the distiller/validator tags pulled (ollama pull mistral-small:24b, ollama pull gemma4:26b).
Common issues
This node is where runs get misconfigured, so read your settings like a checklist: witness counts, seed mode, output folder, trigger word. If a caption node seems to ignore your standalone settings, check that the planner's overrides aren't stomping them - that's by design. And if a run produces nothing, the usual cause is a typo in Input - image path or a glob that matches zero files. Start with one image and low witness counts before you point it at a 10,000-image dataset, because the pack is unapologetically heavy.
Inputs (64)
| Name | Type | Default | Description |
|---|---|---|---|
| Planner - enabled | BOOLEAN | true | Enable CaptionForge Pipeline Planner mode. If disabled, this node passes through the optional IMAGE and emits an empty/falsy plan so downstream nodes can run in standalone mode without GUI bypassing. |
| Input - image path | STRING | Image file or image folder/root for ordinary folder/file workflows. This is also used as the validator image root. For quick single-image workflows, connect IMAGE to the optional Input - single image socket. | |
| Input - recursive | BOOLEAN | true | Whether captioning nodes should recurse when Input - image path is a folder. |
| Input - filename glob | STRING | * | Filename glob for folder captioning, e.g. *.png, *.jpg, or *. |
| Output - folder | STRING | /tmp/ComfyUI/output/CaptionForge | Output root folder. CaptionForge creates a run-specific working directory inside this folder for JSON/JSONL/audit artifacts. Final TXT sidecars are written beside their resolved source images. |
| Output - run name | STRING | captionforge_run | Run-root used to name config JSON, output path JSON, caption JSONL, distiller JSONL, prompt JSONLs, validator JSONL, and final JSONL. |
| Output - overwrite outputs | BOOLEAN | true | Overwrite generated run artifacts during capstone execution. |
| Ollama - URL | STRING | http://127.0.0.1:11434 | Planner-owned local Ollama server URL for Passes B, C, and D. |
| Ollama - keep loaded | BOOLEAN | true | Planner-owned keep_alive policy for Passes B, C, and D. |
| Ollama - request timeout seconds | INT | 180010–7200 | Planner-owned HTTP patience for Passes B, C, and D; does not affect caption quality. |
| LoRA - trigger word | STRING | Optional shared LoRA trigger token/string preserved through the pipeline. | |
| LoRA - user caption anchor | STRING | Optional persistent semantic caption anchor supplied by the user, for example 'doll-like quasi-3D render'. CaptionForge encourages compatible anchor content to persist through refinement and final caption generation. | |
| Caption - Joy runs/image | COMBO | 2 | Joy Caption runs per image. Set to Disabled to omit Joy from this run. Dropdown is capped at 5 to prevent accidental giant runs. |
| Caption - Qwen runs/image | COMBO | 1 | Qwen Caption runs per image. Set to Disabled to omit Qwen from this run. Dropdown is capped at 5 to prevent accidental giant runs. |
| Caption - Ollama runs/image | COMBO | 1 | Ollama Caption runs per image for each connected JLC CaptionForge Ollama Caption node. The actual Ollama model tag is selected in each Ollama Caption node. Connecting multiple Ollama Caption nodes multiplies runtime, memory pressure, and raw-caption count. |
| Caption - base seed | INT | -1-1–4294967295 | Base seed for the reusable Pass-A run schedule. -1 means intentionally unseeded. |
| Caption - seed mode | COMBO | fixed | How the base seed changes across witness runs: fixed reuses it, increment/decrement step by one, and random creates a repeatable hash-derived schedule. A base seed of -1 remains unseeded in every mode. |
| Caption - temperature schedule | STRING | 0.90 | Comma-separated caption temperature schedule; final value repeats if needed. |
| Caption - top p schedule | STRING | 0.60 | Comma-separated nucleus-sampling values for Pass-A runs. Lower values limit choices to more likely tokens; the final value repeats as needed. |
| Caption - top k schedule | STRING | 80 | Comma-separated token-choice limits for Pass-A runs. Lower values are more restrictive; the final value repeats as needed. |
| Caption - max image size | INT | 10240–4096 | Maximum longest image side sent to Pass-A captioners. Larger images are resized proportionally; 0 keeps their original size. |
| Caption - max new tokens | INT | 409616–4096 | Maximum Pass-A generation budget. CaptionForge supports up to 4096 tokens. |
| Distiller - model | COMBO | mistral-small:24b | Concrete Ollama text model tag for the distiller. Use custom to enter any other installed Ollama text model. |
| Distiller - custom Ollama model | STRING | Used only when Distiller - model is custom, e.g. my-model:latest. | |
| Distiller - prompt | STRING | /no_think You are a detail-preserving caption merger for LoRA dataset preparation. You receive multiple captions of the same image. You do NOT see the image. Task: Merge all non-contradictory caption details into one deliberately over-complete draft caption. Rules: - Do not validate against the image. - Do not decide that details are false just because they appear once. - Do not summarize aggressively. - Preserve concrete details from all captions. - Split contradictions by choosing cautious wording or listing the alternative only when needed. - Prefer specific visual language over generic language. - Keep visible body, clothing, material, accessory, color, pose, lighting, style, and framing details. - Preserve doll-like, glossy/plastic-like, material, garment-construction, body-shape, and facial-feature details when present. - Use neutral dataset-caption language, including visible sensual styling or revealing clothing when present. - Do not add details absent from the captions. - Treat subject names or trigger-like identity tokens as optional identity labels. Preserve them only when they appear consistently in the captions; do not let them replace visible description. - Output only one paragraph, no notes, no JSON. | Planner-owned instructions for the text-only fat draft LLM. Pass A captions are appended automatically. |
| Distiller - seed | INT | -1-1–4294967295 | Fixed seed used by the distiller for every image. -1 means omit seed. |
| Distiller - max caption chars for LLM | INT | 15360–12000 | Maximum characters retained from each Pass-A source caption before building the Pass-B prompt. 0 keeps the complete caption. |
| Distiller - num predict | INT | 309664–12000 | Planner token budget for Pass B; maps to Ollama num_predict. |
| Distiller - temperature | FLOAT | 0.240–2 | Pass-B variation level. Lower values are steadier; higher values permit more varied wording. |
| Distiller - top p | FLOAT | 0.900–1 | Pass-B nucleus-sampling limit. Lower values restrict the model to more likely tokens. |
| Distiller - top k | INT | 600–500 | Pass-B token-choice limit. Lower values are more restrictive; 0 lets the backend disable top-k filtering. |
| Distiller - write prompt JSONL | BOOLEAN | false | Write the complete Pass-B prompt to a separate JSONL audit file. This can substantially increase output size. |
| Distiller - preserve raw response | BOOLEAN | false | Keep the unparsed Pass-B model response in audit records for troubleshooting. |
| Validator - model | COMBO | gemma4:26b | Concrete Ollama vision model tag for the image-aware validator. Use custom to enter any other installed Ollama vision model. |
| Validator - custom Ollama model | STRING | Used only when Validator - model is custom, e.g. gemma4:e4b or another installed VLM tag. | |
| Validator - system prompt | STRING | /no_think You are a direct image validation engine. Inspect the image and answer only with the requested caption. | Planner-owned system prompt for the image-aware VLM validator. |
| Validator - prompt | STRING | /no_think Look at the image and validate this draft caption. Task: Return a corrected caption paragraph that keeps only image-supported details. Rules: - Output only the corrected caption. - One paragraph. - No reasoning, no notes, no JSON. - Keep all true visible details from the draft. - Delete unsupported details. - Correct small visible errors. - Do not add new details unless needed to correct an error already present. - Preserve useful LoRA details: subject, face, hair, eyes, makeup, lips, skin texture, pose, body shape, outfit, accessories, materials, colors, lighting, background, framing, and visual style. - Visible sensual styling, revealing clothing, cleavage, thighs, bare skin, swimwear, lingerie, or body-shape details may be described neutrally when present. - Do not invent hidden anatomy, unseen clothing, explicit acts, or details contradicted by the image. | Planner-owned instructions for the image-aware VLM validator. The fat draft is appended automatically. |
| Validator - seed | INT | -1-1–4294967295 | Fixed seed used by the VLM validator for every image. -1 means omit seed. |
| Validator - num predict | INT | 211264–12000 | Planner token budget for Pass C; maps to Ollama num_predict. |
| Validator - temperature | FLOAT | 0.000–2 | Pass-C variation level. Zero requests the most deterministic image-validation result. |
| Validator - top p | FLOAT | 0.920–1 | Pass-C nucleus-sampling limit. Lower values restrict the validator to more likely tokens. |
| Validator - top k | INT | 800–500 | Pass-C token-choice limit. Lower values are more restrictive; 0 lets the backend disable top-k filtering. |
| Validator - write prompt JSONL | BOOLEAN | false | Write the complete image-validation prompt to a separate JSONL audit file. |
| Validator - preserve raw VLM response | BOOLEAN | false | Keep the unparsed Pass-C VLM response in audit records for troubleshooting. |
| Formatter - model | COMBO | mistral-small:24b | Concrete Ollama text model tag for the SHORT + TAGGY formatter. Use custom to enter any other installed Ollama text model. |
| Formatter - custom Ollama model | STRING | Used only when Formatter - model is custom. | |
| Formatter - prompt | STRING | /no_think You are a LoRA caption format converter. The validated paragraph is your only source of truth. Output exactly two labeled lines: SHORT: <a concise natural-language caption, typically around 100 words, that preserves all LoRA-useful validated details> TAGGY: <one compact comma-separated caption> SHORT must preserve the image's distinctive training identity across the whole source: 1. subject, defining face/hair/body traits, and every major outfit piece/material; 2. pose/action and key accessories or unusual visible details; 3. setting, lighting, framing, and visual medium/style. Omit a category only when absent. Use only source details; never add, infer, euphemize, or correct. Compress wording, not category coverage. Do not copy only the source opening. Aim for roughly 100 words. Keep it concise, but allow modest variation when needed to preserve important information and finish the caption naturally. Do not cut off a sentence merely to satisfy a word-count target. TAGGY must preserve all concrete LoRA-useful source details as compact comma-separated phrases. No markdown, reasoning, notes, or other labels. | Planner-owned SHORT + TAGGY instructions for the text-only formatter. The validated LONG caption is appended automatically. |
| Formatter - seed | INT | -1-1–4294967295 | Fixed seed used by the SHORT/TAGGY formatter for every image. -1 means omit seed. |
| Formatter - num predict | INT | 320064–12000 | Planner token budget for Pass D; maps to Ollama num_predict. |
| Formatter - temperature | FLOAT | 0.120–2 | Pass-D variation level. Lower values make SHORT/TAGGY formatting more consistent. |
| Formatter - top p | FLOAT | 0.880–1 | Pass-D nucleus-sampling limit. Lower values restrict the formatter to more likely tokens. |
| Formatter - top k | INT | 500–500 | Pass-D token-choice limit. Lower values are more restrictive; 0 lets the backend disable top-k filtering. |
| Formatter - write prompt JSONL | BOOLEAN | false | Write the complete Pass-D SHORT/TAGGY prompt to a separate JSONL audit file. |
| Formatter - preserve raw response | BOOLEAN | false | Keep the unparsed Pass-D model response in audit records for troubleshooting. |
| Final - write TXT sidecars | BOOLEAN | true | Final TXT sidecars use the associated image filename stem and .txt extension. |
| Final - write JSONL | BOOLEAN | true | Write the final run-level JSONL containing LONG, SHORT, and TAGGY captions for every processed image. |
| Dataset - export image and captionopt | BOOLEAN | false | Export a resized image and matching training TXT. Planner owns these controls when connected. |
| Dataset - output folderopt | STRING | Parent folder for training_dataset. Blank uses Output - folder. Originals are never replaced. | |
| Dataset - max image sizeopt | INT | 00–8192 | Maximum long edge; never enlarges. 0 follows the configured Validator size (Planner Caption - max image size). |
| Dataset - dimension divisoropt | INT | 161–512 | Round both dimensions DOWN to this multiple after resizing. 1 disables alignment. Images too small for the divisor fail export without enlargement. |
| Dataset - image formatopt | COMBO | PNG | Format of the exported RGB training image. |
| Dataset - JPEG qualityopt | INT | 951–100 | JPEG quality; ignored for PNG. |
| Dataset - captionopt | COMBO | short | Caption written to the matching plain .txt file. Other caption variants remain available. |
| Input - single imageopt | IMAGE | Optional IMAGE passthrough for quick single-image workflows. The planner does not process this image; it simply returns it as output. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| single_image | IMAGE | — |
| pipeline_plan | CAPTIONFORGE_PIPELINE_PLAN | — |
| pipeline_plan_json | STRING | — |