Vision
Let a text-only model answer about images by having a vision model describe each one first.
3 min readUpdated for v2.4.0Edit on GitHub
vision_boonTurn it on
- 1
Mark the describer as vision-capable
On the describer model's page, add the
visiontag under Routing and save. The tag sets the model'ssupports_visionflag, which makes it selectable as a describer. Through the Management API, send"supports_vision": trueon the model. - 2
Turn Vision on and pick the describer
Open Settings › Boons, switch Vision on and Open its row. Pick the Describer model, and adjust Images per request, Describe timeout and the Describe prompt if you need to. Save with the save bar.
- 3
Grant it to the text-only model
On the text-only model's page, go to Boons, switch Vision on and save.
Or through the Management API:
curl -X PUT http://localhost:9180/api/v1/settings/boons \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"vision_enabled": true,
"vision_fallback_model": "glm-4-5v",
"vision_max_images": 6,
"vision_timeout_ms": 30000
}'
curl -X PUT http://localhost:9180/api/v1/models/$MODEL_ID \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "boons": ["vision"] }'
Send an image
Send an ordinary chat request with an image to the text-only model:
curl http://localhost/v1/chat/completions \
-H "Authorization: Bearer $OBLETH_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "minimax-m2-7-fast",
"messages": [{
"role": "user",
"content": [
{"type": "text", "text": "What does this error dialog say?"},
{"type": "image_url", "image_url": {"url": "data:image/png;base64,iVBORw0KGgo..."}}
]
}]
}'
The response carries x-obleth-boons: vision. The request log shows two rows: a vision_boon call against the describer and the chat call against the model you asked for.
Callers can see the grant too. The model's entry on GET /v1/models/{id} keeps supports_vision: false and adds boon_details.vision with active: true and the describer as helper_model.
What the model sees
Each image part is replaced, in place, by a text part:
[Image description: A dialog titled "Disk full" with the text "Cannot save report.pdf: 0 bytes free on /home" and two buttons, Retry and Cancel.]
The model never receives the image. It answers from the description, which by default asks the describer for all visible text verbatim, UI elements, code, diagrams, charts and layout.
Settings
| Settings label | Field | Default |
|---|---|---|
| Vision on | vision_enabled | false |
| Describer model | vision_fallback_model | none; "" clears it, which turns the boon off |
| Images per request | vision_max_images | 6 |
| Describe timeout | vision_timeout_ms | 30000 ms, per image |
| Describe prompt | vision_describe_prompt | built-in prompt asking for a faithful, complete description |
A 0 for either number keeps the stored value.
When it applies
The boon runs on a chat request only when all of these hold:
- Vision is on in Settings and a describer model is picked.
- The model has
visionin itsboonslist. - The model is not flagged
supports_vision. A model that sees images natively gets its images unchanged, even if it holds the boon. - The request contains at least one
image_urlcontent part.
A registered vision model is one option and the boon is the other: give a model that sees images the vision tag instead of the boon. For image, audio and embedding endpoints as first-class models, see Images, audio and speech.
Limits and failures
Images are described concurrently, so the added latency is about one describer call, not one per image. Only the first Images per request images are described.
Each image is handled on its own, and no image is ever passed on to the model. One that can't be described (the describer is missing, disabled, errors or times out, or the image is past Images per request) becomes a short note in its place, [An image was attached here, but it couldn't be described: <why>.], so the model can tell the user. The others keep their descriptions, and the request is never failed by the boon.
Billing
Each described image is a usage row against the calling tenant and key: model is the describer, admission is boon, request_type is vision_boon, and the cost comes from the describer's per-token prices and the tokens it reported. The main request is billed against the model you asked for, as usual. Both count toward term budgets.
