Prhub

#26433 fix(tool_call): reland schema type normalization

原始 PR 作者 JustinTong0323 合并时间 2026-05-28 14:31 文件变更 3 提交数 1 评论 3 代码增减 +585 / -1

执行摘要

恢复并改进工具参数 JSON Schema 类型归一化

工具参数模式从数据库/ORM/本机工具导出时,常使用非标准 JSON Schema 类型名称(如 varcharenumint32)。若不归一化,Draft202012Validator.check_schema 会在请求到达模型之前拒绝这些模式,导致合法的工具定义无法使用。此 PR 恢复并改进了之前被还原的类型归一化逻辑。

此 PR 解决了工具模式验证中的一个实际兼容性问题,值得合并。建议关注后续可能出现的边缘类型处理请求。

讨论亮点

无实质技术讨论。审查者 ispobock 直接批准,仅有的评论是作者多次触发 CI 重试。

实现拆解

  1. 定义标准类型集与别名映射python/sglang/srt/function_call/utils.py):新建 _STANDARD_JSON_SCHEMA_TYPES_JSON_SCHEMA_TYPE_ALIASES 字典,将 DB/ORM 常用类型(如 varcharenum)映射到 JSON Schema 标准类型。

  2. 实现前缀匹配规则python/sglang/srt/function_call/utils.py):定义 _PREFIX_RULES 元组,利用 _matches_type_prefix 函数精确匹配参数化类型(如 int32list[str]),通过 _PREFIX_BOUNDARY_CHARS 确保前缀边界,防止误匹配(如 int 不匹配 internal)。

  3. 归一化单类型与类型列表python/sglang/srt/function_call/utils.py):_normalize_single_type 处理字符串类型,_normalize_type_list 处理数组类型(包括去重)。normalize_json_schema_types 递归遍历 Schema 结构,重写所有 type 字段。

  4. 集成到请求验证python/sglang/srt/entrypoints/openai/serving_chat.py):在 _validate_request 方法中,于 Draft202012Validator.check_schema 前调用 normalize_json_schema_types,并捕获 RecursionError 防止循环 Schema 导致服务器崩溃。

  5. 添加单元测试test/registered/unit/function_call/test_normalize_json_schema_types.py):验证常见别名、参数化类型、前缀边界、递归覆盖及去重逻辑,共 23 个测试用例,通过 register_cpu_ci 注册到 CI 套件。

文件 模块 状态 重要度
python/sglang/srt/function_call/utils.py 工具调用 modified 8.21
test/registered/unit/function_call/test_normalize_json_schema_types.py 测试 added 8.02
python/sglang/srt/entrypoints/openai/serving_chat.py 请求入口 modified 5.81

关键符号

_matches_type_prefix _normalize_single_type _normalize_type_list normalize_json_schema_types

关键源码片段

python/sglang/srt/entrypoints/openai/serving_chat.py dependency-wiring

集成点,在请求验证前调用归一化函数,并捕获循环引用异常。

# python/sglang/srt/entrypoints/openai/serving_chat.py (_validate_request 部分 )
# 导入新增 normalize_json_schema_types
from sglang.srt.function_call.utils import (
    get_json_schema_constraint,
    normalize_json_schema_types, # 新增
)# 在工具验证循环内
for i, tool in enumerate(request.tools or []):
    if tool.function.parameters is None:
        continue
    try:
        # 先归一化非标准类型,再标准验证
        normalize_json_schema_types(tool.function.parameters)
        Draft202012Validator.check_schema(tool.function.parameters)
    except SchemaError as e:
        return f"Tool {i} function has invalid 'parameters' schema: {str(e)}"
    except RecursionError: # 新增:防止循环 Schema 导致 500
        return (
            f"Tool {i} function 'parameters' schema is too deeply nested "
            "or contains a cycle."
        )

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

变更在模式验证路径中增加递归遍历,通过 RecursionError 捕获防循环,风险可控。性能影响:模式通常较小,归一化开销可忽略。兼容性:保留未知类型原样传递,不破坏现有模式。

影响所有通过 OpenAI 兼容 API 使用工具定义的请求。正常化后,更多非标准类型被接受,提升了与各类工具框架的兼容性。对不使用工具的场景无影响。

核心路径变更 递归深度已处理 兼容性影响仅限工具模式

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论