Consistent formatting pass: line wrapping, import sorting, trailing whitespace removal, let-chain indentation, merged derive attributes, and unsafe block reformatting. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
702 lines
22 KiB
Rust
702 lines
22 KiB
Rust
//! Advanced LLM serving server with comprehensive features
|
|
//!
|
|
//! Integrates all advanced LLM capabilities into a unified server:
|
|
//! - Rate limiting and token management
|
|
//! - Structured generation and validation
|
|
//! - Grammar-constrained sampling
|
|
//! - Cost attribution and billing
|
|
//! - Request prioritization and queuing
|
|
//! - Advanced sampling strategies
|
|
//! - Streaming responses (SSE/WebSocket)
|
|
//! - Multi-model serving and routing
|
|
|
|
use axum::{
|
|
Router,
|
|
extract::{Path, State},
|
|
response::{Json, Sse},
|
|
routing::{get, post},
|
|
};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::{sync::Arc, time::Duration};
|
|
use tokio::net::TcpListener;
|
|
use tower_http::{cors::CorsLayer, timeout::TimeoutLayer, trace::TraceLayer};
|
|
|
|
use crate::{
|
|
ApiError,
|
|
ApiResult,
|
|
// grammar_sampling::{ContextFreeGrammar, GrammarGuidedSampler}, // Temporarily disabled
|
|
billing::BillingManager,
|
|
multi_model::MultiModelManager,
|
|
queue_management::QueueManager,
|
|
rate_limiting::RateLimitManager,
|
|
streaming::StreamManager,
|
|
structured_generation::StructuredGenerationManager,
|
|
};
|
|
|
|
/// Advanced server configuration
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct AdvancedServerConfig {
|
|
/// Basic server settings
|
|
pub host: String,
|
|
pub port: u16,
|
|
pub timeout_seconds: u64,
|
|
|
|
/// Rate limiting configuration
|
|
pub enable_rate_limiting: bool,
|
|
pub rate_limit_config: RateLimitConfig,
|
|
|
|
/// Structured generation settings
|
|
pub enable_structured_generation: bool,
|
|
pub max_schema_size: usize,
|
|
|
|
/// Grammar sampling settings
|
|
pub enable_grammar_sampling: bool,
|
|
pub max_grammar_complexity: usize,
|
|
|
|
/// Billing configuration
|
|
pub enable_billing: bool,
|
|
pub billing_currency: String,
|
|
|
|
/// Queue management settings
|
|
pub enable_queue_management: bool,
|
|
pub max_queue_size: usize,
|
|
pub max_concurrent_requests: usize,
|
|
|
|
/// Streaming configuration
|
|
pub enable_streaming: bool,
|
|
pub max_stream_connections: usize,
|
|
pub stream_buffer_size: usize,
|
|
|
|
/// Multi-model settings
|
|
pub enable_multi_model: bool,
|
|
pub max_loaded_models: usize,
|
|
pub model_cache_size_gb: f32,
|
|
|
|
/// Security settings
|
|
pub enable_auth: bool,
|
|
pub api_key_required: bool,
|
|
pub cors_origins: Vec<String>,
|
|
}
|
|
|
|
/// Rate limiting configuration
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct RateLimitConfig {
|
|
pub requests_per_minute: u64,
|
|
pub tokens_per_minute: u64,
|
|
pub burst_allowance: f64,
|
|
pub enable_user_tiers: bool,
|
|
}
|
|
|
|
impl Default for AdvancedServerConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
host: "127.0.0.1".to_string(),
|
|
port: 8080,
|
|
timeout_seconds: 300,
|
|
|
|
enable_rate_limiting: true,
|
|
rate_limit_config: RateLimitConfig {
|
|
requests_per_minute: 60,
|
|
tokens_per_minute: 10000,
|
|
burst_allowance: 2.0,
|
|
enable_user_tiers: true,
|
|
},
|
|
|
|
enable_structured_generation: true,
|
|
max_schema_size: 1024 * 1024, // 1MB
|
|
|
|
enable_grammar_sampling: true,
|
|
max_grammar_complexity: 1000,
|
|
|
|
enable_billing: true,
|
|
billing_currency: "USD".to_string(),
|
|
|
|
enable_queue_management: true,
|
|
max_queue_size: 1000,
|
|
max_concurrent_requests: 100,
|
|
|
|
enable_streaming: true,
|
|
max_stream_connections: 10000,
|
|
stream_buffer_size: 4096,
|
|
|
|
enable_multi_model: true,
|
|
max_loaded_models: 10,
|
|
model_cache_size_gb: 100.0,
|
|
|
|
enable_auth: false,
|
|
api_key_required: false,
|
|
cors_origins: vec!["*".to_string()],
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Advanced serving server state
|
|
#[derive(Clone)]
|
|
pub struct AdvancedServerState {
|
|
pub config: AdvancedServerConfig,
|
|
pub rate_limiter: Option<Arc<RateLimitManager>>,
|
|
pub structured_gen: Option<Arc<StructuredGenerationManager>>,
|
|
pub billing_manager: Option<Arc<BillingManager>>,
|
|
pub queue_manager: Option<Arc<QueueManager>>,
|
|
pub stream_manager: Option<Arc<StreamManager>>,
|
|
pub model_manager: Option<Arc<MultiModelManager>>,
|
|
}
|
|
|
|
impl AdvancedServerState {
|
|
/// Create new advanced server state
|
|
#[must_use]
|
|
pub fn new(config: AdvancedServerConfig) -> Self {
|
|
let rate_limiter = if config.enable_rate_limiting {
|
|
Some(Arc::new(RateLimitManager::new()))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let structured_gen = if config.enable_structured_generation {
|
|
Some(Arc::new(StructuredGenerationManager::new()))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let billing_manager = if config.enable_billing {
|
|
Some(Arc::new(BillingManager::new()))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let queue_manager = if config.enable_queue_management {
|
|
Some(Arc::new(QueueManager::new(
|
|
config.max_concurrent_requests,
|
|
config.max_queue_size,
|
|
)))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let stream_manager = if config.enable_streaming {
|
|
let stream_config = crate::streaming::StreamConfig {
|
|
buffer_size: config.stream_buffer_size,
|
|
max_concurrent_streams: config.max_stream_connections,
|
|
..Default::default()
|
|
};
|
|
Some(Arc::new(StreamManager::new(stream_config)))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let model_manager = if config.enable_multi_model {
|
|
Some(Arc::new(MultiModelManager::new()))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
Self {
|
|
config,
|
|
rate_limiter,
|
|
structured_gen,
|
|
billing_manager,
|
|
queue_manager,
|
|
stream_manager,
|
|
model_manager,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Advanced serving server
|
|
pub struct AdvancedServingServer {
|
|
state: AdvancedServerState,
|
|
}
|
|
|
|
impl AdvancedServingServer {
|
|
/// Create new advanced serving server
|
|
#[must_use]
|
|
pub fn new(config: AdvancedServerConfig) -> Self {
|
|
Self {
|
|
state: AdvancedServerState::new(config),
|
|
}
|
|
}
|
|
|
|
/// Build the complete router with all advanced features
|
|
pub fn app(&self) -> Router {
|
|
let mut router = Router::new()
|
|
// Basic health and model endpoints
|
|
.route("/health", get(health_check))
|
|
.route("/health/ready", get(health_check))
|
|
.route("/health/live", get(health_check))
|
|
.route("/v1/models", get(list_models))
|
|
// Standard inference endpoints
|
|
.route("/v1/completions", post(completions))
|
|
.route("/v1/chat/completions", post(chat_completions))
|
|
// Cached inference endpoints (existing)
|
|
.route("/v1/cached/completions", post(cached_completions))
|
|
.route("/v1/cached/chat/completions", post(cached_chat_completions))
|
|
.route("/v1/cache/stats", get(cache_stats))
|
|
.route("/v1/cache/clear", post(clear_cache));
|
|
|
|
// Add rate limiting endpoints if enabled
|
|
if self.state.config.enable_rate_limiting {
|
|
router = router
|
|
.route("/v1/rate-limits/status/:user_id", get(rate_limit_status))
|
|
.route("/v1/rate-limits/reset/:user_id", post(reset_rate_limits));
|
|
}
|
|
|
|
// Add structured generation endpoints if enabled
|
|
if self.state.config.enable_structured_generation {
|
|
router = router
|
|
.route("/v1/structured/generate", post(structured_generate))
|
|
.route("/v1/structured/schemas", get(list_schemas))
|
|
.route("/v1/structured/schemas", post(create_schema))
|
|
.route("/v1/structured/validate", post(validate_output));
|
|
}
|
|
|
|
// Add grammar sampling endpoints if enabled
|
|
if self.state.config.enable_grammar_sampling {
|
|
router = router
|
|
.route("/v1/grammar/generate", post(grammar_generate))
|
|
.route("/v1/grammar/validate", post(validate_grammar))
|
|
.route("/v1/grammar/parse", post(parse_bnf_grammar));
|
|
}
|
|
|
|
// Add billing endpoints if enabled
|
|
if self.state.config.enable_billing {
|
|
router = router
|
|
.route("/v1/billing/costs/:user_id", get(get_user_costs))
|
|
.route("/v1/billing/budgets", get(list_budgets))
|
|
.route("/v1/billing/budgets", post(create_budget))
|
|
.route("/v1/billing/forecasts/:entity_id", get(get_forecast));
|
|
}
|
|
|
|
// Add queue management endpoints if enabled
|
|
if self.state.config.enable_queue_management {
|
|
router = router
|
|
.route("/v1/queue/stats", get(queue_stats))
|
|
.route("/v1/queue/status/:request_id", get(request_status))
|
|
.route("/v1/queue/cancel/:request_id", post(cancel_request))
|
|
.route("/v1/queue/priority", post(priority_completions));
|
|
}
|
|
|
|
// Add streaming endpoints if enabled
|
|
if self.state.config.enable_streaming {
|
|
router = router
|
|
.route("/v1/stream/sse/:user_id", get(sse_stream))
|
|
.route("/v1/stream/ws/:user_id", get(websocket_upgrade))
|
|
.route("/v1/stream/chunked/:user_id", get(chunked_stream))
|
|
.route("/v1/stream/stats", get(stream_stats));
|
|
}
|
|
|
|
// Add multi-model endpoints if enabled
|
|
if self.state.config.enable_multi_model {
|
|
router = router
|
|
.route("/v1/models/load", post(load_model))
|
|
.route("/v1/models/unload/:instance_id", post(unload_model))
|
|
.route("/v1/models/stats", get(model_stats))
|
|
.route("/v1/models/ab-test", post(create_ab_test))
|
|
.route("/v1/models/canary", post(create_canary))
|
|
.route("/v1/models/ensemble", post(create_ensemble));
|
|
}
|
|
|
|
// Add state and middleware
|
|
router
|
|
.with_state(self.state.clone())
|
|
.layer(CorsLayer::permissive())
|
|
.layer(TraceLayer::new_for_http())
|
|
.layer(TimeoutLayer::new(Duration::from_secs(
|
|
self.state.config.timeout_seconds,
|
|
)))
|
|
}
|
|
|
|
/// Start the advanced server
|
|
pub async fn serve(&self) -> ApiResult<()> {
|
|
let app = self.app();
|
|
|
|
let addr = format!("{}:{}", self.state.config.host, self.state.config.port);
|
|
let listener = TcpListener::bind(&addr)
|
|
.await
|
|
.map_err(|e| ApiError::internal(format!("Failed to bind to {addr}: {e}")))?;
|
|
|
|
tracing::info!("Advanced LLM serving server listening on {}", addr);
|
|
tracing::info!(
|
|
"Features enabled: rate_limiting={}, structured_gen={}, grammar_sampling={}, billing={}, queue_mgmt={}, streaming={}, multi_model={}",
|
|
self.state.config.enable_rate_limiting,
|
|
self.state.config.enable_structured_generation,
|
|
self.state.config.enable_grammar_sampling,
|
|
self.state.config.enable_billing,
|
|
self.state.config.enable_queue_management,
|
|
self.state.config.enable_streaming,
|
|
self.state.config.enable_multi_model
|
|
);
|
|
|
|
// Start background tasks
|
|
self.start_background_tasks().await;
|
|
|
|
axum::serve(listener, app)
|
|
.await
|
|
.map_err(|e| ApiError::internal(format!("Server error: {e}")))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Start background tasks for cleanup and monitoring
|
|
async fn start_background_tasks(&self) {
|
|
if let Some(queue_manager) = &self.state.queue_manager {
|
|
let queue_manager = queue_manager.clone();
|
|
tokio::spawn(async move {
|
|
let mut interval = tokio::time::interval(Duration::from_secs(60));
|
|
loop {
|
|
interval.tick().await;
|
|
queue_manager.cleanup_expired_requests().await;
|
|
}
|
|
});
|
|
}
|
|
|
|
if let Some(rate_limiter) = &self.state.rate_limiter {
|
|
let rate_limiter = rate_limiter.clone();
|
|
tokio::spawn(async move {
|
|
let mut interval = tokio::time::interval(Duration::from_secs(300));
|
|
loop {
|
|
interval.tick().await;
|
|
rate_limiter.cleanup_expired_entries().await;
|
|
}
|
|
});
|
|
}
|
|
|
|
if let Some(stream_manager) = &self.state.stream_manager {
|
|
stream_manager.start_cleanup_task();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handler implementations (simplified for brevity - would implement full logic)
|
|
|
|
async fn health_check() -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"status": "healthy"})))
|
|
}
|
|
|
|
async fn list_models(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"models": []})))
|
|
}
|
|
|
|
async fn completions(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(
|
|
serde_json::json!({"response": "Generated completion"}),
|
|
))
|
|
}
|
|
|
|
async fn chat_completions(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(
|
|
serde_json::json!({"response": "Generated chat completion"}),
|
|
))
|
|
}
|
|
|
|
async fn cached_completions(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"response": "Cached completion"})))
|
|
}
|
|
|
|
async fn cached_chat_completions(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(
|
|
serde_json::json!({"response": "Cached chat completion"}),
|
|
))
|
|
}
|
|
|
|
async fn cache_stats(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"cache_stats": {}})))
|
|
}
|
|
|
|
async fn clear_cache(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"status": "cache cleared"})))
|
|
}
|
|
|
|
// Rate limiting handlers
|
|
async fn rate_limit_status(
|
|
Path(user_id): Path<String>,
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
if let Some(rate_limiter) = &state.rate_limiter {
|
|
if let Some(usage) = rate_limiter.get_user_usage(&user_id) {
|
|
Ok(Json(serde_json::json!(usage)))
|
|
} else {
|
|
Ok(Json(serde_json::json!({"error": "User not found"})))
|
|
}
|
|
} else {
|
|
Err(ApiError::not_found("Rate limiting not enabled"))
|
|
}
|
|
}
|
|
|
|
async fn reset_rate_limits(
|
|
Path(user_id): Path<String>,
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"status": "rate limits reset"})))
|
|
}
|
|
|
|
// Structured generation handlers
|
|
async fn structured_generate(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(
|
|
serde_json::json!({"structured_response": "Generated"}),
|
|
))
|
|
}
|
|
|
|
async fn list_schemas(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"schemas": []})))
|
|
}
|
|
|
|
async fn create_schema(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"schema_id": "new_schema"})))
|
|
}
|
|
|
|
async fn validate_output(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"valid": true})))
|
|
}
|
|
|
|
// Grammar sampling handlers
|
|
async fn grammar_generate(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"grammar_response": "Generated"})))
|
|
}
|
|
|
|
async fn validate_grammar(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"valid": true})))
|
|
}
|
|
|
|
async fn parse_bnf_grammar(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"parsed": true})))
|
|
}
|
|
|
|
// Billing handlers
|
|
async fn get_user_costs(
|
|
Path(user_id): Path<String>,
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"costs": []})))
|
|
}
|
|
|
|
async fn list_budgets(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"budgets": []})))
|
|
}
|
|
|
|
async fn create_budget(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"budget_id": "new_budget"})))
|
|
}
|
|
|
|
async fn get_forecast(
|
|
Path(entity_id): Path<String>,
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"forecast": {}})))
|
|
}
|
|
|
|
// Queue management handlers
|
|
async fn queue_stats(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
if let Some(queue_manager) = &state.queue_manager {
|
|
let stats = queue_manager.get_queue_stats();
|
|
Ok(Json(serde_json::json!(stats)))
|
|
} else {
|
|
Err(ApiError::not_found("Queue management not enabled"))
|
|
}
|
|
}
|
|
|
|
async fn request_status(
|
|
Path(request_id): Path<String>,
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"status": "processing"})))
|
|
}
|
|
|
|
async fn cancel_request(
|
|
Path(request_id): Path<String>,
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"status": "cancelled"})))
|
|
}
|
|
|
|
async fn priority_completions(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"response": "Priority completion"})))
|
|
}
|
|
|
|
// Streaming handlers
|
|
async fn sse_stream(
|
|
Path(user_id): Path<String>,
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<
|
|
Sse<
|
|
impl futures_util::Stream<Item = Result<axum::response::sse::Event, axum::Error>> + 'static,
|
|
>,
|
|
> {
|
|
// Clone the Arc to get 'static lifetime for the stream
|
|
let stream_manager = state
|
|
.stream_manager
|
|
.clone()
|
|
.ok_or_else(|| ApiError::not_found("Streaming not enabled"))?;
|
|
|
|
let stream = stream_manager
|
|
.create_sse_stream(user_id, "default".to_string())
|
|
.await
|
|
.map_err(|e| ApiError::internal(format!("Failed to create SSE stream: {e}")))?;
|
|
|
|
Ok(Sse::new(stream).keep_alive(
|
|
axum::response::sse::KeepAlive::new()
|
|
.interval(Duration::from_secs(30))
|
|
.text("keep-alive"),
|
|
))
|
|
}
|
|
|
|
async fn websocket_upgrade(
|
|
Path(user_id): Path<String>,
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(
|
|
serde_json::json!({"message": "WebSocket upgrade would be handled here"}),
|
|
))
|
|
}
|
|
|
|
async fn chunked_stream(
|
|
Path(user_id): Path<String>,
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(
|
|
serde_json::json!({"message": "Chunked stream would be handled here"}),
|
|
))
|
|
}
|
|
|
|
async fn stream_stats(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
if let Some(stream_manager) = &state.stream_manager {
|
|
let stats = stream_manager.get_stream_stats();
|
|
Ok(Json(serde_json::json!(stats)))
|
|
} else {
|
|
Err(ApiError::not_found("Streaming not enabled"))
|
|
}
|
|
}
|
|
|
|
// Multi-model handlers
|
|
async fn load_model(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(
|
|
serde_json::json!({"instance_id": "new_model_instance"}),
|
|
))
|
|
}
|
|
|
|
async fn unload_model(
|
|
Path(instance_id): Path<String>,
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"status": "model unloaded"})))
|
|
}
|
|
|
|
async fn model_stats(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
if let Some(model_manager) = &state.model_manager {
|
|
let stats = model_manager.get_model_stats();
|
|
Ok(Json(serde_json::json!(stats)))
|
|
} else {
|
|
Err(ApiError::not_found("Multi-model serving not enabled"))
|
|
}
|
|
}
|
|
|
|
async fn create_ab_test(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"test_id": "new_ab_test"})))
|
|
}
|
|
|
|
async fn create_canary(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"deployment_id": "new_canary"})))
|
|
}
|
|
|
|
async fn create_ensemble(
|
|
State(state): State<AdvancedServerState>,
|
|
) -> ApiResult<Json<serde_json::Value>> {
|
|
Ok(Json(serde_json::json!({"ensemble_id": "new_ensemble"})))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_advanced_server_config_default() {
|
|
let config = AdvancedServerConfig::default();
|
|
assert_eq!(config.host, "127.0.0.1");
|
|
assert_eq!(config.port, 8080);
|
|
assert!(config.enable_rate_limiting);
|
|
assert!(config.enable_streaming);
|
|
assert!(config.enable_multi_model);
|
|
}
|
|
|
|
#[test]
|
|
fn test_advanced_server_state_creation() {
|
|
let config = AdvancedServerConfig::default();
|
|
let state = AdvancedServerState::new(config.clone());
|
|
|
|
assert!(state.rate_limiter.is_some());
|
|
assert!(state.structured_gen.is_some());
|
|
assert!(state.billing_manager.is_some());
|
|
assert!(state.queue_manager.is_some());
|
|
assert!(state.stream_manager.is_some());
|
|
assert!(state.model_manager.is_some());
|
|
}
|
|
|
|
#[test]
|
|
fn test_advanced_server_creation() {
|
|
let config = AdvancedServerConfig::default();
|
|
let server = AdvancedServingServer::new(config);
|
|
|
|
// Should be able to build the app router
|
|
let _app = server.app();
|
|
}
|
|
|
|
#[test]
|
|
fn test_rate_limit_config() {
|
|
let config = RateLimitConfig {
|
|
requests_per_minute: 100,
|
|
tokens_per_minute: 50000,
|
|
burst_allowance: 1.5,
|
|
enable_user_tiers: true,
|
|
};
|
|
|
|
assert_eq!(config.requests_per_minute, 100);
|
|
assert_eq!(config.tokens_per_minute, 50000);
|
|
assert!(config.enable_user_tiers);
|
|
}
|
|
}
|