Prhub

#43019 [Bugfix] Use shared coerce_to_schema_type in DeepSeekV32 tool parser

原始 PR 作者 sfeng33 合并时间 2026-05-20 22:21 文件变更 2 提交数 3 评论 4 代码增减 +270 / -285

执行摘要

复用共享工具重构 DeepSeekV32 参数转换

PR body 指出替换私有方法为共享 coerce_to_schema_type,并 supersedes #30797(object/array 类型转换)和 #36003(list 参数类型崩溃)。统一参数转换逻辑以减少重复并修复边界行为。

值得精读。本 PR 展示了如何通过提取共享工具函数消除重复代码并修复 bug,尤其关注 coerce_to_schema_type 函数的设计与 extract_types_from_schema 对多类型 schema 的处理。

讨论亮点
  • null 类型潜在崩溃: gemini-code-assist[bot] 指出当 param_type 为 None 时 coerce_to_schema_type 可能崩溃,建议添加 param_type or 'string'。sfeng33 回应 param_config[name].get('type', 'string') 已默认返回 'string',无需额外处理。该建议未被采纳,因默认值已足够。
  • 使用 extract_types_from_schema: yewentao256 建议使用 extract_types_from_schema 替代直接读取 param_config[name].get('type'),参考 minimax_m2_tool_parser。sfeng33 采纳并更新代码。

实现拆解

  1. vllm/tool_parsers/deepseekv32_tool_parser.py 导入中新增 coerce_to_schema_typeextract_types_from_schemafind_tool_properties
  2. 移除私有方法 _convert_param_value_checked_convert_param_value,消除手写类型转换与多类型回退逻辑。
  3. 重写 _convert_params_with_schema:使用 find_tool_properties 替代内联查找,通过 extract_types_from_schema 提取类型列表,调用 coerce_to_schema_type 统一转换。
  4. 在测试文件中删除直接针对私有方法的 TestConvertParamValue 类,改为在 TestExtractToolCalls 中新增 test_object_and_array_params 等集成测试,覆盖 object/array、number float、多类型 schema 及 null 语义。
  5. 测试文件简化 mock 设置,移除未使用的导入。
文件 模块 状态 重要度
vllm/tool_parsers/deepseekv32_tool_parser.py 工具解析 modified 7.42
tests/tool_parsers/test_deepseekv32_tool_parser.py 工具解析测试 modified 7.24

关键符号

_convert_param_value_checked _convert_param_value _convert_params_with_schema

关键源码片段

vllm/tool_parsers/deepseekv32_tool_parser.py dependency-wiring

核心源码文件,移除私有转换方法并引入共享工具函数,实现类型转换的统一。

## from vllm.tool_parsers.utils import coerce_to_schema_type, extract_types_from_schema, find_tool_properties
​
    def _convert_params_with_schema(
        self,
        function_name: str,
        param_dict: dict[str, tuple[str, str]],
    ) -> dict[str, Any]:
        """使用工具 schema 类型转换原始字符串参数值。"""
        ## 从 self.tools 中根据函数名查找参数配置
        param_config = find_tool_properties(self.tools, function_name)
​
        converted: dict[str, Any] = {}
        for name, (value, string_attr) in param_dict.items():
            ## string_attr 为 'true' 时保持字符串原样
            if string_attr == 'true':
                converted[name] = value
                continue
​
            ## 提取 schema 中的类型列表(支持多类型如 ['integer', 'null'])
            param_types = extract_types_from_schema(param_config.get(name, {}))
            ## 使用共享函数进行类型强制转换
            converted[name] = coerce_to_schema_type(value, param_types)
        ## 修复 'arguments'/'input' 包装器
        return self._repair_param_dict(converted, param_config)
tests/tool_parsers/test_deepseekv32_tool_parser.py test-coverage

测试文件,移除对私有方法的直接测试,新增集成测试覆盖 object/array、number、多类型 schema 等关键场景。

    def test_object_and_array_params(self):
        """Object/Array schema 类型必须被 JSON 解析,不能保留为字符串。"""
        ## 定义支持 object 类型参数的工具
        tool = ChatCompletionToolsParam(
            function=FunctionDefinition(
                name='update',
                parameters={
                    'type': 'object',
                    'properties': {
                        'location': {'type': 'object'},
                    },
                },
            ),
        )
        parser = make_parser(tools=[tool])
        ## 构造模型输出,模拟 string_attr='true' 场景
        model_output = (
            f'{FC_START}\n'
            f'{INV_START}update">\n'
            f'{PARAM_START}location" string="true">{"city":"Beijing"}{PARAM_END}\n'
            f'{INV_END}\n'
            f'{FC_END}'
        )
        result = parser.extract_tool_calls(model_output, None)
        assert result.tools_called
        args = json.loads(result.tool_calls[0].function.arguments)
        ## 验证 location 被正确解析为字典
        assert args == {'location': {'city': 'Beijing'}}

评论区精华

参数类型为 None 的潜在崩溃 安全

gemini-code-assist[bot] 指出当 param_type 为 None 时 coerce_to_schema_type 可能崩溃,建议添加 param_type or 'string' 保底。

结论:sfeng33 回应已有默认 'string' 值,无需更改。 · 已解决

使用 extract_types_from_schema 替换直接取 type 设计

yewentao256 建议使用 extract_types_from_schema 替代直接读取 param_config[name].get('type'),参考 minimax_m2_tool_parser 实现。

结论:sfeng33 采纳并更新代码,现在使用 extract_types_from_schema 获取类型列表。 · 已解决

风险与影响

  1. 行为变化风险: 共享函数 coerce_to_schema_type 对多类型 schema 的处理与旧私有方法可能略有不同(例如 ['integer', 'null'] 的尝试顺序),需确认兼容性。
  2. 回归风险: 参数类型解析逻辑完全替换,若共享函数有未发现的 bug 会影响 DeepSeekV32 解析。
  3. 测试覆盖: 新增测试覆盖主要边缘,但未覆盖超大浮点数、Unicode 文字等极端场景,风险较低。
  4. 依赖耦合: 引入 extract_types_from_schemafind_tool_properties 等工具函数,未来修改这些工具需同步考虑所有消费者。
  • 用户影响: 修复了 object/array 参数被误转为字符串的 bug,使工具调用更符合 JSON 用户预期。
  • 系统影响: 代码量减少约 50 行,重复逻辑消除,提升可维护性。参数类型转换行为与其他 parser(如 minimax_m2)保持一致。
  • 团队影响: 共享工具函数 coerce_to_schema_type 的设计模式可供新 parser 参考,降低后续开发难度。
私有方法移除 依赖共享工具行为 边缘边界覆盖

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论