[model-gateway]Enable IGW mode with gRPC router and auto enable IGW when service discovery is turned on (#15459)

This commit is contained in:
Arthur Cheng
2025-12-24 00:27:58 -08:00
committed by GitHub
parent 7e027691c8
commit f65fa04748
3 changed files with 99 additions and 44 deletions
+9 -13
View File
@@ -8,9 +8,7 @@ use tracing::{debug, info};
use crate::{ use crate::{
config::RouterConfig, config::RouterConfig,
core::{ core::{JobQueue, LoadMonitor, WorkerRegistry, WorkerService, UNKNOWN_MODEL_ID},
ConnectionMode, JobQueue, LoadMonitor, WorkerRegistry, WorkerService, UNKNOWN_MODEL_ID,
},
data_connector::{ data_connector::{
create_storage, ConversationItemStorage, ConversationStorage, ResponseStorage, create_storage, ConversationItemStorage, ConversationStorage, ResponseStorage,
}, },
@@ -290,8 +288,8 @@ impl AppContextBuilder {
.with_client(&router_config, request_timeout_secs)? .with_client(&router_config, request_timeout_secs)?
.maybe_rate_limiter(&router_config) .maybe_rate_limiter(&router_config)
.with_tokenizer_registry(&router_config)? .with_tokenizer_registry(&router_config)?
.maybe_reasoning_parser_factory(&router_config) .with_reasoning_parser_factory()
.maybe_tool_parser_factory(&router_config) .with_tool_parser_factory()
.with_worker_registry() .with_worker_registry()
.with_policy_registry(&router_config) .with_policy_registry(&router_config)
.with_storage(&router_config)? .with_storage(&router_config)?
@@ -435,19 +433,17 @@ impl AppContextBuilder {
Ok(Some(tokenizer)) Ok(Some(tokenizer))
} }
/// Create reasoning parser factory for gRPC mode /// Create reasoning parser factory for gRPC mode or IGW mode
fn maybe_reasoning_parser_factory(mut self, config: &RouterConfig) -> Self { fn with_reasoning_parser_factory(mut self) -> Self {
if matches!(config.connection_mode, ConnectionMode::Grpc { .. }) { // Initialize reasoning parser factory
self.reasoning_parser_factory = Some(ReasoningParserFactory::new()); self.reasoning_parser_factory = Some(ReasoningParserFactory::new());
}
self self
} }
/// Create tool parser factory for gRPC mode /// Create tool parser factory for gRPC mode or IGW mode
fn maybe_tool_parser_factory(mut self, config: &RouterConfig) -> Self { fn with_tool_parser_factory(mut self) -> Self {
if matches!(config.connection_mode, ConnectionMode::Grpc { .. }) { // Initialize tool parser factory
self.tool_parser_factory = Some(ToolParserFactory::new()); self.tool_parser_factory = Some(ToolParserFactory::new());
}
self self
} }
+11 -11
View File
@@ -516,26 +516,20 @@ impl CliArgs {
&self, &self,
prefill_urls: Vec<(String, Option<u16>)>, prefill_urls: Vec<(String, Option<u16>)>,
) -> ConfigResult<RouterConfig> { ) -> ConfigResult<RouterConfig> {
let mode = if self.enable_igw { // Determine routing mode based on backend type and PD disaggregation flag
RoutingMode::Regular { // IGW mode doesn't change routing mode, only affects router initialization
worker_urls: vec![], let mode = if matches!(self.backend, Backend::Openai) {
}
} else if matches!(self.backend, Backend::Openai) {
RoutingMode::OpenAI { RoutingMode::OpenAI {
worker_urls: self.worker_urls.clone(), worker_urls: self.worker_urls.clone(),
} }
} else if self.pd_disaggregation { } else if self.pd_disaggregation {
let decode_urls = self.decode.clone();
// Allow empty URLs to support dynamic worker addition
RoutingMode::PrefillDecode { RoutingMode::PrefillDecode {
prefill_urls, prefill_urls,
decode_urls, decode_urls: self.decode.clone(),
prefill_policy: self.prefill_policy.as_ref().map(|p| self.parse_policy(p)), prefill_policy: self.prefill_policy.as_ref().map(|p| self.parse_policy(p)),
decode_policy: self.decode_policy.as_ref().map(|p| self.parse_policy(p)), decode_policy: self.decode_policy.as_ref().map(|p| self.parse_policy(p)),
} }
} else { } else {
// Allow empty URLs to support dynamic worker addition
RoutingMode::Regular { RoutingMode::Regular {
worker_urls: self.worker_urls.clone(), worker_urls: self.worker_urls.clone(),
} }
@@ -762,11 +756,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let cli = Cli::parse_from(filtered_args); let cli = Cli::parse_from(filtered_args);
// Handle subcommands or use direct args // Handle subcommands or use direct args
let cli_args = match cli.command { let mut cli_args = match cli.command {
Some(Commands::Launch { args }) => args, Some(Commands::Launch { args }) => args,
None => cli.router_args, None => cli.router_args,
}; };
// Automatically enable IGW mode when service discovery is turned on
if cli_args.service_discovery && !cli_args.enable_igw {
println!("INFO: IGW mode automatically enabled because service discovery is turned on");
cli_args.enable_igw = true;
}
println!("SGLang Router starting..."); println!("SGLang Router starting...");
println!("Host: {}:{}", cli_args.host, cli_args.port); println!("Host: {}:{}", cli_args.host, cli_args.port);
let mode_str = if cli_args.enable_igw { let mode_str = if cli_args.enable_igw {
+72 -13
View File
@@ -93,6 +93,23 @@ impl RouterManager {
} }
} }
// Always create gRPC Regular router in IGW mode
match RouterFactory::create_grpc_router(app_context).await {
Ok(grpc_regular) => {
info!("Created gRPC Regular router");
manager.register_router(
RouterId::new("grpc-regular".to_string()),
Arc::from(grpc_regular),
);
}
Err(e) => {
warn!("Failed to create gRPC Regular router: {e}");
}
}
info!("PD disaggregation auto-enabled for IGW mode, creating PD routers");
// Create HTTP PD router
match RouterFactory::create_pd_router( match RouterFactory::create_pd_router(
None, None,
None, None,
@@ -111,11 +128,28 @@ impl RouterManager {
} }
} }
// TODO: Add gRPC routers once we have dynamic tokenizer loading // Create gRPC PD router
match RouterFactory::create_grpc_pd_router(
None,
None,
&config.router_config.policy,
app_context,
)
.await
{
Ok(grpc_pd) => {
info!("Created gRPC PD router");
manager
.register_router(RouterId::new("grpc-pd".to_string()), Arc::from(grpc_pd));
}
Err(e) => {
warn!("Failed to create gRPC PD router: {e}");
}
}
info!( info!(
"RouterManager initialized with {} routers for multi-router mode", "RouterManager initialized with {} routers for multi-router mode",
manager.router_count() manager.router_count(),
); );
} else { } else {
info!("Initializing RouterManager in single-router mode"); info!("Initializing RouterManager in single-router mode");
@@ -236,25 +270,40 @@ impl RouterManager {
pub fn get_router_for_model(&self, model_id: &str) -> Option<Arc<dyn RouterTrait>> { pub fn get_router_for_model(&self, model_id: &str) -> Option<Arc<dyn RouterTrait>> {
let workers = self.worker_registry.get_by_model(model_id); let workers = self.worker_registry.get_by_model(model_id);
if !workers.is_empty() { // Find the best worker type and derive router ID from it
let has_pd_workers = workers.iter().any(|w| { // Priority: grpc-pd (3) > http-pd (2) > grpc-regular (1) > http-regular (0)
matches!( let best_score = workers
.iter()
.map(|w| {
let is_pd = matches!(
w.worker_type(), w.worker_type(),
WorkerType::Prefill { .. } | WorkerType::Decode WorkerType::Prefill { .. } | WorkerType::Decode
) );
}); let is_grpc = matches!(w.connection_mode(), ConnectionMode::Grpc { .. });
let router_id = if has_pd_workers { match (is_grpc, is_pd) {
RouterId::new("http-pd".to_string()) (true, true) => 3, // grpc-pd (best)
} else { (false, true) => 2, // http-pd
RouterId::new("http-regular".to_string()) (true, false) => 1, // grpc-regular
(false, false) => 0, // http-regular
}
})
.max();
if let Some(score) = best_score {
let router_id = match score {
3 => "grpc-pd",
2 => "http-pd",
1 => "grpc-regular",
_ => "http-regular",
}; };
if let Some(router) = self.routers.get(&router_id) { if let Some(router) = self.routers.get(&RouterId::new(router_id.to_string())) {
return Some(router.clone()); return Some(router.clone());
} }
} }
// Fallback to default router
let default_router = self.default_router.read().unwrap(); let default_router = self.default_router.read().unwrap();
if let Some(ref default_id) = *default_router { if let Some(ref default_id) = *default_router {
self.routers.get(default_id).map(|r| r.clone()) self.routers.get(default_id).map(|r| r.clone())
@@ -340,13 +389,23 @@ impl RouterTrait for RouterManager {
} }
async fn health_generate(&self, _req: Request<Body>) -> Response { async fn health_generate(&self, _req: Request<Body>) -> Response {
// TODO: Should check if any router has healthy workers // IGW readiness: return 200 if at least one router has healthy workers
let has_healthy_workers = self
.worker_registry
.get_all()
.iter()
.any(|w| w.is_healthy());
if has_healthy_workers {
(StatusCode::OK, "At least one router has healthy workers").into_response()
} else {
( (
StatusCode::SERVICE_UNAVAILABLE, StatusCode::SERVICE_UNAVAILABLE,
"No routers with healthy workers available", "No routers with healthy workers available",
) )
.into_response() .into_response()
} }
}
async fn get_server_info(&self, _req: Request<Body>) -> Response { async fn get_server_info(&self, _req: Request<Body>) -> Response {
// TODO: Aggregate info from all routers with healthy workers // TODO: Aggregate info from all routers with healthy workers