Prhub

#34500 Fix tokenizer warning filtering for processors

原始 PR 作者 JustinTong0323 合并时间 2026-08-12 13:26 文件变更 2 提交数 1 评论 2 代码增减 +13 / -3

执行摘要

统一 tokenizer 警告过滤器安装,补齐 processor 路径

PR body 说明:远程 Kimi tokenizer 在正常 encode 调用时记录 Calling super().encode with {'add_special_tokens': False} 日志;原有抑制只在 AutoTokenizer.from_pretrained 之后安装,而通过 AutoProcessor 加载的多模态 tokenizer 绕过了该路径,导致警告仍然泄漏到用户日志。该问题对应 sglang issue #8082 中报告的日志噪音。

值得快速阅读,作为小范围 bugfix 的样板:它展示了如何用模块级单例加统一后处理入口来收敛跨路径的行为修补。没有需要深入研究的算法或架构,后续可在 tokenizer 加载链路补一个针对警告过滤行为的单元测试,防止回归。

讨论亮点

本 PR 没有 review 评论或实质设计争论。唯一活动是合并前的 CI 验证:Fridge003 发起 /rerun-test test/registered/models_e2e/test_kimi_k3_b300.py,GitHub Actions bot 返回 8-gpu-b300 全部通过,证明 Kimi K3 目标模型路径无回归。作者在 PR body 中强调的模块级单例过滤器幂等性,是这次实现里最值得注意的取舍。

实现拆解

  1. python/sglang/srt/utils/hf_transformers/tokenizer.py 中把 TokenizerWarningsFilter 实例化为模块级单例 _tokenizer_warnings_filter,并新增 _install_tokenizer_warnings_filter(tokenizer),按 tokenizer.__class__.__module__ 定位 logger 后添加过滤器。

  2. 移除 _auto_tokenizer_from_pretrained 内部内联的 addFilter 调用,改为在统一后处理入口 _apply_post_load_fixes 中调用安装函数,从而覆盖动态类加载、GGUF、fallback 等所有 get_tokenizer 出口分支。

  3. python/sglang/srt/utils/hf_transformers/processor.pyget_processor 中,在 TokenizersBackend 替换逻辑之后调用 _install_tokenizer_warnings_filter(tokenizer),补齐 AutoProcessor 加载的多模态 tokenizer 路径,并复用同一过滤器实例避免多次加载时过滤器累积。

  4. 配套验证:无新增测试文件;PR 通过 py_compilepre-commit,作者手动验证目标警告被压制、无关警告保留、重复安装幂等;Fridge003 触发 /rerun-test test/registered/models_e2e/test_kimi_k3_b300.py,8-gpu-b300 运行通过。

文件 模块 状态 重要度
python/sglang/srt/utils/hf_transformers/tokenizer.py 分词器 modified 6.22
python/sglang/srt/utils/hf_transformers/processor.py 处理器 modified 4.42

关键符号

_install_tokenizer_warnings_filter TokenizerWarningsFilter _apply_post_load_fixes get_processor

关键源码片段

python/sglang/srt/utils/hf_transformers/tokenizer.py core-logic

核心改动文件:抽取共享过滤器安装函数,移除 _auto_tokenizer_from_pretrained 中的局部安装,并统一在 _apply_post_load_fixes 中覆盖所有 get_tokenizer 分支。

# 用于压制 issue #8082 中报告的噪音日志:
# 远程 Kimi tokenizer 在普通 encode 调用时会打印类似
# Calling super().encode with {...} 的调试信息。
class TokenizerWarningsFilter(logging.Filter):
    def filter(self, record: logging.LogRecord) -> bool:
        # 只要消息中不包含目标文本就放行;其他警告不受影响。
        return 'Calling super().encode with' not in record.getMessage()
