Prhub

#50200 [Bugfix][Rust Frontend] Select earliest-completing stop string

原始 PR 作者 samlaf 合并时间 2026-07-31 08:33 文件变更 1 提交数 1 评论 1 代码增减 +50 / -3

执行摘要

修复 Rust 前端 stop string 选用逻辑

Rust 前端 matches_stop_string 与 Python 前端行为不一致:Python 前端选择在文本中最早完成的 stop string(#49391 已修复),而 Rust 前端仍使用旧逻辑(按列表顺序选择第一个匹配的)。这导致相同请求下两个前端输出可能不同,包括输出内容仍包含 stop string 和 stop_reason 错误。该修复是 Rust 前端功能对等(#44280)的一部分。

建议精读:这是一个小而精确的 bugfix,展示了如何通过修改一行核心逻辑(find_mapfilter_map + min_by_key)对齐跨前端行为。测试设计值得学习,尤其是边缘情况的覆盖。

讨论亮点

无 review 讨论。审核者 njhill 直接批准(LGTM)。

实现拆解

  1. 修改 matches_stop_string 函数rust/src/text/src/output/decoded.rs):将原 find_map 短路逻辑替换为 filter_map 收集所有匹配项,然后通过 min_by_key 按完成偏移量(end)选取最早完成者;平局时 min_by_key 保留第一个最小值,自然实现按列表顺序平局。
  2. 更新函数文档注释:明确说明多 stop string 同时匹配时的选择规则。
  3. 新增三个单元测试
    • stop_string_earliest_completing_wins_regardless_of_list_order:验证在同一个窗口内两个 stop string 均匹配时,最早完成的获胜,忽略列表顺序。
    • stop_string_ties_broken_by_list_order:验证完成偏移量相同时,按列表顺序决定。
    • stop_string_completion_position_not_start_position:验证选择依据是完成位置而非起始位置。
  4. 调整原有测试注释stop_string_matches_first_of_multiple 的注释更新为更准确地描述当前测试场景(只有一个 stop string 在窗口内)。
文件 模块 状态 重要度
rust/src/text/src/output/decoded.rs 前端文本 modified 7.27

关键符号

matches_stop_string

关键源码片段

rust/src/text/src/output/decoded.rs core-logic

核心修复文件:修改了 `matches_stop_string` 函数的实现逻辑并新增三个测试用例。

fn matches_stop_string(stops: &[String], output: &str, new_bytes: usize) -> Option<(usize, usize)> {
    // We compare byte subslices to avoid utf8 boundary problem
    let output = output.as_bytes();
    let next_off = (output.len() + 1) - new_bytes;
    stops
        .iter()
        .map(|ss| (ss.as_bytes(), ss.len(), next_off.saturating_sub(ss.len())))
        .enumerate()
        // filter_map 收集所有匹配项,不再短路
        .filter_map(|(ss_idx, (ss, len, start_off))| {
            output[start_off..]
                .windows(len)
                .position(|w| w == ss)
                .map(|pos| (ss_idx, start_off + pos, start_off + pos + len))
        })
        // min_by_key 按完成偏移量选择最早完成的 stop string
        // 平局时保留第一个最小值,即按列表顺序
        .min_by_key(|&(_, _, end)| end)
        .map(|(ss_idx, start, _)| (ss_idx, start))
}

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

低风险。变更集中在单个私有函数 matches_stop_string 内,输出类型和接口不变,所有已有测试通过。新行为与 Python 前端对齐,消除不一致性。未涉及引擎核心、网络或存储层。

  • 用户影响:使用 Rust 前端(VLLM_USE_RUST_FRONTEND=1)时,多 stop string 场景下输出文本和 stop_reason 将与 Python 前端一致。
  • 系统影响:无性能退化风险,min_by_key 额外遍历所有匹配项,但匹配项数通常很小。
  • 团队影响:消除了 Rust 前端的一个已知对等差距(#44280),推进了 Rust 前端功能完整度。

关联 Issue

#44280 [Roadmap] Rust Frontend Feature Parity

完整报告

参与讨论