Multimodal input

Multimodal input

Three wire protocols accept media, but the model decides what it can read. Check
capabilities.modalities before sending, because a model that does not declare a
modality refuses it with 400 modality_not_supported before the request reaches a
provider.

curl -s https://hisoka.ai/api/v1/models \
  | jq '.data[] | {id, modalities: .capabilities.modalities}'
Modality Meaning
text every model
image screenshots, photos, diagrams
video clips, screen recordings
audio speech input, where the model takes it

Why refusing early matters

An unknown part forwarded to a provider came back as an HTTP 500, which reads as our
outage rather than your request. A modality the model does declare has a working path
through; one it does not is refused at the edge with a message naming the model’s real
capabilities.

Silent dropping is the failure this prevents: a clip the gateway discarded still produced
a normal 200, and the caller paid for a reply that never saw it.

Images

{
  "model": "glm-5.3-flash",
  "messages": [{
    "role": "user",
    "content": [
      { "type": "text", "text": "What is in this screenshot?" },
      { "type": "image_url", "image_url": { "url": "data:image/png;base64,iVBORw0..." } }
    ]
  }]
}

An https:// URL works as well as a data URL, where the provider supports fetching one.

Accepted spellings for the part type: image_url, image, input_image. The gateway
normalises all three, so a client using its own dialect does not produce a provider error.

Video

{
  "role": "user",
  "content": [
    { "type": "text", "text": "Describe the motion in this clip." },
    { "type": "video_url", "video_url": { "url": "https://example.com/clip.mp4" } }
  ]
}

Accepted spellings: video_url, video, input_video. Only send video to a model whose
capabilities.modalities includes video; that is a short list, and the catalogue is the
authority rather than a blog post.

Audio

Accepted spellings: input_audio, audio. The shape follows the provider’s own object,
so a client that already works against an OpenAI-compatible provider needs no change here.

Sizing a request

Images and video become prompt tokens, and prompt tokens are what you pay for. Two habits
keep the bill predictable:

  • Resize before sending. A 4096x4096 screenshot costs far more than the same screen at
    1920x1080, and the model reads both.
  • Send the frames you need, not the whole recording. A video contributes tokens per
    sampled frame.

A useful check before blaming a model for a bad answer: look at the usage.prompt_tokens
in the reply. A megabyte of base64 that produces double-digit prompt_tokens means the
media never arrived, and that is worth reporting with the request_id.

What is refused

Situation Answer
image sent to a text-only model 400 modality_not_supported
video sent to a model without video 400 modality_not_supported
malformed base64 provider 400, surfaced as invalid_request
media larger than the body limit 413 payload_too_large

The body limit is the gateway’s, not the model’s; shrinking the attachment is usually
faster than raising it.