server : accept data: URLs for input_video and input_audio - #27735
Conversation
input_video and input_audio passed accept_base64_uri=false to handle_media(), so data: URLs got treated as raw base64 strings and failed later with a confusing media probe error (ggml-org#27724). pass true for these two content types the same way image_url already does, and allow video/audio mime types in the data: url check instead of image only. data URL validation now throws std::invalid_argument so malformed input comes back as 400 instead of 500, matching the other input validation in this file.
|
Hi @geckguy, 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. |
|
Hi @ngxson ! The gpu-vulkan-apple job failed on the Test step (build/clone passed), but this appears to be unrelated to my changes. Could you please re-run the failed gpu-vulkan-apple job when you get a chance? I believe it's a transient/flaky failure Thanks |
|
Thanks for the PR! Confirming this resolves the exact repro from #27724 on our side (2× V100, Qwen3.8-27B + mmproj): the |
|
/bot review |
Automated code reviewReviewed the diff in BlockingNone. The fix is correct and minimal: passing Will slow the review(point 1) The new (point 2) The MIME allowlist is now shared across all three input kinds. (point 3) No test covers the regression. Nits(point 4) The comment at (point 5) Pre-existing, not introduced by this PR: This review was generated automatically by pi coding agent using |
| out_files.push_back(decoded_data); | ||
| } | ||
|
|
||
| } else if (string_starts_with(url, "data:")) { |
There was a problem hiding this comment.
please confirm if bot's review is correct: this branch seems to be unreachable
There was a problem hiding this comment.
yes, the bot is right. that branch only runs if accept_base64_uri is false and the string starts with data:. after this PR all three callers pass true, so it never hits.
i added it as a safety net from the original issue, but it's redundant now. the data: parser above already rejects unknown mime types with the same 400.
happy to drop it.
| std::string url = json_value(input_video, "data", | ||
| json_value(input_video, "url", std::string())); | ||
| handle_media(out_files, url, opt.media_path, false); | ||
| handle_media(out_files, url, opt.media_path, true); |
There was a problem hiding this comment.
all call sites now use accept_base64_uri=true, so =false case is dead code now?
There was a problem hiding this comment.
yes. image_url already passed true, and this PR flipped audio/video to true as well, so nothing currently calls handle_media with false.
I originally left the parameter in place to keep the diff minimal, but since the false case is dead code now, I'll push an update to drop the parameter and remove the extra branch.
There was a problem hiding this comment.
dropped both. handle_media no longer takes accept_base64_uri, and the extra data: branch is gone. all three call sites just call handle_media(out_files, url, opt.media_path).
|
Could somebody review this? |
|
Traced the decode path and the diagnosis is exact: base64_decode stops at the first non-base64 character, so data:video/mp4;base64,... silently yields three bytes of garbage instead of failing, and the error only surfaces later at the mtmd probe. Routing everything through the single data: branch is the right call, and the compat endpoints get it for free since they normalize into image_url. Nit: the comment above that branch still says "try to decode base64 image" while it now handles video and audio too. Nit: one line in the existing parametrize of test_vision_api.py locks this in without needing a video model: |
|
Updated @ServeurpersoCom . Thanks for the review |
|
I've started the CI and am testing it at home. |
…27735) * server : accept data: URLs for input_video and input_audio input_video and input_audio passed accept_base64_uri=false to handle_media(), so data: URLs got treated as raw base64 strings and failed later with a confusing media probe error (ggml-org#27724). pass true for these two content types the same way image_url already does, and allow video/audio mime types in the data: url check instead of image only. data URL validation now throws std::invalid_argument so malformed input comes back as 400 instead of 500, matching the other input validation in this file. * server : simplify handle_media and drop unused accept_base64_uri flag * server : update comment and add unit test for invalid data URI MIME
…27735) * server : accept data: URLs for input_video and input_audio input_video and input_audio passed accept_base64_uri=false to handle_media(), so data: URLs got treated as raw base64 strings and failed later with a confusing media probe error (ggml-org#27724). pass true for these two content types the same way image_url already does, and allow video/audio mime types in the data: url check instead of image only. data URL validation now throws std::invalid_argument so malformed input comes back as 400 instead of 500, matching the other input validation in this file. * server : simplify handle_media and drop unused accept_base64_uri flag * server : update comment and add unit test for invalid data URI MIME
Overview
input_videoandinput_audiocontent parts were previously not handled asdata:URLs inhandle_media(), so a standard data: URL likedata:video/mp4;base64,...was decoded directly as raw base64. That decode produced garbage bytes, the media probe failed afterwards, and the request died with a misleading"Failed to load image or audio file"400 error (#27724).This PR extends
data:URL parsing inhandle_media()to acceptdata:image/,data:video/, anddata:audio/uniformly acrossimage_url,input_audio, andinput_video, and removes the now-redundantaccept_base64_uriflag. Malformed data URLs and unsupported MIME types throwstd::invalid_argumentso they return400 invalid_request_errorinstead of 500, matching the rest of the file.Additional information
Tested locally on an Apple Silicon Metal build with
ggml-org/SmolVLM2-500M-Video-Instruct-GGUFand a short MP4:input_videowith adata:URL decodes and the model answers correctly (previously 400 from input_video rejects data URLs: "Failed to load image or audio file" — data: URLs fall into the raw-base64 branch (accept_base64_uri=false) #27724)data:text/html;base64,...returns400 {"type":"invalid_request_error","message":"Invalid uri format: data:text/html;base64"}Kept the MIME check to an explicit image/video/audio allowlist rather than accepting arbitrary
data:*prefixes.Requirements