Prhub

#46457 Filter Pydantic-internal markers from validation error param

原始 PR 作者 muhammadfawaz1 合并时间 2026-06-23 21:20 文件变更 2 提交数 3 评论 4 代码增减 +115 / -2

执行摘要

过滤 Pydantic 内部标记,清理错误响应 param 字段

PR body指出,PR #46038添加的fallback在union类型或内部包裹字段上会泄露Pydantic-core的内部标记(如'body.function-wrap[log_extra_fields()].prompt'),让API消费者看到实现细节而不是干净的字段名。这是同一fuzz测试运行中暴露的问题,与#46415文件路径泄露无关。

值得精读。该PR展示了如何在不破坏功能的前提下有策略地过滤内部实现细节,其维护注释方法(区分结构自动检测和手动列表)对其他需要屏蔽内部标记的场景有参考价值。

讨论亮点

核心讨论是reviewer DarkLight1337询问“当Pydantic内部变更时如何更新这个列表?”作者回应已添加维护注释,说明括号段由正则自动处理,裸词列表需要手动维护,并建议通过fuzz易验证错误的端点来发现新标记。评论已关闭且PR被批准。

实现拆解

实现分为五步:

  1. 新增正则和内部标记集合:在server_utils.py中定义_BRACKETED_INTERNAL_RE(匹配方括号、花括号、圆括号)和_INTERNAL_LOC_MARKERS(frozenset,包含pydantic-core已知的schema种类标记如'function-wrap', 'str', 'int'等),并附上维护说明。
  2. 实现判断函数_is_internal_loc_segment(segment) - 如果segment含有括号字符或(将小写后)在内部标记集合中,则视为内部段。
  3. 实现清理函数clean_loc_for_param(loc) - 遍历loc元组,用_is_internal_loc_segment过滤掉内部段,保留真实字段名和列表索引。如果全部被过滤则回退到原始点连接,避免返回空字符串。
  4. 修改validation_exception_handler:在fallback分支中,将原来的"..".join(...)替换为调用clean_loc_for_param(loc)
  5. 添加单元测试TestCleanLocForParam类,使用参数化测试覆盖普通字段路径、列表索引、已知的function-wrap和union分支案例、全部内部段回退情况。
文件 模块 状态 重要度
vllm/entrypoints/serve/utils/server_utils.py 服务工具 modified 7.79
tests/entrypoints/serve/utils/test_server_utils.py 服务工具 modified 6.37

关键符号

_is_internal_loc_segment clean_loc_for_param test_strips_internal_markers test_all_internal_falls_back_to_raw_join

关键源码片段

vllm/entrypoints/serve/utils/server_utils.py core-logic

核心实现文件:新增了两个函数和两个常量,并修改了 validation_exception_handler 的 fallback 逻辑。所有过滤逻辑均在此文件中。

# vllm/entrypoints/serve/utils/server_utils.pyimport regex as re# 匹配任何包含方括号、花括号、圆括号的段(结构内部标记)
_BRACKETED_INTERNAL_RE = re.compile(r"[\[\]{}()]")# 已知的 pydantic-core 内部 schema 种类标记(裸词,不包括括号包裹的)
# 注意:这不是稳定 API,随 pydantic-core 升级可能需要更新。
# 更新方式见上方注释。
_INTERNAL_LOC_MARKERS = frozenset({
    "function-wrap", "function-after", "function-before", "function-plain",
    "json-or-python", "lax-or-strict", "chain", "default", "nullable",
    "tagged-union", "union", "call", "arguments", "is-instance", "is-subclass",
    "callable", "str", "int", "float", "bool", "bytes", "bytearray",
    "list", "tuple", "dict", "set", "frozenset", "complex", "none", "nonetype",
})def _is_internal_loc_segment(segment: str) -> bool:
    """判断segment是否为Pydantic内部标记段"""
    if _BRACKETED_INTERNAL_RE.search(segment):
        return True
    return segment.lower() in _INTERNAL_LOC_MARKERSdef clean_loc_for_param(loc: tuple) -> str:
    """将Pydantic错误loc元组清理为干净的param路径,丢弃内部标记"""
    parts = [str(p) for p in loc if not _is_internal_loc_segment(str(p))]
    if not parts:
        # 全部被过滤时回退到原始连接,避免空字符串
        return ".".join(str(p) for p in loc)
    return ".".join(parts)
tests/entrypoints/serve/utils/test_server_utils.py test-coverage

测试文件:新增 TestCleanLocForParam 类,包含 7 个测试用例,覆盖各种内部标记场景和全部内部段回退情况。

# tests/entrypoints/serve/utils/test_server_utils.pyclass TestCleanLocForParam:
    """验证clean_loc_for_param正确过滤Pydantic内部标记"""
​
    @pytest.mark.parametrize(
        "loc,expected",
        [
            (("body", "prompt"), "body.prompt"), # 普通字段路径,不变
            (("body", "messages", 2, "content"), "body.messages.2.content"), # 列表索引保留
            (("body", "function-wrap[__log_extra_fields__()]", "prompt"), "body.prompt"), # function-wrap 被过滤
            (("body", "stop", "str"), "body.stop"), # union 分支标记 str 被过滤
            (("body", "stop", "list[str]"), "body.stop"), # 括号段 list[str] 被过滤
            (("body", "prompt", "list[constrained-int]"), "body.prompt"), # constrained-int 括号段
        ],
    )
    def test_strips_internal_markers(self, loc, expected):
        assert clean_loc_for_param(loc) == expected
​
    def test_all_internal_falls_back_to_raw_join(self):
        """如果所有段都是内部标记,应回退到原始连接避免空字符串"""
        loc = ("function-wrap[__log_extra_fields__()]",)
        assert clean_loc_for_param(loc) == "function-wrap[__log_extra_fields__()]"

评论区精华

如何维护内部标记列表 documentation

DarkLight1337 询问如果 Pydantic 内部变更,如何更新 _INTERNAL_LOC_MARKERS 列表。

结论:作者添加了维护注释,说明括号段由正则自动处理,裸词列表需要手动通过 fuzz 端点发现并添加,并建议查阅 pydantic-core 的 Rust 源码作为权威参考。 · 已解决

风险与影响

主要风险:

  • _INTERNAL_LOC_MARKERS列表可能随Pydantic-core更新而过时,导致新的内部标记泄露。但PR已通过添加维护说明和正则兜底(检测括号段)来缓解。
  • 修改了param字段的输出内容,对依赖param精确值的客户端可能有兼容性影响,但此前泄露的内部标记本身就不是预期值,清理后更合理。
  • 总体风险较低,测试覆盖了关键路径。

影响范围:仅影响vLLM API服务中验证错误的param响应字段。对于使用/v1/chat/completions/tokenize等端点的客户端,param值不再包含pydantic内部标记,变得更加干净可读。message字段不受影响。无性能影响,改动仅涉及异常处理路径。

内部标记列表可能过时需要更新 param 字段输出变化影响客户端兼容性 全部内部段回退策略可能隐藏问题

关联 Issue

#46415 Sanitize server file paths from validation error responses

完整报告

参与讨论