Prhub

#38827 feat: add max_tokens_per_doc in rerank request.

vllm-project/vllm · 作者 jefp · 合并时间 2026-04-13 16:24

分析状态 已生成
文件变更 9提交数 8 · 评论 50
代码增减 +544 / -27
frontend feature pooling v1 refactor

执行摘要

为 rerank 和 score 请求添加 max_tokens_per_doc/query 参数,支持文档 / 查询独立截断以对齐 Cohere/Jina API。

PR body 明确说明目的是添加 max_tokens_per_doc 和 max_tokens_per_query 参数到 rerank 和 score 请求,以对齐 Cohere 和 Jina rerank APIs,确保文档/查询长于限制时能在评分前被截断。这是对旧 PR #33315 的 rebase 和增强,由于 score/ → scoring/ 目录重构和 IO 处理器重构,无法直接机械 rebase。

该 PR 值得精读,重点关注 io_processor.py 中的截断策略设计(三种模型路径处理)、protocol.py 的继承重构以消除重复代码,以及测试中参数化 fixture 的优化模式。这些决策展示了在兼容性和性能间的权衡。

讨论亮点

review 中核心讨论包括:1) noooop 建议将 max_tokens_per_doc 移至 ScoreRequestMixin 以便 /score 端点也能使用,并让 RerankRequest 继承以减少重复,已采纳;2) 默认值使用 0 而非 None 表示无截断,已统一;3) DarkLight1337 指出需处理 pooling_params 为 list 的情况,但后续确认为 score API 不支持 list,因此添加 assert;4) 测试优化:避免多服务器并行导致 OOM,改为参数化 fixture,并将测试移至 tests/models/language/pooling/;5) 添加 max_tokens_per_query 参数以实现对称截断;6) 移除 base/serving.py 中的重复验证逻辑,集中到 io_processor。所有反馈均被解决,PR 最终获批。

实现拆解

实现拆分为三个层次:1) 协议层:在 ScoreRequestMixin 中添加 max_tokens_per_query 和 max_tokens_per_doc 字段(默认值 0 表示无截断),并使 RerankRequest 继承自 ScoreRequestMixin 以消除重复代码;2) 处理层:在 io_processor.py 中新增 _validate_token_limit 验证、_get_token_limits 提取参数、_truncate_scoring_data 执行截断,支持三种截断策略(交叉编码器使用 sep token、聊天模板/Jinja 路径基于 offset_mapping 文本截断、LLM-as-reranker 直接 token ID 切片);3) 工具层:在 utils.py 添加 truncate_text_to_tokens 和 get_num_special_tokens_for_pair 辅助函数。离线支持通过 PoolingParams.extra_kwargs 实现。

文件 模块 状态 重要度
vllm/entrypoints/pooling/scoring/protocol.py pooling/scoring modified 7.0
vllm/entrypoints/pooling/scoring/io_processor.py pooling/scoring modified 8.0
tests/models/language/pooling/test_max_tokens_per_doc.py tests/pooling added 6.0
vllm/entrypoints/pooling/scoring/utils.py pooling/scoring modified 5.0

分析完成后,这里会展示 LLM 生成的相对完整源码片段和详细注释。

关键符号

_validate_token_limit _get_token_limits _truncate_scoring_data truncate_text_to_tokens get_num_special_tokens_for_pair

评论区精华

参数位置与继承设计 设计

noooop 建议将 max_tokens_per_doc 移至 ScoreRequestMixin 以便 /score 端点也能使用,并让 RerankRequest 继承以消除重复代码。

结论:已采纳:字段添加到 ScoreRequestMixin,RerankRequest 继承自它,移除了重复的 build_tok_params 和 to_pooling_params。 · 已解决

默认值与验证逻辑 正确性

讨论使用 0 作为默认值表示无截断(而非 None),并确保验证逻辑(负值、超过 max_model_len)集中处理。

结论:统一使用 default=0,验证集中在 io_processor 的 _validate_token_limit,移除了 base/serving.py 中的重复验证。 · 已解决

测试优化与 OOM 避免 性能

noooop 指出原测试可能同时启动多个服务器导致 OOM,建议使用参数化 fixture 逐个运行。

结论:已改为 @pytest.fixture(scope="module", params=...) 模式,服务器逐一运行,避免内存问题。 · 已解决

pooling_params 列表处理 设计

DarkLight1337 和 noooop 讨论 pooling_params 是否为 list 的情况,最终确认为 score API 不支持 per-prompt 不同限制,因此添加 assert 确保非序列。

结论:添加 assert 检查 pooling_params 不是 Sequence,因为 API 设计为单个值 per request,与 Cohere/Jina 一致。 · 已解决

风险与影响

技术风险包括:1) 回归风险:修改了 io_processor.py 中的核心处理逻辑,可能影响现有 rerank/score 功能,但通过 23 个现有测试零回归得到缓解;2) 验证漏洞:_validate_token_limit 检查负值和超过 max_model_len,但依赖外部传入参数,需确保所有路径覆盖;3) 截断精度:truncate_text_to_tokens 使用 offset_mapping 避免 BPE 边界问题,但对不返回 offset_mapping 的 tokenizer 可能回退,需确保兼容性;4) 兼容性:API 变更添加新参数,默认值 0 保持向后兼容,但用户需知晓行为变化;5) 性能影响:文本截断增加额外计算开销,但对于长文档处理是必要的。

