Prhub

#43854 [Rust Frontend] Add `/version` endpoint using engine-reported value

原始 PR 作者 BugenZhao 合并时间 2026-05-29 08:32 文件变更 17 提交数 3 评论 2 代码增减 +158 / -194

执行摘要

为 Rust 前端新增 `/version` 端点,引擎报告版本

Rust 前端需要知道引擎的 vLLM 版本以进行行为适配或输出给用户。之前尝试直接导入 Python 全局变量不可靠。通过扩展引擎握手协议,让引擎在就绪响应中包含 vllm_version 字段,Rust 前端直接从连接状态中读取,使版本获取与引擎生命周期一致。

值得精读。展示了 Rust 前端与 Python 引擎握手协议的演进方式,以及在类型层面强化契约的手法。适合理解 vLLM 前端架构设计。

讨论亮点

无审查讨论,PR 由维护者 @njhill 直接批准。作者在提交中逐步简化 Option 兼容代码后添加版本字段。

实现拆解

  1. Python 引擎侧添加版本字段:在 vllm/v1/engine/__init__.pyEngineCoreReadyResponse 数据类中新增 vllm_version 字段,使用 vllm.__version__ 赋值。
  2. Rust 协议结构体更新:在 rust/src/engine-core-client/src/protocol/handshake.rsEngineCoreReadyResponse 结构体中新增 vllm_version: String 字段,并将 dtypeOption<ModelDtype> 改为必选 ModelDtype
  3. Rust 传输层简化:在 rust/src/engine-core-client/src/transport.rs 中,将 ConnectedEngine.ready_responseOption<EngineCoreReadyResponse> 改为直接持有,并重构 wait_for_input_registrations 使其返回 Vec<ConnectedEngine>,移除了对旧引擎空注册消息的兼容处理。
  4. Rust 客户端暴露便捷方法:在 rust/src/engine-core-client/src/client.rs 中添加 vllm_version() -> &str 方法,并从第一个引擎的响应中获取版本;同时因 ready_response 不再可选,简化了 model_dtype()max_model_len() 等方法,移除 Option 包装。
  5. 新增 /version 路由与测试:在 rust/src/server/src/routes/version.rs 中实现 GET /version 处理函数,返回 JSON 对象包含 version(引擎版本)和 rust_frontend_version(通过 env!("CARGO_PKG_VERSION") 编译时注入);并在 tests.rs 中添加集成测试验证返回内容,使用 mock 引擎返回预先定义的版本字符串。
文件 模块 状态 重要度
rust/src/engine-core-client/src/protocol/handshake.rs 协议定义 modified 7.4
rust/src/engine-core-client/src/client.rs 客户端 modified 7.69
rust/src/engine-core-client/src/transport.rs 传输层 modified 7.15
rust/src/server/src/routes/version.rs 路由 added 7.1
rust/src/server/src/routes/tests.rs 路由 modified 6.1

关键符号

vllm_version model_dtype max_model_len version version_returns_engine_vllm_version wait_for_input_registrations

关键源码片段

rust/src/engine-core-client/src/protocol/handshake.rs data-contract

协议数据结构的核心变更:新增 `vllm_version` 字段并将 `dtype` 从 `Option` 提升为必选,是版本功能实现的基础。

/// Post-initialization configuration sent from each engine on the input socket
/// registration message, after the handshake completes.
///
/// Contains values that may differ from the original config (e.g.
/// `max_model_len` after KV cache auto-fitting, `num_gpu_blocks` after
/// profiling).
///
/// Original Python definition:
/// <https://github.com/vllm-project/vllm/blob/c8d98f81f6/vllm/v1/engine/__init__.py#L67-L77>
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EngineCoreReadyResponse {
    /// Engine-reported maximum model context length (auto-fitted after
    /// KV cache profiling and may differ from the original config value).
    pub max_model_len: u64,
    /// Number of GPU blocks available for KV cache on this engine.
    pub num_gpu_blocks: u64,
    /// DP coordinator stats publish address, if applicable.
    pub dp_stats_address: Option<String>,
    /// Effective model dtype after Python vLLM resolves `--dtype`.
    // 不再可选,引擎必须提供 dtype
    pub dtype: ModelDtype,
    /// Python vLLM version reported by the engine process.
    // 新增字段,引擎返回的 vLLM 版本字符串
    pub vllm_version: String,
}
rust/src/engine-core-client/src/client.rs core-logic

客户端 API 的简化与版本访问方法添加:`vllm_version()` 成为公开方法,同时多个查询方法移除 `Option` 返回类型。

    /// Return the ready responses received from all engines on the input
    /// socket.
    pub fn ready_responses(&self) -> Vec<&EngineCoreReadyResponse> {
        self.engines.iter().map(|engine| &engine.ready_response).collect()
    }    /// Return the engine-reported effective model dtype.
    // 从第一个引擎的响应中直接获取 dtype,不再返回 Option
    pub fn model_dtype(&self) -> ModelDtype {
        self.engines
            .first()
            .expect("engine core client requires at least one engine")
            .ready_response
            .dtype
    }    /// Return the engine-reported Python vLLM version.
    pub fn vllm_version(&self) -> &str {
        self.engines
            .first()
            .expect("engine core client requires at least one engine")
            .ready_response
            .vllm_version
            .as_str()
    }
rust/src/server/src/routes/version.rs entrypoint

新增的 `/version` 路由处理函数,是该 PR 面向用户的核心产出。

use std::sync::Arc;use axum::Json;
use axum::extract::State;
use serde::Serialize;use crate::state::AppState;/// 版本响应结构体,包含引擎版本和 Rust 前端版本
#[derive(Serialize)]
pub(crate) struct VersionResponse {
    /// 引擎 vLLM 版本(从 Python 引擎获取)
    version: String,
    /// Rust 前端 Cargo 包版本(编译时注入)
    rust_frontend_version: &'static str,
}/// 处理 GET /version 请求,返回版本元信息
pub async fn version(State(state): State<Arc<AppState>>) -> Json<VersionResponse> {
    // 从引擎客户端获取 vLLM 版本
    let version = state.engine_core_client().vllm_version().to_string();    Json(VersionResponse {
        version,
        // `CARGO_PKG_VERSION` 在编译时由 Cargo 注入
        rust_frontend_version: env!("CARGO_PKG_VERSION"),
    })
}

评论区精华

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

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

风险与影响

主要风险点:

1) 握手协议兼容性ready_responseOption 改为必选后,连接到旧版本引擎(未发送注册消息)将导致 panic。PR 已删除了相关兼容代码,假设所有引擎都能提供有效响应。
2) dtype 必选:若引擎未发送 dtype,反序列化将失败。但 PR 在 Python 侧已确保 dtype 始终设置。
3) 新版本字段缺失:若 Python 引擎未更新,vllm_version 字段将缺失导致反序列化失败。需要确保 Python 和 Rust 前端同步部署。整体风险可控,测试覆盖了正常路径。

用户侧:新增 GET /version 端点,可用于监控和调试。系统侧:握手协议收紧,不再容忍旧引擎;Rust 客户端代码简化,少一层 Option 过滤,性能微提升。对团队:明确了引擎版本的生命周期与连接绑定,避免 Python 全局导包。

接口契约变化 兼容性风险

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论