Return 400 instead of 500 for unfetchable or unparseable multimodal inputs (#31417)
Co-authored-by: hnyls2002 <lsyincs@gmail.com> Co-authored-by: Liangsheng Yin <hnyls2002@gmail.com>
This commit is contained in:
co-authored by
hnyls2002
Liangsheng Yin
parent
161fffedfc
commit
32c30c0f96
@@ -69,6 +69,7 @@ from sglang.srt.server_args import (
|
||||
set_global_server_args_for_scheduler,
|
||||
)
|
||||
from sglang.srt.utils import (
|
||||
CLIENT_MEDIA_EXCEPTIONS,
|
||||
add_prometheus_middleware,
|
||||
configure_logger,
|
||||
load_audio,
|
||||
@@ -624,6 +625,9 @@ class MMEncoder:
|
||||
elif modality == Modality.AUDIO:
|
||||
return load_audio(data, self.model_audio_sr)
|
||||
|
||||
except CLIENT_MEDIA_EXCEPTIONS as e:
|
||||
# Not ValueError: the DP envelope classifies by `.code`, which only MMError carries.
|
||||
raise BadRequestError(f"Error while loading data {data}: {e}") from e
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"Error while loading data {data}: {e}")
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ from sglang.srt.managers.schedule_batch import (
|
||||
from sglang.srt.multimodal.processors.executor import MultimodalProcessorExecutor
|
||||
from sglang.srt.runtime_context import get_server_args
|
||||
from sglang.srt.utils import (
|
||||
CLIENT_MEDIA_EXCEPTIONS,
|
||||
envs,
|
||||
is_cpu,
|
||||
is_npu,
|
||||
@@ -652,8 +653,7 @@ class BaseMultimodalProcessor(ABC):
|
||||
elif modality == Modality.AUDIO:
|
||||
return load_audio(data, audio_sample_rate)
|
||||
|
||||
except ValueError as e:
|
||||
# Bad input (e.g. invalid base64) -> 400, not 500.
|
||||
except CLIENT_MEDIA_EXCEPTIONS as e:
|
||||
data_str = str(data)
|
||||
if len(data_str) > 100:
|
||||
data_str = data_str[:100] + "..."
|
||||
|
||||
@@ -86,7 +86,7 @@ import torch
|
||||
import torch.distributed as dist
|
||||
import triton
|
||||
from packaging import version as pkg_version
|
||||
from PIL import Image
|
||||
from PIL import Image, UnidentifiedImageError
|
||||
from starlette.routing import Mount
|
||||
from torch import nn
|
||||
from torch.library import Library
|
||||
@@ -1514,6 +1514,15 @@ def get_mm_http_session() -> requests.Session:
|
||||
return session
|
||||
|
||||
|
||||
# Raised by the loaders below when client-supplied media cannot be fetched or
|
||||
# decoded. ValueError is in the set because invalid base64 raises binascii.Error.
|
||||
CLIENT_MEDIA_EXCEPTIONS = (
|
||||
ValueError,
|
||||
UnidentifiedImageError,
|
||||
requests.exceptions.RequestException,
|
||||
)
|
||||
|
||||
|
||||
def load_audio(
|
||||
audio_file: str, sr: Optional[int] = None, mono: bool = True
|
||||
) -> np.ndarray:
|
||||
@@ -1582,10 +1591,13 @@ def load_audio(
|
||||
import torch
|
||||
import torchaudio
|
||||
|
||||
if isinstance(source, bytes):
|
||||
audio, original_sr = sf.read(BytesIO(source))
|
||||
else:
|
||||
audio, original_sr = sf.read(source)
|
||||
try:
|
||||
if isinstance(source, bytes):
|
||||
audio, original_sr = sf.read(BytesIO(source))
|
||||
else:
|
||||
audio, original_sr = sf.read(source)
|
||||
except sf.LibsndfileError as e:
|
||||
raise ValueError(f"Could not decode audio: {e}") from e
|
||||
|
||||
if mono and len(audio.shape) > 1:
|
||||
audio = np.mean(audio, axis=1)
|
||||
@@ -1785,7 +1797,13 @@ def load_video(video_file: Union[str, bytes, VideoData], use_gpu: bool = True):
|
||||
raise ValueError(f"Unsupported video input type: {type(video_file)}")
|
||||
|
||||
device = "cuda" if use_gpu else "cpu"
|
||||
return VideoDecoderWrapper(source, device=device)
|
||||
try:
|
||||
return VideoDecoderWrapper(source, device=device)
|
||||
except (ImportError, MemoryError):
|
||||
raise # missing backend / OOM is not a bad payload
|
||||
except Exception as e:
|
||||
# Broad on purpose: torchcodec raises RuntimeError, decord its own type.
|
||||
raise ValueError(f"Could not decode video: {e}") from e
|
||||
|
||||
|
||||
def sample_video_frames(video, *, desired_fps: int, max_frames: int) -> list[int]:
|
||||
|
||||
Reference in New Issue
Block a user