> ## Documentation Index
> Fetch the complete documentation index at: https://chatcli.edilsonfreitas.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Image Input (Vision)

> The model SEES images you attach — in chat, coder, agent and the gateway channels. Native multimodal on vision providers, with an automatic describe-fallback for the rest.

Beyond **generating** images, ChatCLI lets the model **see and understand** images you attach — and answer based on them. It works in **chat, `/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):

```text theme={"system"}
@file diagram.png what does this flow do?
@file ~/Downloads/error.jpg why does this stacktrace happen?
@file screenshot.png       # image only, no text, also works
```

Supported formats: **PNG, JPEG, GIF, WebP**. Local path or an image inside a directory. Images with the wrong extension are still detected by content (MIME sniff).

<Info>
  **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`](/tools/image-generation) tool.
</Info>

***

## `@view` — look at an image mid-task

`@file` attaches an image when **you** start a turn. But inside `/agent` and `/coder`, the model often produces a picture **itself** — a screenshot from [`@browser`](/tools/browser-automation), a rendered diagram — and needs to see it to keep going. That's `@view`:

```text theme={"system"}
@view {"cmd":"view","args":{"file":"/tmp/chatcli-browser/screenshot-1.png"}}
@view /tmp/shot.png          # bare path also works
```

`@view` loads a local image (**PNG, JPEG, GIF, WebP**) through the **same vision pipeline** as `@file` — size caps, compression, native multimodal or describe-fallback — and **stages** it so it is attached to the conversation on the **next turn**. The model then analyzes what is visible. It is **read-only** and available in `/agent` and `/coder` (in plain chat, use `@file`).

The natural pairing is with `@browser`: **screenshot a page, then look at it.**

```text theme={"system"}
@browser screenshot                         # capture the current page → returns a path
@view    /tmp/chatcli-browser/screenshot-*.png   # the model actually sees it
```

<Note>
  **PDFs are not supported yet** — render the relevant page to an image (PNG/JPEG) first, then `@view` it.
</Note>

***

## Hybrid strategy (B + A)

ChatCLI decides automatically, looking at the **active model's** `vision` 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).

No variable is required — `@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`):

1. **Override** `CHATCLI_VISION_INPUT=native|describe|off` — explicit control.
2. **Catalog** (`vision` capability) — authoritative for known models.
3. **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.
4. Otherwise → describe-fallback.

The heuristic matches explicit markers only, **never family prefixes** (which have text-only exceptions like `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:

| Dialect | Providers |
| - | - |
| OpenAI `image_url` | OpenAI, xAI, Z.AI, OpenRouter, Copilot, Moonshot, MiniMax, Bedrock-OpenAI |
| Anthropic blocks | Anthropic (API-key + OAuth), Bedrock-Claude, MiniMax-Anthropic |
| Gemini `inline_data` | Google Gemini |
| Bedrock SDK | Bedrock Converse |
| Ollama `images[]` | Ollama |
| Responses `input_image` | OpenAI Responses |

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`](/tools/image-generation)), it is attached automatically to the reply on photo-capable adapters.

| Variable | Purpose | Default |
| - | - | - |
| `CHATCLI_GATEWAY_IMAGE_REPLY` | Attach a generated/edited image to the reply: `auto` / `never` | `auto` |
| `CHATCLI_GATEWAY_MAX_IMAGE_BYTES` | Inbound image download cap (bytes) | 20 MB |

***

## Configuration

```text theme={"system"}
/config integrations          # shows CHATCLI_VISION_*, CHATCLI_GATEWAY_IMAGE_REPLY etc.
```

| Variable | Purpose | Default |
| - | - | - |
| `CHATCLI_VISION_INPUT` | Mode: `auto`/`native`/`describe`/`off` | `auto` |
| `CHATCLI_VISION_PROVIDER` | describe-fallback provider | (auto) |
| `CHATCLI_VISION_MODEL` | describe-fallback model | (auto) |
| `CHATCLI_VISION_COMPRESS` | Compress images before sending (`on`/`off`) | `on` |
| `CHATCLI_VISION_MAX_EDGE` | Longest-edge cap in px | `1568` |
| `CHATCLI_VISION_JPEG_QUALITY` | JPEG re-encode quality (1–100) | `82` |

***

## 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](/context/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.

Disable with `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)](/tools/image-generation) — the model **creates/edits** images
* [Chat Gateway](/gateway/chat-gateway)
* [Voice Replies](/gateway/voice-replies)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.