Ollama 이미지 형식은 OpenAI 형식 아님
이게 Ollama 두 번째로 흔한 footgun (NDJSON-vs-SSE 다음). Ollama는 이미지를 raw base64 문자열로, message에 붙은 images array에 받아. data:image/png;base64, prefix는 붙이지 마. image_url 객체를 담은 content array 모양도 안 통하고.
- Ollama:
{"role": "user", "content": "describe this", "images": ["iVBORw0KGgoA..."]} - OpenAI:
{"role": "user", "content": [{"type": "text", "text": "describe"}, {"type": "image_url", "image_url": {"url": "data:image/png;base64,iVBORw0KGgoA..."}}]}
OpenAI 모양을 그대로 Ollama의 native /api/chat에 복사해 넣으면 조용히 텍스트만 가지고 답하는 게 아니라 HTTP 400이 요란하게 떨어져. json: cannot unmarshal array into Go struct field Alias.content of type string. Ollama는 Message.Content를 그냥 문자열로 선언해 놔서 content array는 아예 해독이 안 돼. data: prefix도 똑같이 요란하게 떨어지고. images 항목은 raw base64 바이트로 해독되니까 data:image/png;base64,…는 illegal base64 data at input byte 4가 나와.
두 endpoint가 원하는 모양이 정반대고, 서로 상대방 모양을 거절해:
| Endpoint | 원하는 모양 | 반대 모양을 주면 |
|---|---|---|
/api/chat (native) | images: ["<맨 base64>"] | 400 — cannot unmarshal array into … content of type string |
/v1/chat/completions (OpenAI 호환) | image_url.url = "data:image/png;base64,…"를 담은 content array | 400 — 맨 base64 문자열엔 invalid image input |
이 footgun의 좋은 소식이 그거야. 어느 쪽으로 틀리든 에러 본문이 어느 필드가 터졌는지 말해 줘. 찍지 말고 그걸 읽어.
보내기 전에 resize
Local vision 모델은 보통 max input 차원이 정해져 있어 (~896에서 ~1568 px 사이, 모델마다 달라). Encoding 전에 long edge를 ~1024 px로 줄여. 그보다 큰 이미지는 내부에서 downscale돼서 encode를 낭비하거나, context limit에 걸려.