use std::collections::{BTreeMap, HashMap};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
#[derive(Debug, Clone, PartialEq)]
#[allow(dead_code)]
pub enum ModelType {
LanguageModel {
model_name: String,
max_tokens: usize,
temperature: f32,
},
ImageClassifier {
model_name: String,
input_size: (usize, usize),
num_classes: usize,
},
EmbeddingModel {
model_name: String,
dimensions: usize,
},
TextToSpeech {
model_name: String,
voice_id: String,
},
SpeechToText {
model_name: String,
language: String,
},
TranslationModel {
model_name: String,
source_lang: String,
target_lang: String,
},
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub enum ModelInput {
Text {
content: String,
max_tokens: Option<usize>,
temperature: Option<f32>,
},
Image {
data: Vec<u8>,
format: String, },
Audio {
data: Vec<u8>,
format: String, sample_rate: Option<u32>,
},
Embedding {
text: String,
},
Translation {
text: String,
source_lang: String,
target_lang: String,
},
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub enum ModelOutput {
Text {
content: String,
tokens_used: usize,
finish_reason: String,
},
Classification {
predictions: Vec<(String, f32)>,
confidence: f32,
},
Embedding {
vector: Vec<f32>,
dimensions: usize,
},
Audio {
data: Vec<u8>,
format: String,
duration: Duration,
},
Translation {
translated_text: String,
source_lang: String,
target_lang: String,
confidence: f32,
},
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct ModelConfig {
pub model_type: ModelType,
pub max_batch_size: usize,
pub timeout: Duration,
pub retry_count: usize,
pub enable_caching: bool,
pub cache_ttl: Duration,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct ModelMetrics {
pub total_requests: usize,
pub successful_requests: usize,
pub failed_requests: usize,
pub average_latency: Duration,
pub p95_latency: Duration,
pub p99_latency: Duration,
pub throughput_rps: f64,
pub error_rate: f64,
pub last_updated: Instant,
}
impl Default for ModelMetrics {
fn default() -> Self {
Self {
total_requests: 0,
successful_requests: 0,
failed_requests: 0,
average_latency: Duration::from_nanos(0),
p95_latency: Duration::from_nanos(0),
p99_latency: Duration::from_nanos(0),
throughput_rps: 0.0,
error_rate: 0.0,
last_updated: Instant::now(),
}
}
}
impl ModelMetrics {
#[allow(dead_code)]
pub fn update(&mut self, latency: Duration, success: bool) {
self.total_requests += 1;
if success {
self.successful_requests += 1;
} else {
self.failed_requests += 1;
}
let current_avg: _ = self.average_latency;
let count: _ = self.successful_requests as u64;
self.average_latency = Duration::from_nanos(
(current_avg.as_nanos() as u64 * (count - 1) + latency.as_nanos() as u64) / count,
);
self.error_rate = self.failed_requests as f64 / self.total_requests as f64;
self.last_updated = Instant::now();
}
#[allow(dead_code)]
pub fn success_rate(&self) -> f64 {
if self.total_requests > 0 {
self.successful_requests as f64 / self.total_requests as f64
} else {
0.0
}
}
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct AiModel {
pub id: String,
pub config: ModelConfig,
pub is_loaded: bool,
pub load_time: Option<Instant>,
pub metrics: Arc<Mutex<ModelMetrics>>,
}
#[allow(dead_code)]
pub struct AiModelManager {
models: Arc<Mutex<HashMap<String, AiModel>>>,
default_model_id: Arc<Mutex<Option<String>>>,
routing_strategy: Arc<Mutex<ModelRoutingStrategy>>,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
#[derive(Default)]
pub enum ModelRoutingStrategy {
#[default]
RoundRobin,
Fastest,
LoadBalanced,
TypeBased,
}
#[allow(dead_code)]
impl AiModelManager {
pub fn new() -> Self {
Self {
models: Arc::new(Mutex::new(HashMap::new()))
default_model_id: Arc::new(Mutex::new(None)))
routing_strategy: Arc::new(Mutex::new(ModelRoutingStrategy::default()))
}
}
pub fn register_model(
&self,
model: AiModel,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut models = self.models.lock().unwrap();
if models.contains_key(&model.id) {
return Err("模型已存在".into());
}
models.insert(model.id.clone(), model);
if models.len() == 1 {
let model_id: _ = models.keys().next().unwrap().clone();
*self.default_model_id.lock().unwrap() = Some(model_id);
}
Ok(())
}
pub async fn load_model(
&self,
model_id: &str,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let start_time: _ = Instant::now();
let need_load: _ = {
let models: _ = self.models.lock().unwrap();
if let Some(model) = models.get(model_id) {
!model.is_loaded
} else {
return Err("模型不存在".into());
}
};
if need_load {
tokio::time::sleep(Duration::from_millis(100)).await;
let mut models = self.models.lock().unwrap();
if let Some(model) = models.get_mut(model_id) {
model.is_loaded = true;
model.load_time = Some(start_time);
}
println!(
"模型 {} 加载完成,耗时: {:.2}ms",
model_id,
start_time.elapsed().as_secs_f64() * 1000.0
);
}
Ok(())
}
pub async fn unload_model(
&self,
model_id: &str,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let need_unload: _ = {
let models: _ = self.models.lock().unwrap();
if let Some(model) = models.get(model_id) {
model.is_loaded
} else {
return Err("模型不存在".into());
}
};
if need_unload {
tokio::time::sleep(Duration::from_millis(50)).await;
let mut models = self.models.lock().unwrap();
if let Some(model) = models.get_mut(model_id) {
model.is_loaded = false;
model.load_time = None;
}
println!("模型 {} 已卸载", model_id);
}
Ok(())
}
pub async fn call_model(
&self,
model_id: Option<&str>,
input: ModelInput,
) -> Result<ModelOutput, Box<dyn std::error::Error + Send + Sync>> {
let actual_model_id: _ = if let Some(id) = model_id {
id.to_string()
} else {
self.get_default_model_id()?
};
let start_time: _ = Instant::now();
let (model_type, _config) = {
let models: _ = self.models.lock().unwrap();
if let Some(model) = models.get(&actual_model_id) {
if !model.is_loaded {
return Err("模型未加载".into());
}
(model.config.model_type.clone(), model.config.clone())
} else {
return Err("模型不存在".into());
}
};
let output: _ = self.execute_inference(&model_type, input).await?;
let latency: _ = start_time.elapsed();
{
let models: _ = self.models.lock().unwrap();
if let Some(model) = models.get(&actual_model_id) {
let mut metrics = model.metrics.lock().unwrap();
metrics.update(latency, true);
}
}
Ok(output)
}
async fn execute_inference(
&self,
model_type: &ModelType,
input: ModelInput,
) -> Result<ModelOutput, Box<dyn std::error::Error + Send + Sync>> {
match (model_type, input) {
(
ModelType::LanguageModel { .. },
ModelInput::Text {
content,
max_tokens: _,
temperature: _,
},
) => {
tokio::time::sleep(Duration::from_millis(50)).await;
Ok(ModelOutput::Text {
content: format!("Generated: {}", content),
tokens_used: content.len() / 4,
finish_reason: "stop".to_string(),
})
}
(ModelType::ImageClassifier { .. }, ModelInput::Image { data: _, format: _ }) => {
tokio::time::sleep(Duration::from_millis(100)).await;
Ok(ModelOutput::Classification {
predictions: vec![
("cat".to_string(), 0.85),
("dog".to_string(), 0.75),
("bird".to_string(), 0.65),
],
confidence: 0.85,
})
}
(ModelType::EmbeddingModel { dimensions, .. }, ModelInput::Embedding { text: _ }) => {
tokio::time::sleep(Duration::from_millis(30)).await;
Ok(ModelOutput::Embedding {
vector: vec![0.1; *dimensions],
dimensions: *dimensions,
})
}
(ModelType::TextToSpeech { .. }, ModelInput::Text { content: _, .. }) => {
tokio::time::sleep(Duration::from_millis(200)).await;
Ok(ModelOutput::Audio {
data: vec![0; 44100], format: "wav".to_string(),
duration: Duration::from_secs(1),
})
}
(
ModelType::SpeechToText { .. },
ModelInput::Audio {
data: _, format: _, ..
},
) => {
tokio::time::sleep(Duration::from_millis(150)).await;
Ok(ModelOutput::Text {
content: "Transcribed speech".to_string(),
tokens_used: 3,
finish_reason: "stop".to_string(),
})
}
(
ModelType::TranslationModel { .. },
ModelInput::Translation {
text,
source_lang,
target_lang,
},
) => {
tokio::time::sleep(Duration::from_millis(80)).await;
Ok(ModelOutput::Translation {
translated_text: format!("Translated: {}", text),
source_lang: source_lang.clone(),
target_lang: target_lang.clone(),
confidence: 0.92,
})
}
_ => Err("模型类型与输入不匹配".into()),
}
}
fn get_default_model_id(&self) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
let default_id: _ = self.default_model_id.lock().unwrap();
if let Some(id) = default_id.as_ref() {
Ok(id.clone())
} else {
Err("未设置默认模型".into())
}
}
pub fn list_models(&self) -> Vec<String> {
let models: _ = self.models.lock().unwrap();
models.keys().cloned().collect()
}
pub fn get_model_metrics(&self, model_id: &str) -> Option<ModelMetrics> {
let models: _ = self.models.lock().unwrap();
models
.get(model_id)
.map(|m| m.metrics.lock().unwrap().clone())
}
pub fn get_all_metrics(&self) -> HashMap<String, ModelMetrics> {
let models: _ = self.models.lock().unwrap();
let mut metrics = HashMap::new();
for (id, model) in models.iter() {
metrics.insert(id.clone(), model.metrics.lock().unwrap().clone());
}
metrics
}
pub fn set_default_model(
&self,
model_id: &str,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let models: _ = self.models.lock().unwrap();
if models.contains_key(model_id) {
*self.default_model_id.lock().unwrap() = Some(model_id.to_string());
Ok(())
} else {
Err("模型不存在".into())
}
}
pub fn set_routing_strategy(&self, strategy: ModelRoutingStrategy) {
*self.routing_strategy.lock().unwrap() = strategy;
}
pub async fn health_check(&self) -> HashMap<String, bool> {
let models: _ = self.models.lock().unwrap();
let mut health_status = HashMap::new();
for (id, model) in models.iter() {
let metrics: _ = model.metrics.lock().unwrap();
let has_requests: _ = metrics.total_requests > 0;
let success_rate: _ = metrics.success_rate();
let is_healthy: _ = model.is_loaded && (!has_requests || success_rate > 0.5);
health_status.insert(id.clone(), is_healthy);
}
health_status
}
pub async fn cleanup(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let model_ids: Vec<String> = {
let models: _ = self.models.lock().unwrap();
models.keys().cloned().collect()
};
for model_id in model_ids {
self.unload_model(&model_id).await?;
}
Ok(())
}
}
#[allow(dead_code)]
pub fn create_text_generation_model(model_id: &str) -> AiModel {
let config: _ = ModelConfig {
model_type: ModelType::LanguageModel {
model_name: "gpt-3.5-turbo".to_string(),
max_tokens: 4096,
temperature: 0.7,
},
max_batch_size: 10,
timeout: Duration::from_secs(30),
retry_count: 3,
enable_caching: true,
cache_ttl: Duration::from_secs(3600),
};
AiModel {
id: model_id.to_string(),
config,
is_loaded: false,
load_time: None,
metrics: Arc::new(Mutex::new(ModelMetrics::default()))
}
}
#[allow(dead_code)]
pub fn create_image_classification_model(model_id: &str) -> AiModel {
let config: _ = ModelConfig {
model_type: ModelType::ImageClassifier {
model_name: "resnet50".to_string(),
input_size: (224, 224),
num_classes: 1000,
},
max_batch_size: 32,
timeout: Duration::from_secs(10),
retry_count: 2,
enable_caching: false,
cache_ttl: Duration::from_secs(0),
};
AiModel {
id: model_id.to_string(),
config,
is_loaded: false,
load_time: None,
metrics: Arc::new(Mutex::new(ModelMetrics::default()))
}
}
#[cfg(test)]
mod tests {
#[tokio::test]
async fn test_model_manager_creation() {
let manager: _ = AiModelManager::new();
let models: _ = manager.list_models();
assert!(models.is_empty());
}
#[tokio::test]
async fn test_register_model() {
let manager: _ = AiModelManager::new();
let model: _ = create_text_generation_model("test-model");
let result: _ = manager.register_model(model);
assert!(result.is_ok());
assert_eq!(manager.list_models(), vec!["test-model"]);
}
#[tokio::test]
async fn test_load_model() {
let manager: _ = AiModelManager::new();
let model: _ = create_text_generation_model("test-model");
manager.register_model(model).unwrap();
let result: _ = manager.load_model("test-model").await;
assert!(result.is_ok());
let models: _ = manager.models.lock().unwrap();
assert!(models.get("test-model").unwrap().is_loaded);
}
#[tokio::test]
async fn test_call_model() {
let manager: _ = AiModelManager::new();
let model: _ = create_text_generation_model("test-model");
manager.register_model(model).unwrap();
manager.load_model("test-model").await.unwrap();
let input: _ = ModelInput::Text {
content: "Hello, world!".to_string(),
max_tokens: Some(100),
temperature: Some(0.7),
};
let result: _ = manager.call_model(Some("test-model"), input).await;
assert!(result.is_ok());
if let Ok(ModelOutput::Text { content, .. }) = result {
assert!(content.contains("Generated:"));
}
}
#[test]
fn test_model_metrics() {
let mut metrics = ModelMetrics::default();
metrics.update(Duration::from_millis(100), true);
metrics.update(Duration::from_millis(200), true);
assert_eq!(metrics.total_requests, 2);
assert_eq!(metrics.successful_requests, 2);
assert_eq!(metrics.failed_requests, 0);
assert_eq!(metrics.success_rate(), 1.0);
}
#[tokio::test]
async fn test_health_check() {
let manager: _ = AiModelManager::new();
let model: _ = create_text_generation_model("test-model");
manager.register_model(model).unwrap();
manager.load_model("test-model").await.unwrap();
let health: _ = manager.health_check().await;
assert!(health.contains_key("test-model"));
assert!(health.get("test-model").unwrap());
}
#[test]
fn test_create_text_generation_model() {
let model: _ = create_text_generation_model("gpt-3.5");
assert_eq!(model.id, "gpt-3.5");
assert!(!model.is_loaded);
}
#[test]
fn test_create_image_classification_model() {
let model: _ = create_image_classification_model("resnet50");
assert_eq!(model.id, "resnet50");
assert!(!model.is_loaded);
}
}