Prhub

#47787 [Rust Frontend] Stamp `arrival_time` at the frontend entry

原始 PR 作者 tahsintunan 合并时间 2026-07-07 11:27 文件变更 10 提交数 1 评论 2 代码增减 +63 / -12

执行摘要

将 arrival_time 标记前移到前端入口,修复 Rust 前端 TTFT/e2e 测量偏短

PR body 指出 'Python stamps it at the renderer entry, before both. So Rust's TTFT and e2e histograms run short.' 关联 RFC #44757 和 roadmap #44280,目的是使 Rust 前端的请求追踪与 Python 前端一致。

此 PR 虽然变更简单,但展示了跨 crate(text、llm、chat)协调字段添加的典型模式,值得阅读以理解 Rust 前端的数据流。特别是 current_unix_timestamp_secs 的复用和透传设计值得参考。

讨论亮点

无实质讨论。维护者 BugenZhao 直接批准并回复 'Thanks!'。

实现拆解

实现分为四步:

  1. rust/src/llm/src/request_metrics.rs 中将 current_unix_timestamp_secs 的可见性从 pub(crate) 提升为 pub,以便其他 crate 调用。
  2. rust/src/llm/src/request.rs 中删除私有的 current_unix_timestamp_secs 定义,改为导入 request_metrics 中的公开版本,并更新 arrival_time 的文档注释。
  3. rust/src/text/src/request.rsTextRequest 结构体中新增 arrival_time 字段;在 rust/src/text/src/lib.rsgenerate_inner 方法入口处,若 arrival_time 未设置则调用 current_unix_timestamp_secs 填充。
  4. rust/src/chat/src/lib.rschat 方法入口处直接计算 arrival_time,并将其传给 TextRequest
  5. rust/src/text/src/lower.rs 中,将 arrival_time 从硬编码 None 改为透传 request.arrival_time,并添加两项单元测试验证透传和缺失行为。
  6. rust/src/server 下的多个入口点将 arrival_time 字段序列化传递给 TextRequest,确保端到端一致。
文件 模块 状态 重要度
rust/src/text/src/lower.rs 文本层 modified 6.82
rust/src/text/src/lib.rs 文本层 modified 5.23
rust/src/chat/src/lib.rs 对话层 modified 5.07
rust/src/llm/src/request.rs LLM 层 modified 5.94
rust/src/llm/src/request_metrics.rs LLM 层 modified 4.32
rust/src/text/src/request.rs 文本层 modified 4.83
rust/src/server/src/grpc/convert.rs 服务层 modified 3.86
rust/src/server/src/routes/inference/generate/convert.rs 服务层 modified 3.86
rust/src/server/src/routes/openai/completions/convert.rs 服务层 modified 3.86
rust/src/llm/src/lib.rs LLM 层 modified 3.38

关键符号

lower_text_request generate_inner chat current_unix_timestamp_secs lower_text_request_passes_arrival_time_through lower_text_request_leaves_arrival_time_unset_when_absent

关键源码片段

rust/src/text/src/lower.rs core-logic

核心变更:在 lower_text_request 中透传 arrival_time,并添加两项测试验证透传和缺失行为

/// Convert a high-level TextRequest into GenerateRequest.
pub fn lower_text_request(
    request: TextRequest,
    prompt_token_ids: Vec<u32>,
    sampling_hints: SamplingHints,
    sampling_limits: SamplingLimits,
    tokenizer: &dyn Tokenizer,
) -> Result<PreparedTextRequest> {
    // ... 省略前置验证 ...
    let generate_request = GenerateRequest {
        request_id: request.request_id.clone(),
        prompt_token_ids,
        mm_features: request.mm_features.clone(),
        sampling_params: lower_sampling_params(/* ... */)?,
        cache_salt: request.cache_salt.clone(),
        priority: request.priority,
        data_parallel_rank: request.data_parallel_rank,
        reasoning_parser_kwargs: request.reasoning_parser_kwargs.clone(),
        lora_request: request.lora_request.clone(),
        arrival_time: request.arrival_time, // 透传,不再硬编码为 None
        trace_headers: None,
    };
    Ok(PreparedTextRequest { text_request: request, generate_request })
}// 测试:验证透传
#[test]
fn lower_text_request_passes_arrival_time_through() {
    let request = TextRequest { arrival_time: Some(42.5), ..sample_request() };
    let prepared = lower_text_request(
        request,
        vec![1, 2, 3],
        sample_sampling_hints(),
        sample_sampling_limits(),
        &stub_tokenizer(),
    )
    .unwrap();
    assert_eq!(prepared.generate_request.arrival_time, Some(42.5));
}// 测试:验证缺失时保持 None
#[test]
fn lower_text_request_leaves_arrival_time_unset_when_absent() {
    let request = TextRequest { arrival_time: None, ..sample_request() };
    let prepared = lower_text_request(
        request,
        vec![1, 2, 3],
        sample_sampling_hints(),
        sample_sampling_limits(),
        &stub_tokenizer(),
    )
    .unwrap();
    assert_eq!(prepared.generate_request.arrival_time, None);
}
rust/src/text/src/lib.rs core-logic

在 generate_inner 入口处填充默认 arrival_time,实现前移标记

async fn generate_inner(
    &self,
    mut request: TextRequest,
) -> Result<(TextRequest, GenerateOutputStream)> {
    request.validate()?;    // 在 render 和 tokenization 之前标记 arrival_time,以匹配 Python 行为
    if request.arrival_time.is_none() {
        request.arrival_time = Some(vllm_llm::current_unix_timestamp_secs());
    }    let tokenizer = self.backend.tokenizer();
    let prompt_token_ids = match take(&mut request.prompt) {
        Prompt::Text(text) => tokenizer.encode(&text, request.add_special_tokens)?,
        Prompt::TokenIds(token_ids) => token_ids,
    };
    // ... 后续 lower 和 generate ...
}

评论区精华

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

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

风险与影响

风险很低。主要风险是现有监控系统可能因 TTFT/e2e 时间轴变长而告警,但这是修正后的正确行为。此外,如果存在直接构造 TextRequest 且未设置 arrival_time 的调用点,现在会被自动填充时间戳,行为一致。

影响所有使用 Rust 前端的请求:请求的 arrival_time 现在在渲染和分词之前标记,因此 TTFT 和 e2e 直方图会包含这些额外的时间,与 Python 前端对齐。这仅影响前端侧的指标收集,不涉及引擎协议变化,对吞吐量无影响。

指标偏移 低风险

关联 Issue

#44757 [Rust Frontend][RFC] Request tracing for the Rust frontend

完整报告

参与讨论