Repository navigation
Conversation
|
Hi @Fino-wind, thanks for your contribution! Per our contribution guidelines, the automated PR checker found the following issue(s) that need your attention:
Please note that maintainers reserve the right to make final decisions on PRs. If you believe there is a mistake, please comment below. |
|
I don't think the statement that chat templates cannot render media originating from a We have a concrete counterexample in our Qwen3.8/Codex setup: In our Responses converter, The resulting That is also what the current branch diff implements. On the template side, the companion Qwen3.8/Codex template accepts both
The important part is that:
is executed for every message before role-specific handling: The template then handles There is an important distinction here: Qwen's template ultimately projects tool responses into its own user-side ChatML framing. So I am not claiming that the final serialized prompt literally contains a native The point is that this projection happens in the model-specific template, while the Responses -> Chat Completions conversion preserves the tool association of the content. That separation seems architecturally cleaner to me. The image can remain semantically where it originated: in the tool result. The protocol converter does not need to turn part of that result into an additional standalone That is also the relevant difference compared with the approach in #27958:
To be clear, this does not show that all existing llama.cpp chat templates already support multimodal tool content. Some may not. It does show that multimodal content originating from a tool result is technically workable without requiring the generic protocol conversion layer to rewrite that content into a separate user turn. For that reason, I would prefer keeping In other words: the protocol converter should preserve the semantic message structure and tool association; the model-dependent projection belongs in the template. |
In this case I would say that the error message below is too general, and therefore misleading as to the source of the bug:
I believe it should say something like |
The Responses API's function_call_output only accepts input_text, so returning an image from a tool gives a 400. This breaks Codex when it uses the vision capability of a local model. Convert input_image to image_url and keep it inside the tool message. This works because oaicompat_chat_params_parse() handles image_url the same way for every message role - it extracts the media into out_files and replaces it with a media marker, so the chat template never sees the media itself. Same approach as ggml-org#22536 for the Anthropic path.
80868a7 to
7aa1f58
Compare
The Responses API's
function_call_outputonly acceptsinput_text, so returning an image from a tool gives a 400:This breaks Codex when it uses the vision capability of a local model - every screenshot step fails. Input messages already accept
input_imagea few lines above in the same file, so it is only the tool output path that is missing it.The fix is to convert
input_imagetoimage_urland keep it inside the tool message.This works because
oaicompat_chat_params_parse()handlesimage_urlthe same way for every message role - it extracts the media intoout_filesand replaces it with a media marker, so the chat template never sees the media itself. This is the same approach as #22536, which already does this for the Anthropic -> OpenAI path.Testing
Local CUDA build, Qwen3.8-27B-Q8_0 + mmproj-model-bf16, sending a real 50 KB JPEG screenshot as
input_imageinsidefunction_call_output.outputas a plain stringshould be 'Input text' or 'Input image'In the two image cases the model describes what is actually in the picture ("a pixel-art game scene, rocky pillars, a signpost, a Poke Ball near the water"), so the image reaches the vision encoder rather than only passing validation.
The converted request keeps 3 messages (user / assistant / tool) with the image inside the tool message content array, no extra user message:
Requirements