Prhub

#32540 fix(reasoning): honor Poolside template thinking defaults

原始 PR 作者 Oxygen56 合并时间 2026-07-30 05:02 文件变更 2 提交数 6 评论 4 代码增减 +219 / -8

执行摘要

修复 Poolside 推理模板 thinking 默认值检测

Issue #32536 指出 poolside/Laguna-S-2.1poolside/Laguna-XS-2.1 使用相同的 poolside_v1 推理解析器,但聊天模板中 enable_thinking 的默认值相反(S-2.1 为 true,XS-2.1 为 false)。原有基于正则的检测仅识别 {% set enable_thinking = false %} 形式,导致 S-2.1 的 thinking 被关闭,推理内容泄漏到 message.contentreasoning_content 为空。

该 PR 值得精读,尤其关注 _has_toggle_default_assignment 的实现方式:通过解析 Jinja AST 而不是正则,从根本上解决了模板语法干扰问题。同时,_is_poolside_v1 的签名重构也提供了一个“检测目标应基于不变特征而非可变默认值”的设计思路。

讨论亮点
  • Reviewer (JustinTong0323) 指出两参数 default() 形式排除过宽:default(true, false) 等价于单参数 toggle,default(false, true) 仍保留 False→False/True→True 映射,不应排除。建议解析 Filter 节点并覆盖所有布尔/默认组合,仅排除折叠 toggle 的形式如 default(true, true)
  • Author (Jiminator) 在 commit 9600889 中采纳了建议,切换为 Filter 节点解析,并确认所有组合按语义分类:default(x, false)default(x)default(false, true) 视为默认关闭 toggle,仅 default(true, true) / boolean=true 折叠态被排除。同时修复了一个副作用:原 _is_poolside_v1default(true) 检测而将真实 S-2.1 模板误判为非 Poolside,导致在 auto 模式下回退到 qwen3 解析器。

实现拆解

  1. 新增 Jinja AST 解析函数:在 template_detection.py 中定义 _has_toggle_default_assignment(ctx, param, default),使用 jinja2.Environment.parse 构建 AST,搜索 Filter 节点匹配 default/d 过滤器。处理 boolean 参数语义,排除 default(true, true) 这种折叠 toggle 的写法。同时添加 _GenerationTagExtension 以支持 transformers 的 {% generation %} 块。
  2. 修改 Poolside 模板签名检测:将 _is_poolside_v1 的判断依据从 enable_thinking 默认值切换为共享的工具调用 preamble(<tool_call>/<arg_key>/<arg_value>),确保缺少 XS 风格描述的 S-2.1 模板也能正确识别为 poolside_v1
  3. 替换推理模式规则:在 REASONING_MODE_RULES 中新增基于 _has_toggle_default_assignment 的规则,覆盖 default(true)default(false) 及其别名 dboolean 参数等形式,替代原先只匹配单一赋值形态的正则规则。
  4. 添加异常保护:在 _has_toggle_default_assignment 中捕获 jinja2.TemplateError 和通用异常(如 RecursionError),解析失败时返回 False,避免服务器启动崩溃。
  5. 补充单元测试:在 test_template_manager.py 中添加 9 个新测试方法,覆盖变体默认值检测(test_poolside_v1_detects_variant_template_defaults)、真实 S-2.1 形状模板(test_poolside_v1_laguna_s21_shaped_template)、filter 变体(别名、空格、boolean 参数、注释/raw 块、if 表达式外使用)、病态模板不崩溃、折叠 default 不被误检以及工具调用解析器映射(test_poolside_s21_shape_resolves_poolside_tool_call_parser)。
文件 模块 状态 重要度
python/sglang/srt/parser/template_detection.py 推理解析 modified 8.17
test/registered/unit/parser/test_template_manager.py 测试覆盖 modified 7.31

关键符号

_has_toggle_default_assignment _GenerationTagExtension.parse _is_poolside_v1

分析完成后,这里会展示 LLM 生成的相对完整源码片段和详细注释。

评论区精华

两参数 default() 排除过宽的问题 正确性

JustinTong0323 评论指出 `_has_toggle_default_assignment` 最初版本直接用正则排除所有两参数形式,但 `default(true, false)` 等价于单参数 toggle,`default(false, true)` 仍保留语义,不应排除。建议解析 Filter 节点。

结论:Jiminator 采纳建议,在 commit 9600889 中切换为 Jinja Filter 节点解析,按语义分类所有组合,仅排除 `default(true, true)` 这种折叠形式。 · 已解决

Laguna-S-2.1 模板签名误判导致 fallback 到 qwen3 解析器 设计

Jiminator 在评论中报告:新增 `default(true)` 检测后,真实 Laguna-S-2.1 模板不再匹配 `_is_poolside_v1`,在 `auto` 模式下 fallback 到 `qwen3`,而 qwen 工具调用解析器无法读取 Poolside 的 XML-KV 格式。

结论:通过将 Poolside 签名检测从 thinking 默认值切换为共有的工具调用 preamble(`<tool_call>`/`<arg_key>`/`<arg_value>`)修复。 · 已解决

风险与影响

主要风险在于 AST 解析可能引发异常(如病态嵌套模板导致 RecursionError),但已通过异常捕获和降级处理(返回 False)避免服务器启动失败。此外,新增的 _GenerationTagExtension 仅用于解析态,不影响运行时性能。整体回归风险低,但需确保所有已有模板检测规则不受新规则干扰——测试中包含了回归用例。

直接影响使用 --reasoning-parser poolside_v1poolside/Laguna-S-2.1 模型用户,修复后推理内容正确分离到 reasoning_contentcontent 不再包含 thinking 标签。对于 poolside/Laguna-XS-2.1 用户,行为不变。间接影响其他使用 Jinja default 过滤器设置 enable_thinking 的模板(如 gemma-4),现在也能被正确检测。团队可在下次发版时包含此修复。

模板解析异常降级 AST 解析健壮性 回归风险低 仅影响启动时模板检测

关联 Issue

#32536 [Bug] poolside_v1 reasoning parser doesn't separate reasoning for Laguna-S-2.1 (thinking-on template default)

完整报告

参与讨论