Prhub

#53071 [Bugfix] Return HTTP 400 instead of 501 for unknown chat roles in DeepSeek encoders

原始 PR 作者 JC-ut0 合并时间 2026-08-20 17:23 文件变更 4 提交数 1 评论 5 代码增减 +29 / -2

执行摘要

DeepSeek 未知角色返回 400 而非 501

PR body 指出:DeepSeek V3.2/V4 chat-template 编码器对未知角色(如大写 SYSTEM)抛出 NotImplementedError,OpenAI serving 层将其映射为 HTTP 501,而未知角色属于客户端输入错误,应返回 HTTP 400。V3.2 路径自 #29837 引入,V4 路径自 #40860 引入,本次统一行为。

该 PR 值得精读,因为它展示了如何处理客户端输入错误与 HTTP 状态码的映射,以及如何通过测试保证行为一致性。值得关注的设计决策是异常类型与错误码的对应关系,以及参数化测试覆盖多个模块的做法。

讨论亮点

评审中 DarkLight1337 建议将错误消息从 "Unknown role" 改为 "Invalid role",作者 JC-ut0 同意并修改。其他评论为合并触发 CI 和 bot 辅助,无重大争议。

实现拆解

  1. 修改异常类型:在 vllm/tokenizers/deepseek_v32_encoding.py 和 vllm/tokenizers/deepseek_v4_encoding.py 的 render_message() 函数中,将 else 分支的 raise NotImplementedError(f"Unknown role: {role}") 改为 raise ValueError(f"Invalid role: {role}")。
  2. 调整错误消息措辞:根据 reviewer DarkLight1337 建议,将错误消息从 "Unknown role" 改为 "Invalid role",使语义更准确。
  3. 新增测试覆盖:
    • tests/test_request_input_bounds.py 新增 test_encode_messages_unknown_role_raises_value_error,参数化覆盖 deepseek_v32 和 deepseek_v4 编码模块,断言抛出 ValueError 且消息匹配 "Invalid role: SYSTEM"。
    • tests/tokenizers_/test_deepseek_v4.py 新增 test_deepseek_v4_unknown_role_raises_value_error,通过 apply_chat_template 包装验证同样行为。
  4. 测试验证:PR body 中手动验证了两个编码模块 standalone 均通过,且正常编码与 lowercase system 角色行为不受影响。
文件 模块 状态 重要度
tests/test_request_input_bounds.py 测试 modified 4.82
tests/tokenizers_/test_deepseek_v4.py 测试 modified 4.7
vllm/tokenizers/deepseek_v32_encoding.py 编码器 modified 4.66
vllm/tokenizers/deepseek_v4_encoding.py 编码器 modified 4.66

关键符号

render_message

关键源码片段

tests/test_request_input_bounds.py test-coverage

新增参数化测试覆盖 deepseek_v32 和 deepseek_v4 编码模块,验证未知角色抛出 ValueError,是行为验证的关键。

# tests/test_request_input_bounds.py
@pytest.mark.parametrize(
    "encoding_module",
    ENCODING_MODULES,
    ids=["deepseek_v32", "deepseek_v4"],
)
def test_encode_messages_unknown_role_raises_value_error(encoding_module):
    # 非法角色(如大写 SYSTEM)属于客户端错误,应抛出 ValueError
    # 以便 OpenAI serving 层映射为 HTTP 400,而不是 NotImplementedError(HTTP 501)。
    with pytest.raises(ValueError, match="Invalid role: SYSTEM"):
        encoding_module.encode_messages(
            [{"role": "SYSTEM", "content": "Hello"}],
            thinking_mode="chat",
        )
tests/tokenizers_/test_deepseek_v4.py test-coverage

新增测试通过 apply_chat_template 包装验证未知角色抛出 ValueError,覆盖了更深层调用链。

# tests/tokenizers_/test_deepseek_v4.py
def test_deepseek_v4_unknown_role_raises_value_error():
    # 非法角色属于客户端错误:必须以 ValueError 形式抛出(OpenAI serving 层映射为 HTTP 400),
    # 而不是 NotImplementedError(映射为 HTTP 501)。
    with pytest.raises(ValueError, match="Invalid role: SYSTEM"):
        _tokenizer().apply_chat_template(
            [{"role": "SYSTEM", "content": "Hello"}],
            tokenize=False,
        )
vllm/tokenizers/deepseek_v32_encoding.py core-logic

核心源码变更:将未知角色的异常从 NotImplementedError 改为 ValueError,并调整消息。

# vllm/tokenizers/deepseek_v32_encoding.py
def render_message(
    msg: dict[str, Any],
    index: int,
    messages: list[dict[str, Any]],
    thinking_mode: str,
    drop_thinking: bool,
    add_default_bos_token: bool,
    is_prefix: bool,
    last_user_idx: int,
) -> str:
    role = msg.get("role")
    # ... 省略其他分支处理逻辑 ...
    else:
        # 未知角色属于客户端输入错误,抛出 ValueError 让 serving 层返回 HTTP 400
        raise ValueError(f"Invalid role: {role}")

评论区精华

错误消息措辞建议 style

DarkLight1337 建议将错误消息从 "Unknown role" 改为 "Invalid role",并 @chaunceyjiang 参与。

结论:作者同意并修改,消息改为 "Invalid role"。 · 已解决

风险与影响

变更仅影响 render_message() 中对未知角色的异常类型,属于边缘路径修改,风险低。但需注意:此改动可能影响依赖异常类型的行为,例如调用方捕获 NotImplementedError 或依赖异常消息的测试或监控,需检查项目内是否有其他对 "Unknown role" 字符串的引用。

影响范围限于 DeepSeek V3.2/V4 编码器,对用户而言,发送未知角色请求时错误码从 501 变为 400,更符合语义;对系统而言,无性能或安全影响。团队需注意错误消息变更可能影响依赖字符串匹配的日志或监控。

错误消息变更可能影响依赖字符串的监控或日志

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论