pub mod deployment;
pub mod endpoints;
pub mod monitoring;
pub mod registry;
pub mod serialization;
pub mod server;
use crate::core::error::{Error, Result};
use crate::dataframe::DataFrame;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::Path;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelMetadata {
pub name: String,
pub version: String,
pub model_type: String,
pub feature_names: Vec<String>,
pub target_name: Option<String>,
pub description: String,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
pub metrics: HashMap<String, f64>,
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PredictionRequest {
pub data: HashMap<String, serde_json::Value>,
pub model_version: Option<String>,
pub options: Option<PredictionOptions>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PredictionOptions {
pub include_probabilities: Option<bool>,
pub include_feature_importance: Option<bool>,
pub include_confidence_intervals: Option<bool>,
pub threshold: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PredictionResponse {
pub prediction: serde_json::Value,
pub probabilities: Option<HashMap<String, f64>>,
pub feature_importance: Option<HashMap<String, f64>>,
pub confidence_intervals: Option<ConfidenceInterval>,
pub model_metadata: ModelMetadata,
pub timestamp: chrono::DateTime<chrono::Utc>,
pub processing_time_ms: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfidenceInterval {
pub lower: f64,
pub upper: f64,
pub confidence_level: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchPredictionRequest {
pub data: Vec<HashMap<String, serde_json::Value>>,
pub model_version: Option<String>,
pub options: Option<PredictionOptions>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchPredictionResponse {
pub predictions: Vec<PredictionResponse>,
pub summary: BatchProcessingSummary,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchProcessingSummary {
pub total_predictions: usize,
pub successful_predictions: usize,
pub failed_predictions: usize,
pub total_processing_time_ms: u64,
pub avg_processing_time_ms: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeploymentConfig {
pub model_name: String,
pub model_version: String,
pub environment: String,
pub resources: ResourceConfig,
pub scaling: ScalingConfig,
pub health_check: HealthCheckConfig,
pub monitoring: MonitoringConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceConfig {
pub cpu_cores: f64,
pub memory_mb: u64,
pub gpu_memory_mb: Option<u64>,
pub max_concurrent_requests: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScalingConfig {
pub min_instances: usize,
pub max_instances: usize,
pub target_cpu_utilization: f64,
pub target_memory_utilization: f64,
pub scale_up_threshold: f64,
pub scale_down_threshold: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthCheckConfig {
pub path: String,
pub interval_seconds: u64,
pub timeout_seconds: u64,
pub failure_threshold: usize,
pub success_threshold: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MonitoringConfig {
pub enable_metrics: bool,
pub enable_logging: bool,
pub enable_tracing: bool,
pub metrics_interval_seconds: u64,
pub log_level: String,
}
pub trait ModelServing {
fn predict(&self, request: &PredictionRequest) -> Result<PredictionResponse>;
fn predict_batch(&self, request: &BatchPredictionRequest) -> Result<BatchPredictionResponse>;
fn get_metadata(&self) -> &ModelMetadata;
fn health_check(&self) -> Result<HealthStatus>;
fn info(&self) -> ModelInfo;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthStatus {
pub status: String,
pub details: HashMap<String, String>,
pub timestamp: chrono::DateTime<chrono::Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelInfo {
pub metadata: ModelMetadata,
pub statistics: ModelStatistics,
pub configuration: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelStatistics {
pub total_predictions: u64,
pub avg_prediction_time_ms: f64,
pub error_rate: f64,
pub throughput_per_second: f64,
pub last_prediction_at: Option<chrono::DateTime<chrono::Utc>>,
}
pub struct ModelServingFactory;
impl ModelServingFactory {
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Box<dyn ModelServing>> {
let model_path = path.as_ref();
match model_path.extension().and_then(|ext| ext.to_str()) {
Some("json") => {
let serializer = serialization::JsonModelSerializer;
serializer.load(model_path)
}
Some("yaml") | Some("yml") => {
let serializer = serialization::YamlModelSerializer;
serializer.load(model_path)
}
Some("toml") => {
let serializer = serialization::TomlModelSerializer;
serializer.load(model_path)
}
Some("bin") | Some("pandrs") => {
let serializer = serialization::BinaryModelSerializer;
serializer.load(model_path)
}
_ => Err(Error::InvalidInput(format!(
"Unsupported model file format: {:?}",
model_path.extension()
))),
}
}
pub fn from_registry(
registry: &dyn registry::ModelRegistry,
model_name: &str,
version: Option<&str>,
) -> Result<Box<dyn ModelServing>> {
let model_version = version.unwrap_or("latest");
registry.load_model(model_name, model_version)
}
pub fn with_deployment_config(
model: Box<dyn ModelServing>,
config: DeploymentConfig,
) -> Result<deployment::DeployedModel> {
deployment::DeployedModel::new(model, config)
}
}
pub struct ModelServer {
models: HashMap<String, Box<dyn ModelServing>>,
config: ServerConfig,
registry: Option<Box<dyn registry::ModelRegistry>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerConfig {
pub host: String,
pub port: u16,
pub max_request_size: usize,
pub request_timeout_seconds: u64,
pub enable_cors: bool,
pub enable_auth: bool,
pub api_key: Option<String>,
}
impl Default for ServerConfig {
fn default() -> Self {
Self {
host: "0.0.0.0".to_string(),
port: 8080,
max_request_size: 10 * 1024 * 1024, request_timeout_seconds: 30,
enable_cors: true,
enable_auth: false,
api_key: None,
}
}
}
impl ModelServer {
pub fn new(config: ServerConfig) -> Self {
Self {
models: HashMap::new(),
config,
registry: None,
}
}
pub fn register_model(&mut self, name: String, model: Box<dyn ModelServing>) -> Result<()> {
if self.models.contains_key(&name) {
return Err(Error::InvalidOperation(format!(
"Model '{}' is already registered",
name
)));
}
self.models.insert(name, model);
Ok(())
}
pub fn unregister_model(&mut self, name: &str) -> Result<()> {
if self.models.remove(name).is_none() {
return Err(Error::KeyNotFound(format!(
"Model '{}' is not registered",
name
)));
}
Ok(())
}
pub fn set_registry(&mut self, registry: Box<dyn registry::ModelRegistry>) {
self.registry = Some(registry);
}
pub fn list_models(&self) -> Vec<String> {
self.models.keys().cloned().collect()
}
pub fn get_model(&self, name: &str) -> Result<&dyn ModelServing> {
self.models
.get(name)
.map(|model| model.as_ref())
.ok_or_else(|| Error::KeyNotFound(format!("Model '{}' not found", name)))
}
pub fn start(&self) -> Result<()> {
log::info!(
"Starting model server on {}:{}",
self.config.host,
self.config.port
);
Err(Error::NotImplemented(
"HTTP server implementation requires additional dependencies".to_string(),
))
}
}
pub use deployment::{DeployedModel, DeploymentManager, DeploymentMetrics, DeploymentStatus};
pub use endpoints::{
ApiResponse, BatchPredictionEndpoint, HealthEndpoint, ModelInfoEndpoint, PredictionEndpoint,
};
pub use monitoring::{
AlertConfig, AlertSeverity, ComparisonOperator, MetricsCollector, ModelMonitor,
PerformanceMetrics,
};
pub use registry::{
FileSystemModelRegistry, InMemoryModelRegistry, ModelRegistry, ModelRegistryEntry,
};
pub use serialization::{
GenericServingModel, ModelSerializer, SerializableModel, SerializationFormat,
};
pub use server::{HttpModelServer, HttpResponse, RequestContext, ServerStats};