LC Vision Caption π
Three labeled references beat one mushed batch
- vision_model
- reference_image_1
- reference_image_2
- reference_image_3
- reference_video
- text
Feed it a handle, an instruction and some pictures, get text back. That's the whole node - and if you've used a single-image captioner before, the thing you'll notice is that nothing gets merged.
The gap it closes is real. Most ComfyUI vision nodes give you one image socket, so three angles of the same character arrive as one undifferentiated pile. LC Vision Caption takes up to three independent reference images plus a separate reference video and labels each one distinctly in the message: <Reference 1>, <Reference 2>, <Reference 3>, <Target Video>. Ask it to compare Reference 1 against Reference 2 and you get an answer that knows which is which.
Where it fits
Two jobs, both boring and both worth automating. Captioning a LoRA or fine-tuning dataset, where the alternative is a hosted API that costs per call and filters what you're describing. And reading an existing image to seed an img2img or image-to-video prompt, where the captioner feeds an enhancer which feeds the sampler.
One honest limitation, and it applies to every VLM in this class rather than this node: multi-subject attribution is where these models fall over. Two people in frame and they'll mix up who's wearing what. Auto-caption a large set, audit a small one by hand.
How it works
References get downscaled so their longer edge fits max_image_side and encoded as base64 JPEG at jpeg_quality. reference_video takes a frame-sequence batch - the shape a video loader hands you - and evenly samples it down to video_max_frames; sending every frame of a long clip would eat the token budget for no benefit. Each reference is inserted with its own label, and it all goes through one chat completion on the Loader's model handle.
Every call passes through the pack's shared self-healing path: reset the context, clear the fork's _hybrid_cache_mgr state if present, and if the specific llama_decode failed error still surfaces, rebuild the model from the Loader's recorded build parameters and retry exactly once. Any other error is not swallowed. For Qwen3-family models - detected from the GGUF architecture header - a /no_think prefix goes ahead of your content, the actual mechanism for suppressing Qwen3's reasoning path here, since the installed create_chat_completion has no chat_template_kwargs passthrough.
Inputs that matter
vision_model comes from LC Vision Loader. prompt is your instruction; reference labels are added automatically for whatever you wire in. The default system_prompt is worth reading - it asks for the final answer only, no reasoning, no <think> blocks.
The settings that decide whether it works on your machine:
max_image_side(default 768) downscales any reference before sending. Oversized references were the direct cause of the "exceeding capacity" crashes in the original node this pack grew out of.0disables resizing, which you almost never want.jpeg_quality(default 90). If you're hitting capacity limits, lower this before you lowermax_image_side, and keep it in proportion withn_batchon the Loader.video_max_frames(default 8, up to 64) applies to the video reference only.style_tagpushes the response toward a stated visual style regardless of what the reference actually looks like: Realistic, Anime, Cartoon, Cinematic, Hentai, Fantasy. Handy when you're captioning a photo but want anime-framed wording out. It's an override, so don't be surprised when it overrides.
max_tokens, temperature, top_p, repetition_penalty and seed behave as you'd expect; the defaults (0.6 / 0.9 / 1.1) suit descriptive output. Output is one text string - into CLIPTextEncode, a save-text node, or your dataset pipeline.
Install
ComfyUI Manager, search ComfyUI LC Vision Nodes, or clone it:
cd ComfyUI/custom_nodes
git clone https://github.com/lonecatone23/ComfyUI_LC_Vision_nodes.git
__init__.py has to sit at the top of that folder. Restart ComfyUI - the console prints an LC Vision load line with four nodes. Model downloads live on the Loader node, not here. The dependency story is install.py, which resolves a vision-capable llama-cpp-python wheel for your exact Python/platform/CUDA combination from the JamePeng fork; those wheels aren't on PyPI, so a plain pip install -r requirements.txt can never find one.
One caveat that matters more for captioner nodes than most: custom nodes run arbitrary Python with your full OS access on import, and there's no sandbox. This pack is small and readable.
When it goes wrong
A capacity error on big inputs. Drop max_image_side, then jpeg_quality. Raising n_batch on the Loader helps too - but raise batch_max_tokens with it, or the vision handler's per-batch image limit becomes the new wall.
[LC Vision] Decode failed on a reused model instance (...); rebuilding and retrying once. in the console. Expected, not a bug. That's the self-heal catching the reused-model decode failure, and you should still get a real answer. Only worry if the retry fails too.
Repeated runs are slow. Known and documented: on some installs the rebuild fires on most reused calls, so back-to-back Caption runs cost about what a fresh load would.
Output starts with "Here is your description:" or similar. Unlike the Prompt Enhancer, this node has no leftover-planning retry pass - tighten the system_prompt instead. It's the highest-leverage field on the node.
Inputs (16)
| Name | Type | Default | Description |
|---|---|---|---|
| vision_model | LC_VISION_MODEL | Handle from LC Vision Loader. Stays resident across queues as long as the Loader's own inputs don't change. | |
| prompt | STRING | Describe this image. | The user instruction. Reference labels are added automatically for whatever's connected. |
| system_promptopt | STRING | You are a helpful vision assistant. Describe what you are shown clearly and concisely. Respond with the final answer only -- no reasoning, no <think> blocks. | Instruction that frames the whole response -- role, tone, and output format. Applies before the per-call prompt and reference images. |
| reference_image_1opt | IMAGE | Labeled <Reference 1> in the prompt. | |
| reference_image_2opt | IMAGE | Labeled <Reference 2> in the prompt. | |
| reference_image_3opt | IMAGE | Labeled <Reference 3> in the prompt. | |
| reference_videoopt | IMAGE | A frame-sequence batch, labeled <Target Video>. Sampled down to video_max_frames. | |
| video_max_framesopt | INT | 81β64 | Reference video frames are evenly sampled down to this count before sending -- sending every frame of a long clip would blow the token budget. |
| style_tagopt | COMBO | None | Pushes the response toward a specific visual-style description regardless of the reference's actual look -- useful when captioning a realistic reference but wanting an anime/cinematic/etc. framing out. 'None' leaves style unconstrained. |
| max_image_sideopt | INT | 7680β4096 | Downscale any reference longer edge to this before sending. 0 = no resize. Oversized references were the direct cause of the original node's 'exceeding capacity' crashes; keep this and n_batch (on the Loader) in proportion to each other. |
| jpeg_qualityopt | INT | 9010β100 | JPEG encoding quality for references sent to the model. Higher = better fidelity but a larger payload -- lower this before raising max_image_side if you're hitting capacity limits. |
| max_tokensopt | INT | 102416β16384 | Upper limit on generated tokens. Generation can stop earlier on its own; this only caps the ceiling. |
| temperatureopt | FLOAT | 0.600β2 | Sampling randomness. 0 = deterministic and literal, higher = more varied wording at some cost to focus. |
| top_popt | FLOAT | 0.900β1 | Nucleus sampling: only sample from the smallest set of tokens covering this cumulative probability. Lower = more focused, higher = more varied. |
| repetition_penaltyopt | FLOAT | 1.100.5β2 | Penalizes tokens the model has already used, to discourage repetitive or looping output. 1.0 = no penalty. |
| seedopt | INT | 00β4294967295 | Sampling seed. Same seed + same inputs should reproduce the same output, modulo hardware/threading nondeterminism. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| text | STRING | β |