执行摘要
PR #51104 为 Rust vllm-bench 引入的 HF ShareGPT 多轮数据集加载基于 8 月 17 日的过期分支头实现,与 8 月 21 日 #51570 改造后的 download_hf_dataset 行返回 API 不兼容,导致 main 分支在 CPU/CUDA/XPU/AMD 全部镜像构建及 CPU Rust 门禁上以 E0061/E0308 确定性编译失败。本 PR 将 DatasetName::Hf 分支升级为六参数调用(新增 seed、disable_shuffle)并直接消费返回的 rows,同时把 ShareGPT 解析核心抽为 load_sharegpt_multi_turn_from_rows 供文件与 HF 两条入口共用,并新增离线回归测试。总计 68 增 10 删,已在精确 head 的 Buildkite #88963 上全量通过(Cargo 1,798/1,798),是「小改动、全局性影响」的集成修复样本。
功能与动机
PR #51104 于 9 月 14 日合入 main,但其最后一次 rebase 停留在 8 月 17 日;8 月 21 日 #51570 把 download_hf_dataset 从「四参数、返回文件路径」改为「六参数、返回反序列化行」。过期分支头在「精确 CI」(仅编译测试自身 head)上通过,掩盖了与当前 main 合成后的集成断裂,导致 #88956/#88957 等 main 构建全平台失败。PR body 明确修复策略为「把新特性更新到当前 API,而非回退任一兼容特性」,并完成 duplicate-work 检查:#55937 只做 timed-trace 回放、不触及 HF/ShareGPT 加载契约。
实现拆解
- 调用点升级(
rust/src/bench/src/multi_turn.rs):run_multi_turn_benchmark 中 DatasetName::Hf 分支原按四参数调用 download_hf_dataset 并拿回 downloaded_path 字符串路径;现改为六参数(增传 config.seed 与 config.disable_shuffle),返回值解构为 (rows, _config, _split),下游改调 load_sharegpt_multi_turn_from_rows。这正是 E0061「参数个数不匹配」与 E0308「路径 vs 行数组类型不匹配」两个编译错误的直接位置。
- 解析器抽取(
rust/src/bench/src/datasets/multi_turn.rs):原 load_sharegpt_multi_turn 保留「读文件 → 解析 JSON 数组」职责,拿到 entries 后委托给新函数 load_sharegpt_multi_turn_from_rows;「至少 4 条消息过滤、StdRng 固定种子洗牌、conversation 构建」等核心逻辑整体下移,文件输入与 HF 行输入从此共享同一套解析语义。
- 离线回归测试:
mod tests 新增 test_load_sharegpt_multi_turn_from_hf_rows,用 serde_json::json! 直接构造 4 条消息的 ShareGPT 行,断言 conversation_id == "hf-conv-0"、两轮 user_message 与 expected_output_len == 7,无网络依赖,可在纯 CPU Cargo 门禁运行。
- 验证配套:作者本机无 Cargo,先跑
pre-commit 与 git diff --check,再以字面 head 启动 Buildkite #88963(Cargo 1,798/1,798 通过、13 skipped;style+clippy 通过)。期间 #88961 暴露 rustfmt 差异,按 CI 格式化输出原样修正后重推 head 0ce0bec。未改动 config.rs,seed、disable_shuffle 复用既有配置字段,无配置或 schema 迁移。
rust/src/bench/src/multi_turn.rs
触发编译错误的直接调用点:DatasetName::Hf 分支从四参数路径返回调用升级为六参数行返回调用,是修复 E0061/E0308 的关键一环。
// rust/src/bench/src/multi_turn.rs(run_multi_turn_benchmark 内 DatasetName::Hf 分支)
DatasetName::Hf => {
let tok = tokenizer
.as_ref()
.ok_or_else(|| BenchError::Config("HF dataset requires a tokenizer".into()))?;
let dataset_id = config.dataset_path.as_deref().ok_or_else(|| {
BenchError::Config("--dataset-path is required for --dataset-name hf".into())
})?;
// #51570 之后 download_hf_dataset 变为六参数、直接返回反序列化行;
// 旧的四参数「返回文件路径」调用正是 E0061/E0308 编译失败的根源
let (rows, _config, _split) = crate::datasets::hf_dataset::download_hf_dataset(
dataset_id,
config.hf_subset.as_deref(),
config.hf_split.as_deref(),
config.num_prompts,
config.seed,
config.disable_shuffle,
)
.await?;
// 不再把 rows 当作文件系统路径,直接交给与文件输入共享的 ShareGPT 解析器
crate::datasets::multi_turn::load_sharegpt_multi_turn_from_rows(
tok,
&rows,
config.num_prompts,
config.hf_output_len,
config.sharegpt_multi_turn_max_turns,
config.seed,
&config.request_id_prefix,
)?
}
评论区精华
「#88961 不是 literal-head evidence……style lane 捕获了本机 pre-commit 未暴露的 rustfmt-only 差异……只有 #88963 应作为 merge-gate 证据。」—— khluu
「#88964 独立复现同一 E0061/E0308 vllm-bench 编译断裂……重试无意义。合并加上合并后首个自然 Rust/image 通过才是闭环。」—— khluu
PR body:「AI assistance was used to diagnose and implement this fix. This PR is intentionally draft: @khluu must review and be able to defend every changed line before marking it ready.」
风险与影响
- 契约耦合:修复硬依赖
download_hf_dataset 六参数行返回签名,该 API 两周内已发生一次破坏性变更,后续演进需同步此调用点;作者把「合并后首个自然 main 构建通过 Rust 门禁」设为事故闭环条件。
- 双重 shuffle 语义:
download_hf_dataset 内部按 seed 洗牌,load_sharegpt_multi_turn_from_rows 又用同一 seed 二次洗牌并截取 num_prompts 行;相同 seed 下结果确定,但抽样子集与单轮 HF 路径可能不一致,且 disable_shuffle 只能关闭第一层。
- 测试覆盖:新增测试仅覆盖行解析层,未覆盖
run_multi_turn_benchmark HF 分支整体;既有 test_prefix_sharing_structure 为 #[ignore] 网络测试。
- 影响面:仅 Rust 基准客户端,不触碰模型执行、调度与推理内核;但编译失败影响所有平台镜像构建,合并前失败即全红。
关联脉络
- #51104:本 PR 修复的原始特性,其「过期头 + 精确 CI 通过」是事故根因。
- #51570:PR body 提及的
download_hf_dataset API 破坏性变更(提供材料中无其标题)。
- #55937:与 #51104 互补的 timed-trace 回放功能,不触及 HF 加载契约,预计后续合入不受影响。
- #56169(分支新鲜度检查):与本次事故直接呼应——若 CI 在启动前校验目标分支新鲜度或做合成 merge 编译验证,此类「过期头合入」可提前拦截。
参与讨论