Skip to main content

voirs_conversion/
fallback.rs

1//! Graceful degradation and fallback strategies for voice conversion
2//!
3//! This module provides robust error recovery mechanisms and quality-based fallback
4//! strategies to ensure the system continues to function even when optimal conversion
5//! fails or produces poor quality results.
6
7use crate::{
8    config::ConversionConfig,
9    quality::ArtifactDetector,
10    transforms::{PitchTransform, SpeedTransform, Transform},
11    types::{
12        ConversionRequest, ConversionResult, ConversionTarget, ConversionType, DetectedArtifacts,
13        ObjectiveQualityMetrics, VoiceCharacteristics,
14    },
15    Error, Result,
16};
17use serde::{Deserialize, Serialize};
18use std::collections::HashMap;
19use std::time::{Duration, Instant, SystemTime};
20use tracing::{debug, error, info, warn};
21
22/// Graceful degradation controller
23#[derive(Debug)]
24pub struct GracefulDegradationController {
25    /// Configuration for degradation strategies
26    config: DegradationConfig,
27    /// Quality thresholds for different fallback levels
28    quality_thresholds: QualityThresholds,
29    /// Fallback strategy executor
30    strategy_executor: FallbackStrategyExecutor,
31    /// Performance metrics tracker
32    performance_tracker: PerformanceTracker,
33    /// Failure history for learning
34    failure_history: FailureHistory,
35}
36
37/// Configuration for graceful degradation
38#[derive(Debug, Clone)]
39pub struct DegradationConfig {
40    /// Enable graceful degradation
41    pub enable_degradation: bool,
42    /// Enable quality-based fallbacks
43    pub enable_quality_fallbacks: bool,
44    /// Enable error recovery attempts
45    pub enable_error_recovery: bool,
46    /// Maximum number of retry attempts
47    pub max_retry_attempts: u32,
48    /// Maximum time to spend on recovery attempts
49    pub max_recovery_time_ms: u64,
50    /// Enable fallback learning
51    pub enable_learning: bool,
52    /// Enable performance monitoring
53    pub enable_performance_monitoring: bool,
54}
55
56impl Default for DegradationConfig {
57    fn default() -> Self {
58        Self {
59            enable_degradation: true,
60            enable_quality_fallbacks: true,
61            enable_error_recovery: true,
62            max_retry_attempts: 3,
63            max_recovery_time_ms: 5000,
64            enable_learning: true,
65            enable_performance_monitoring: true,
66        }
67    }
68}
69
70/// Quality thresholds for triggering different fallback strategies
71#[derive(Debug, Clone)]
72pub struct QualityThresholds {
73    /// Minimum acceptable quality score (0.0 to 1.0)
74    pub min_acceptable_quality: f32,
75    /// Quality threshold for reducing conversion complexity
76    pub complexity_reduction_threshold: f32,
77    /// Quality threshold for switching to simpler algorithms
78    pub simple_algorithm_threshold: f32,
79    /// Quality threshold for passthrough fallback
80    pub passthrough_threshold: f32,
81    /// Maximum acceptable artifact score
82    pub max_artifact_score: f32,
83}
84
85impl Default for QualityThresholds {
86    fn default() -> Self {
87        Self {
88            min_acceptable_quality: 0.3,
89            complexity_reduction_threshold: 0.5,
90            simple_algorithm_threshold: 0.4,
91            passthrough_threshold: 0.2,
92            max_artifact_score: 0.7,
93        }
94    }
95}
96
97/// Fallback strategy executor
98#[derive(Debug)]
99pub struct FallbackStrategyExecutor {
100    /// Available fallback strategies
101    strategies: Vec<Box<dyn FallbackStrategy>>,
102    /// Strategy performance history
103    strategy_performance: HashMap<String, StrategyPerformance>,
104}
105
106/// Fallback strategy trait  
107pub trait FallbackStrategy: Send + Sync + std::fmt::Debug {
108    /// Name of the strategy
109    fn name(&self) -> &str;
110
111    /// Check if this strategy can handle the given failure
112    fn can_handle(&self, failure_type: &FailureType, conversion_type: &ConversionType) -> bool;
113
114    /// Estimate the success probability for this strategy
115    fn success_probability(&self, context: &FallbackContext) -> f32;
116
117    /// Apply the fallback strategy
118    fn apply(
119        &self,
120        request: &ConversionRequest,
121        context: &FallbackContext,
122        config: &ConversionConfig,
123    ) -> Result<ConversionResult>;
124
125    /// Get strategy priority (higher = more preferred)
126    fn priority(&self) -> i32;
127}
128
129/// Context for fallback decisions
130#[derive(Debug, Clone)]
131pub struct FallbackContext {
132    /// Original error that triggered fallback
133    pub original_error: Option<String>,
134    /// Current quality metrics
135    pub current_quality: Option<f32>,
136    /// Detected artifacts
137    pub artifacts: Option<DetectedArtifacts>,
138    /// Previous attempt results
139    pub previous_attempts: Vec<FallbackAttempt>,
140    /// Available processing resources
141    pub available_resources: ResourceContext,
142    /// Time constraints
143    pub time_constraints: TimeConstraints,
144}
145
146/// Previous fallback attempt
147#[derive(Debug, Clone)]
148pub struct FallbackAttempt {
149    /// Name of the fallback strategy that was attempted
150    pub strategy_name: String,
151    /// Whether the fallback attempt was successful
152    pub success: bool,
153    /// Quality score achieved by this fallback attempt if successful
154    pub quality_achieved: Option<f32>,
155    /// Time taken to execute this fallback attempt
156    pub processing_time: Duration,
157    /// Error message if the fallback attempt failed
158    pub error: Option<String>,
159}
160
161/// Available processing resources
162#[derive(Debug, Clone)]
163pub struct ResourceContext {
164    /// Current CPU utilization as a percentage (0.0 to 100.0)
165    pub cpu_usage_percent: f32,
166    /// Available system memory in megabytes
167    pub memory_available_mb: f64,
168    /// Whether GPU acceleration is available for processing
169    pub gpu_available: bool,
170    /// Overall processing capacity normalized to 0.0 to 1.0 scale
171    pub processing_capacity: f32, // 0.0 to 1.0
172}
173
174/// Time constraints for fallback processing
175#[derive(Debug, Clone)]
176pub struct TimeConstraints {
177    /// Maximum allowed time for fallback processing to complete
178    pub max_processing_time: Duration,
179    /// Optional absolute deadline for fallback completion
180    pub deadline: Option<Instant>,
181    /// Whether real-time processing constraints must be met
182    pub real_time_requirement: bool,
183}
184
185/// Types of failures that can trigger fallbacks
186#[derive(Debug, Clone, PartialEq, Eq, Hash)]
187pub enum FailureType {
188    /// Processing error
189    ProcessingError,
190    /// Quality below threshold
191    QualityFailure,
192    /// High artifact levels
193    ArtifactFailure,
194    /// Timeout or performance issue
195    PerformanceFailure,
196    /// Resource exhaustion
197    ResourceFailure,
198    /// Model loading failure
199    ModelFailure,
200    /// Memory allocation failure
201    MemoryFailure,
202    /// Unknown error
203    Unknown,
204}
205
206/// Strategy performance tracking
207#[derive(Debug, Clone, Default)]
208pub struct StrategyPerformance {
209    /// Total number of times this strategy was attempted
210    pub total_attempts: u32,
211    /// Number of successful fallback attempts using this strategy
212    pub successful_attempts: u32,
213    /// Average quality score achieved by this strategy
214    pub average_quality: f32,
215    /// Average time taken by this strategy to execute
216    pub average_processing_time: Duration,
217    /// Recent success rate calculated from latest attempts
218    pub recent_success_rate: f32,
219    /// Overall effectiveness score combining success rate and quality
220    pub effectiveness_score: f32,
221}
222
223/// Performance tracking for the degradation controller
224#[derive(Debug, Default, Clone)]
225pub struct PerformanceTracker {
226    /// Total number of degradation attempts across all strategies
227    pub total_degradations: u64,
228    /// Number of degradation attempts that successfully recovered
229    pub successful_degradations: u64,
230    /// Average time spent executing fallback strategies
231    pub average_fallback_time: Duration,
232    /// Count of how many times each strategy has been used
233    pub strategy_usage: HashMap<String, u64>,
234    /// History of quality improvements achieved through fallbacks
235    pub quality_improvements: Vec<QualityImprovement>,
236}
237
238/// Quality improvement tracking
239#[derive(Debug, Clone)]
240pub struct QualityImprovement {
241    /// Quality score before applying the fallback strategy
242    pub original_quality: f32,
243    /// Quality score after applying the fallback strategy
244    pub final_quality: f32,
245    /// Name of the strategy that achieved the improvement
246    pub strategy_used: String,
247    /// Time taken to achieve the quality improvement
248    pub processing_time: Duration,
249    /// When this quality improvement was recorded
250    pub timestamp: Instant,
251}
252
253/// Failure history for learning and adaptation
254#[derive(Debug)]
255pub struct FailureHistory {
256    /// Recent failures by type
257    pub failures_by_type: HashMap<FailureType, Vec<FailureRecord>>,
258    /// Success patterns
259    pub success_patterns: Vec<SuccessPattern>,
260    /// Maximum history length
261    max_history_length: usize,
262}
263
264/// Individual failure record
265#[derive(Debug, Clone)]
266pub struct FailureRecord {
267    /// When this failure occurred
268    pub timestamp: Instant,
269    /// Type of failure that was encountered
270    pub failure_type: FailureType,
271    /// Type of conversion that was being attempted
272    pub conversion_type: ConversionType,
273    /// Additional context about the failure conditions
274    pub context: String,
275    /// Description of the resolution strategy if attempted
276    pub resolution: Option<String>,
277    /// Whether the resolution attempt was successful
278    pub resolution_success: bool,
279}
280
281/// Pattern of successful fallback resolution
282#[derive(Debug, Clone)]
283pub struct SuccessPattern {
284    /// Type of failure this pattern successfully handles
285    pub failure_type: FailureType,
286    /// Type of conversion this pattern applies to
287    pub conversion_type: ConversionType,
288    /// Name of the strategy that successfully resolved this pattern
289    pub successful_strategy: String,
290    /// Confidence score in this pattern's reliability (0.0 to 1.0)
291    pub confidence: f32,
292    /// Number of times this pattern has been successfully applied
293    pub usage_count: u32,
294}
295
296// Concrete fallback strategy implementations
297
298/// Passthrough fallback strategy - returns original audio
299#[derive(Debug)]
300pub struct PassthroughStrategy;
301
302impl FallbackStrategy for PassthroughStrategy {
303    fn name(&self) -> &str {
304        "passthrough"
305    }
306
307    fn can_handle(&self, _failure_type: &FailureType, _conversion_type: &ConversionType) -> bool {
308        true // Can always handle by doing nothing
309    }
310
311    fn success_probability(&self, _context: &FallbackContext) -> f32 {
312        1.0 // Always succeeds
313    }
314
315    fn apply(
316        &self,
317        request: &ConversionRequest,
318        _context: &FallbackContext,
319        _config: &ConversionConfig,
320    ) -> Result<ConversionResult> {
321        warn!("Applying passthrough fallback for request: {}", request.id);
322
323        // Return original audio unchanged
324        let processing_time = Duration::from_millis(1);
325        let mut result = ConversionResult::success(
326            request.id.clone(),
327            request.source_audio.clone(),
328            request.source_sample_rate,
329            processing_time,
330            request.conversion_type.clone(),
331        );
332
333        // Set quality metrics to indicate this is a fallback
334        result
335            .quality_metrics
336            .insert("overall_quality".to_string(), 0.5);
337        result
338            .quality_metrics
339            .insert("fallback_applied".to_string(), 1.0);
340        result
341            .quality_metrics
342            .insert("passthrough_strategy".to_string(), 1.0);
343
344        Ok(result)
345    }
346
347    fn priority(&self) -> i32 {
348        -100 // Lowest priority - last resort
349    }
350}
351
352/// Simplified processing strategy - uses basic algorithms
353#[derive(Debug)]
354pub struct SimplifiedProcessingStrategy;
355
356impl FallbackStrategy for SimplifiedProcessingStrategy {
357    fn name(&self) -> &str {
358        "simplified_processing"
359    }
360
361    fn can_handle(&self, failure_type: &FailureType, conversion_type: &ConversionType) -> bool {
362        match failure_type {
363            FailureType::QualityFailure
364            | FailureType::ProcessingError
365            | FailureType::PerformanceFailure => {
366                matches!(
367                    conversion_type,
368                    ConversionType::PitchShift | ConversionType::SpeedTransformation
369                )
370            }
371            _ => false,
372        }
373    }
374
375    fn success_probability(&self, context: &FallbackContext) -> f32 {
376        if context.available_resources.processing_capacity > 0.3 {
377            0.8
378        } else {
379            0.6
380        }
381    }
382
383    fn apply(
384        &self,
385        request: &ConversionRequest,
386        _context: &FallbackContext,
387        config: &ConversionConfig,
388    ) -> Result<ConversionResult> {
389        info!(
390            "Applying simplified processing fallback for request: {}",
391            request.id
392        );
393
394        let start_time = Instant::now();
395
396        // Use simple, robust algorithms
397        let converted_audio = match request.conversion_type {
398            ConversionType::PitchShift => {
399                let pitch_factor = 1.2; // Simple 20% pitch increase as fallback
400                let transform = PitchTransform::new(pitch_factor);
401                transform.apply(&request.source_audio)?
402            }
403            ConversionType::SpeedTransformation => {
404                let speed_factor = 1.1; // Simple 10% speed increase as fallback
405                let transform = SpeedTransform::new(speed_factor);
406                transform.apply(&request.source_audio)?
407            }
408            _ => request.source_audio.clone(), // Fallback to passthrough for unsupported types
409        };
410
411        let processing_time = start_time.elapsed();
412
413        let mut result = ConversionResult::success(
414            request.id.clone(),
415            converted_audio,
416            config.output_sample_rate,
417            processing_time,
418            request.conversion_type.clone(),
419        );
420
421        // Set quality metrics
422        result
423            .quality_metrics
424            .insert("overall_quality".to_string(), 0.6);
425        result
426            .quality_metrics
427            .insert("fallback_applied".to_string(), 1.0);
428        result
429            .quality_metrics
430            .insert("simplified_processing".to_string(), 1.0);
431
432        Ok(result)
433    }
434
435    fn priority(&self) -> i32 {
436        0 // Medium priority
437    }
438}
439
440// Implementation of GracefulDegradationController
441impl GracefulDegradationController {
442    /// Create new graceful degradation controller
443    pub fn new() -> Self {
444        Self::with_config(DegradationConfig::default())
445    }
446
447    /// Create with custom configuration
448    pub fn with_config(config: DegradationConfig) -> Self {
449        let quality_thresholds = QualityThresholds::default();
450        let strategy_executor = FallbackStrategyExecutor::new();
451        let performance_tracker = PerformanceTracker::default();
452        let failure_history = FailureHistory::new(1000);
453
454        Self {
455            config,
456            quality_thresholds,
457            strategy_executor,
458            performance_tracker,
459            failure_history,
460        }
461    }
462
463    /// Handle conversion failure with graceful degradation
464    pub async fn handle_failure(
465        &mut self,
466        request: &ConversionRequest,
467        original_error: Error,
468        config: &ConversionConfig,
469    ) -> Result<ConversionResult> {
470        let failure_type = self.classify_failure(&original_error);
471        let context = self
472            .build_fallback_context(&original_error, &failure_type, request)
473            .await;
474
475        info!(
476            "Handling failure type {:?} for request {}",
477            failure_type, request.id
478        );
479
480        // Record the failure for learning
481        self.record_failure(&failure_type, request, &original_error)
482            .await;
483
484        // Check if degradation is enabled
485        if !self.config.enable_degradation {
486            return Err(original_error);
487        }
488
489        // Attempt graceful degradation
490        self.attempt_graceful_degradation(request, &context, config)
491            .await
492    }
493
494    /// Handle quality-based degradation
495    pub async fn handle_quality_degradation(
496        &mut self,
497        request: &ConversionRequest,
498        result: &ConversionResult,
499        config: &ConversionConfig,
500    ) -> Result<Option<ConversionResult>> {
501        if !self.config.enable_quality_fallbacks {
502            return Ok(None);
503        }
504
505        // Check if quality is below acceptable thresholds
506        let overall_quality = result
507            .quality_metrics
508            .get("overall_quality")
509            .copied()
510            .unwrap_or(1.0);
511        let artifact_score = result
512            .artifacts
513            .as_ref()
514            .map(|a| a.overall_score)
515            .unwrap_or(0.0);
516
517        let needs_degradation = overall_quality < self.quality_thresholds.min_acceptable_quality
518            || artifact_score > self.quality_thresholds.max_artifact_score;
519
520        if !needs_degradation {
521            return Ok(None);
522        }
523
524        warn!(
525            "Quality degradation triggered: quality={:.3}, artifacts={:.3}",
526            overall_quality, artifact_score
527        );
528
529        let failure_type = if artifact_score > self.quality_thresholds.max_artifact_score {
530            FailureType::ArtifactFailure
531        } else {
532            FailureType::QualityFailure
533        };
534
535        let context = self
536            .build_quality_fallback_context(result, &failure_type)
537            .await;
538
539        match self
540            .attempt_graceful_degradation(request, &context, config)
541            .await
542        {
543            Ok(fallback_result) => Ok(Some(fallback_result)),
544            Err(_) => Ok(None), // Don't propagate fallback errors
545        }
546    }
547
548    /// Classify the type of failure
549    fn classify_failure(&self, error: &Error) -> FailureType {
550        match error {
551            Error::Processing { .. } => FailureType::ProcessingError,
552            Error::Model { .. } => FailureType::ModelFailure,
553            Error::Audio { .. } => FailureType::ProcessingError,
554            Error::Realtime { .. } => FailureType::PerformanceFailure,
555            Error::Streaming { .. } => FailureType::PerformanceFailure,
556            Error::Buffer { .. } => FailureType::MemoryFailure,
557            Error::Transform { .. } => FailureType::ProcessingError,
558            Error::Validation { .. } => FailureType::ProcessingError,
559            Error::Runtime { .. } => FailureType::Unknown,
560            Error::Io(_) => FailureType::ResourceFailure,
561            Error::Candle(_) => FailureType::ModelFailure,
562            _ => FailureType::Unknown,
563        }
564    }
565
566    /// Build fallback context from error and request
567    async fn build_fallback_context(
568        &self,
569        error: &Error,
570        failure_type: &FailureType,
571        request: &ConversionRequest,
572    ) -> FallbackContext {
573        let resource_context = self.assess_current_resources().await;
574        let time_constraints = self.build_time_constraints(request).await;
575
576        FallbackContext {
577            original_error: Some(error.to_string()),
578            current_quality: None,
579            artifacts: None,
580            previous_attempts: Vec::new(),
581            available_resources: resource_context,
582            time_constraints,
583        }
584    }
585
586    /// Build context for quality-based fallback
587    async fn build_quality_fallback_context(
588        &self,
589        result: &ConversionResult,
590        failure_type: &FailureType,
591    ) -> FallbackContext {
592        let resource_context = self.assess_current_resources().await;
593        let overall_quality = result.quality_metrics.get("overall_quality").copied();
594
595        FallbackContext {
596            original_error: Some(format!("Quality below threshold: {overall_quality:?}")),
597            current_quality: overall_quality,
598            artifacts: result.artifacts.clone(),
599            previous_attempts: Vec::new(),
600            available_resources: resource_context,
601            time_constraints: TimeConstraints {
602                max_processing_time: Duration::from_millis(self.config.max_recovery_time_ms),
603                deadline: None,
604                real_time_requirement: false,
605            },
606        }
607    }
608
609    /// Attempt graceful degradation using available strategies
610    async fn attempt_graceful_degradation(
611        &mut self,
612        request: &ConversionRequest,
613        context: &FallbackContext,
614        config: &ConversionConfig,
615    ) -> Result<ConversionResult> {
616        let start_time = Instant::now();
617        let mut current_context = context.clone();
618        let mut attempt_count = 0;
619
620        while attempt_count < self.config.max_retry_attempts {
621            if start_time.elapsed() > Duration::from_millis(self.config.max_recovery_time_ms) {
622                warn!("Fallback timeout exceeded, using final strategy");
623                break;
624            }
625
626            // Find best strategy for current situation
627            if let Some(strategy) = self
628                .strategy_executor
629                .select_best_strategy(&current_context, &request.conversion_type)
630            {
631                let attempt_start = Instant::now();
632                let strategy_name = strategy.name().to_string(); // Clone the name to avoid borrowing issues
633
634                info!(
635                    "Attempting fallback strategy '{}' (attempt {}/{})",
636                    strategy_name,
637                    attempt_count + 1,
638                    self.config.max_retry_attempts
639                );
640
641                match strategy.apply(request, &current_context, config) {
642                    Ok(result) => {
643                        let processing_time = attempt_start.elapsed();
644
645                        // Record successful attempt
646                        self.record_successful_fallback(&strategy_name, &result, processing_time)
647                            .await;
648
649                        // Validate result quality
650                        if self.validate_fallback_result(&result) {
651                            info!("Fallback strategy '{}' succeeded", strategy_name);
652                            self.performance_tracker.successful_degradations += 1;
653                            return Ok(result);
654                        } else {
655                            warn!(
656                                "Fallback strategy '{}' produced poor quality",
657                                strategy_name
658                            );
659                        }
660                    }
661                    Err(e) => {
662                        warn!("Fallback strategy '{}' failed: {}", strategy_name, e);
663
664                        // Record failed attempt
665                        let failed_attempt = FallbackAttempt {
666                            strategy_name: strategy_name.clone(),
667                            success: false,
668                            quality_achieved: None,
669                            processing_time: attempt_start.elapsed(),
670                            error: Some(e.to_string()),
671                        };
672                        current_context.previous_attempts.push(failed_attempt);
673                    }
674                }
675            }
676
677            attempt_count += 1;
678        }
679
680        // Final fallback - use passthrough strategy
681        warn!("All fallback strategies failed, applying final passthrough");
682        let passthrough = PassthroughStrategy;
683        passthrough.apply(request, &current_context, config)
684    }
685
686    /// Validate fallback result quality
687    fn validate_fallback_result(&self, result: &ConversionResult) -> bool {
688        // Basic validation - result should be successful and have audio
689        if !result.success || result.converted_audio.is_empty() {
690            return false;
691        }
692
693        // Check for reasonable quality metrics
694        if let Some(quality) = result.quality_metrics.get("overall_quality") {
695            if *quality < 0.1 {
696                return false;
697            }
698        }
699
700        true
701    }
702
703    /// Assess current system resources
704    async fn assess_current_resources(&self) -> ResourceContext {
705        // In a real implementation, this would query actual system resources
706        // For now, provide reasonable defaults
707        ResourceContext {
708            cpu_usage_percent: 50.0,
709            memory_available_mb: 1024.0,
710            gpu_available: true,
711            processing_capacity: 0.7,
712        }
713    }
714
715    /// Build time constraints for processing
716    async fn build_time_constraints(&self, request: &ConversionRequest) -> TimeConstraints {
717        let is_realtime = request.realtime;
718        let max_time = if is_realtime {
719            Duration::from_millis(100) // Strict for real-time
720        } else {
721            Duration::from_millis(self.config.max_recovery_time_ms)
722        };
723
724        TimeConstraints {
725            max_processing_time: max_time,
726            deadline: None,
727            real_time_requirement: is_realtime,
728        }
729    }
730
731    /// Record failure for learning and analysis
732    async fn record_failure(
733        &mut self,
734        failure_type: &FailureType,
735        request: &ConversionRequest,
736        error: &Error,
737    ) {
738        let failure_record = FailureRecord {
739            timestamp: Instant::now(),
740            failure_type: failure_type.clone(),
741            conversion_type: request.conversion_type.clone(),
742            context: format!("Request: {}, Error: {}", request.id, error),
743            resolution: None,
744            resolution_success: false,
745        };
746
747        self.failure_history.record_failure(failure_record);
748        self.performance_tracker.total_degradations += 1;
749    }
750
751    /// Record successful fallback attempt
752    async fn record_successful_fallback(
753        &mut self,
754        strategy_name: &str,
755        result: &ConversionResult,
756        processing_time: Duration,
757    ) {
758        // Update strategy performance
759        self.strategy_executor
760            .record_success(strategy_name, result, processing_time);
761
762        // Update overall performance tracking
763        if let Some(quality) = result.quality_metrics.get("overall_quality") {
764            let improvement = QualityImprovement {
765                original_quality: 0.0, // Would track from original failure
766                final_quality: *quality,
767                strategy_used: strategy_name.to_string(),
768                processing_time,
769                timestamp: Instant::now(),
770            };
771            self.performance_tracker
772                .quality_improvements
773                .push(improvement);
774        }
775
776        // Update strategy usage count
777        *self
778            .performance_tracker
779            .strategy_usage
780            .entry(strategy_name.to_string())
781            .or_insert(0) += 1;
782    }
783
784    /// Get performance statistics
785    pub fn get_performance_stats(&self) -> &PerformanceTracker {
786        &self.performance_tracker
787    }
788
789    /// Update quality thresholds
790    pub fn update_quality_thresholds(&mut self, thresholds: QualityThresholds) {
791        self.quality_thresholds = thresholds;
792    }
793
794    /// Get current quality thresholds
795    pub fn get_quality_thresholds(&self) -> &QualityThresholds {
796        &self.quality_thresholds
797    }
798
799    /// Enable or disable specific degradation features
800    pub fn configure(&mut self, config: DegradationConfig) {
801        self.config = config;
802    }
803}
804
805impl Default for FallbackStrategyExecutor {
806    fn default() -> Self {
807        Self::new()
808    }
809}
810
811// Implementation of FallbackStrategyExecutor
812impl FallbackStrategyExecutor {
813    /// Create new strategy executor with default strategies
814    pub fn new() -> Self {
815        let strategies: Vec<Box<dyn FallbackStrategy>> = vec![
816            Box::new(SimplifiedProcessingStrategy),
817            Box::new(PassthroughStrategy),
818            Box::new(QualityAdjustmentStrategy),
819            Box::new(ResourceOptimizationStrategy),
820            Box::new(AlternativeAlgorithmStrategy),
821        ];
822
823        Self {
824            strategies,
825            strategy_performance: HashMap::new(),
826        }
827    }
828
829    /// Select the best strategy for the given context
830    pub fn select_best_strategy(
831        &self,
832        context: &FallbackContext,
833        conversion_type: &ConversionType,
834    ) -> Option<&dyn FallbackStrategy> {
835        let failure_type = if let Some(ref error) = context.original_error {
836            if error.contains("quality") {
837                FailureType::QualityFailure
838            } else if error.contains("timeout") {
839                FailureType::PerformanceFailure
840            } else if error.contains("memory") {
841                FailureType::MemoryFailure
842            } else {
843                FailureType::ProcessingError
844            }
845        } else {
846            FailureType::Unknown
847        };
848
849        // Filter strategies that can handle this failure type
850        let mut candidates: Vec<&dyn FallbackStrategy> = self
851            .strategies
852            .iter()
853            .map(|s| s.as_ref())
854            .filter(|s| s.can_handle(&failure_type, conversion_type))
855            .collect();
856
857        if candidates.is_empty() {
858            return None;
859        }
860
861        // Sort by effectiveness (combination of priority and success probability)
862        candidates.sort_by(|a, b| {
863            let score_a = (a.priority() as f32) + a.success_probability(context);
864            let score_b = (b.priority() as f32) + b.success_probability(context);
865            score_b
866                .partial_cmp(&score_a)
867                .unwrap_or(std::cmp::Ordering::Equal)
868        });
869
870        candidates.into_iter().next()
871    }
872
873    /// Record successful strategy execution
874    pub fn record_success(
875        &mut self,
876        strategy_name: &str,
877        result: &ConversionResult,
878        processing_time: Duration,
879    ) {
880        let perf = self
881            .strategy_performance
882            .entry(strategy_name.to_string())
883            .or_default();
884
885        perf.total_attempts += 1;
886        perf.successful_attempts += 1;
887
888        if let Some(quality) = result.quality_metrics.get("overall_quality") {
889            perf.average_quality = (perf.average_quality * (perf.successful_attempts - 1) as f32
890                + quality)
891                / perf.successful_attempts as f32;
892        }
893
894        perf.average_processing_time = Duration::from_nanos(
895            ((perf.average_processing_time.as_nanos() * (perf.successful_attempts - 1) as u128
896                + processing_time.as_nanos())
897                / perf.successful_attempts as u128) as u64,
898        );
899
900        // Update recent success rate (simplified)
901        perf.recent_success_rate = perf.successful_attempts as f32 / perf.total_attempts as f32;
902        perf.effectiveness_score = perf.recent_success_rate * perf.average_quality;
903    }
904
905    /// Add custom fallback strategy
906    pub fn add_strategy(&mut self, strategy: Box<dyn FallbackStrategy>) {
907        self.strategies.push(strategy);
908    }
909
910    /// Get strategy performance statistics
911    pub fn get_strategy_stats(&self) -> &HashMap<String, StrategyPerformance> {
912        &self.strategy_performance
913    }
914}
915
916// Implementation of FailureHistory
917impl FailureHistory {
918    /// Create new failure history tracker
919    pub fn new(max_history_length: usize) -> Self {
920        Self {
921            failures_by_type: HashMap::new(),
922            success_patterns: Vec::new(),
923            max_history_length,
924        }
925    }
926
927    /// Record a failure
928    pub fn record_failure(&mut self, failure: FailureRecord) {
929        let failures = self
930            .failures_by_type
931            .entry(failure.failure_type.clone())
932            .or_default();
933
934        failures.push(failure);
935
936        // Limit history size
937        if failures.len() > self.max_history_length {
938            failures.remove(0);
939        }
940    }
941
942    /// Update success pattern
943    pub fn record_success_pattern(
944        &mut self,
945        failure_type: FailureType,
946        conversion_type: ConversionType,
947        successful_strategy: String,
948    ) {
949        // Look for existing pattern
950        if let Some(pattern) = self.success_patterns.iter_mut().find(|p| {
951            p.failure_type == failure_type
952                && p.conversion_type == conversion_type
953                && p.successful_strategy == successful_strategy
954        }) {
955            pattern.usage_count += 1;
956            pattern.confidence = (pattern.confidence * 0.9 + 0.1).min(1.0);
957        } else {
958            // Add new pattern
959            self.success_patterns.push(SuccessPattern {
960                failure_type,
961                conversion_type,
962                successful_strategy,
963                confidence: 0.5,
964                usage_count: 1,
965            });
966        }
967
968        // Limit pattern history
969        if self.success_patterns.len() > self.max_history_length / 2 {
970            self.success_patterns.sort_by_key(|a| a.usage_count);
971            self.success_patterns.remove(0);
972        }
973    }
974
975    /// Get failure statistics
976    pub fn get_failure_stats(&self) -> HashMap<FailureType, usize> {
977        self.failures_by_type
978            .iter()
979            .map(|(k, v)| (k.clone(), v.len()))
980            .collect()
981    }
982
983    /// Get most successful strategy for a failure type
984    pub fn get_best_strategy(
985        &self,
986        failure_type: &FailureType,
987        conversion_type: &ConversionType,
988    ) -> Option<String> {
989        self.success_patterns
990            .iter()
991            .filter(|p| &p.failure_type == failure_type && &p.conversion_type == conversion_type)
992            .max_by(|a, b| {
993                let score_a = a.confidence * a.usage_count as f32;
994                let score_b = b.confidence * b.usage_count as f32;
995                score_a
996                    .partial_cmp(&score_b)
997                    .unwrap_or(std::cmp::Ordering::Equal)
998            })
999            .map(|p| p.successful_strategy.clone())
1000    }
1001}
1002
1003/// Quality Adjustment Strategy - Adjusts quality parameters to improve results
1004#[derive(Debug)]
1005pub struct QualityAdjustmentStrategy;
1006
1007impl FallbackStrategy for QualityAdjustmentStrategy {
1008    fn name(&self) -> &str {
1009        "quality_adjustment"
1010    }
1011
1012    fn can_handle(&self, failure_type: &FailureType, conversion_type: &ConversionType) -> bool {
1013        match failure_type {
1014            FailureType::QualityFailure | FailureType::ArtifactFailure => {
1015                matches!(
1016                    conversion_type,
1017                    ConversionType::SpeakerConversion
1018                        | ConversionType::PitchShift
1019                        | ConversionType::SpeedTransformation
1020                        | ConversionType::EmotionalTransformation
1021                )
1022            }
1023            _ => false,
1024        }
1025    }
1026
1027    fn success_probability(&self, context: &FallbackContext) -> f32 {
1028        // Higher probability if we have quality metrics and moderate artifact levels
1029        let quality_factor = context.current_quality.unwrap_or(0.5);
1030        let artifact_factor = context
1031            .artifacts
1032            .as_ref()
1033            .map(|a| 1.0 - a.overall_score)
1034            .unwrap_or(0.5);
1035
1036        // Base probability adjusted by quality and artifact metrics
1037        let base_probability = 0.7;
1038        base_probability * quality_factor * artifact_factor
1039    }
1040
1041    fn apply(
1042        &self,
1043        request: &ConversionRequest,
1044        context: &FallbackContext,
1045        config: &ConversionConfig,
1046    ) -> Result<ConversionResult> {
1047        debug!("Applying quality adjustment strategy");
1048
1049        // Create adjusted configuration with more conservative settings
1050        let mut adjusted_config = config.clone();
1051
1052        // Reduce quality level to be more conservative
1053        adjusted_config.quality_level = (adjusted_config.quality_level * 0.8).max(0.1);
1054
1055        // Apply more conservative processing parameters
1056        let input_audio = &request.source_audio;
1057        let mut output_audio = input_audio.clone();
1058
1059        // Apply gentle processing based on conversion type
1060        match &request.conversion_type {
1061            ConversionType::PitchShift => {
1062                // Apply conservative pitch shifting (1.1x factor)
1063                let pitch_transform = PitchTransform::new(1.1);
1064                output_audio = pitch_transform.apply(&output_audio)?;
1065            }
1066            ConversionType::SpeedTransformation => {
1067                // Apply conservative speed transformation (0.95x factor)
1068                let speed_transform = SpeedTransform::new(0.95);
1069                output_audio = speed_transform.apply(&output_audio)?;
1070            }
1071            _ => {
1072                // For other types, apply minimal processing
1073                output_audio = input_audio.clone();
1074            }
1075        }
1076
1077        Ok(ConversionResult {
1078            request_id: request.id.clone(),
1079            converted_audio: output_audio,
1080            output_sample_rate: request.source_sample_rate,
1081            quality_metrics: HashMap::new(),
1082            artifacts: None,
1083            objective_quality: Some(ObjectiveQualityMetrics {
1084                overall_score: 0.7,
1085                spectral_similarity: 0.7,
1086                temporal_consistency: 0.8,
1087                prosodic_preservation: 0.7,
1088                naturalness: 0.7,
1089                perceptual_quality: 0.7,
1090                snr_estimate: 20.0,
1091                segmental_snr: 18.0,
1092            }),
1093            processing_time: context
1094                .time_constraints
1095                .max_processing_time
1096                .min(Duration::from_millis(100)),
1097            conversion_type: request.conversion_type.clone(),
1098            success: true,
1099            error_message: None,
1100            timestamp: SystemTime::now(),
1101        })
1102    }
1103
1104    fn priority(&self) -> i32 {
1105        75 // High priority for quality issues
1106    }
1107}
1108
1109/// Resource Optimization Strategy - Optimizes resource usage when resources are constrained
1110#[derive(Debug)]
1111pub struct ResourceOptimizationStrategy;
1112
1113impl FallbackStrategy for ResourceOptimizationStrategy {
1114    fn name(&self) -> &str {
1115        "resource_optimization"
1116    }
1117
1118    fn can_handle(&self, failure_type: &FailureType, _conversion_type: &ConversionType) -> bool {
1119        matches!(
1120            failure_type,
1121            FailureType::ResourceFailure
1122                | FailureType::MemoryFailure
1123                | FailureType::PerformanceFailure
1124        )
1125    }
1126
1127    fn success_probability(&self, context: &FallbackContext) -> f32 {
1128        // Higher probability if we have resource constraints
1129        let cpu_factor = 1.0 - (context.available_resources.cpu_usage_percent / 100.0);
1130        let memory_factor = if context.available_resources.memory_available_mb > 1000.0 {
1131            0.8
1132        } else {
1133            0.3
1134        };
1135
1136        // Base probability adjusted by available resources
1137        let base_probability = 0.8_f32;
1138        base_probability * ((cpu_factor + memory_factor) / 2.0).max(0.2)
1139    }
1140
1141    fn apply(
1142        &self,
1143        request: &ConversionRequest,
1144        context: &FallbackContext,
1145        config: &ConversionConfig,
1146    ) -> Result<ConversionResult> {
1147        debug!("Applying resource optimization strategy");
1148
1149        // Create optimized configuration for low resource usage
1150        let mut optimized_config = config.clone();
1151
1152        // Reduce buffer sizes and processing complexity
1153        optimized_config.buffer_size = optimized_config.buffer_size.min(1024);
1154        // Note: enable_gpu_acceleration not available in this config, using CPU-optimized settings
1155
1156        // Reduce processing quality for speed
1157        optimized_config.quality_level = optimized_config.quality_level.min(0.5);
1158
1159        let input_audio = &request.source_audio;
1160        let mut output_audio = input_audio.clone();
1161
1162        // Apply minimal processing to conserve resources
1163        match &request.conversion_type {
1164            ConversionType::PitchShift => {
1165                // Use simple pitch shifting for minimal resource usage
1166                let pitch_transform = PitchTransform::new(1.05);
1167                output_audio = pitch_transform.apply(&output_audio)?;
1168            }
1169            ConversionType::SpeedTransformation => {
1170                let speed_transform = SpeedTransform::new(0.98);
1171                output_audio = speed_transform.apply(&output_audio)?;
1172            }
1173            _ => {
1174                // For complex conversions, use simplified processing
1175                output_audio = input_audio.clone();
1176            }
1177        }
1178
1179        Ok(ConversionResult {
1180            request_id: request.id.clone(),
1181            converted_audio: output_audio,
1182            output_sample_rate: request.source_sample_rate,
1183            quality_metrics: HashMap::new(),
1184            artifacts: None,
1185            objective_quality: Some(ObjectiveQualityMetrics {
1186                overall_score: 0.6,
1187                spectral_similarity: 0.6,
1188                temporal_consistency: 0.7,
1189                prosodic_preservation: 0.6,
1190                naturalness: 0.6,
1191                perceptual_quality: 0.6,
1192                snr_estimate: 18.0,
1193                segmental_snr: 16.0,
1194            }),
1195            processing_time: Duration::from_millis(50),
1196            conversion_type: request.conversion_type.clone(),
1197            success: true,
1198            error_message: None,
1199            timestamp: SystemTime::now(),
1200        })
1201    }
1202
1203    fn priority(&self) -> i32 {
1204        60 // Medium-high priority for resource issues
1205    }
1206}
1207
1208/// Alternative Algorithm Strategy - Uses alternative algorithms when primary ones fail
1209#[derive(Debug)]
1210pub struct AlternativeAlgorithmStrategy;
1211
1212impl FallbackStrategy for AlternativeAlgorithmStrategy {
1213    fn name(&self) -> &str {
1214        "alternative_algorithm"
1215    }
1216
1217    fn can_handle(&self, failure_type: &FailureType, conversion_type: &ConversionType) -> bool {
1218        match failure_type {
1219            FailureType::ProcessingError | FailureType::ModelFailure => {
1220                matches!(
1221                    conversion_type,
1222                    ConversionType::SpeakerConversion
1223                        | ConversionType::PitchShift
1224                        | ConversionType::SpeedTransformation
1225                        | ConversionType::AgeTransformation
1226                        | ConversionType::GenderTransformation
1227                )
1228            }
1229            _ => false,
1230        }
1231    }
1232
1233    fn success_probability(&self, context: &FallbackContext) -> f32 {
1234        // Higher probability if previous attempts have failed
1235        let attempt_factor = if context.previous_attempts.len() > 1 {
1236            0.8
1237        } else {
1238            0.6
1239        };
1240
1241        // Consider available processing time
1242        let time_factor = if context.time_constraints.max_processing_time.as_millis() > 1000 {
1243            1.0
1244        } else {
1245            0.7
1246        };
1247
1248        attempt_factor * time_factor
1249    }
1250
1251    fn apply(
1252        &self,
1253        request: &ConversionRequest,
1254        context: &FallbackContext,
1255        _config: &ConversionConfig,
1256    ) -> Result<ConversionResult> {
1257        debug!("Applying alternative algorithm strategy");
1258
1259        let input_audio = &request.source_audio;
1260        let mut output_audio = input_audio.clone();
1261
1262        // Use alternative, more robust algorithms
1263        match &request.conversion_type {
1264            ConversionType::PitchShift => {
1265                // Use basic time-domain pitch shifting instead of complex frequency-domain methods
1266                let factor = 1.2; // Fixed factor for alternative approach
1267                let samples_per_frame = 512;
1268
1269                for chunk in output_audio.chunks_mut(samples_per_frame) {
1270                    if chunk.len() == samples_per_frame {
1271                        // Simple pitch shifting by resampling
1272                        let mut resampled = Vec::with_capacity(chunk.len());
1273                        for i in 0..chunk.len() {
1274                            let src_idx = ((i as f32) / factor) as usize;
1275                            if src_idx < chunk.len() {
1276                                resampled.push(chunk[src_idx]);
1277                            } else {
1278                                resampled.push(0.0);
1279                            }
1280                        }
1281                        chunk.copy_from_slice(&resampled);
1282                    }
1283                }
1284            }
1285            ConversionType::SpeedTransformation => {
1286                // Use simple time-stretching without pitch preservation
1287                let speed_factor = 0.9; // Fixed factor for alternative approach
1288                let target_len = ((input_audio.len() as f32) / speed_factor) as usize;
1289                let mut stretched = Vec::with_capacity(target_len);
1290
1291                for i in 0..target_len {
1292                    let src_idx = ((i as f32) * speed_factor) as usize;
1293                    if src_idx < input_audio.len() {
1294                        stretched.push(input_audio[src_idx]);
1295                    } else {
1296                        stretched.push(0.0);
1297                    }
1298                }
1299                output_audio = stretched;
1300            }
1301            ConversionType::AgeTransformation => {
1302                // Use simple formant shifting approximation
1303                for sample in output_audio.iter_mut() {
1304                    *sample *= 0.9; // Simple age simulation through amplitude reduction
1305                }
1306            }
1307            ConversionType::GenderTransformation => {
1308                // Use basic spectral modification
1309                for (i, sample) in output_audio.iter_mut().enumerate() {
1310                    if i % 2 == 0 {
1311                        *sample *= 1.1; // Simple formant approximation
1312                    }
1313                }
1314            }
1315            _ => {
1316                // For other types, apply passthrough with minimal processing
1317                output_audio = input_audio.clone();
1318            }
1319        }
1320
1321        Ok(ConversionResult {
1322            request_id: request.id.clone(),
1323            converted_audio: output_audio,
1324            output_sample_rate: request.source_sample_rate,
1325            quality_metrics: HashMap::new(),
1326            artifacts: None,
1327            objective_quality: Some(ObjectiveQualityMetrics {
1328                overall_score: 0.65,
1329                spectral_similarity: 0.65,
1330                temporal_consistency: 0.7,
1331                prosodic_preservation: 0.6,
1332                naturalness: 0.65,
1333                perceptual_quality: 0.65,
1334                snr_estimate: 19.0,
1335                segmental_snr: 17.0,
1336            }),
1337            processing_time: context
1338                .time_constraints
1339                .max_processing_time
1340                .min(Duration::from_millis(200)),
1341            conversion_type: request.conversion_type.clone(),
1342            success: true,
1343            error_message: None,
1344            timestamp: SystemTime::now(),
1345        })
1346    }
1347
1348    fn priority(&self) -> i32 {
1349        50 // Medium priority - try after quality adjustment but before passthrough
1350    }
1351}
1352
1353/// Default implementation
1354impl Default for GracefulDegradationController {
1355    fn default() -> Self {
1356        Self::new()
1357    }
1358}