Prhub

#39780 [Bugfix] Reject empty tools array with HTTP 400

原始 PR 作者 jigangz 合并时间 2026-04-16 12:08 文件变更 4 提交数 9 评论 12 代码增减 +23 / -23

执行摘要

拒绝空 tools 数组并返回 400

Issue #39741 报告空 tools 数组被接受并返回 HTTP 200,违反了 OpenAI API 行为。PR 引用 OpenAI 返回 400 错误码的事实,旨在修复协议兼容性。

值得阅读 review 讨论中关于 OpenAI 行为演变的调查部分,展示了如何通过社区报告推断外部 API 变更。变更本身简单清晰,适合作为 API 兼容性修复的参考。

讨论亮点

OpenAI 行为变化调查:DarkLight1337 注意到原始 fallback 声称对齐 OpenAI 行为,jigangz 通过社区报告确认 OpenAI 在 2024 年末已改变行为,现在接受空数组时返回 400。

增强校验健壮性:gemini-code-assist[bot] 建议添加类型守卫,被作者采纳并集成到最终代码中。

用户质疑:steelannelida 评论 "So we're matching 15-month-old behavior which contradicts the written spec? Why?",未得到直接回应。

实现拆解

  1. 增加前置校验:在 check_tool_usage 方法开头添加防御性类型检查和空数组判断,若 tools[] 则抛出 ValueError
  2. 移除 fallback 逻辑:原 tool_choice="required" 且空 tools 数组时静默转化为 "none" 的代码因前置校验不可达而被删除。
  3. 更新主测试test_chat_completion_request_validations.py 中将空数组用例从断言 tool_choice="none" 改为断言抛出 ValueError
  4. 修复工具解析测试test_ernie45_moe_tool_parser.pytest_xlam_tool_parser.py 中移除构造请求时传入的 tools=[],因该值现在会被拒绝。
文件 模块 状态 重要度
vllm/entrypoints/openai/chat_completion/protocol.py 请求验证 modified 6.2
tests/tool_use/test_chat_completion_request_validations.py 验证测试 modified 4.46
tests/tool_parsers/test_ernie45_moe_tool_parser.py 工具解析测试 modified 3.42
tests/tool_parsers/test_xlam_tool_parser.py 工具解析测试 modified 3.42

关键符号

check_tool_usage

关键源码片段

vllm/entrypoints/openai/chat_completion/protocol.py core-logic

核心校验逻辑变更:新增空 tools 拒绝和防御性类型检查,移除旧的 fallback。

# 文件 : vllm/entrypoints/openai/chat_completion/protocol.py
# check_tool_usage 校验器的新版本@model_validator(mode='before')
@classmethod
def check_tool_usage(cls, data):
    # 防御性:传播前序校验器的 ValueError
    if isinstance(data, ValueError):
        raise data
    # 若非 dict(如已有实例传入),跳过校验
    if not isinstance(data, dict):
        return data
​
    # 新增:拒绝空 tools 数组,匹配 OpenAI 行为(返回 400)
    if data.get('tools') == []:
        raise ValueError(
            '`tools` must not be an empty array. '
            'Either provide at least one tool or omit the field entirely.'
        )
​
    # 原有逻辑:未指定 tool_choice 且提供了 tools 时默认为 'auto'
    if 'tool_choice' not in data and data.get('tools'):
        data['tool_choice'] = 'auto'
​
    if 'tool_choice' in data and data['tool_choice'] == 'none':
        return data
​
    # ... 其余 tool_choice 有效性校验(略)
tests/tool_use/test_chat_completion_request_validations.py test-coverage

核心测试更新:将空数组用例从接受改为拒绝。

# 文件 : tests/tool_use/test_chat_completion_request_validations.py
# 测试变更:空 tools 数组现在触发 ValueErrordef test_chat_completion_request_with_no_tools():
    # tools 键不存在 -> tool_choice='none'
    request = ChatCompletionRequest.model_validate({
        'messages': [{'role': 'user', 'content': 'Hello'}],
        'model': 'facebook/opt-125m',
    })
    assert request.tool_choice == 'none'
​
    # tools 键为 None -> tool_choice='none'
    request = ChatCompletionRequest.model_validate({
        'messages': [{'role': 'user', 'content': 'Hello'}],
        'model': 'facebook/opt-125m',
        'tools': None,
    })
    assert request.tool_choice == 'none'
​
    # 变更:空 tools 数组现在应被拒绝
    with pytest.raises(ValueError, match='must not be an empty array'):
        ChatCompletionRequest.model_validate({
            'messages': [{'role': 'user', 'content': 'Hello'}],
            'model': 'facebook/opt-125m',
            'tools': [], # 触发 ValueError
        })

评论区精华

OpenAI 行为是否已改变? 正确性

DarkLight1337 询问原始 fallback 的意图(对齐 OpenAI)与本次变更的矛盾。jigangz 调查后确认 OpenAI 在 2024 年末已改变行为,现在拒绝空 tools。

结论:确认 OpenAI 行为变化,新校验正确。 · 已解决

validator 防御性类型检查建议 设计

gemini-code-assist 建议增加 isinstance(data, ValueError) 和 isinstance(data, dict) 检查,并简化空列表判断。

结论:采纳建议,后续提交中添加。 · 已解决

对行为变更的质疑 question

steelannelida 评论 "So we're matching 15-month-old behavior which contradicts the written spec? Why and why now?"

结论:未得到回应,PR 已合并,用户可能需适应。 · unresolved

风险与影响

主要风险是向前兼容性:之前发送 tools: [] 以禁用工具的客户端在更新后会收到 400 错误,需要调整为不传入 tools 或显式指定 tool_choice: "none"。另外,两个工具解析测试中移除了 tools=[],若这些测试原本依赖该行为来测试特定代码路径,可能减少覆盖。整体风险较低。

影响使用 tool-calling 功能的用户和开发者。对 vLLM 系统本身无性能影响,属于请求验证层变更。对 API 兼容性是改进,降低与 OpenAI 的差异。

API 行为变更 空 tools 兼容性 测试覆盖调整

关联 Issue

#39741 [Bug]: Empty tools array accepted with HTTP 200, should return 400

完整报告

参与讨论