影响范围:1) 用户:为使用 rerank 和 score 端点的用户提供更灵活的截断控制,对齐行业标准,提升体验;2) 系统:扩展了 pooling/scoring 模块功能,支持多模型类型截断,增强 API 一致性;3) 团队:代码重构减少了重复(如 RerankRequest 继承),但增加了 io_processor 复杂度,需维护新参数和截断策略。影响程度中等,主要限于前端 API 和特定模型路径。

API 变更 验证逻辑集中化 截断策略复杂度 测试覆盖扩展

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

执行摘要

本 PR 在 vLLM 的 rerank 和 score API 中新增 max_tokens_per_docmax_tokens_per_query 参数,支持对长文档和查询进行独立截断,以对齐 Cohere、Jina 等行业标准。实现涵盖协议层扩展、三种模型类型的截断策略、全面验证和测试,作为 PR #33315 的 rebase 版本解决了目录重构导致的合并困难,并经多轮 review 优化后合并,对前端 API 和 pooling 模块有中等影响。

功能与动机

为什么做:PR body 明确指出,目的是添加 max_tokens_per_docmax_tokens_per_query 参数到 rerank 和 score 请求,使 vLLM 与 Cohere、Jina 等外部 rerank API 对齐。当文档或查询过长时,能在评分前进行截断,提升处理效率和兼容性。此变更源于旧 PR #33315,但因 score/scoring/ 目录重构和 IO 处理器重构无法机械 rebase,故重新实现并增强。

实现拆解

实现按模块分层梳理:

  1. 协议层protocol.py):
    • ScoreRequestMixin 中添加 max_tokens_per_querymax_tokens_per_doc 字段,类型为 int,默认值 0(表示无截断)。
    • 使 RerankRequest 继承自 ScoreRequestMixin,移除重复的 build_tok_paramsto_pooling_params 方法。
  2. 处理层io_processor.py):
    • 新增 _validate_token_limit:验证参数非负且小于 max_model_len
    • _get_token_limits:从请求或 PoolingParams 提取参数,支持在线和离线路径。
    • _truncate_scoring_data:根据限制截断文本,调用工具函数。
    • 三种截断策略:
      • 交叉编码器(带 sep token):使用 tokenizer 的 truncation="only_second"
    • 聊天模板/Jinja 路径:通过 offset_mapping 在模板应用前进行文本截断,避免编码-解码损耗。
    • LLM-as-reranker:直接切片 token ID。
  3. 工具层utils.py):
    • truncate_text_to_tokens:利用 offset_mapping 精确截断文本至指定 token 数。
    • get_num_special_tokens_for_pair:计算成对编码的特殊 token 数量。
  4. 测试层:新增 test_max_tokens_per_doc.py 覆盖多模型场景,优化为参数化 fixture 避免 OOM。

评论区精华

review 讨论中的关键交锋:

  • 参数设计:noooop 建议“将 max_tokens_per_doc 移至 ScoreRequestMixin”,以实现 /score 端点共享,并让 RerankRequest 继承以减少重复。作者采纳并回复:“Done. Moved... and RerankRequest now inherits”。
  • 验证优化:noooop 指出“base/serving.py 中的验证重复”,作者移除并集中到 io_processor,确保一致性。
  • 测试性能:针对测试可能 OOM 的问题,noooop 建议“使用 @pytest.fixture(scope="module", params=[.....])”,作者改为参数化运行,逐个启动服务器。
  • API 限制:关于 pooling_params 是否为 list 的讨论,最终确认为“score API 不支持 Sequence[PoolingParams]”,因此添加 assert 强化设计假设。

风险与影响

技术风险

  1. 回归风险:修改核心处理逻辑可能影响现有 rerank/score 功能,但 23 个现有测试零回归提供了保障。
  2. 截断精度:truncate_text_to_tokens 依赖 offset_mapping,对不支持此功能的 tokenizer 可能回退,需确保广泛兼容。
  3. 验证覆盖:参数验证集中在 io_processor,需确保所有调用路径(如离线 via PoolingParams)都经过检查。

影响评估

  • 用户:提供更精细的截断控制,对齐行业实践,提升长文档处理体验。
  • 系统:扩展 pooling/scoring 模块,支持多模型截断,增加约 500 行代码,复杂度可控。
  • 团队:代码重构(如继承优化)提升可维护性,但新参数需文档更新和用户教育。

关联脉络

此 PR 是 PR #33315 的直接后继,由于目录重构无法直接合并,故重新实现并整合了原 PR 的反馈。同时,它与 PR #39153 关联,后者引入了 _params_to_seq helper,影响本 PR 中 pooling_params 处理的代码演进(最终在 rebase 后移除了临时 helper)。从近期历史 PR 看,vLLM 正在持续增强 pooling 和 scoring 功能(如 #39530 的重命名、#39655 的 bugfix),本 PR 是该趋势的一部分,旨在提升 API 兼容性和灵活性。

参与讨论