Skip to content

[WIP] feat: support xgrammer#803

Closed
wanglining97 wants to merge 1 commit intomainfrom
feature/support_grammer
Closed

[WIP] feat: support xgrammer#803
wanglining97 wants to merge 1 commit intomainfrom
feature/support_grammer

Conversation

@wanglining97
Copy link
Copy Markdown
Collaborator

No description provided.

@wanglining97 wanglining97 requested a review from LLLLKKKK as a code owner March 19, 2026 06:51
@wanglining97 wanglining97 changed the title feat: support xgrammer [WIP] feat: support xgrammer Mar 19, 2026
@LLLLKKKK
Copy link
Copy Markdown
Collaborator

🤖 AI Code Review — PR #803 [WIP] feat: support xgrammer

概述

为 RTP-LLM 引入 xgrammar 支持结构化输出(constrained decoding)。改动贯穿 Python 前端 → pybind → C++ Engine → Stream → Sampler 全链路,共 20 文件,+248/-54。

核心流程:Python 端编译 grammar dict → 通过 pybind 传入 EngineInitParams → NormalEngine 持有并传给每个 Stream → NormalBatchStreamProcessor 组装 per-row grammar → Sampler 中 apply bitmask 到 logits。

建议改进

P0 — 正确性 / 性能严重问题

1. applyXGrammarCheck 每步重建 Matcher 并 replay 全部 token — O(n²) 复杂度

rtp_llm/cpp/models/Sampler.cc applyXGrammarCheck

每个 decode step 都为每个 batch row 创建新的 GrammarMatcher,然后从 input_lengths[i]sequence_lengths[i] replay 所有已生成 token。生成 N 个 token 的总 accept_token 调用次数为 O(N²)。对于长序列会导致严重性能退化。

建议:将 GrammarMatcher 作为有状态对象缓存在 stream 中,每步只 accept 新生成的 token。

2. GIL 在采样热路径中被持有

rtp_llm/cpp/models/Sampler.cc applyXGrammarCheck

通过 py::gil_scoped_acquire 在每个 decode step 获取 GIL,阻塞所有 Python 线程(包括 async serving 层)。结合 O(n²) replay,会造成延迟尖峰和吞吐下降。

建议:使用 xgrammar 的 C++ API 避免 GIL,或将 matcher 状态缓存以最小化 GIL 持有时间。

3. token_ids 可能在 GPU 上,直接 data<int32_t>() 访问不安全

rtp_llm/cpp/models/Sampler.cc L223

auto token_ids = inputs.token_ids->data<int32_t>();

如果 token_ids 是 GPU buffer,这是对 device 指针的 host 端解引用,会导致 segfault 或读到垃圾数据。需要先确保数据在 CPU 上。

P1 — 兼容性 / 测试

4. xgrammar 顶层 import 导致硬依赖

rtp_llm/async_decoder_engine/rpc_engine.py L5, rtp_llm/ops/rtp_llm/rtp_llm_op.py L4

import xgrammar as xgr 在模块顶层。如果 xgrammar 未安装,整个引擎无法导入,即使用户不需要 grammar 功能。应改为 lazy import 或 try/except。

5. pybind 注册 compiled_grammars 无默认值 — 破坏性 API 变更

rtp_llm/cpp/pybind/multi_gpu_gpt/RtpLLMOp.cc L414

py::arg("compiled_grammars")

没有默认值。所有现有调用方如果不传此参数会直接报错。应加 py::arg("compiled_grammars") = py::none()

6. row_grammars 检查逻辑有误 — 即使无 grammar 也会触发 xgrammar 路径

rtp_llm/cpp/normal_engine/NormalBatchStreamProcessor.cc L493

if (row_grammars.size() > 0) 永远为 true(只要有 stream 就会 append py::none())。应检查是否有任何非 none 的 grammar,否则每次采样都会有不必要的 GIL 获取开销。

7. response_format 未完整支持 OpenAI 规范

rtp_llm/openai/openai_endpoint.py

只提取 response_format.type,忽略了 json_schema 中的实际 schema 定义。用户传 {"type": "json_schema", "json_schema": {"schema": {...}}} 时,自定义 schema 会被丢弃。

8. 无测试覆盖

20 个文件的全链路改动,没有任何测试。至少需要 Python 单元测试验证 response_format 解析、C++ 单元测试验证 GenerateConfig JSON 反序列化、以及集成测试验证 grammar 约束生效。

P2 — 代码质量

9. backward-compat 解析用 try/catch 控制流

rtp_llm/cpp/engine_base/stream/GenerateConfig.h L191-207

try { json.Jsonize(...) } catch (...) {} 检测字段是否存在。如果 autil JSON 库有 hasField API,应优先使用。

10. 大量格式化噪音

多个文件包含纯格式化改动(对齐、空白行、引号风格),增加 review 难度。建议将格式化改动与功能改动分开提交。

总结

架构思路合理,但当前实现有 3 个 P0 问题(O(n²) replay、GIL 热路径、GPU buffer 安全)和 5 个 P1 问题(硬依赖、API 兼容、逻辑错误、规范不完整、无测试)。建议先解决 P0 问题并补充测试后再合入。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants