Prhub

#54692 [Bugfix][Frontend] Preserve token offset origins after left text pre-trimming

原始 PR 作者 waizuichougou 合并时间 2026-09-01 16:02 文件变更 3 提交数 1 评论 6 代码增减 +112 / -0

执行摘要

修复左截断场景下 token 偏移量坐标原点未恢复的静默数据错误。

当请求显式设置 truncation_side 时,vLLM 会禁用 tokenizer 级别截断,改为先通过 _text_len_check 进行字符级预截断,再进行 token 化。预截断后,fast tokenizer 返回的偏移量是相对于截断后字符串的。但 vLLM 未将这些偏移量重新映射回原始坐标就直接返回,导致偏移量指向错误字符。这是一个静默的数据损坏问题,因为偏移量数量与 token ID 数量匹配,调用者可能不会发现错误。该问题可通过公共 GPU-less render server 暴露。

此 PR 值得精读,特别是对前端渲染器或 token 化流程有责任的工程师。它展示了一个隐蔽的、静默的数据损坏 bug 如何被精确地识别和修复,修复方案小而精巧,测试覆盖到位。其设计决策(计算与应用分离、保留特殊 token 偏移量)值得参考。

讨论亮点

本 PR 的 review 讨论较少,主要为维护者的 approve 和 CI 触发。核心讨论体现在 PR body 和关联 Issue (#54407) 中,明确了问题的精确边界(字符级预截断 vs token 级别截断)和解决方案的独立性(本 PR 恢复坐标原点,PR#54407 对齐 token 级截断)。解决方案被接受后,未有进一步的技术争议。

实现拆解

  1. 计算左截断偏移量: 在 vllm/renderers/params.py 的 TokenizeParams 类中新增 _get_text_truncation_offset 方法。该方法复用 _text_len_check 中的计算逻辑(max_input_chars = max_input_tokens * tokenizer.max_chars_per_token),但仅计算并返回左截断时被移除的源字符串前缀长度,即 max(len(text) - max_input_chars, 0)。
  2. 应用偏移量调整: 在 vllm/renderers/base.py 的 BaseRenderer 类中新增静态方法 _apply_prompt_char_offset。该方法遍历 prompt_token_offsets,将每个偏移量 (start, end) 加上预截断偏移量 char_offset,但保留特殊 token 的 (0, 0) 偏移量不变。
  3. 集成至核心流程: 修改 _tokenize_singleton_prompt 和 _tokenize_singleton_prompt_async 方法。在调用 apply_pre_tokenization(该方法内部调用 _text_len_check 进行预截断)之前,先调用 _get_text_truncation_offset 计算偏移量。在完成 token 化后、调用 apply_post_tokenization 之前,调用 _apply_prompt_char_offset 将偏移量映射回原始坐标。
  4. 配套测试: 在 tests/renderers/test_token_offsets.py 中新增 _OffsetTokenizer(一个确定性的快速 tokenizer mock)以及两个测试用例 test_text_pretrim_preserves_source_offsets 和 test_async_left_text_pretrim_preserves_source_offsets,分别验证同步和异步路径下,左/右截断后偏移量的正确性。
文件 模块 状态 重要度
vllm/renderers/base.py 渲染器 modified 6.94
vllm/renderers/params.py 渲染器 modified 6.89
tests/renderers/test_token_offsets.py 偏移量测试 modified 6.73

关键符号

_get_text_truncation_offset _apply_prompt_char_offset _tokenize_singleton_prompt _tokenize_singleton_prompt_async

关键源码片段

vllm/renderers/base.py core-logic

核心逻辑变更:新增 `_apply_prompt_char_offset` 静态方法,并在 `_tokenize_singleton_prompt` 和 `_tokenize_singleton_prompt_async` 中集成偏移量恢复逻辑,是修复的执行点。

@staticmethod
def _apply_prompt_char_offset(
    prompt: TokensPrompt, char_offset: int
) -> TokensPrompt:
    """Map token offsets back to the original prompt after a text pre-trim.
​
    When _text_len_check pre-truncates the prompt text from the left,
    the fast tokenizer's offset_mapping becomes relative to the trimmed
    suffix. This method adds the length of the removed prefix back to
    each offset, restoring the original coordinate origin.
    """
    if char_offset == 0:
        return prompt
​
    offsets = prompt.get("prompt_token_offsets")
    if offsets is not None:
        prompt["prompt_token_offsets"] = [
            # Fast tokenizers use (0, 0) for special tokens, which are
            # not character spans in the source prompt and should remain
            # unchanged.
            (start, end)
            if (start, end) == (0, 0)
            else (start + char_offset, end + char_offset)
            for start, end in offsets
        ]
​
    return prompt

评论区精华

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

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

风险与影响

风险极低。变更仅影响返回给调用者的元数据 (prompt_token_offsets),不触及模型输入 (prompt_token_ids)、模型输出、权重或采样逻辑,因此不影响模型准确性。修复逻辑是直接的算术偏移,且与现有测试(test_explicit_truncation_side_truncates_offsets)协同工作,新测试用例专门覆盖了修复前失败的场景。唯一的潜在风险是计算偏移量时复用的 _text_len_check 逻辑未来可能被修改,但两处代码紧邻,易于维护。

  1. 用户: 修复了公共 API 返回的 prompt_token_offsets 字段在特定截断组合下的数据错误,提升了 API 输出数据的可靠性。对于依赖偏移量进行文本定位的消费者(如前端调试、上下文分析),此修复至关重要。
  2. 系统: 无系统性影响。变更局限在前端渲染器的 token 化流程内。
  3. 团队: 为 token 化流程中的多层截断(字符级预截断 + token 级截断)提供了一个清晰的修复范例。增强了 BaseRenderer 和 TokenizeParams 在处理偏移量方面的协作健壮性。
API 元数据修复 静默数据损坏修复

关联 Issue

#54407 [Bugfix][Frontend] Truncate prompt_token_offsets with the prompt
#54539 [Bugfix][Frontend] Truncate the assistant tokens mask with the prompt

完整报告

参与讨论