执行摘要
- 一句话:结构化多模态错误响应与客户端断开处理
- 推荐动作:值得精读,展示了如何通过异常分层与请求状态检查来提升 API 的健壮性和客户端体验。
功能与动机
多模态请求中如 base64 格式错误等校验失败时,原先返回的是裸错误消息,不利于客户端结构化处理;此外流式请求中客户端断开连接会错误地触发 400 响应。PR 旨在统一响应格式并区分客户端错误与服务器错误。
实现拆解
- 异常区分:在
base_processor.py 的 _load_single_item 方法中,先捕获 ValueError(如无效 base64),重新抛出带上下文信息的 ValueError;原有 Exception 则抛出 RuntimeError 用于内部错误。
- 流式错误结构化:在
http_server.py 的 generate_request 流式分支中,捕获 ValueError 后先检查客户端是否断开,若是则记录并返回;否则返回结构化 JSON 包含 type: invalid_request_error、code: 400、retryable: false。
- 数据截断:在
base_processor.py 的两个异常路径中,将 data 截断为 100 字符后再放入错误消息,防止大 base64 导致日志膨胀。
- 非流式路径保持不变:非流式分支仍使用
_create_error_response,存在与流式路径的不一致。
关键文件:
python/sglang/srt/multimodal/processors/base_processor.py(模块 多模态处理器;类别 source;类型 core-logic;符号 _load_single_item): 核心多模态加载逻辑,区分 ValueError 与 RuntimeError,并截断错误消息中的 data。
python/sglang/srt/entrypoints/http_server.py(模块 HTTP 服务;类别 source;类型 core-logic;符号 stream_results, generate_request): 流式请求错误处理,检测客户端断开并返回结构化 400 错误。
关键符号:_load_single_item, stream_results, generate_request
关键源码片段
python/sglang/srt/multimodal/processors/base_processor.py
核心多模态加载逻辑,区分 ValueError 与 RuntimeError,并截断错误消息中的 data。
# python/sglang/srt/multimodal/processors/base_processor.py (head)
# _load_single_item 方法中异常处理部分的变更
try:
if modality == Modality.IMAGE:
img, _ = load_image(data, cls.gpu_image_decode)
if discard_alpha_channel and not isinstance(img, torch.Tensor) and img.mode != "RGB":
img = img.convert("RGB")
return img
elif modality == Modality.VIDEO:
return load_video(data, frame_count_limit)
elif modality == Modality.AUDIO:
return load_audio(data, audio_sample_rate)
except ValueError as e:
# 捕获 ValueError(如无效 base64),作为客户端错误抛出,确保调用方返回 4xx
data_str = str(data)
if len(data_str) > 100:
data_str = data_str[:100] + "..."
raise ValueError(f"Error while loading data {data_str}: {e}") from e
except Exception as e:
# 其他异常(加载失败等)作为 RuntimeError,标记为 5xx 内部错误
data_str = str(data)
if len(data_str) > 100:
data_str = data_str[:100] + "..."
raise RuntimeError(f"Error while loading data {data_str}: {e}") from e
python/sglang/srt/entrypoints/http_server.py
流式请求错误处理,检测客户端断开并返回结构化 400 错误。
# python/sglang/srt/entrypoints/http_server.py (head)
# generate_request 中流式分支的变更
async def stream_results() -> AsyncIterator[bytes]:
try:
async for out in _global_state.tokenizer_manager.generate_request(
obj, request
):
yield b"data: " + dumps_json(out) + b"
"
except ValueError as e:
# 客户端断开连接在此处也可能被捕获(流式取消),不是服务器错误或坏输入
if request is not None and await request.is_disconnected():
logger.info(f"[http_server] Client disconnected: {e}")
return # 不发送任何错误事件
# 这才是真正的客户端输入错误,返回结构化 400
out = {
"error": {
"message": str(e),
"type": "invalid_request_error",
"code": 400,
"retryable": False,
}
}
logger.error(f"[http_server] Error: {e}")
yield b"data: " + dumps_json(out) + b"
"
yield b"data: [DONE]
"
评论区精华
Review 中 gemini-code-assist[bot] 提出两个改进点:
- 截断
base_processor.py 中异常消息的 data 字符串,以防大 base64 导致日志和内存问题(高优先级)。此建议已在第二 commit 中实现。
- 统一流式与非流式错误响应格式,确保 API 一致性(中等优先级)。此建议未被采纳。
- 错误消息中 data 字符串截断 (performance): 已采纳,第二 commit 实现了 100 字符截断。
- 流式与非流式错误格式一致性 (design): 未采纳,此版本保持非流式路径不变。
风险与影响
- 风险:低风险。变更集中于错误处理路径,不影响正常请求流程。但非流式路径未同步更新,可能导致同一 API 流式与非流式错误格式不一致,对客户端解析造成轻微混淆。
- 影响:影响所有使用多模态功能(图像、视频、音频)的请求,特别是流式生成场景。客户端现在能收到结构化的 400 错误,方便自动化处理;同时客户端断开不再产生错误日志。
- 风险标记:错误处理路径变更, 缺少测试覆盖
关联脉络
参与讨论