JLC CaptionForge Orchestrator
The capstone that turns witness captions into auditable LoRA captions
- Input - single image
- pipeline_plan
- long_captions
- short_captions
- taggy_captions
- final_records
- status
CaptionForge's core bet is that one captioner is easy to fool, so its main node is the part that doesn't look at the image first. JLC CaptionForge Node - the capstone - takes the raw "witness" captions your Joy/Qwen/Ollama nodes produced in Pass A, distills them with a text-only LLM, checks the result against the actual image with a vision model, and then writes your final captions. It's the orchestration hub of the pack, and it's the slowest part of it.
The design intent is worth understanding before you run it: the semantic work mostly ends at the validated long caption. The short and taggy outputs are deliberately lightweight formatting steps derived from that validated paragraph, not fresh reinterpretations of the image. The formatter prompt even says, in effect, "do not rewrite the validated paragraph." So if you want better captions, tune the distiller and validator, not the formatter.
How it works
Three Ollama-backed stages, in order:
- Fat Draft (Pass B) - a text-only LLM (default
mistral-small:24b) merges all the witness captions into one deliberately over-complete draft. It's told not to validate against the image, not to summarize aggressively, and to preserve concrete details. That's why it's called fat. - Validator (Pass C) - an image-aware VLM (default
gemma4:26b) looks at the actual image and returns a corrected paragraph keeping only image-supported details. This is where hallucinations get cut. - Formatter (Pass D) - a text-only LLM derives the comma-separated taggy caption from the validated paragraph.
Then it exports: _long.txt, _short.txt, and _taggy.txt sidecars written beside each source image, plus a JSONL audit trail (_B_DISTILL.jsonl, _C_VLM_VALIDATED.jsonl, _D_FINAL_EXPORT.jsonl, run configs) under your output root. If you ever need to defend a caption to a client or debug why a training set learned something weird, that trail is the selling point.
Inputs that matter
- Input - captions JSONL - the Pass A raw captions your caption nodes appended to. This is the node's primary fuel.
- Input - image path - the folder/file root the validator uses to resolve source images. It has to be findable, or Pass C can't do its job.
- Input - include caption families -
joy,qwen,ollamaby default; useallor*for everything. - LoRA - trigger word and LoRA - user caption anchor - your trigger token and any identity/style anchor passed through to distiller and validator.
- Fat Draft / Validator / Formatter - model - dropdowns loaded from
config/captionforge_ollama_models.json; pickCustomto type any installed Ollama tag. - Final - TXT export format -
natural,taggy, orboth_separate. - Ollama - URL - defaults to
http://127.0.0.1:11434. Nothing happens until that server is up.
Outputs: natural_captions, taggy_captions, final_jsonl_records, output_paths_json, and status. The JSONL/paths outputs are the audit trail in string form if you want to inspect or re-export.
Install
Same as every node in this pack - clone and restart:
git clone https://github.com/Damkohler/CaptionForge.git ComfyUI/custom_nodes/CaptionForge
Then the real requirement: a working local Ollama with the stage models pulled.
ollama pull mistral-small:24b
ollama pull gemma4:26b
The pack ships no weights and pulls nothing for this node by itself - it's pure Ollama calls.
Common issues
If you see connection errors, Ollama isn't running or the URL is wrong - check ollama serve. It's slow by design: three model passes per image, and a 24B Ollama model can churn for a while on a 16 GB card (the author's own dev box is an RTX 4090 Laptop). The request timeout default is a generous 1800 seconds because large models may still be pulling on first call. And remember: this node clears the resident Joy/Qwen Python models before it calls Ollama, so the GPU handoff can feel like a reload. The fat draft is supposed to contain conflicting details - the validator is the one that trims them, so don't panic when Pass B looks overstuffed.
Inputs (54)
| Name | Type | Default | Description |
|---|---|---|---|
| Input - captions JSONL | STRING | Pass A raw caption JSONL produced by CaptionForge Caption nodes. | |
| Input - image path | STRING | Image file/folder root used by the VLM validator to resolve source images. | |
| Input - include caption families | STRING | joy,qwen,ollama | Comma-separated model_family values to use from Pass A. Use all or * to include everything. |
| Input - max captions per family | INT | 50–50 | Maximum selected Pass A captions per model family. 0 means no per-family cap. |
| Input - max total captions | INT | 200–100 | Maximum selected Pass A captions per image. 0 means no total cap. |
| Output - folder | STRING | Output folder. Planner value overrides this when pipeline_plan is connected. | |
| Output - run name | STRING | captionforge_run | Base name used for the run working folder and B/C/D JSONL/audit files. |
| Output - overwrite outputs | BOOLEAN | true | Replace run-level files that already use this folder and run name. Final image sidecars are also replaced when enabled. |
| Ollama - URL | STRING | http://127.0.0.1:11434 | Local Ollama server URL. |
| Ollama - keep loaded | BOOLEAN | true | Ask Ollama to keep the most recently used model in memory between calls. Ollama makes the final residency decision. |
| Ollama - request timeout seconds | INT | 180010–7200 | HTTP patience for Ollama calls. This does not affect caption quality. |
| LoRA - trigger word | STRING | Optional LoRA trigger token or phrase preserved in final captions as training metadata. | |
| LoRA - user caption anchor | STRING | Optional phrase you want preserved when it remains compatible with the image, such as a character or rendering-style anchor. | |
| Fat Draft - model | COMBO | mistral-small:24b | Concrete Ollama text-model tag for Pass B. Choose custom to enter another installed tag below. |
| Fat Draft - custom Ollama model | STRING | Used only when Fat Draft - model is Custom. | |
| Fat Draft - 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. | Instructions for the text-only fat draft LLM. Captions are appended automatically. |
| Fat Draft - max caption chars | INT | 15360–12000 | Maximum characters kept from each source witness caption before Pass B. 0 keeps the complete caption. |
| Fat Draft - max new tokens | INT | 309664–12000 | Maximum Pass-B output-token budget sent to Ollama (num_predict). |
| Fat Draft - temperature | FLOAT | 0.240–2 | Pass-B variation level. Lower values are steadier; higher values permit more varied wording. |
| Fat Draft - top p | FLOAT | 0.900–1 | Pass-B nucleus-sampling limit. Lower values restrict the model to more likely tokens. |
| Fat Draft - top k | INT | 600–500 | Pass-B token-choice limit. Lower values are more restrictive; 0 lets the backend disable top-k filtering. |
| Validator - model | COMBO | gemma4:26b | Concrete Ollama vision-model tag for image-aware Pass C. Choose custom to enter another installed VLM tag below. |
| Validator - custom Ollama model | STRING | Used only when Validator - model is Custom. | |
| Validator - system prompt | STRING | /no_think You are a direct image validation engine. Inspect the image and answer only with the requested caption. | 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. | Instructions for the image-aware VLM validator. The fat draft is appended automatically. |
| Validator - max new tokens | INT | 211264–12000 | Maximum Pass-C output-token budget sent to Ollama (num_predict). |
| Validator - max image size | INT | 10240–8192 | Longest image edge sent to the Validator VLM; aspect ratio is preserved. 0 disables resizing. The Pipeline Planner overrides this value when connected. |
| 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. |
| Formatter - model | COMBO | mistral-small:24b | Concrete Ollama text-model tag for the Pass-D SHORT/TAGGY formatter. Choose custom to enter another installed tag below. |
| 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. | Instructions for the text-only short/taggy formatter. The validated paragraph is appended automatically. |
| Formatter - max new tokens | INT | 320064–12000 | Maximum Pass-D output-token budget sent 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. |
| Audit - write prompt JSONL | BOOLEAN | false | Write full B/C/D prompts to separate JSONL audit files. This can substantially increase output size. |
| Audit - preserve raw responses | BOOLEAN | false | Keep unparsed B/C/D model responses in audit records for troubleshooting. |
| Final - TXT export format | COMBO | natural | Select the primary final_caption field in JSONL: natural uses LONG, taggy uses TAGGY, and both_separate keeps LONG primary while retaining separate named fields. TXT sidecars always include LONG, SHORT, and TAGGY files. |
| Final - write TXT sidecars | BOOLEAN | true | Write LONG, SHORT, and TAGGY text variants beside each resolved source image, plus the selected plain .txt training sidecar. |
| 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/reference for planned single-image workflows. | |
| pipeline_planopt | CAPTIONFORGE_PIPELINE_PLAN | Connect the CaptionForge Pipeline Planner pipeline_plan output here. | |
| Distiller seedopt | INT | Optional standalone Pass-B seed. Planner seed overrides it when connected. | |
| Validator seedopt | INT | Optional standalone Pass-C seed. Planner seed overrides it when connected. | |
| Formatter seedopt | INT | Optional standalone Pass-D seed. Planner seed overrides it when connected. |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| long_captions | STRING | — |
| short_captions | STRING | — |
| taggy_captions | STRING | — |
| final_records | STRING | — |
| status | STRING | — |