/coder, /agent, one-shot (-p) and across the gateway channels (Telegram, WhatsApp, Slack, Discord, webhook).
How to attach
Point@file at an image — it detects the type and attaches it as vision input (not inlined as text):
Chat is tool-less by design — attaching an image in chat works (it’s an attachment, not a tool). To generate/edit an image, use
/coder or /agent with the @image tool.Hybrid strategy (B + A)
ChatCLI decides automatically, looking at the active model’svision capability in the catalog:
- Vision model (GPT-4o/4.1/5.x, Claude 3+/4.x, Gemini, Kimi, GLM, Bedrock Claude…) → the image goes natively, the model truly sees the pixels. (Path B.)
- Non-vision model → describe-fallback: a vision model describes the image and the text is folded into the prompt, so a text-only model can still reason about the content. (Path A.)
- No vision model available → a clear warning and the answer continues text-only (never breaks).
@file image just works. CHATCLI_VISION_PROVIDER/CHATCLI_VISION_MODEL only override the fallback captioner (e.g. gpt-4o-mini as a cheap captioner).
Off-catalog vision models
Models fetched from a provider’s/models API may have no catalog entry. The decision is layered (CHATCLI_VISION_INPUT):
- Override
CHATCLI_VISION_INPUT=native|describe|off— explicit control. - Catalog (
visioncapability) — authoritative for known models. - Conservative heuristic — if the id carries an unambiguous vision marker (
-vl,vl-,vision,pixtral,llava,internvl,qwen-vl,omni,multimodal), treat it as native. Those names exist only on multimodal models → near-zero false positives. - Otherwise → describe-fallback.
claude-3-5-haiku/o3-mini), so it never sends an image block to a model that would hard-error. Know your off-catalog model sees? CHATCLI_VISION_INPUT=native.
Per-provider coverage (native vision)
Image serialization is done by a shared helper, across 6 dialects covering the vision-capable providers:
The gate is generic:
catalog.HasCapability(provider, model, "vision"). Text-only API providers (e.g. StackSpot) automatically fall back to describe.
In the gateway (messaging channels)
Receive an image — send a photo on Telegram/WhatsApp/etc. and the gateway downloads it, then the configured model sees it (native or describe-fallback, same logic above). Image-only messages (no text) get a default analysis request. Send an image — if the agent generated/edited an image during the reply (via@image), it is attached automatically to the reply on photo-capable adapters.
Configuration
Image compression (before sending)
When native vision is active, ChatCLI shrinks the image before sending it to the model — keyless and pure-Go (no new dependency, no cgo). It is part of Context Compression:- Downscale the longest edge to
CHATCLI_VISION_MAX_EDGE(1568px). Providers already reduce larger images server-side to this size for token accounting, so this is token-equivalent — it just avoids uploading pixels that would be thrown away — and for images above the cap it lowers the billed vision tokens. - JPEG re-encode of photos (quality
CHATCLI_VISION_JPEG_QUALITY), far smaller than PNG, preserving transparency (alpha images stay PNG). - Safe by construction: never inflates the payload and leaves formats it cannot round-trip (animated GIF, WebP) untouched. URL-only images pass through unchanged.
CHATCLI_VISION_COMPRESS=off.
Notes
- Images cost prompt tokens (a large image can be worth hundreds/thousands of tokens). Confirm before attaching large batches on paid models.
- The image stays in history and is re-sent on subsequent turns (standard multimodal behavior).
- To force a specific fallback captioner:
CHATCLI_VISION_PROVIDER=openai CHATCLI_VISION_MODEL=gpt-4o-mini.
Related
- Image Generation & Editing (@image) — the model creates/edits images
- Chat Gateway
- Voice Replies