From 6bad6a36553b204859d1a1af12239919afaff73d Mon Sep 17 00:00:00 2001 From: Tony Lu Date: Sat, 29 Nov 2025 04:34:19 +0800 Subject: [PATCH] [model-gateway] Add version command support to SMG (#12558) --- sgl-router/Cargo.toml | 2 + sgl-router/README.md | 14 ++++ sgl-router/build.rs | 131 ++++++++++++++++++++++++++++++++++++++ sgl-router/src/lib.rs | 1 + sgl-router/src/main.rs | 14 ++++ sgl-router/src/version.rs | 75 ++++++++++++++++++++++ 6 files changed, 237 insertions(+) create mode 100644 sgl-router/src/version.rs diff --git a/sgl-router/Cargo.toml b/sgl-router/Cargo.toml index 4fcf4a1c5..92720350c 100644 --- a/sgl-router/Cargo.toml +++ b/sgl-router/Cargo.toml @@ -106,6 +106,8 @@ deadpool-postgres = "0.14.1" [build-dependencies] tonic-prost-build = "0.14.2" prost-build = "0.14.1" +chrono = { version = "0.4", features = ["clock"] } +toml = "0.9" [dev-dependencies] criterion = { version = "0.5", features = ["html_reports"] } diff --git a/sgl-router/README.md b/sgl-router/README.md index 051c32487..e1f89173a 100644 --- a/sgl-router/README.md +++ b/sgl-router/README.md @@ -88,6 +88,20 @@ pip install --force-reinstall dist/*.whl ``` > **Note:** Python bindings are located in `bindings/python/` with their own Cargo.toml. Use `maturin develop` for fast iteration during development (builds in debug mode and installs directly). Use `maturin build --release --features vendored-openssl` for production wheels with full optimizations (opt-level="z", lto="fat") and cross-platform compatibility. The package uses abi3 support for Python 3.8+ compatibility. +## Checking Version + +After installation, verify the installation and check version information: + +```bash +# Short version info (Rust binary) +./target/release/sglang-router -v + +# Full version info with build details (Rust binary) +./target/release/sglang-router --version +``` + +The `-v` flag displays a concise version string, while `--version` (or `-V`) shows comprehensive build information including Git commit, build time, compiler versions, and platform details. + ## Quick Start ### Regular HTTP Routing - **Rust binary** diff --git a/sgl-router/build.rs b/sgl-router/build.rs index efed70ae6..3fe81974b 100644 --- a/sgl-router/build.rs +++ b/sgl-router/build.rs @@ -1,7 +1,14 @@ +use std::process::Command; + +// Default values for version and project name when pyproject.toml is unavailable +const DEFAULT_VERSION: &str = "0.0.0"; +const DEFAULT_PROJECT_NAME: &str = "sgl-router"; + fn main() -> Result<(), Box> { // Only regenerate if proto files change println!("cargo:rerun-if-changed=src/proto/sglang_scheduler.proto"); println!("cargo:rerun-if-changed=src/proto/vllm_engine.proto"); + println!("cargo:rerun-if-changed=pyproject.toml"); // Configure tonic-prost-build for gRPC code generation tonic_prost_build::configure() @@ -23,5 +30,129 @@ fn main() -> Result<(), Box> { println!("cargo:info=Protobuf compilation completed successfully"); + // Read version and project name from pyproject.toml with fallback + let version = + read_field_from_pyproject("version").unwrap_or_else(|_| DEFAULT_VERSION.to_string()); + let project_name = + read_field_from_pyproject("name").unwrap_or_else(|_| DEFAULT_PROJECT_NAME.to_string()); + println!("cargo:rustc-env=SGL_ROUTER_VERSION={}", version); + println!("cargo:rustc-env=SGL_ROUTER_PROJECT_NAME={}", project_name); + + // Generate build time (UTC) + let build_time = chrono::Utc::now() + .format("%Y-%m-%d %H:%M:%S UTC") + .to_string(); + println!("cargo:rustc-env=SGL_ROUTER_BUILD_TIME={}", build_time); + + // Try to get Git branch + let git_branch = get_git_branch().unwrap_or_else(|| "unknown".to_string()); + println!("cargo:rustc-env=SGL_ROUTER_GIT_BRANCH={}", git_branch); + + // Try to get Git commit hash + let git_commit = get_git_commit().unwrap_or_else(|| "unknown".to_string()); + println!("cargo:rustc-env=SGL_ROUTER_GIT_COMMIT={}", git_commit); + + // Try to get Git status (clean/dirty) + let git_status = get_git_status().unwrap_or_else(|| "unknown".to_string()); + println!("cargo:rustc-env=SGL_ROUTER_GIT_STATUS={}", git_status); + + // Get Rustc version + let rustc_version = get_rustc_version().unwrap_or_else(|| "unknown".to_string()); + println!("cargo:rustc-env=SGL_ROUTER_RUSTC_VERSION={}", rustc_version); + + // Get Cargo version + let cargo_version = get_cargo_version().unwrap_or_else(|| "unknown".to_string()); + println!("cargo:rustc-env=SGL_ROUTER_CARGO_VERSION={}", cargo_version); + + // Get target triple (platform) + let target_triple = std::env::var("TARGET").unwrap_or_else(|_| { + // Try to get from rustc if not set + get_target_from_rustc().unwrap_or_else(|| "unknown".to_string()) + }); + println!("cargo:rustc-env=SGL_ROUTER_TARGET_TRIPLE={}", target_triple); + + // Get build mode (debug/release) + let build_mode = if std::env::var("PROFILE").unwrap_or_default() == "release" { + "release" + } else { + "debug" + }; + println!("cargo:rustc-env=SGL_ROUTER_BUILD_MODE={}", build_mode); + Ok(()) } + +fn read_field_from_pyproject(field: &str) -> Result> { + let content = std::fs::read_to_string("pyproject.toml")?; + let toml: toml::Value = toml::from_str(&content)?; + + // Navigate to [project] section + let project = toml + .get("project") + .ok_or("Missing [project] section in pyproject.toml")?; + + // Get the field value + let value = project + .get(field) + .ok_or_else(|| format!("Missing '{}' field in [project] section", field))?; + + // Convert to string + match value { + toml::Value::String(s) => Ok(s.clone()), + toml::Value::Integer(i) => Ok(i.to_string()), + toml::Value::Float(f) => Ok(f.to_string()), + toml::Value::Boolean(b) => Ok(b.to_string()), + _ => Err(format!("Field '{}' is not a string value", field).into()), + } +} + +/// Execute a command and return its output as a trimmed string +fn run_command(command: &str, args: &[&str]) -> Option { + let output = Command::new(command).args(args).output().ok()?; + + if output.status.success() { + String::from_utf8(output.stdout) + .ok() + .map(|s| s.trim().to_string()) + } else { + None + } +} + +fn get_git_branch() -> Option { + run_command("git", &["rev-parse", "--abbrev-ref", "HEAD"]) +} + +fn get_git_commit() -> Option { + run_command("git", &["rev-parse", "--short", "HEAD"]) +} + +fn get_git_status() -> Option { + // Check if there are uncommitted changes + let output = run_command("git", &["status", "--porcelain"])?; + if output.is_empty() { + Some("clean".to_string()) + } else { + Some("dirty".to_string()) + } +} + +fn get_rustc_version() -> Option { + run_command("rustc", &["--version"]) +} + +fn get_cargo_version() -> Option { + run_command("cargo", &["--version"]) +} + +fn get_target_from_rustc() -> Option { + let output_str = run_command("rustc", &["-vV"])?; + for line in output_str.lines() { + if line.starts_with("host: ") { + if let Some(host) = line.strip_prefix("host: ") { + return Some(host.trim().to_string()); + } + } + } + None +} diff --git a/sgl-router/src/lib.rs b/sgl-router/src/lib.rs index 647d0b82b..c22064586 100644 --- a/sgl-router/src/lib.rs +++ b/sgl-router/src/lib.rs @@ -16,3 +16,4 @@ pub mod server; pub mod service_discovery; pub mod tokenizer; pub mod tool_parser; +pub mod version; diff --git a/sgl-router/src/main.rs b/sgl-router/src/main.rs index 724d643df..968a35aa8 100644 --- a/sgl-router/src/main.rs +++ b/sgl-router/src/main.rs @@ -11,6 +11,7 @@ use sglang_router_rs::{ metrics::PrometheusConfig, server::{self, ServerConfig}, service_discovery::ServiceDiscoveryConfig, + version, }; fn parse_prefill_args() -> Vec<(String, Option)> { let args: Vec = std::env::args().collect(); @@ -683,6 +684,19 @@ impl CliArgs { } fn main() -> Result<(), Box> { + // Check for version flags before parsing other args to avoid errors + let args: Vec = std::env::args().collect(); + for arg in &args { + if arg == "--version" || arg == "-V" { + println!("{}", version::get_version_string()); + return Ok(()); + } + if arg == "-v" { + println!("{}", version::get_short_version_string()); + return Ok(()); + } + } + let prefill_urls = parse_prefill_args(); let mut filtered_args: Vec = Vec::new(); diff --git a/sgl-router/src/version.rs b/sgl-router/src/version.rs new file mode 100644 index 000000000..76c0c3b38 --- /dev/null +++ b/sgl-router/src/version.rs @@ -0,0 +1,75 @@ +//! Version information module +//! +//! Provides version information including version number, build time, and Git metadata. + +/// Project name from pyproject.toml (set at compile time) +pub const PROJECT_NAME: &str = env!("SGL_ROUTER_PROJECT_NAME"); + +/// Version string from pyproject.toml (set at compile time) +pub const VERSION: &str = env!("SGL_ROUTER_VERSION"); + +/// Build time in UTC format (set at compile time) +pub const BUILD_TIME: &str = env!("SGL_ROUTER_BUILD_TIME"); + +/// Git branch name (set at compile time, "unknown" if not available) +pub const GIT_BRANCH: &str = env!("SGL_ROUTER_GIT_BRANCH"); + +/// Git commit hash (short) (set at compile time, "unknown" if not available) +pub const GIT_COMMIT: &str = env!("SGL_ROUTER_GIT_COMMIT"); + +/// Git repository status (clean/dirty) (set at compile time) +pub const GIT_STATUS: &str = env!("SGL_ROUTER_GIT_STATUS"); + +/// Rustc version (set at compile time) +pub const RUSTC_VERSION: &str = env!("SGL_ROUTER_RUSTC_VERSION"); + +/// Cargo version (set at compile time) +pub const CARGO_VERSION: &str = env!("SGL_ROUTER_CARGO_VERSION"); + +/// Target triple (platform) (set at compile time) +pub const TARGET_TRIPLE: &str = env!("SGL_ROUTER_TARGET_TRIPLE"); + +/// Build mode (debug/release) (set at compile time) +pub const BUILD_MODE: &str = env!("SGL_ROUTER_BUILD_MODE"); + +/// Get formatted version information string with structured format +pub fn get_version_string() -> String { + format!( + "{}\n\n\ +Build Information:\n\ + Build Time: {}\n\ + Build Mode: {}\n\ + Platform: {}\n\n\ +Version Control:\n\ + Git Branch: {}\n\ + Git Commit: {}\n\ + Git Status: {}\n\n\ +Compiler:\n\ + {}\n\ + {}", + get_title(), + BUILD_TIME, + BUILD_MODE, + TARGET_TRIPLE, + GIT_BRANCH, + GIT_COMMIT, + GIT_STATUS, + RUSTC_VERSION, + CARGO_VERSION + ) +} + +/// Get version title line +pub fn get_title() -> String { + format!("{} version {}", PROJECT_NAME, VERSION) +} + +/// Get version number only +pub fn get_version() -> &'static str { + VERSION +} + +/// Get short version information string +pub fn get_short_version_string() -> String { + format!("{} version {}, build {}", PROJECT_NAME, VERSION, GIT_COMMIT) +}