​
​
# 模块级单例:对同一 logger 重复 addFilter 同一个实例是幂等的,
# 避免 get_tokenizer / get_processor 多次调用时过滤器不断累积。
_tokenizer_warnings_filter = TokenizerWarningsFilter()
​
​
def _install_tokenizer_warnings_filter(tokenizer):
    # 挂到 tokenizer 类所在模块的 logger 上,作用域比 root logger 更精确,
    # 避免影响其他模块的正常 warn 输出。
    logging.getLogger(tokenizer.__class__.__module__).addFilter(
        _tokenizer_warnings_filter
    )
​
​
def _apply_post_load_fixes(tokenizer, tokenizer_name, revision):
    """应用所有加载后补丁并返回最终 tokenizer。"""
    # 所有 get_tokenizer 出口都经过这里,所以把过滤器安装集中到此,
    # 能覆盖动态类加载、GGUF、fallback 等之前遗漏的分支。
    _install_tokenizer_warnings_filter(tokenizer)
    _fix_v5_tokenizer_components(tokenizer, tokenizer_name, revision)
    _fix_v5_add_bos_eos_token(tokenizer, tokenizer_name, revision)
    # ... 其余 post-load 补丁逻辑保持原样(slow tokenizer 告警、mistral 补丁等)。
    return patch_tokenizer(tokenizer)
python/sglang/srt/utils/hf_transformers/processor.py core-logic

补齐 AutoProcessor 加载路径:get_processor 在 tokenizer 就位后调用共享安装函数,使多模态 tokenizer 不再绕过警告过滤。

# get_processor 节选:在 tokenizer 就位后统一安装警告过滤器。
# 此前该路径没有任何过滤,AutoProcessor 加载的多模态 tokenizer
# 会把远程 tokenizer 的调试日志原样打出来。
_install_tokenizer_warnings_filter(tokenizer)if tokenizer.chat_template is None:
    local_path = download_from_hf(
        tokenizer_name, allow_patterns=['*.json', '*.jinja', '*.model']
    )
    jinja_path = Path(local_path) / 'chat_template.jinja'
    if jinja_path.is_file():
        tokenizer.chat_template = jinja_path.read_text()
        logger.info('Loaded chat_template from %s', jinja_path)patch_mistral_common_tokenizer(tokenizer)
_fix_special_tokens_pattern(tokenizer)
_fix_added_tokens_encoding(tokenizer)
attach_additional_stop_token_ids(tokenizer)
return processor

评论区精华

Kimi K3 e2e 回归验证 other

Fridge003 发起 /rerun-test test/registered/models_e2e/test_kimi_k3_b300.py,GitHub Actions bot 返回 8-gpu-b300 全部通过。

结论:目标模型路径通过回归验证,随后合并。 · 已解决

风险与影响

过滤行为全局化:过滤器挂在 tokenizer 类所在模块的 logger 上,属于进程级全局抑制;任何模块出现相似文本 Calling super().encode with 都会被静默,可能掩盖其他真实问题。

加载路径行为变更:安装时机从局部 _auto_tokenizer_from_pretrained 移到 _apply_post_load_fixesget_processor,覆盖范围扩大但更统一;若 get_processor 中 tokenizer 为 None,访问 __class__ 会抛异常(但现有代码后续访问 chat_template 同样会失败,理论上该路径不会被触发)。

缺少自动化测试:本次未新增测试文件,仅靠人工日志检查与 Kimi K3 e2e 覆盖;后续重构 tokenizer 加载链路时该过滤行为可能再次丢失。

影响范围限定在日志输出与 tokenizer 加载工具链:Kimi 系列与多模态模型启动时不再打印多余警告,对用户和运维的日志可读性有正向改善;推理路径、请求处理与性能不受影响。对维护者而言,共享 helper 消除了 get_tokenizerget_processor 两条路径的重复实现,单例过滤器也避免了长运行服务中重复添加过滤器导致的内存与匹配开销。

全局日志过滤影响面 无自动化测试覆盖 加载路径行为统一变更

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论