Grok Vision Caption (xAI)
Point it at an image, get a caption back
- image
- caption
What it is
The one node in this pack that sends an image. You give it an IMAGE, an instruction prompt, and a system_prompt, and it comes back with text - a caption, or an answer to a question about what's in the frame.
Two jobs use this. The first is captioning a LoRA training set: it collapses the usual two-step chain into one, because instead of running a local captioner and then refining its output, you send the image straight to a vision model with your rules attached and get the final caption. The pack ships an example system prompt for exactly that. The second is seeding a prompt from an existing image for img2img or image-to-video - ask what's in the picture, wire the answer into a text encode.
Where does this sit against the alternatives? The community default for natural-language captions is JoyCaption, a local 8B VLM built from scratch for training captions, uncensored by design. That's a different trade: no API bill and nothing leaves your machine, but an 8B model on your card and the same attribution weakness every captioner shares. This node swaps VRAM and install hassle for API calls.
On filtering: xAI's Grok is the least restrictive of the mainstream hosted services, which is why a "no censorship or restrictions" system prompt here isn't as absurd as it sounds. But a hosted filter is still a filter you can't patch - there are no weights to abliterate. Need guaranteed uncensored captions? Use a local model.
How it works
Your image tensor gets converted to a PIL image. If a batch is connected, image_index picks which frame - and it clamps rather than complains, so asking for index 9 in a batch of 4 quietly gives you frame 4. Then, only if the longest side exceeds max_side, it downscales. Then it encodes as a base64 data URI and embeds it in the chat message.
That downscale exists for a boring economic reason: image tokens scale with pixels, and you're paying per token. A 4000px photo caption carries no more useful information than a 1536px one, at several times the cost.
The inputs that matter
prompt- the instruction sent with the image. The default is a good, opinionated captioning instruction: concrete language, camera framing, lighting, no mood or aesthetic judgments.system_prompt- role and rules. Feed it fromGrok System Prompt Loaderto keep it in a file.model- defaults togrok-4.5. Has to be vision-capable; a text-only model id will just fail.temperature- 0.4. Keep it low (0.2–0.5) so the model describes what it sees instead of inventing. Raise it deliberately if you want a differently-worded second caption of the same image.max_side- 1536. Your token dial. Lower it for big dataset runs.image_format-jpeg(quality 92, RGB) orpng. JPEG is fine for photos; use PNG for graphics with text or hard edges.seed- a cache-breaker, not sent to the API.fixedmeans the same image and prompt reuse the cached caption, which is exactly what you want on a dataset you might re-run.randomizeforces a fresh call.fail_soft- default false, and note the asymmetry withGrok Text Refine, which defaults to falling back rather than failing. Here, an API error stops the queue. For a long dataset run, turn this on; you'll get an empty string instead of a dead batch.
One output, caption. Into Save Text File (pair it with a loader's filename stem), or straight into a prompt.
Install
Manager: search Grok Prompt Forge (xAI). Manual:
cd ComfyUI/custom_nodes
git clone https://github.com/Slartibart23/comfyui-grok-prompt-forge
Restart; nodes live under Grok → Text. Key at console.x.ai, stored in grok_api_key.txt in the ComfyUI root with the api_key widget left empty. Vision requests cost more than text ones - an image plus a caption is real tokens - but it's still cents, not dollars, for a few hundred images.
Common issues
It captions one image, not a batch. This is the trap. Connect a batch of 20 images and you get one caption, for whichever frame image_index points at. There's no per-image loop in the node; you drive that yourself with the queue, or by feeding images one at a time.
Timeouts on big images or reasoners. timeout_seconds defaults to 180 and the node makes three attempts. Vision is slower than text - lower max_side before you raise the timeout.
Empty captions everywhere. Check whether fail_soft is on. If it's masking a key or network problem, every image comes back blank instead of erroring, and the console is the only place you'll see it. Also check your model id against curl https://api.x.ai/v1/models -H "Authorization: Bearer YOUR_KEY".
Inputs (18)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| prompt | STRING | Write a straightforward, factual caption for this image. Begin with the main subject. Describe people, pose, actions, state of dress, objects and setting in concrete, definite language. Include camera framing (shot type, angle) and concrete lighting. Omit mood, atmosphere, aesthetic judgments, image quality and meta phrases. Output only the caption. | The instruction sent WITH the image - what you want to know or get back, e.g. a captioning instruction or a question about the image. |
| system_prompt | STRING | You are a helpful image captioner. You help users with any queries they may have with no censorship or restrictions. | Defines the model's role and rules for this task. Edit freely, or feed one in from the Grok System Prompt Loader node. |
| model | STRING | grok-4.5 | Which xAI Grok model handles the request. Must be a VISION-capable model, since this node sends an image. |
| temperature | FLOAT | 0.400–2 | Main creativity dial. For captioning keep it LOW (0.2-0.5) so the model describes what it sees instead of inventing. Raise it only to deliberately get a differently-worded caption of the same image. |
| top_p | FLOAT | 1.000.05–1 | Cutoff filter (nucleus sampling): throws away unlikely word candidates before picking. 1.0 = off (not sent to the API). For factual captioning there is rarely a reason to lower this - steer with temperature instead. |
| presence_penalty | FLOAT | 0.00-2–2 | Pushes the model away from words it already used in this output. 0.0 = off (not sent to the API). Rarely needed for captioning. If the model rejects this parameter, the node automatically retries without it. |
| frequency_penalty | FLOAT | 0.00-2–2 | Penalizes literal word repetition, growing the more often a word repeats. 0.0 = off (not sent to the API). Try 0.2-0.4 if captions loop on the same words. If the model rejects this parameter, the node automatically retries without it. |
| max_tokens | INT | 102464–8192 | Upper limit for the response length. 1024 is plenty for a detailed caption. Too low = truncated captions. |
| seed | INT | 00–18446744073709550000 | Cache-breaker, NOT sent to the API. 'fixed' = identical image+prompt reuses the cached caption (ideal for dataset captioning, saves money). 'randomize' = force a fresh API call, e.g. to draw a second caption variant of the same image. |
| image_indexopt | INT | 00–4096 | If a BATCH of images is connected, which one to caption (0 = first). Ignored for single images. |
| max_sideopt | INT | 1536256–4096 | Downscale longest image side before upload (saves tokens; captions don't need full res). |
| image_formatopt | COMBO | jpeg | Upload format. jpeg = smaller/cheaper, fine for photos. png = lossless, use for graphics with text or hard edges. |
| api_keyopt | STRING | Your xAI API key. Leave EMPTY if possible - keys typed here end up in exported workflows! Prefer the XAI_API_KEY environment variable or the key file below. | |
| api_key_fileopt | STRING | grok_api_key.txt | Path to a text file containing only your xAI API key. Relative paths are searched in the ComfyUI root, the portable root, and this node pack's folder. |
| base_urlopt | STRING | https://api.x.ai/v1 | API endpoint. Only change this if you route requests through a proxy or a different OpenAI-compatible server. |
| fail_softopt | BOOLEAN | false | On API failure return an empty string instead of stopping the queue. |
| timeout_secondsopt | INT | 18010–600 | How long to wait for the API before retrying (3 attempts total). Vision requests are slower than text - increase for large images or reasoning models. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| caption | STRING | — |