1use crate::{
9 config::CloningConfig,
10 model_loading::{ModelInterface, ModelLoadingManager},
11 performance_monitoring::{
12 PerformanceMeasurement, PerformanceMetrics, PerformanceMonitor, PerformanceTargets,
13 },
14 quality::{CloningQualityAssessor, QualityMetrics},
15 types::{
16 CloningMethod, SpeakerData, SpeakerProfile, VoiceCloneRequest, VoiceCloneResult,
17 VoiceSample,
18 },
19 Error, Result,
20};
21use serde::{Deserialize, Serialize};
22use std::collections::HashMap;
23use std::path::{Path, PathBuf};
24use std::sync::Arc;
25use std::time::{Duration, Instant, SystemTime};
26use tokio::sync::{Mutex, RwLock};
27use tracing::{debug, error, info, warn};
28
29pub struct PluginManager {
31 plugins: Arc<RwLock<HashMap<String, Arc<dyn CloningPlugin>>>>,
33 plugin_configs: Arc<RwLock<HashMap<String, PluginConfig>>>,
35 registry: Arc<PluginRegistry>,
37 metrics: Arc<RwLock<PluginMetrics>>,
39 performance_monitor: Arc<PerformanceMonitor>,
41 config: PluginManagerConfig,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct PluginManagerConfig {
48 pub plugin_paths: Vec<PathBuf>,
50 pub auto_discovery: bool,
52 pub max_plugins: usize,
54 pub loading_timeout: Duration,
56 pub enable_validation: bool,
58 pub cache_directory: Option<PathBuf>,
60 pub enable_hot_reload: bool,
62 pub api_version: String,
64}
65
66impl Default for PluginManagerConfig {
67 fn default() -> Self {
68 Self {
69 plugin_paths: vec![
70 PathBuf::from("./plugins"),
71 PathBuf::from("/usr/local/lib/voirs/plugins"),
72 ],
73 auto_discovery: true,
74 max_plugins: 50,
75 loading_timeout: Duration::from_secs(30),
76 enable_validation: true,
77 cache_directory: None,
78 enable_hot_reload: false,
79 api_version: "1.0.0".to_string(),
80 }
81 }
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct PluginConfig {
87 pub id: String,
89 pub name: String,
91 pub version: String,
93 pub description: String,
95 pub author: String,
97 pub supported_methods: Vec<CloningMethod>,
99 pub capabilities: PluginCapabilities,
101 pub parameters: HashMap<String, PluginParameter>,
103 pub dependencies: Vec<PluginDependency>,
105 pub min_api_version: String,
107 pub license: Option<String>,
109 pub website: Option<String>,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct PluginCapabilities {
116 pub realtime_synthesis: bool,
118 pub streaming_adaptation: bool,
120 pub cross_lingual: bool,
122 pub emotion_transfer: bool,
124 pub voice_morphing: bool,
126 pub zero_shot: bool,
128 pub requires_gpu: bool,
130 pub max_concurrent_sessions: usize,
132 pub supported_sample_rates: Vec<u32>,
134 pub supported_languages: Vec<String>,
136 pub memory_requirements: usize,
138}
139
140impl Default for PluginCapabilities {
141 fn default() -> Self {
142 Self {
143 realtime_synthesis: false,
144 streaming_adaptation: false,
145 cross_lingual: false,
146 emotion_transfer: false,
147 voice_morphing: false,
148 zero_shot: false,
149 requires_gpu: false,
150 max_concurrent_sessions: 1,
151 supported_sample_rates: vec![16000, 22050, 44100],
152 supported_languages: vec!["en".to_string()],
153 memory_requirements: 512, }
155 }
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct PluginParameter {
161 pub name: String,
163 pub param_type: ParameterType,
165 pub description: String,
167 pub default_value: ParameterValue,
169 pub constraints: Option<ParameterConstraints>,
171 pub required: bool,
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize)]
177pub enum ParameterType {
178 String,
179 Integer,
180 Float,
181 Boolean,
182 Array(Box<ParameterType>),
183 Object,
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize)]
188pub enum ParameterValue {
189 String(String),
190 Integer(i64),
191 Float(f64),
192 Boolean(bool),
193 Array(Vec<ParameterValue>),
194 Object(HashMap<String, ParameterValue>),
195 None,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct ParameterConstraints {
201 pub min: Option<f64>,
203 pub max: Option<f64>,
205 pub allowed_values: Option<Vec<ParameterValue>>,
207 pub pattern: Option<String>,
209 pub min_length: Option<usize>,
211 pub max_length: Option<usize>,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize)]
217pub struct PluginDependency {
218 pub name: String,
220 pub version: String,
222 pub optional: bool,
224}
225
226#[async_trait::async_trait]
228pub trait CloningPlugin: Send + Sync {
229 fn get_config(&self) -> &PluginConfig;
231
232 async fn initialize(&mut self, parameters: HashMap<String, ParameterValue>) -> Result<()>;
234
235 async fn shutdown(&mut self) -> Result<()>;
237
238 async fn clone_voice(
240 &self,
241 request: VoiceCloneRequest,
242 context: PluginContext,
243 ) -> Result<VoiceCloneResult>;
244
245 async fn validate_speaker_data(&self, data: &SpeakerData) -> Result<PluginValidationResult>;
247
248 async fn health_check(&self) -> Result<PluginHealth>;
250
251 async fn update_config(&mut self, parameters: HashMap<String, ParameterValue>) -> Result<()>;
253
254 fn get_capabilities(&self) -> &PluginCapabilities;
256
257 async fn get_metrics(&self) -> Result<PluginOperationMetrics>;
259}
260
261#[derive(Clone)]
263pub struct PluginContext {
264 pub quality_assessor: Arc<CloningQualityAssessor>,
266 pub model_loader: Arc<ModelLoadingManager>,
268 pub global_config: Arc<CloningConfig>,
270 pub request_metadata: HashMap<String, String>,
272 pub session_id: String,
274 pub user_context: Option<HashMap<String, String>>,
276}
277
278#[derive(Debug, Clone)]
280pub struct PluginValidationResult {
281 pub is_valid: bool,
283 pub errors: Vec<String>,
285 pub warnings: Vec<String>,
287 pub compatibility_score: f32,
289 pub required_adaptations: Vec<String>,
291}
292
293#[derive(Debug, Clone, Serialize, Deserialize)]
295pub struct PluginHealth {
296 pub status: PluginHealthStatus,
298 pub health_score: f32,
300 pub memory_usage: usize,
302 pub cpu_usage: f32,
304 pub active_sessions: usize,
306 pub last_check: SystemTime,
308 pub issues: Vec<String>,
310 pub performance: PluginPerformanceMetrics,
312}
313
314#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
316pub enum PluginHealthStatus {
317 Healthy,
318 Warning,
319 Critical,
320 Offline,
321}
322
323#[derive(Debug, Clone, Serialize, Deserialize)]
325pub struct PluginPerformanceMetrics {
326 pub avg_processing_time: Duration,
328 pub requests_per_second: f32,
330 pub success_rate: f32,
332 pub error_rate: f32,
334 pub avg_quality_score: f32,
336}
337
338impl Default for PluginPerformanceMetrics {
339 fn default() -> Self {
340 Self {
341 avg_processing_time: Duration::from_millis(100),
342 requests_per_second: 0.0,
343 success_rate: 1.0,
344 error_rate: 0.0,
345 avg_quality_score: 0.0,
346 }
347 }
348}
349
350#[derive(Debug, Clone, Serialize, Deserialize)]
352pub struct PluginOperationMetrics {
353 pub total_operations: u64,
355 pub successful_operations: u64,
357 pub failed_operations: u64,
359 pub total_processing_time: Duration,
361 pub cache_hits: u64,
363 pub cache_misses: u64,
365 pub memory_stats: PluginMemoryStats,
367}
368
369impl Default for PluginOperationMetrics {
370 fn default() -> Self {
371 Self {
372 total_operations: 0,
373 successful_operations: 0,
374 failed_operations: 0,
375 total_processing_time: Duration::from_secs(0),
376 cache_hits: 0,
377 cache_misses: 0,
378 memory_stats: PluginMemoryStats::default(),
379 }
380 }
381}
382
383#[derive(Debug, Clone, Serialize, Deserialize)]
385pub struct PluginMemoryStats {
386 pub current_usage: usize,
388 pub peak_usage: usize,
390 pub allocations: u64,
392 pub deallocations: u64,
394}
395
396impl Default for PluginMemoryStats {
397 fn default() -> Self {
398 Self {
399 current_usage: 0,
400 peak_usage: 0,
401 allocations: 0,
402 deallocations: 0,
403 }
404 }
405}
406
407pub struct PluginRegistry {
409 available_plugins: Arc<RwLock<HashMap<String, PluginManifest>>>,
411 discovery_paths: Vec<PathBuf>,
413 cache: Arc<RwLock<Option<RegistryCache>>>,
415}
416
417#[derive(Debug, Clone, Serialize, Deserialize)]
419pub struct PluginManifest {
420 pub config: PluginConfig,
422 pub path: PathBuf,
424 pub last_modified: SystemTime,
426 pub size: usize,
428 pub checksum: String,
430}
431
432#[derive(Debug, Clone)]
434pub struct RegistryCache {
435 pub manifests: HashMap<String, PluginManifest>,
437 pub cached_at: SystemTime,
439 pub expires_at: SystemTime,
441}
442
443#[derive(Debug, Default, Clone)]
445pub struct PluginMetrics {
446 pub total_plugins: usize,
448 pub active_plugins: usize,
450 pub total_loading_time: Duration,
452 pub initialization_failures: usize,
454 pub health_check_failures: usize,
456 pub total_operations: u64,
458}
459
460impl PluginManager {
461 pub fn new(config: PluginManagerConfig) -> Self {
463 let plugin_targets = PerformanceTargets {
465 adaptation_time_target: Duration::from_secs(60), synthesis_rtf_target: 0.15, memory_usage_target: 512 * 1024 * 1024, quality_score_target: 0.75, concurrent_adaptations_target: 8, };
471 let performance_monitor = Arc::new(PerformanceMonitor::with_targets(plugin_targets));
472
473 Self {
474 plugins: Arc::new(RwLock::new(HashMap::new())),
475 plugin_configs: Arc::new(RwLock::new(HashMap::new())),
476 registry: Arc::new(PluginRegistry::new(config.plugin_paths.clone())),
477 metrics: Arc::new(RwLock::new(PluginMetrics::default())),
478 performance_monitor,
479 config,
480 }
481 }
482
483 pub async fn discover_plugins(&self) -> Result<Vec<PluginManifest>> {
485 if !self.config.auto_discovery {
486 return Ok(Vec::new());
487 }
488
489 info!("Discovering plugins in configured paths");
490 let manifests = self.registry.discover_plugins().await?;
491
492 let mut metrics = self.metrics.write().await;
493 metrics.total_plugins = manifests.len();
494
495 info!("Discovered {} plugins", manifests.len());
496 Ok(manifests)
497 }
498
499 pub async fn register_plugin(&self, plugin: Arc<dyn CloningPlugin>) -> Result<()> {
501 let config = plugin.get_config().clone(); let plugin_id = config.id.clone();
503
504 if self.config.enable_validation {
506 self.validate_plugin(&plugin).await?;
507 }
508
509 let plugins_count = self.plugins.read().await.len();
511 if plugins_count >= self.config.max_plugins {
512 return Err(Error::Validation(format!(
513 "Maximum number of plugins ({}) exceeded",
514 self.config.max_plugins
515 )));
516 }
517
518 let mut plugins = self.plugins.write().await;
520 let mut plugin_configs = self.plugin_configs.write().await;
521
522 plugins.insert(plugin_id.clone(), plugin);
523 plugin_configs.insert(plugin_id.clone(), config.clone());
524
525 info!("Registered plugin: {} ({})", config.name, plugin_id);
526 Ok(())
527 }
528
529 pub async fn unregister_plugin(&self, plugin_id: &str) -> Result<()> {
531 let mut plugins = self.plugins.write().await;
532 let mut plugin_configs = self.plugin_configs.write().await;
533
534 if let Some(mut plugin) = plugins.remove(plugin_id) {
535 if let Some(plugin_ref) = Arc::get_mut(&mut plugin) {
537 if let Err(e) = plugin_ref.shutdown().await {
538 warn!("Error shutting down plugin {}: {}", plugin_id, e);
539 }
540 }
541 }
542
543 plugin_configs.remove(plugin_id);
544 info!("Unregistered plugin: {}", plugin_id);
545 Ok(())
546 }
547
548 pub async fn get_plugin(&self, plugin_id: &str) -> Option<Arc<dyn CloningPlugin>> {
550 let plugins = self.plugins.read().await;
551 plugins.get(plugin_id).cloned()
552 }
553
554 pub async fn list_plugins(&self) -> Vec<PluginConfig> {
556 let plugin_configs = self.plugin_configs.read().await;
557 plugin_configs.values().cloned().collect()
558 }
559
560 pub async fn clone_voice_with_plugin(
562 &self,
563 plugin_id: &str,
564 request: VoiceCloneRequest,
565 context: PluginContext,
566 ) -> Result<VoiceCloneResult> {
567 let start_time = Instant::now();
568
569 let plugin = self
570 .get_plugin(plugin_id)
571 .await
572 .ok_or_else(|| Error::Validation(format!("Plugin not found: {}", plugin_id)))?;
573
574 let validation = plugin.validate_speaker_data(&request.speaker_data).await?;
576 if !validation.is_valid {
577 return Err(Error::Validation(format!(
578 "Speaker data validation failed: {}",
579 validation.errors.join(", ")
580 )));
581 }
582
583 let result = plugin.clone_voice(request, context).await?;
585
586 let processing_time = start_time.elapsed();
588 let mut metrics = self.metrics.write().await;
589 metrics.total_operations += 1;
590
591 let memory_usage = self.estimate_plugin_memory_usage(plugin_id).await;
593 let quality_score = result.similarity_score as f64;
594
595 let performance_metrics = PerformanceMetrics {
596 adaptation_time: processing_time,
597 synthesis_rtf: processing_time.as_secs_f64()
598 / result.processing_time.as_secs_f64().max(0.001),
599 memory_usage,
600 quality_score,
601 concurrent_adaptations: 1, timestamp: SystemTime::now(),
603 };
604
605 let targets = self.performance_monitor.get_targets();
606 let measurement = PerformanceMeasurement {
607 metrics: performance_metrics.clone(),
608 targets: targets.clone(),
609 target_results: crate::performance_monitoring::TargetResults {
610 adaptation_time_met: processing_time <= targets.adaptation_time_target,
611 synthesis_rtf_met: performance_metrics.synthesis_rtf
612 <= targets.synthesis_rtf_target,
613 memory_usage_met: memory_usage <= targets.memory_usage_target,
614 quality_score_met: quality_score >= targets.quality_score_target,
615 concurrent_adaptations_met: true, },
617 overall_score: if quality_score >= targets.quality_score_target {
618 1.0
619 } else {
620 0.8
621 },
622 };
623
624 let performance_monitor = Arc::clone(&self.performance_monitor);
626 tokio::spawn(async move {
627 let _ = performance_monitor.record_measurement(measurement).await;
628 });
629
630 debug!(
631 "Plugin {} completed operation in {:?}",
632 plugin_id, processing_time
633 );
634 Ok(result)
635 }
636
637 pub async fn find_best_plugin(&self, request: &VoiceCloneRequest) -> Result<Option<String>> {
639 let plugins = self.plugins.read().await;
640 let mut best_plugin = None;
641 let mut best_score = 0.0f32;
642
643 for (plugin_id, plugin) in plugins.iter() {
644 let validation = plugin.validate_speaker_data(&request.speaker_data).await?;
645 if validation.is_valid && validation.compatibility_score > best_score {
646 best_score = validation.compatibility_score;
647 best_plugin = Some(plugin_id.clone());
648 }
649 }
650
651 Ok(best_plugin)
652 }
653
654 pub async fn health_check_all(&self) -> HashMap<String, PluginHealth> {
656 let plugins = self.plugins.read().await;
657 let mut health_status = HashMap::new();
658
659 for (plugin_id, plugin) in plugins.iter() {
660 match plugin.health_check().await {
661 Ok(health) => {
662 health_status.insert(plugin_id.clone(), health);
663 }
664 Err(e) => {
665 warn!("Health check failed for plugin {}: {}", plugin_id, e);
666 let mut metrics = self.metrics.write().await;
667 metrics.health_check_failures += 1;
668
669 health_status.insert(
670 plugin_id.clone(),
671 PluginHealth {
672 status: PluginHealthStatus::Offline,
673 health_score: 0.0,
674 memory_usage: 0,
675 cpu_usage: 0.0,
676 active_sessions: 0,
677 last_check: SystemTime::now(),
678 issues: vec![format!("Health check failed: {}", e)],
679 performance: PluginPerformanceMetrics::default(),
680 },
681 );
682 }
683 }
684 }
685
686 health_status
687 }
688
689 pub async fn get_metrics(&self) -> PluginMetrics {
691 let metrics = self.metrics.read().await;
692 let plugins = self.plugins.read().await;
693
694 let mut result = metrics.clone();
695 result.active_plugins = plugins.len();
696 result
697 }
698
699 async fn validate_plugin(&self, plugin: &Arc<dyn CloningPlugin>) -> Result<()> {
701 let config = plugin.get_config();
702
703 if !self.is_api_version_compatible(&config.min_api_version) {
705 return Err(Error::Validation(format!(
706 "Plugin requires API version {} but current version is {}",
707 config.min_api_version, self.config.api_version
708 )));
709 }
710
711 let plugin_configs = self.plugin_configs.read().await;
713 if plugin_configs.contains_key(&config.id) {
714 return Err(Error::Validation(format!(
715 "Plugin ID already exists: {}",
716 config.id
717 )));
718 }
719
720 self.validate_plugin_config(config)?;
722
723 Ok(())
724 }
725
726 fn is_api_version_compatible(&self, required_version: &str) -> bool {
728 let current_parts: Vec<u32> = self
730 .config
731 .api_version
732 .split('.')
733 .filter_map(|s| s.parse().ok())
734 .collect();
735
736 let required_parts: Vec<u32> = required_version
737 .split('.')
738 .filter_map(|s| s.parse().ok())
739 .collect();
740
741 if current_parts.len() < 3 || required_parts.len() < 3 {
742 return false;
743 }
744
745 current_parts[0] == required_parts[0]
747 && (current_parts[1] > required_parts[1]
748 || (current_parts[1] == required_parts[1] && current_parts[2] >= required_parts[2]))
749 }
750
751 fn validate_plugin_config(&self, config: &PluginConfig) -> Result<()> {
753 if config.id.is_empty() {
754 return Err(Error::Validation("Plugin ID cannot be empty".to_string()));
755 }
756
757 if config.name.is_empty() {
758 return Err(Error::Validation("Plugin name cannot be empty".to_string()));
759 }
760
761 if config.version.is_empty() {
762 return Err(Error::Validation(
763 "Plugin version cannot be empty".to_string(),
764 ));
765 }
766
767 for parameter in config.parameters.values() {
769 self.validate_parameter(parameter)?;
770 }
771
772 Ok(())
773 }
774
775 fn validate_parameter(&self, parameter: &PluginParameter) -> Result<()> {
777 if parameter.name.is_empty() {
778 return Err(Error::Validation(
779 "Parameter name cannot be empty".to_string(),
780 ));
781 }
782
783 if let Some(constraints) = ¶meter.constraints {
785 if let (Some(min), Some(max)) = (constraints.min, constraints.max) {
786 if min > max {
787 return Err(Error::Validation(format!(
788 "Parameter {} min value ({}) cannot be greater than max value ({})",
789 parameter.name, min, max
790 )));
791 }
792 }
793 }
794
795 Ok(())
796 }
797
798 async fn estimate_plugin_memory_usage(&self, plugin_id: &str) -> u64 {
800 128 * 1024 * 1024 }
804}
805
806impl PluginRegistry {
807 pub fn new(discovery_paths: Vec<PathBuf>) -> Self {
809 Self {
810 available_plugins: Arc::new(RwLock::new(HashMap::new())),
811 discovery_paths,
812 cache: Arc::new(RwLock::new(None)),
813 }
814 }
815
816 pub async fn discover_plugins(&self) -> Result<Vec<PluginManifest>> {
818 {
820 let cache = self.cache.read().await;
821 if let Some(cached) = cache.as_ref() {
822 if cached.expires_at > SystemTime::now() {
823 return Ok(cached.manifests.values().cloned().collect());
824 }
825 }
826 }
827
828 let mut manifests = Vec::new();
829
830 for path in &self.discovery_paths {
832 if path.exists() && path.is_dir() {
833 if let Ok(entries) = std::fs::read_dir(path) {
834 for entry in entries.flatten() {
835 if let Ok(manifest) = self.parse_plugin_manifest(&entry.path()).await {
836 manifests.push(manifest);
837 }
838 }
839 }
840 }
841 }
842
843 {
845 let mut cache = self.cache.write().await;
846 *cache = Some(RegistryCache {
847 manifests: manifests
848 .iter()
849 .map(|m| (m.config.id.clone(), m.clone()))
850 .collect(),
851 cached_at: SystemTime::now(),
852 expires_at: SystemTime::now() + Duration::from_secs(300), });
854 }
855
856 Ok(manifests)
857 }
858
859 async fn parse_plugin_manifest(&self, path: &Path) -> Result<PluginManifest> {
861 let manifest_path = if path.join("plugin.json").exists() {
863 path.join("plugin.json")
864 } else if path.join("plugin.toml").exists() {
865 path.join("plugin.toml")
866 } else {
867 return Err(Error::Validation("No plugin manifest found".to_string()));
868 };
869
870 let content = tokio::fs::read_to_string(&manifest_path)
871 .await
872 .map_err(|e| Error::Validation(format!("Failed to read manifest: {}", e)))?;
873
874 let config: PluginConfig =
875 if manifest_path.extension().and_then(|s| s.to_str()) == Some("json") {
876 serde_json::from_str(&content)
877 .map_err(|e| Error::Validation(format!("Invalid JSON manifest: {}", e)))?
878 } else {
879 return Err(Error::Validation(
880 "Only JSON manifests are currently supported".to_string(),
881 ));
882 };
883
884 let metadata = tokio::fs::metadata(&manifest_path)
885 .await
886 .map_err(|e| Error::Validation(format!("Failed to get manifest metadata: {}", e)))?;
887
888 Ok(PluginManifest {
889 config,
890 path: path.to_path_buf(),
891 last_modified: metadata.modified().unwrap_or(SystemTime::now()),
892 size: metadata.len() as usize,
893 checksum: format!("{:x}", content.len()), })
895 }
896}
897
898pub struct ExamplePlugin {
900 config: PluginConfig,
901 initialized: bool,
902 parameters: HashMap<String, ParameterValue>,
903 metrics: PluginOperationMetrics,
904}
905
906impl Default for ExamplePlugin {
907 fn default() -> Self {
908 Self::new()
909 }
910}
911
912impl ExamplePlugin {
913 pub fn new() -> Self {
915 let mut parameters = HashMap::new();
916 parameters.insert(
917 "quality_level".to_string(),
918 PluginParameter {
919 name: "quality_level".to_string(),
920 param_type: ParameterType::Float,
921 description: "Voice cloning quality level".to_string(),
922 default_value: ParameterValue::Float(0.8),
923 constraints: Some(ParameterConstraints {
924 min: Some(0.0),
925 max: Some(1.0),
926 allowed_values: None,
927 pattern: None,
928 min_length: None,
929 max_length: None,
930 }),
931 required: false,
932 },
933 );
934
935 let config = PluginConfig {
936 id: "example_plugin".to_string(),
937 name: "Example Voice Cloning Plugin".to_string(),
938 version: "1.0.0".to_string(),
939 description: "Example plugin for demonstration purposes".to_string(),
940 author: "VoiRS Team".to_string(),
941 supported_methods: vec![CloningMethod::FewShot, CloningMethod::OneShot],
942 capabilities: PluginCapabilities::default(),
943 parameters,
944 dependencies: Vec::new(),
945 min_api_version: "1.0.0".to_string(),
946 license: Some("MIT".to_string()),
947 website: Some("https://github.com/voirs/voirs".to_string()),
948 };
949
950 Self {
951 config,
952 initialized: false,
953 parameters: HashMap::new(),
954 metrics: PluginOperationMetrics::default(),
955 }
956 }
957}
958
959#[async_trait::async_trait]
960impl CloningPlugin for ExamplePlugin {
961 fn get_config(&self) -> &PluginConfig {
962 &self.config
963 }
964
965 async fn initialize(&mut self, parameters: HashMap<String, ParameterValue>) -> Result<()> {
966 self.parameters = parameters;
967 self.initialized = true;
968 info!("Example plugin initialized");
969 Ok(())
970 }
971
972 async fn shutdown(&mut self) -> Result<()> {
973 self.initialized = false;
974 info!("Example plugin shutdown");
975 Ok(())
976 }
977
978 async fn clone_voice(
979 &self,
980 request: VoiceCloneRequest,
981 _context: PluginContext,
982 ) -> Result<VoiceCloneResult> {
983 if !self.initialized {
984 return Err(Error::Processing("Plugin not initialized".to_string()));
985 }
986
987 let start_time = Instant::now();
988
989 tokio::time::sleep(Duration::from_millis(100)).await;
991
992 let processing_time = start_time.elapsed();
993
994 let result = VoiceCloneResult::success(
996 request.id,
997 vec![0.0; 16000], 16000,
999 0.8, processing_time,
1001 request.method,
1002 )
1003 .with_quality_metric("example_score".to_string(), 0.75);
1004
1005 Ok(result)
1006 }
1007
1008 async fn validate_speaker_data(&self, data: &SpeakerData) -> Result<PluginValidationResult> {
1009 let mut result = PluginValidationResult {
1010 is_valid: true,
1011 errors: Vec::new(),
1012 warnings: Vec::new(),
1013 compatibility_score: 1.0,
1014 required_adaptations: Vec::new(),
1015 };
1016
1017 if data.reference_samples.is_empty() {
1019 result.is_valid = false;
1020 result
1021 .errors
1022 .push("No reference samples provided".to_string());
1023 result.compatibility_score = 0.0;
1024 }
1025
1026 Ok(result)
1027 }
1028
1029 async fn health_check(&self) -> Result<PluginHealth> {
1030 Ok(PluginHealth {
1031 status: if self.initialized {
1032 PluginHealthStatus::Healthy
1033 } else {
1034 PluginHealthStatus::Offline
1035 },
1036 health_score: if self.initialized { 1.0 } else { 0.0 },
1037 memory_usage: 50, cpu_usage: 5.0, active_sessions: 0,
1040 last_check: SystemTime::now(),
1041 issues: Vec::new(),
1042 performance: PluginPerformanceMetrics::default(),
1043 })
1044 }
1045
1046 async fn update_config(&mut self, parameters: HashMap<String, ParameterValue>) -> Result<()> {
1047 self.parameters.extend(parameters);
1048 info!("Example plugin configuration updated");
1049 Ok(())
1050 }
1051
1052 fn get_capabilities(&self) -> &PluginCapabilities {
1053 &self.config.capabilities
1054 }
1055
1056 async fn get_metrics(&self) -> Result<PluginOperationMetrics> {
1057 Ok(self.metrics.clone())
1058 }
1059}
1060
1061#[cfg(test)]
1062mod tests {
1063 use super::*;
1064 use tokio;
1065
1066 #[tokio::test]
1067 async fn test_plugin_manager_creation() {
1068 let config = PluginManagerConfig::default();
1069 let manager = PluginManager::new(config);
1070
1071 let metrics = manager.get_metrics().await;
1072 assert_eq!(metrics.total_plugins, 0);
1073 assert_eq!(metrics.active_plugins, 0);
1074 }
1075
1076 #[tokio::test]
1077 async fn test_example_plugin_creation() {
1078 let plugin = ExamplePlugin::new();
1079 let config = plugin.get_config();
1080
1081 assert_eq!(config.id, "example_plugin");
1082 assert_eq!(config.name, "Example Voice Cloning Plugin");
1083 assert!(!config.supported_methods.is_empty());
1084 }
1085
1086 #[tokio::test]
1087 async fn test_plugin_registration() {
1088 let config = PluginManagerConfig::default();
1089 let manager = PluginManager::new(config);
1090
1091 let plugin = Arc::new(ExamplePlugin::new());
1092 let result = manager.register_plugin(plugin).await;
1093 assert!(result.is_ok());
1094
1095 let plugins = manager.list_plugins().await;
1096 assert_eq!(plugins.len(), 1);
1097 assert_eq!(plugins[0].id, "example_plugin");
1098 }
1099
1100 #[tokio::test]
1101 async fn test_plugin_initialization() {
1102 let mut plugin = ExamplePlugin::new();
1103 let mut parameters = HashMap::new();
1104 parameters.insert("quality_level".to_string(), ParameterValue::Float(0.9));
1105
1106 let result = plugin.initialize(parameters).await;
1107 assert!(result.is_ok());
1108 assert!(plugin.initialized);
1109 }
1110
1111 #[tokio::test]
1112 async fn test_plugin_health_check() {
1113 let mut plugin = ExamplePlugin::new();
1114 plugin.initialize(HashMap::new()).await.unwrap();
1115
1116 let health = plugin.health_check().await.unwrap();
1117 assert_eq!(health.status, PluginHealthStatus::Healthy);
1118 assert_eq!(health.health_score, 1.0);
1119 }
1120
1121 #[tokio::test]
1122 async fn test_speaker_data_validation() {
1123 let plugin = ExamplePlugin::new();
1124 let profile = SpeakerProfile::new("test".to_string(), "Test Speaker".to_string());
1125 let speaker_data = SpeakerData::new(profile);
1126
1127 let validation = plugin.validate_speaker_data(&speaker_data).await.unwrap();
1128 assert!(!validation.is_valid); assert!(!validation.errors.is_empty());
1130 }
1131
1132 #[tokio::test]
1133 async fn test_plugin_voice_cloning() {
1134 let mut plugin = ExamplePlugin::new();
1135 plugin.initialize(HashMap::new()).await.unwrap();
1136
1137 let profile = SpeakerProfile::new("test".to_string(), "Test Speaker".to_string());
1138 let mut speaker_data = SpeakerData::new(profile);
1139 speaker_data.reference_samples.push(VoiceSample::new(
1140 "test".to_string(),
1141 vec![0.0; 16000],
1142 16000,
1143 ));
1144
1145 let request = VoiceCloneRequest::new(
1146 "test_request".to_string(),
1147 speaker_data,
1148 CloningMethod::FewShot,
1149 "Hello world".to_string(),
1150 );
1151
1152 let context = PluginContext {
1153 quality_assessor: Arc::new(CloningQualityAssessor::new().unwrap()),
1154 model_loader: Arc::new(ModelLoadingManager::new(Default::default())),
1155 global_config: Arc::new(CloningConfig::default()),
1156 request_metadata: HashMap::new(),
1157 session_id: "test_session".to_string(),
1158 user_context: None,
1159 };
1160
1161 let result = plugin.clone_voice(request, context).await;
1162 assert!(result.is_ok());
1163
1164 let clone_result = result.unwrap();
1165 assert!(!clone_result.audio.is_empty());
1166 assert!(clone_result.success);
1167 assert!(clone_result.similarity_score > 0.0);
1168 }
1169
1170 #[tokio::test]
1171 async fn test_plugin_manager_find_best_plugin() {
1172 let config = PluginManagerConfig::default();
1173 let manager = PluginManager::new(config);
1174
1175 let plugin = Arc::new(ExamplePlugin::new());
1176 manager.register_plugin(plugin).await.unwrap();
1177
1178 let profile = SpeakerProfile::new("test".to_string(), "Test Speaker".to_string());
1179 let mut speaker_data = SpeakerData::new(profile);
1180 speaker_data.reference_samples.push(VoiceSample::new(
1181 "test".to_string(),
1182 vec![0.0; 16000],
1183 16000,
1184 ));
1185
1186 let request = VoiceCloneRequest::new(
1187 "test_request".to_string(),
1188 speaker_data,
1189 CloningMethod::FewShot,
1190 "Hello world".to_string(),
1191 );
1192
1193 let best_plugin = manager.find_best_plugin(&request).await.unwrap();
1194 assert!(best_plugin.is_some());
1195 assert_eq!(best_plugin.unwrap(), "example_plugin");
1196 }
1197
1198 #[tokio::test]
1199 async fn test_plugin_registry_creation() {
1200 let paths = vec![PathBuf::from("./test_plugins")];
1201 let registry = PluginRegistry::new(paths);
1202
1203 let manifests = registry.discover_plugins().await.unwrap();
1205 assert!(manifests.is_empty()); }
1207
1208 #[tokio::test]
1209 async fn test_parameter_validation() {
1210 let config = PluginManagerConfig::default();
1211 let manager = PluginManager::new(config);
1212
1213 let mut invalid_parameter = PluginParameter {
1214 name: "".to_string(), param_type: ParameterType::Float,
1216 description: "Test parameter".to_string(),
1217 default_value: ParameterValue::Float(0.5),
1218 constraints: None,
1219 required: false,
1220 };
1221
1222 let result = manager.validate_parameter(&invalid_parameter);
1224 assert!(result.is_err());
1225
1226 invalid_parameter.name = "valid_name".to_string();
1228 let result = manager.validate_parameter(&invalid_parameter);
1229 assert!(result.is_ok());
1230 }
1231}