执行摘要
- 一句话:修复 Poolside 推理模板 thinking 默认值检测
- 推荐动作:该 PR 值得精读,尤其关注
_has_toggle_default_assignment 的实现方式:通过解析 Jinja AST 而不是正则,从根本上解决了模板语法干扰问题。同时,_is_poolside_v1 的签名重构也提供了一个“检测目标应基于不变特征而非可变默认值”的设计思路。
功能与动机
Issue #32536 指出 poolside/Laguna-S-2.1 和 poolside/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.content,reasoning_content 为空。
实现拆解
- 新增 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 %} 块。
- 修改 Poolside 模板签名检测:将
_is_poolside_v1 的判断依据从 enable_thinking 默认值切换为共享的工具调用 preamble(<tool_call>/<arg_key>/<arg_value>),确保缺少 XS 风格描述的 S-2.1 模板也能正确识别为 poolside_v1。
- 替换推理模式规则:在
REASONING_MODE_RULES 中新增基于 _has_toggle_default_assignment 的规则,覆盖 default(true) 和 default(false) 及其别名 d、boolean 参数等形式,替代原先只匹配单一赋值形态的正则规则。
- 添加异常保护:在
_has_toggle_default_assignment 中捕获 jinja2.TemplateError 和通用异常(如 RecursionError),解析失败时返回 False,避免服务器启动崩溃。
- 补充单元测试:在
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(模块 推理解析;类别 source;类型 core-logic;符号 _GenerationTagExtension, parse, _has_toggle_default_assignment): 核心模板检测逻辑,新增 _has_toggle_default_assignment 和 _GenerationTagExtension,修改 _is_poolside_v1 和推理模式规则,实现基于 AST 的 toggle 识别。
test/registered/unit/parser/test_template_manager.py(模块 测试覆盖;类别 test;类型 test-coverage;符号 test_poolside_v1_detects_variant_template_defaults, test_poolside_v1_laguna_s21_shaped_template, test_enable_thinking_default_filter_variants, test_pathological_template_does_not_raise): 新增 9 个测试用例,覆盖所有 default filter 变体、真实 S-2.1 形状模板、病态模板等,确保检测逻辑正确且不退化。
关键符号:_has_toggle_default_assignment, _GenerationTagExtension.parse, _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_v1 因 default(true) 检测而将真实 S-2.1 模板误判为非 Poolside,导致在 auto 模式下回退到 qwen3 解析器。
-
两参数 default() 排除过宽的问题 (correctness): Jiminator 采纳建议,在 commit 9600889 中切换为 Jinja Filter 节点解析,按语义分类所有组合,仅排除 default(true, true) 这种折叠形式。
- Laguna-S-2.1 模板签名误判导致 fallback 到 qwen3 解析器 (design): 通过将 Poolside 签名检测从 thinking 默认值切换为共有的工具调用 preamble(
<tool_call>/<arg_key>/<arg_value>)修复。
风险与影响
- 风险:主要风险在于 AST 解析可能引发异常(如病态嵌套模板导致 RecursionError),但已通过异常捕获和降级处理(返回 False)避免服务器启动失败。此外,新增的
_GenerationTagExtension 仅用于解析态,不影响运行时性能。整体回归风险低,但需确保所有已有模板检测规则不受新规则干扰——测试中包含了回归用例。
- 影响:直接影响使用
--reasoning-parser poolside_v1 的 poolside/Laguna-S-2.1 模型用户,修复后推理内容正确分离到 reasoning_content,content 不再包含 thinking 标签。对于 poolside/Laguna-XS-2.1 用户,行为不变。间接影响其他使用 Jinja default 过滤器设置 enable_thinking 的模板(如 gemma-4),现在也能被正确检测。团队可在下次发版时包含此修复。
- 风险标记:模板解析异常降级, AST 解析健壮性, 回归风险低, 仅影响启动时模板检测
关联脉络
- PR #32536 [Bug] poolside_v1 reasoning parser doesn't separate reasoning for Laguna-S-2.1 (thinking-on template default): 直接关联的 Bug 报告,描述相同问题并提供复现步骤。
- PR #24204 Add Laguna parser: 引入池侧解析器的原始 PR,本 PR 修正了其模板默认值检测不足。
- PR #31918 Add Laguna-S-2.1 cookbook: 添加 Laguna-S-2.1 cookbook 的 PR,与此 PR 涉及的模型对应。
参与讨论