[router] Add builder pattern for RouterConfig with zero duplication (#12030)
This commit is contained in:
@@ -11,8 +11,8 @@ use common::mock_worker::{HealthStatus, MockWorker, MockWorkerConfig, WorkerType
|
||||
use reqwest::Client;
|
||||
use serde_json::json;
|
||||
use sglang_router_rs::{
|
||||
config::{CircuitBreakerConfig, PolicyConfig, RetryConfig, RouterConfig, RoutingMode},
|
||||
core::{ConnectionMode, Job},
|
||||
config::{RouterConfig, RoutingMode},
|
||||
core::Job,
|
||||
routers::{RouterFactory, RouterTrait},
|
||||
server::AppContext,
|
||||
};
|
||||
@@ -30,45 +30,18 @@ struct TestContext {
|
||||
impl TestContext {
|
||||
async fn new(worker_configs: Vec<MockWorkerConfig>) -> Self {
|
||||
// Create default router config
|
||||
let config = RouterConfig {
|
||||
chat_template: None,
|
||||
mode: RoutingMode::Regular {
|
||||
worker_urls: vec![],
|
||||
},
|
||||
policy: PolicyConfig::Random,
|
||||
host: "127.0.0.1".to_string(),
|
||||
port: 3002,
|
||||
max_payload_size: 256 * 1024 * 1024,
|
||||
request_timeout_secs: 600,
|
||||
worker_startup_timeout_secs: 1,
|
||||
worker_startup_check_interval_secs: 1,
|
||||
discovery: None,
|
||||
dp_aware: false,
|
||||
api_key: None,
|
||||
metrics: None,
|
||||
log_dir: None,
|
||||
log_level: None,
|
||||
request_id_headers: None,
|
||||
max_concurrent_requests: 64,
|
||||
queue_size: 0,
|
||||
queue_timeout_secs: 60,
|
||||
rate_limit_tokens_per_second: None,
|
||||
cors_allowed_origins: vec![],
|
||||
retry: RetryConfig::default(),
|
||||
circuit_breaker: CircuitBreakerConfig::default(),
|
||||
disable_retries: false,
|
||||
disable_circuit_breaker: false,
|
||||
health_check: sglang_router_rs::config::HealthCheckConfig::default(),
|
||||
enable_igw: false,
|
||||
connection_mode: ConnectionMode::Http,
|
||||
model_path: None,
|
||||
tokenizer_path: None,
|
||||
history_backend: sglang_router_rs::config::HistoryBackend::Memory,
|
||||
oracle: None,
|
||||
reasoning_parser: None,
|
||||
tool_call_parser: None,
|
||||
tokenizer_cache: sglang_router_rs::config::TokenizerCacheConfig::default(),
|
||||
};
|
||||
let config = RouterConfig::builder()
|
||||
.regular_mode(vec![])
|
||||
.random_policy()
|
||||
.host("127.0.0.1")
|
||||
.port(3002)
|
||||
.max_payload_size(256 * 1024 * 1024)
|
||||
.request_timeout_secs(600)
|
||||
.worker_startup_timeout_secs(1)
|
||||
.worker_startup_check_interval_secs(1)
|
||||
.max_concurrent_requests(64)
|
||||
.queue_timeout_secs(60)
|
||||
.build_unchecked();
|
||||
|
||||
Self::new_with_config(config, worker_configs).await
|
||||
}
|
||||
@@ -1182,45 +1155,18 @@ mod error_tests {
|
||||
#[tokio::test]
|
||||
async fn test_payload_too_large() {
|
||||
// Create context with small payload limit
|
||||
let config = RouterConfig {
|
||||
chat_template: None,
|
||||
mode: RoutingMode::Regular {
|
||||
worker_urls: vec![],
|
||||
},
|
||||
policy: PolicyConfig::Random,
|
||||
host: "127.0.0.1".to_string(),
|
||||
port: 3010,
|
||||
max_payload_size: 1024, // 1KB limit
|
||||
request_timeout_secs: 600,
|
||||
worker_startup_timeout_secs: 1,
|
||||
worker_startup_check_interval_secs: 1,
|
||||
dp_aware: false,
|
||||
api_key: None,
|
||||
discovery: None,
|
||||
metrics: None,
|
||||
log_dir: None,
|
||||
log_level: None,
|
||||
request_id_headers: None,
|
||||
max_concurrent_requests: 64,
|
||||
queue_size: 0,
|
||||
queue_timeout_secs: 60,
|
||||
rate_limit_tokens_per_second: None,
|
||||
cors_allowed_origins: vec![],
|
||||
retry: RetryConfig::default(),
|
||||
circuit_breaker: CircuitBreakerConfig::default(),
|
||||
disable_retries: false,
|
||||
disable_circuit_breaker: false,
|
||||
health_check: sglang_router_rs::config::HealthCheckConfig::default(),
|
||||
enable_igw: false,
|
||||
connection_mode: ConnectionMode::Http,
|
||||
model_path: None,
|
||||
tokenizer_path: None,
|
||||
history_backend: sglang_router_rs::config::HistoryBackend::Memory,
|
||||
oracle: None,
|
||||
reasoning_parser: None,
|
||||
tool_call_parser: None,
|
||||
tokenizer_cache: sglang_router_rs::config::TokenizerCacheConfig::default(),
|
||||
};
|
||||
let config = RouterConfig::builder()
|
||||
.regular_mode(vec![])
|
||||
.random_policy()
|
||||
.host("127.0.0.1")
|
||||
.port(3010)
|
||||
.max_payload_size(1024) // 1KB limit
|
||||
.request_timeout_secs(600)
|
||||
.worker_startup_timeout_secs(1)
|
||||
.worker_startup_check_interval_secs(1)
|
||||
.max_concurrent_requests(64)
|
||||
.queue_timeout_secs(60)
|
||||
.build_unchecked();
|
||||
|
||||
let ctx = TestContext::new_with_config(
|
||||
config,
|
||||
@@ -1509,48 +1455,18 @@ mod pd_mode_tests {
|
||||
.and_then(|p| p.trim_end_matches('/').parse::<u16>().ok())
|
||||
.unwrap_or(9000);
|
||||
|
||||
let config = RouterConfig {
|
||||
chat_template: None,
|
||||
mode: RoutingMode::PrefillDecode {
|
||||
prefill_urls: vec![(prefill_url, Some(prefill_port))],
|
||||
decode_urls: vec![decode_url],
|
||||
prefill_policy: None,
|
||||
decode_policy: None,
|
||||
},
|
||||
policy: PolicyConfig::Random,
|
||||
host: "127.0.0.1".to_string(),
|
||||
port: 3011,
|
||||
max_payload_size: 256 * 1024 * 1024,
|
||||
request_timeout_secs: 600,
|
||||
worker_startup_timeout_secs: 1,
|
||||
worker_startup_check_interval_secs: 1,
|
||||
discovery: None,
|
||||
metrics: None,
|
||||
log_dir: None,
|
||||
dp_aware: false,
|
||||
api_key: None,
|
||||
log_level: None,
|
||||
request_id_headers: None,
|
||||
max_concurrent_requests: 64,
|
||||
queue_size: 0,
|
||||
queue_timeout_secs: 60,
|
||||
rate_limit_tokens_per_second: None,
|
||||
cors_allowed_origins: vec![],
|
||||
retry: RetryConfig::default(),
|
||||
circuit_breaker: CircuitBreakerConfig::default(),
|
||||
disable_retries: false,
|
||||
disable_circuit_breaker: false,
|
||||
health_check: sglang_router_rs::config::HealthCheckConfig::default(),
|
||||
enable_igw: false,
|
||||
connection_mode: ConnectionMode::Http,
|
||||
model_path: None,
|
||||
tokenizer_path: None,
|
||||
history_backend: sglang_router_rs::config::HistoryBackend::Memory,
|
||||
oracle: None,
|
||||
reasoning_parser: None,
|
||||
tool_call_parser: None,
|
||||
tokenizer_cache: sglang_router_rs::config::TokenizerCacheConfig::default(),
|
||||
};
|
||||
let config = RouterConfig::builder()
|
||||
.prefill_decode_mode(vec![(prefill_url, Some(prefill_port))], vec![decode_url])
|
||||
.random_policy()
|
||||
.host("127.0.0.1")
|
||||
.port(3011)
|
||||
.max_payload_size(256 * 1024 * 1024)
|
||||
.request_timeout_secs(600)
|
||||
.worker_startup_timeout_secs(1)
|
||||
.worker_startup_check_interval_secs(1)
|
||||
.max_concurrent_requests(64)
|
||||
.queue_timeout_secs(60)
|
||||
.build_unchecked();
|
||||
|
||||
// Create app context
|
||||
let app_context = common::create_test_context(config);
|
||||
@@ -1676,45 +1592,19 @@ mod request_id_tests {
|
||||
#[tokio::test]
|
||||
async fn test_request_id_with_custom_headers() {
|
||||
// Create config with custom request ID headers
|
||||
let config = RouterConfig {
|
||||
chat_template: None,
|
||||
mode: RoutingMode::Regular {
|
||||
worker_urls: vec![],
|
||||
},
|
||||
policy: PolicyConfig::Random,
|
||||
host: "127.0.0.1".to_string(),
|
||||
port: 3002,
|
||||
max_payload_size: 256 * 1024 * 1024,
|
||||
request_timeout_secs: 600,
|
||||
worker_startup_timeout_secs: 1,
|
||||
worker_startup_check_interval_secs: 1,
|
||||
discovery: None,
|
||||
metrics: None,
|
||||
dp_aware: false,
|
||||
api_key: None,
|
||||
log_dir: None,
|
||||
log_level: None,
|
||||
request_id_headers: Some(vec!["custom-id".to_string(), "trace-id".to_string()]),
|
||||
max_concurrent_requests: 64,
|
||||
queue_size: 0,
|
||||
queue_timeout_secs: 60,
|
||||
rate_limit_tokens_per_second: None,
|
||||
cors_allowed_origins: vec![],
|
||||
retry: RetryConfig::default(),
|
||||
circuit_breaker: CircuitBreakerConfig::default(),
|
||||
disable_retries: false,
|
||||
disable_circuit_breaker: false,
|
||||
health_check: sglang_router_rs::config::HealthCheckConfig::default(),
|
||||
enable_igw: false,
|
||||
connection_mode: ConnectionMode::Http,
|
||||
model_path: None,
|
||||
tokenizer_path: None,
|
||||
history_backend: sglang_router_rs::config::HistoryBackend::Memory,
|
||||
oracle: None,
|
||||
reasoning_parser: None,
|
||||
tool_call_parser: None,
|
||||
tokenizer_cache: sglang_router_rs::config::TokenizerCacheConfig::default(),
|
||||
};
|
||||
let config = RouterConfig::builder()
|
||||
.regular_mode(vec![])
|
||||
.random_policy()
|
||||
.host("127.0.0.1")
|
||||
.port(3002)
|
||||
.max_payload_size(256 * 1024 * 1024)
|
||||
.request_timeout_secs(600)
|
||||
.worker_startup_timeout_secs(1)
|
||||
.worker_startup_check_interval_secs(1)
|
||||
.request_id_headers(vec!["custom-id".to_string(), "trace-id".to_string()])
|
||||
.max_concurrent_requests(64)
|
||||
.queue_timeout_secs(60)
|
||||
.build_unchecked();
|
||||
|
||||
let ctx = TestContext::new_with_config(
|
||||
config,
|
||||
|
||||
@@ -19,16 +19,12 @@ struct TestContext {
|
||||
|
||||
impl TestContext {
|
||||
async fn new(worker_configs: Vec<MockWorkerConfig>) -> Self {
|
||||
let mut config = RouterConfig {
|
||||
chat_template: None,
|
||||
mode: RoutingMode::Regular {
|
||||
worker_urls: vec![],
|
||||
},
|
||||
port: 3003,
|
||||
worker_startup_timeout_secs: 1,
|
||||
worker_startup_check_interval_secs: 1,
|
||||
..Default::default()
|
||||
};
|
||||
let mut config = RouterConfig::builder()
|
||||
.regular_mode(vec![])
|
||||
.port(3003)
|
||||
.worker_startup_timeout_secs(1)
|
||||
.worker_startup_check_interval_secs(1)
|
||||
.build_unchecked();
|
||||
|
||||
let mut workers = Vec::new();
|
||||
let mut worker_urls = Vec::new();
|
||||
|
||||
@@ -14,14 +14,7 @@ use common::{
|
||||
mock_mcp_server::MockMCPServer,
|
||||
mock_worker::{HealthStatus, MockWorker, MockWorkerConfig, WorkerType},
|
||||
};
|
||||
use sglang_router_rs::{
|
||||
config::{
|
||||
CircuitBreakerConfig, HealthCheckConfig, PolicyConfig, RetryConfig, RouterConfig,
|
||||
RoutingMode,
|
||||
},
|
||||
core::ConnectionMode,
|
||||
routers::RouterFactory,
|
||||
};
|
||||
use sglang_router_rs::{config::RouterConfig, routers::RouterFactory};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_non_streaming_mcp_minimal_e2e_with_persistence() {
|
||||
@@ -48,45 +41,19 @@ async fn test_non_streaming_mcp_minimal_e2e_with_persistence() {
|
||||
let worker_url = worker.start().await.expect("start worker");
|
||||
|
||||
// Build router config (HTTP OpenAI mode)
|
||||
let router_cfg = RouterConfig {
|
||||
chat_template: None,
|
||||
mode: RoutingMode::OpenAI {
|
||||
worker_urls: vec![worker_url],
|
||||
},
|
||||
connection_mode: ConnectionMode::Http,
|
||||
policy: PolicyConfig::Random,
|
||||
host: "127.0.0.1".to_string(),
|
||||
port: 0,
|
||||
max_payload_size: 8 * 1024 * 1024,
|
||||
request_timeout_secs: 60,
|
||||
worker_startup_timeout_secs: 5,
|
||||
worker_startup_check_interval_secs: 1,
|
||||
dp_aware: false,
|
||||
api_key: None,
|
||||
discovery: None,
|
||||
metrics: None,
|
||||
log_dir: None,
|
||||
log_level: Some("warn".to_string()),
|
||||
request_id_headers: None,
|
||||
max_concurrent_requests: 32,
|
||||
queue_size: 0,
|
||||
queue_timeout_secs: 5,
|
||||
rate_limit_tokens_per_second: None,
|
||||
cors_allowed_origins: vec![],
|
||||
retry: RetryConfig::default(),
|
||||
circuit_breaker: CircuitBreakerConfig::default(),
|
||||
disable_retries: false,
|
||||
disable_circuit_breaker: false,
|
||||
health_check: HealthCheckConfig::default(),
|
||||
enable_igw: false,
|
||||
model_path: None,
|
||||
tokenizer_path: None,
|
||||
history_backend: sglang_router_rs::config::HistoryBackend::Memory,
|
||||
oracle: None,
|
||||
reasoning_parser: None,
|
||||
tool_call_parser: None,
|
||||
tokenizer_cache: sglang_router_rs::config::TokenizerCacheConfig::default(),
|
||||
};
|
||||
let router_cfg = RouterConfig::builder()
|
||||
.openai_mode(vec![worker_url])
|
||||
.random_policy()
|
||||
.host("127.0.0.1")
|
||||
.port(0)
|
||||
.max_payload_size(8 * 1024 * 1024)
|
||||
.request_timeout_secs(60)
|
||||
.worker_startup_timeout_secs(5)
|
||||
.worker_startup_check_interval_secs(1)
|
||||
.log_level("warn")
|
||||
.max_concurrent_requests(32)
|
||||
.queue_timeout_secs(5)
|
||||
.build_unchecked();
|
||||
|
||||
// Create router and context
|
||||
let ctx = common::create_test_context(router_cfg);
|
||||
@@ -249,45 +216,19 @@ async fn test_non_streaming_mcp_minimal_e2e_with_persistence() {
|
||||
#[tokio::test]
|
||||
async fn test_conversations_crud_basic() {
|
||||
// Router in OpenAI mode (no actual upstream calls in these tests)
|
||||
let router_cfg = RouterConfig {
|
||||
chat_template: None,
|
||||
mode: RoutingMode::OpenAI {
|
||||
worker_urls: vec!["http://localhost".to_string()],
|
||||
},
|
||||
connection_mode: ConnectionMode::Http,
|
||||
policy: PolicyConfig::Random,
|
||||
host: "127.0.0.1".to_string(),
|
||||
port: 0,
|
||||
max_payload_size: 8 * 1024 * 1024,
|
||||
request_timeout_secs: 60,
|
||||
worker_startup_timeout_secs: 1,
|
||||
worker_startup_check_interval_secs: 1,
|
||||
dp_aware: false,
|
||||
api_key: None,
|
||||
discovery: None,
|
||||
metrics: None,
|
||||
log_dir: None,
|
||||
log_level: Some("warn".to_string()),
|
||||
request_id_headers: None,
|
||||
max_concurrent_requests: 8,
|
||||
queue_size: 0,
|
||||
queue_timeout_secs: 5,
|
||||
rate_limit_tokens_per_second: None,
|
||||
cors_allowed_origins: vec![],
|
||||
retry: RetryConfig::default(),
|
||||
circuit_breaker: CircuitBreakerConfig::default(),
|
||||
disable_retries: false,
|
||||
disable_circuit_breaker: false,
|
||||
health_check: HealthCheckConfig::default(),
|
||||
enable_igw: false,
|
||||
model_path: None,
|
||||
tokenizer_path: None,
|
||||
history_backend: sglang_router_rs::config::HistoryBackend::Memory,
|
||||
oracle: None,
|
||||
reasoning_parser: None,
|
||||
tool_call_parser: None,
|
||||
tokenizer_cache: sglang_router_rs::config::TokenizerCacheConfig::default(),
|
||||
};
|
||||
let router_cfg = RouterConfig::builder()
|
||||
.openai_mode(vec!["http://localhost".to_string()])
|
||||
.random_policy()
|
||||
.host("127.0.0.1")
|
||||
.port(0)
|
||||
.max_payload_size(8 * 1024 * 1024)
|
||||
.request_timeout_secs(60)
|
||||
.worker_startup_timeout_secs(1)
|
||||
.worker_startup_check_interval_secs(1)
|
||||
.log_level("warn")
|
||||
.max_concurrent_requests(8)
|
||||
.queue_timeout_secs(5)
|
||||
.build_unchecked();
|
||||
|
||||
let ctx = common::create_test_context(router_cfg);
|
||||
let router = RouterFactory::create_router(&ctx).await.expect("router");
|
||||
@@ -585,45 +526,19 @@ async fn test_multi_turn_loop_with_mcp() {
|
||||
let worker_url = worker.start().await.expect("start worker");
|
||||
|
||||
// Build router config
|
||||
let router_cfg = RouterConfig {
|
||||
chat_template: None,
|
||||
mode: RoutingMode::OpenAI {
|
||||
worker_urls: vec![worker_url],
|
||||
},
|
||||
connection_mode: ConnectionMode::Http,
|
||||
policy: PolicyConfig::Random,
|
||||
host: "127.0.0.1".to_string(),
|
||||
port: 0,
|
||||
max_payload_size: 8 * 1024 * 1024,
|
||||
request_timeout_secs: 60,
|
||||
worker_startup_timeout_secs: 5,
|
||||
worker_startup_check_interval_secs: 1,
|
||||
dp_aware: false,
|
||||
api_key: None,
|
||||
discovery: None,
|
||||
metrics: None,
|
||||
log_dir: None,
|
||||
log_level: Some("info".to_string()),
|
||||
request_id_headers: None,
|
||||
max_concurrent_requests: 32,
|
||||
queue_size: 0,
|
||||
queue_timeout_secs: 5,
|
||||
rate_limit_tokens_per_second: None,
|
||||
cors_allowed_origins: vec![],
|
||||
retry: RetryConfig::default(),
|
||||
circuit_breaker: CircuitBreakerConfig::default(),
|
||||
disable_retries: false,
|
||||
disable_circuit_breaker: false,
|
||||
health_check: HealthCheckConfig::default(),
|
||||
enable_igw: false,
|
||||
model_path: None,
|
||||
tokenizer_path: None,
|
||||
history_backend: sglang_router_rs::config::HistoryBackend::Memory,
|
||||
oracle: None,
|
||||
reasoning_parser: None,
|
||||
tool_call_parser: None,
|
||||
tokenizer_cache: sglang_router_rs::config::TokenizerCacheConfig::default(),
|
||||
};
|
||||
let router_cfg = RouterConfig::builder()
|
||||
.openai_mode(vec![worker_url])
|
||||
.random_policy()
|
||||
.host("127.0.0.1")
|
||||
.port(0)
|
||||
.max_payload_size(8 * 1024 * 1024)
|
||||
.request_timeout_secs(60)
|
||||
.worker_startup_timeout_secs(5)
|
||||
.worker_startup_check_interval_secs(1)
|
||||
.log_level("info")
|
||||
.max_concurrent_requests(32)
|
||||
.queue_timeout_secs(5)
|
||||
.build_unchecked();
|
||||
|
||||
let ctx = common::create_test_context(router_cfg);
|
||||
let router = RouterFactory::create_router(&ctx).await.expect("router");
|
||||
@@ -762,45 +677,19 @@ async fn test_max_tool_calls_limit() {
|
||||
});
|
||||
let worker_url = worker.start().await.expect("start worker");
|
||||
|
||||
let router_cfg = RouterConfig {
|
||||
chat_template: None,
|
||||
mode: RoutingMode::OpenAI {
|
||||
worker_urls: vec![worker_url],
|
||||
},
|
||||
connection_mode: ConnectionMode::Http,
|
||||
policy: PolicyConfig::Random,
|
||||
host: "127.0.0.1".to_string(),
|
||||
port: 0,
|
||||
max_payload_size: 8 * 1024 * 1024,
|
||||
request_timeout_secs: 60,
|
||||
worker_startup_timeout_secs: 5,
|
||||
worker_startup_check_interval_secs: 1,
|
||||
dp_aware: false,
|
||||
api_key: None,
|
||||
discovery: None,
|
||||
metrics: None,
|
||||
log_dir: None,
|
||||
log_level: Some("info".to_string()),
|
||||
request_id_headers: None,
|
||||
max_concurrent_requests: 32,
|
||||
queue_size: 0,
|
||||
queue_timeout_secs: 5,
|
||||
rate_limit_tokens_per_second: None,
|
||||
cors_allowed_origins: vec![],
|
||||
retry: RetryConfig::default(),
|
||||
circuit_breaker: CircuitBreakerConfig::default(),
|
||||
disable_retries: false,
|
||||
disable_circuit_breaker: false,
|
||||
health_check: HealthCheckConfig::default(),
|
||||
enable_igw: false,
|
||||
model_path: None,
|
||||
tokenizer_path: None,
|
||||
history_backend: sglang_router_rs::config::HistoryBackend::Memory,
|
||||
oracle: None,
|
||||
reasoning_parser: None,
|
||||
tool_call_parser: None,
|
||||
tokenizer_cache: sglang_router_rs::config::TokenizerCacheConfig::default(),
|
||||
};
|
||||
let router_cfg = RouterConfig::builder()
|
||||
.openai_mode(vec![worker_url])
|
||||
.random_policy()
|
||||
.host("127.0.0.1")
|
||||
.port(0)
|
||||
.max_payload_size(8 * 1024 * 1024)
|
||||
.request_timeout_secs(60)
|
||||
.worker_startup_timeout_secs(5)
|
||||
.worker_startup_check_interval_secs(1)
|
||||
.log_level("info")
|
||||
.max_concurrent_requests(32)
|
||||
.queue_timeout_secs(5)
|
||||
.build_unchecked();
|
||||
|
||||
let ctx = common::create_test_context(router_cfg);
|
||||
let router = RouterFactory::create_router(&ctx).await.expect("router");
|
||||
@@ -905,45 +794,19 @@ async fn setup_streaming_mcp_test() -> (
|
||||
});
|
||||
let worker_url = worker.start().await.expect("start worker");
|
||||
|
||||
let router_cfg = RouterConfig {
|
||||
chat_template: None,
|
||||
mode: RoutingMode::OpenAI {
|
||||
worker_urls: vec![worker_url],
|
||||
},
|
||||
connection_mode: ConnectionMode::Http,
|
||||
policy: PolicyConfig::Random,
|
||||
host: "127.0.0.1".to_string(),
|
||||
port: 0,
|
||||
max_payload_size: 8 * 1024 * 1024,
|
||||
request_timeout_secs: 60,
|
||||
worker_startup_timeout_secs: 5,
|
||||
worker_startup_check_interval_secs: 1,
|
||||
dp_aware: false,
|
||||
api_key: None,
|
||||
discovery: None,
|
||||
metrics: None,
|
||||
log_dir: None,
|
||||
log_level: Some("info".to_string()),
|
||||
request_id_headers: None,
|
||||
max_concurrent_requests: 32,
|
||||
queue_size: 0,
|
||||
queue_timeout_secs: 5,
|
||||
rate_limit_tokens_per_second: None,
|
||||
cors_allowed_origins: vec![],
|
||||
retry: RetryConfig::default(),
|
||||
circuit_breaker: CircuitBreakerConfig::default(),
|
||||
disable_retries: false,
|
||||
disable_circuit_breaker: false,
|
||||
health_check: HealthCheckConfig::default(),
|
||||
enable_igw: false,
|
||||
model_path: None,
|
||||
tokenizer_path: None,
|
||||
history_backend: sglang_router_rs::config::HistoryBackend::Memory,
|
||||
oracle: None,
|
||||
reasoning_parser: None,
|
||||
tool_call_parser: None,
|
||||
tokenizer_cache: sglang_router_rs::config::TokenizerCacheConfig::default(),
|
||||
};
|
||||
let router_cfg = RouterConfig::builder()
|
||||
.openai_mode(vec![worker_url])
|
||||
.random_policy()
|
||||
.host("127.0.0.1")
|
||||
.port(0)
|
||||
.max_payload_size(8 * 1024 * 1024)
|
||||
.request_timeout_secs(60)
|
||||
.worker_startup_timeout_secs(5)
|
||||
.worker_startup_check_interval_secs(1)
|
||||
.log_level("info")
|
||||
.max_concurrent_requests(32)
|
||||
.queue_timeout_secs(5)
|
||||
.build_unchecked();
|
||||
|
||||
let ctx = common::create_test_context(router_cfg);
|
||||
let router = RouterFactory::create_router(&ctx).await.expect("router");
|
||||
@@ -1347,45 +1210,19 @@ async fn test_streaming_multi_turn_with_mcp() {
|
||||
#[tokio::test]
|
||||
async fn test_conversation_items_create_and_get() {
|
||||
// Test creating items and getting a specific item
|
||||
let router_cfg = RouterConfig {
|
||||
chat_template: None,
|
||||
mode: RoutingMode::OpenAI {
|
||||
worker_urls: vec!["http://localhost".to_string()],
|
||||
},
|
||||
connection_mode: ConnectionMode::Http,
|
||||
policy: PolicyConfig::Random,
|
||||
host: "127.0.0.1".to_string(),
|
||||
port: 0,
|
||||
max_payload_size: 8 * 1024 * 1024,
|
||||
request_timeout_secs: 60,
|
||||
worker_startup_timeout_secs: 1,
|
||||
worker_startup_check_interval_secs: 1,
|
||||
dp_aware: false,
|
||||
api_key: None,
|
||||
discovery: None,
|
||||
metrics: None,
|
||||
log_dir: None,
|
||||
log_level: Some("warn".to_string()),
|
||||
request_id_headers: None,
|
||||
max_concurrent_requests: 8,
|
||||
queue_size: 0,
|
||||
queue_timeout_secs: 5,
|
||||
rate_limit_tokens_per_second: None,
|
||||
cors_allowed_origins: vec![],
|
||||
retry: RetryConfig::default(),
|
||||
circuit_breaker: CircuitBreakerConfig::default(),
|
||||
disable_retries: false,
|
||||
disable_circuit_breaker: false,
|
||||
health_check: HealthCheckConfig::default(),
|
||||
enable_igw: false,
|
||||
model_path: None,
|
||||
tokenizer_path: None,
|
||||
history_backend: sglang_router_rs::config::HistoryBackend::Memory,
|
||||
oracle: None,
|
||||
reasoning_parser: None,
|
||||
tool_call_parser: None,
|
||||
tokenizer_cache: sglang_router_rs::config::TokenizerCacheConfig::default(),
|
||||
};
|
||||
let router_cfg = RouterConfig::builder()
|
||||
.openai_mode(vec!["http://localhost".to_string()])
|
||||
.random_policy()
|
||||
.host("127.0.0.1")
|
||||
.port(0)
|
||||
.max_payload_size(8 * 1024 * 1024)
|
||||
.request_timeout_secs(60)
|
||||
.worker_startup_timeout_secs(1)
|
||||
.worker_startup_check_interval_secs(1)
|
||||
.log_level("warn")
|
||||
.max_concurrent_requests(8)
|
||||
.queue_timeout_secs(5)
|
||||
.build_unchecked();
|
||||
|
||||
let ctx = common::create_test_context(router_cfg);
|
||||
let router = RouterFactory::create_router(&ctx).await.expect("router");
|
||||
@@ -1449,45 +1286,19 @@ async fn test_conversation_items_create_and_get() {
|
||||
#[tokio::test]
|
||||
async fn test_conversation_items_delete() {
|
||||
// Test deleting an item from a conversation
|
||||
let router_cfg = RouterConfig {
|
||||
chat_template: None,
|
||||
mode: RoutingMode::OpenAI {
|
||||
worker_urls: vec!["http://localhost".to_string()],
|
||||
},
|
||||
connection_mode: ConnectionMode::Http,
|
||||
policy: PolicyConfig::Random,
|
||||
host: "127.0.0.1".to_string(),
|
||||
port: 0,
|
||||
max_payload_size: 8 * 1024 * 1024,
|
||||
request_timeout_secs: 60,
|
||||
worker_startup_timeout_secs: 1,
|
||||
worker_startup_check_interval_secs: 1,
|
||||
dp_aware: false,
|
||||
api_key: None,
|
||||
discovery: None,
|
||||
metrics: None,
|
||||
log_dir: None,
|
||||
log_level: Some("warn".to_string()),
|
||||
request_id_headers: None,
|
||||
max_concurrent_requests: 8,
|
||||
queue_size: 0,
|
||||
queue_timeout_secs: 5,
|
||||
rate_limit_tokens_per_second: None,
|
||||
cors_allowed_origins: vec![],
|
||||
retry: RetryConfig::default(),
|
||||
circuit_breaker: CircuitBreakerConfig::default(),
|
||||
disable_retries: false,
|
||||
disable_circuit_breaker: false,
|
||||
health_check: HealthCheckConfig::default(),
|
||||
enable_igw: false,
|
||||
model_path: None,
|
||||
tokenizer_path: None,
|
||||
history_backend: sglang_router_rs::config::HistoryBackend::Memory,
|
||||
oracle: None,
|
||||
reasoning_parser: None,
|
||||
tool_call_parser: None,
|
||||
tokenizer_cache: sglang_router_rs::config::TokenizerCacheConfig::default(),
|
||||
};
|
||||
let router_cfg = RouterConfig::builder()
|
||||
.openai_mode(vec!["http://localhost".to_string()])
|
||||
.random_policy()
|
||||
.host("127.0.0.1")
|
||||
.port(0)
|
||||
.max_payload_size(8 * 1024 * 1024)
|
||||
.request_timeout_secs(60)
|
||||
.worker_startup_timeout_secs(1)
|
||||
.worker_startup_check_interval_secs(1)
|
||||
.log_level("warn")
|
||||
.max_concurrent_requests(8)
|
||||
.queue_timeout_secs(5)
|
||||
.build_unchecked();
|
||||
|
||||
let ctx = common::create_test_context(router_cfg);
|
||||
let router = RouterFactory::create_router(&ctx).await.expect("router");
|
||||
@@ -1557,45 +1368,19 @@ async fn test_conversation_items_delete() {
|
||||
#[tokio::test]
|
||||
async fn test_conversation_items_max_limit() {
|
||||
// Test that creating > 20 items returns error
|
||||
let router_cfg = RouterConfig {
|
||||
chat_template: None,
|
||||
mode: RoutingMode::OpenAI {
|
||||
worker_urls: vec!["http://localhost".to_string()],
|
||||
},
|
||||
connection_mode: ConnectionMode::Http,
|
||||
policy: PolicyConfig::Random,
|
||||
host: "127.0.0.1".to_string(),
|
||||
port: 0,
|
||||
max_payload_size: 8 * 1024 * 1024,
|
||||
request_timeout_secs: 60,
|
||||
worker_startup_timeout_secs: 1,
|
||||
worker_startup_check_interval_secs: 1,
|
||||
dp_aware: false,
|
||||
api_key: None,
|
||||
discovery: None,
|
||||
metrics: None,
|
||||
log_dir: None,
|
||||
log_level: Some("warn".to_string()),
|
||||
request_id_headers: None,
|
||||
max_concurrent_requests: 8,
|
||||
queue_size: 0,
|
||||
queue_timeout_secs: 5,
|
||||
rate_limit_tokens_per_second: None,
|
||||
cors_allowed_origins: vec![],
|
||||
retry: RetryConfig::default(),
|
||||
circuit_breaker: CircuitBreakerConfig::default(),
|
||||
disable_retries: false,
|
||||
disable_circuit_breaker: false,
|
||||
health_check: HealthCheckConfig::default(),
|
||||
enable_igw: false,
|
||||
model_path: None,
|
||||
tokenizer_path: None,
|
||||
history_backend: sglang_router_rs::config::HistoryBackend::Memory,
|
||||
oracle: None,
|
||||
reasoning_parser: None,
|
||||
tool_call_parser: None,
|
||||
tokenizer_cache: sglang_router_rs::config::TokenizerCacheConfig::default(),
|
||||
};
|
||||
let router_cfg = RouterConfig::builder()
|
||||
.openai_mode(vec!["http://localhost".to_string()])
|
||||
.random_policy()
|
||||
.host("127.0.0.1")
|
||||
.port(0)
|
||||
.max_payload_size(8 * 1024 * 1024)
|
||||
.request_timeout_secs(60)
|
||||
.worker_startup_timeout_secs(1)
|
||||
.worker_startup_check_interval_secs(1)
|
||||
.log_level("warn")
|
||||
.max_concurrent_requests(8)
|
||||
.queue_timeout_secs(5)
|
||||
.build_unchecked();
|
||||
|
||||
let ctx = common::create_test_context(router_cfg);
|
||||
let router = RouterFactory::create_router(&ctx).await.expect("router");
|
||||
@@ -1635,45 +1420,19 @@ async fn test_conversation_items_max_limit() {
|
||||
#[tokio::test]
|
||||
async fn test_conversation_items_unsupported_type() {
|
||||
// Test that unsupported item types return error
|
||||
let router_cfg = RouterConfig {
|
||||
chat_template: None,
|
||||
mode: RoutingMode::OpenAI {
|
||||
worker_urls: vec!["http://localhost".to_string()],
|
||||
},
|
||||
connection_mode: ConnectionMode::Http,
|
||||
policy: PolicyConfig::Random,
|
||||
host: "127.0.0.1".to_string(),
|
||||
port: 0,
|
||||
max_payload_size: 8 * 1024 * 1024,
|
||||
request_timeout_secs: 60,
|
||||
worker_startup_timeout_secs: 1,
|
||||
worker_startup_check_interval_secs: 1,
|
||||
dp_aware: false,
|
||||
api_key: None,
|
||||
discovery: None,
|
||||
metrics: None,
|
||||
log_dir: None,
|
||||
log_level: Some("warn".to_string()),
|
||||
request_id_headers: None,
|
||||
max_concurrent_requests: 8,
|
||||
queue_size: 0,
|
||||
queue_timeout_secs: 5,
|
||||
rate_limit_tokens_per_second: None,
|
||||
cors_allowed_origins: vec![],
|
||||
retry: RetryConfig::default(),
|
||||
circuit_breaker: CircuitBreakerConfig::default(),
|
||||
disable_retries: false,
|
||||
disable_circuit_breaker: false,
|
||||
health_check: HealthCheckConfig::default(),
|
||||
enable_igw: false,
|
||||
model_path: None,
|
||||
tokenizer_path: None,
|
||||
history_backend: sglang_router_rs::config::HistoryBackend::Memory,
|
||||
oracle: None,
|
||||
reasoning_parser: None,
|
||||
tool_call_parser: None,
|
||||
tokenizer_cache: sglang_router_rs::config::TokenizerCacheConfig::default(),
|
||||
};
|
||||
let router_cfg = RouterConfig::builder()
|
||||
.openai_mode(vec!["http://localhost".to_string()])
|
||||
.random_policy()
|
||||
.host("127.0.0.1")
|
||||
.port(0)
|
||||
.max_payload_size(8 * 1024 * 1024)
|
||||
.request_timeout_secs(60)
|
||||
.worker_startup_timeout_secs(1)
|
||||
.worker_startup_check_interval_secs(1)
|
||||
.log_level("warn")
|
||||
.max_concurrent_requests(8)
|
||||
.queue_timeout_secs(5)
|
||||
.build_unchecked();
|
||||
|
||||
let ctx = common::create_test_context(router_cfg);
|
||||
let router = RouterFactory::create_router(&ctx).await.expect("router");
|
||||
@@ -1712,45 +1471,19 @@ async fn test_conversation_items_unsupported_type() {
|
||||
#[tokio::test]
|
||||
async fn test_conversation_items_multi_conversation_sharing() {
|
||||
// Test that items can be shared across conversations via soft delete
|
||||
let router_cfg = RouterConfig {
|
||||
chat_template: None,
|
||||
mode: RoutingMode::OpenAI {
|
||||
worker_urls: vec!["http://localhost".to_string()],
|
||||
},
|
||||
connection_mode: ConnectionMode::Http,
|
||||
policy: PolicyConfig::Random,
|
||||
host: "127.0.0.1".to_string(),
|
||||
port: 0,
|
||||
max_payload_size: 8 * 1024 * 1024,
|
||||
request_timeout_secs: 60,
|
||||
worker_startup_timeout_secs: 1,
|
||||
worker_startup_check_interval_secs: 1,
|
||||
dp_aware: false,
|
||||
api_key: None,
|
||||
discovery: None,
|
||||
metrics: None,
|
||||
log_dir: None,
|
||||
log_level: Some("warn".to_string()),
|
||||
request_id_headers: None,
|
||||
max_concurrent_requests: 8,
|
||||
queue_size: 0,
|
||||
queue_timeout_secs: 5,
|
||||
rate_limit_tokens_per_second: None,
|
||||
cors_allowed_origins: vec![],
|
||||
retry: RetryConfig::default(),
|
||||
circuit_breaker: CircuitBreakerConfig::default(),
|
||||
disable_retries: false,
|
||||
disable_circuit_breaker: false,
|
||||
health_check: HealthCheckConfig::default(),
|
||||
enable_igw: false,
|
||||
model_path: None,
|
||||
tokenizer_path: None,
|
||||
history_backend: sglang_router_rs::config::HistoryBackend::Memory,
|
||||
oracle: None,
|
||||
reasoning_parser: None,
|
||||
tool_call_parser: None,
|
||||
tokenizer_cache: sglang_router_rs::config::TokenizerCacheConfig::default(),
|
||||
};
|
||||
let router_cfg = RouterConfig::builder()
|
||||
.openai_mode(vec!["http://localhost".to_string()])
|
||||
.random_policy()
|
||||
.host("127.0.0.1")
|
||||
.port(0)
|
||||
.max_payload_size(8 * 1024 * 1024)
|
||||
.request_timeout_secs(60)
|
||||
.worker_startup_timeout_secs(1)
|
||||
.worker_startup_check_interval_secs(1)
|
||||
.log_level("warn")
|
||||
.max_concurrent_requests(8)
|
||||
.queue_timeout_secs(5)
|
||||
.build_unchecked();
|
||||
|
||||
let ctx = common::create_test_context(router_cfg);
|
||||
let router = RouterFactory::create_router(&ctx).await.expect("router");
|
||||
|
||||
@@ -20,16 +20,12 @@ struct TestContext {
|
||||
|
||||
impl TestContext {
|
||||
async fn new(worker_configs: Vec<MockWorkerConfig>) -> Self {
|
||||
let mut config = RouterConfig {
|
||||
chat_template: None,
|
||||
mode: RoutingMode::Regular {
|
||||
worker_urls: vec![],
|
||||
},
|
||||
port: 3004,
|
||||
worker_startup_timeout_secs: 1,
|
||||
worker_startup_check_interval_secs: 1,
|
||||
..Default::default()
|
||||
};
|
||||
let mut config = RouterConfig::builder()
|
||||
.regular_mode(vec![])
|
||||
.port(3004)
|
||||
.worker_startup_timeout_secs(1)
|
||||
.worker_startup_check_interval_secs(1)
|
||||
.build_unchecked();
|
||||
|
||||
let mut workers = Vec::new();
|
||||
let mut worker_urls = Vec::new();
|
||||
|
||||
@@ -902,17 +902,10 @@ async fn test_openai_router_models_auth_forwarding() {
|
||||
|
||||
#[test]
|
||||
fn oracle_config_validation_requires_config_when_enabled() {
|
||||
let config = RouterConfig {
|
||||
chat_template: None,
|
||||
mode: RoutingMode::OpenAI {
|
||||
worker_urls: vec!["https://api.openai.com".to_string()],
|
||||
},
|
||||
history_backend: HistoryBackend::Oracle,
|
||||
oracle: None,
|
||||
reasoning_parser: None,
|
||||
tool_call_parser: None,
|
||||
..Default::default()
|
||||
};
|
||||
let config = RouterConfig::builder()
|
||||
.openai_mode(vec!["https://api.openai.com".to_string()])
|
||||
.history_backend(HistoryBackend::Oracle)
|
||||
.build_unchecked();
|
||||
|
||||
let err =
|
||||
ConfigValidator::validate(&config).expect_err("config should fail without oracle details");
|
||||
@@ -927,13 +920,9 @@ fn oracle_config_validation_requires_config_when_enabled() {
|
||||
|
||||
#[test]
|
||||
fn oracle_config_validation_accepts_dsn_only() {
|
||||
let config = RouterConfig {
|
||||
chat_template: None,
|
||||
mode: RoutingMode::OpenAI {
|
||||
worker_urls: vec!["https://api.openai.com".to_string()],
|
||||
},
|
||||
history_backend: HistoryBackend::Oracle,
|
||||
oracle: Some(OracleConfig {
|
||||
let config = RouterConfig::builder()
|
||||
.openai_mode(vec!["https://api.openai.com".to_string()])
|
||||
.oracle_history(OracleConfig {
|
||||
wallet_path: None,
|
||||
connect_descriptor: "tcps://db.example.com:1522/service".to_string(),
|
||||
username: "scott".to_string(),
|
||||
@@ -941,22 +930,17 @@ fn oracle_config_validation_accepts_dsn_only() {
|
||||
pool_min: 1,
|
||||
pool_max: 4,
|
||||
pool_timeout_secs: 30,
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
})
|
||||
.build_unchecked();
|
||||
|
||||
ConfigValidator::validate(&config).expect("dsn-based config should validate");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn oracle_config_validation_accepts_wallet_alias() {
|
||||
let config = RouterConfig {
|
||||
chat_template: None,
|
||||
mode: RoutingMode::OpenAI {
|
||||
worker_urls: vec!["https://api.openai.com".to_string()],
|
||||
},
|
||||
history_backend: HistoryBackend::Oracle,
|
||||
oracle: Some(OracleConfig {
|
||||
let config = RouterConfig::builder()
|
||||
.openai_mode(vec!["https://api.openai.com".to_string()])
|
||||
.oracle_history(OracleConfig {
|
||||
wallet_path: Some("/etc/sglang/oracle-wallet".to_string()),
|
||||
connect_descriptor: "db_low".to_string(),
|
||||
username: "app_user".to_string(),
|
||||
@@ -964,9 +948,8 @@ fn oracle_config_validation_accepts_wallet_alias() {
|
||||
pool_min: 1,
|
||||
pool_max: 8,
|
||||
pool_timeout_secs: 45,
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
})
|
||||
.build_unchecked();
|
||||
|
||||
ConfigValidator::validate(&config).expect("wallet-based config should validate");
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
mod test_pd_routing {
|
||||
use serde_json::json;
|
||||
use sglang_router_rs::{
|
||||
config::{CircuitBreakerConfig, PolicyConfig, RetryConfig, RouterConfig, RoutingMode},
|
||||
core::{BasicWorkerBuilder, ConnectionMode, Worker, WorkerType},
|
||||
config::{PolicyConfig, RouterConfig, RoutingMode},
|
||||
core::{BasicWorkerBuilder, Worker, WorkerType},
|
||||
routers::{http::pd_types::PDSelectionPolicy, RouterFactory},
|
||||
};
|
||||
|
||||
@@ -162,42 +162,24 @@ mod test_pd_routing {
|
||||
];
|
||||
|
||||
for (mode, policy) in test_cases {
|
||||
let config = RouterConfig {
|
||||
chat_template: None,
|
||||
mode,
|
||||
policy,
|
||||
host: "127.0.0.1".to_string(),
|
||||
port: 3001,
|
||||
max_payload_size: 1024 * 1024,
|
||||
request_timeout_secs: 60,
|
||||
worker_startup_timeout_secs: 10,
|
||||
worker_startup_check_interval_secs: 1,
|
||||
dp_aware: false,
|
||||
api_key: None,
|
||||
discovery: None,
|
||||
metrics: None,
|
||||
log_dir: None,
|
||||
log_level: None,
|
||||
request_id_headers: None,
|
||||
max_concurrent_requests: 64,
|
||||
queue_size: 0,
|
||||
queue_timeout_secs: 60,
|
||||
cors_allowed_origins: vec![],
|
||||
retry: RetryConfig::default(),
|
||||
circuit_breaker: CircuitBreakerConfig::default(),
|
||||
disable_retries: false,
|
||||
disable_circuit_breaker: false,
|
||||
health_check: sglang_router_rs::config::HealthCheckConfig::default(),
|
||||
enable_igw: false,
|
||||
rate_limit_tokens_per_second: None,
|
||||
connection_mode: ConnectionMode::Http,
|
||||
model_path: None,
|
||||
tokenizer_path: None,
|
||||
history_backend: sglang_router_rs::config::HistoryBackend::Memory,
|
||||
oracle: None,
|
||||
reasoning_parser: None,
|
||||
tool_call_parser: None,
|
||||
tokenizer_cache: sglang_router_rs::config::TokenizerCacheConfig::default(),
|
||||
let config = match mode {
|
||||
RoutingMode::PrefillDecode {
|
||||
prefill_urls,
|
||||
decode_urls,
|
||||
..
|
||||
} => RouterConfig::builder()
|
||||
.prefill_decode_mode(prefill_urls, decode_urls)
|
||||
.policy(policy)
|
||||
.host("127.0.0.1")
|
||||
.port(3001)
|
||||
.max_payload_size(1024 * 1024)
|
||||
.request_timeout_secs(60)
|
||||
.worker_startup_timeout_secs(10)
|
||||
.worker_startup_check_interval_secs(1)
|
||||
.max_concurrent_requests(64)
|
||||
.queue_timeout_secs(60)
|
||||
.build_unchecked(),
|
||||
_ => panic!("Expected PrefillDecode mode"),
|
||||
};
|
||||
|
||||
let app_context = {
|
||||
|
||||
Reference in New Issue
Block a user