# PR #32540 完整报告

- 仓库：`sgl-project/sglang`
- 标题：fix(reasoning): honor Poolside template thinking defaults
- 合并时间：2026-07-30 05:02
- 原文链接：http://prhub.com.cn/sgl-project/sglang/pull/32540

---

# 执行摘要

- 一句话：修复 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` 为空。

# 实现拆解

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)` 及其别名 `d`、`boolean` 参数等形式，替代原先只匹配单一赋值形态的正则规则。
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`（模块 推理解析；类别 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 涉及的模型对应。