Fix dead image branch in OpenAICompatible._conversation_to_list#1944
Fix dead image branch in OpenAICompatible._conversation_to_list#1944chuenchen309 wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
There is definitely a confirmed bug here, the initial code here conflated the chat/completions API and responses API and is in a weird limbo. #1806 targets at adding support for the responses API. For this PR it would be good to focus on support for chat completions and specifcially on getting the format right for image_url type inputs
Testing suggests this is incomplete, the text input need to be "type: "text" and the image needs to be "type": "image_url" with a url entry that contains the base64 data per the error code returned by OpenAI:
Error code: 400 - {'error': {'message': "Invalid value: 'input_text'. Supported values are: 'text', 'image_url', 'input_audio', 'refusal', 'audio', and 'file'.", 'type': 'invalid_request_error', 'param': 'messages[0].content[0].type', 'code': 'invalid_value'}}
Error code: 400 - {'error': {'message': "Invalid value: 'input_image'. Supported values are: 'text', 'image_url', 'input_audio', 'refusal', 'audio', and 'file'.", 'type': 'invalid_request_error', 'param': 'messages[0].content[1].type', 'code': 'invalid_value'}}
Note even after making these adjustments consumer OpenAI endpoints are returning an error when testing with the visual_jailbreak probe:
{'error': {'message': 'Invalid content type. image_url is only supported by certain models.', 'type': 'invalid_request_error', 'param': 'messages.[0].content.[1].type', 'code': None}}
If there is a model that can be hosted via VLLM and fully supports this chat API format that may be the path to getting this validated as working.
|
Thanks — you're right, and thank you for testing it against a live endpoint. I kept the original Pushed: the image branch now emits the chat/completions shape — On your last point — I have no way to verify against a live endpoint or a VLLM-hosted vision model from here, so I can't confirm the |
dbb9f92 to
778ba9c
Compare
The image-modality branch tested `if "image" in turn.content.data_type`,
but data_type is a (mimetype, encoding) tuple from mimetypes.guess_type,
e.g. ("image/gif", None). Membership on a tuple checks element equality, so
"image" in ("image/gif", None) is always False. The image branch was
therefore never taken: every image turn fell through to the audio elif
(which fails on the mimetype subtype) and then to the else, raising
GarakException("Data type image/gif not supported."). This broke all
image/multimodal probes (e.g. visual_jailbreak) against OpenAI-compatible
vision generators.
Test the mimetype string via data_type[0], mirroring the audio branch two
lines below. Also add the missing comma in the base64 data URI
("...;base64," per RFC 2397), which the now-reachable branch would otherwise
emit malformed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Andrew Chen <48723787+chuenchen309@users.noreply.github.com>
Per review: input_text/input_image are the responses API spelling, which
chat/completions rejects with 400 invalid_value ("Supported values are:
'text', 'image_url', ..."). Support for the responses API is NVIDIA#1806's scope.
Switch the image branch to the chat/completions shape -- {"type": "text"}
plus {"type": "image_url", "image_url": {"url": ...}}, where image_url is an
object rather than a bare string. The neighbouring audio branch already emits
{"type": "text"}, so this also makes the two branches consistent.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Andrew Chen <48723787+chuenchen309@users.noreply.github.com>
778ba9c to
784add6
Compare
|
Pushed — the branch now uses the chat/completions spellings you gave ( Your 400s also surfaced a second bug in that branch: the data URI was built as For the nesting I checked the pinned SDK rather than going from memory — One thing I can't close out: I have no access to a model that accepts Also flagging one thing I deliberately left alone: an unknown mimetype ( |
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Andrew Chen <48723787+chuenchen309@users.noreply.github.com>
784add6 to
a8f1fc7
Compare
The image-modality branch in
OpenAICompatible._conversation_to_listis unreachable:data_typeis a(mimetype, encoding)tuple frommimetypes.guess_type(seeattempt.py), e.g.("image/gif", None). Membership on a tuple checks element equality, so"image" in ("image/gif", None)is always False. The image branch is therefore never taken — every image turn falls through to the audioelif(whose pattern fails on the mimetype subtype) and then to theelse, which raises:So all image/multimodal probes (e.g.
visual_jailbreak) crash against OpenAI-compatible vision generators (gpt-4o, and anyOpenAICompatiblesubclass such as NIM/Groq). The audio branch two lines below already does this correctly withdata_type[0].Fix
if "image" in turn.content.data_type[0]:(mirroring the audio branch).data:{mime};base64,{data}per RFC 2397. The now-reachable branch previously built;base64{data}(no comma), which is a malformed data URI.Scope kept minimal — the
"input_text"/"input_image"payload keys are left as-is.Verification
test_conversation_to_list_image_turn_is_reachable: an image turn (using the existingtests/_assets/tinytrans.gif) must produce aninput_imagecontent part with a well-formeddata:image/gif;base64,...URI instead of raisingGarakException. Fails before the change (raisesGarakException), passes after. Fulltest_openai_compatible.pypasses;black --checkclean.garak -t <target_type> -n <model_name>— not run: this is a unit-level fix and I have no live target configured; verified via the tests above rather than ticking a box I didn't exercise.Disclosure: this contribution was prepared with the assistance of an AI agent (Claude Code). The analysis, fix, and test were reviewed by me before submission.