using DramaLing.Api.Models.DTOs; namespace DramaLing.Api.Services.AI; /// /// AI 提供商管理器實作 /// public class AIProviderManager : IAIProviderManager { private readonly IEnumerable _providers; private readonly ILogger _logger; private readonly Random _random = new(); public AIProviderManager(IEnumerable providers, ILogger logger) { _providers = providers ?? throw new ArgumentNullException(nameof(providers)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logger.LogInformation("AIProviderManager initialized with {ProviderCount} providers: {ProviderNames}", _providers.Count(), string.Join(", ", _providers.Select(p => p.ProviderName))); } public async Task GetBestProviderAsync(ProviderSelectionStrategy strategy = ProviderSelectionStrategy.Performance) { var availableProviders = await GetAvailableProvidersAsync(); if (!availableProviders.Any()) { throw new InvalidOperationException("No AI providers are available"); } var selectedProvider = strategy switch { ProviderSelectionStrategy.Performance => await SelectByPerformanceAsync(availableProviders), ProviderSelectionStrategy.Cost => SelectByCost(availableProviders), ProviderSelectionStrategy.Reliability => await SelectByReliabilityAsync(availableProviders), ProviderSelectionStrategy.LoadBalance => SelectByLoadBalance(availableProviders), ProviderSelectionStrategy.Primary => SelectPrimary(availableProviders), _ => availableProviders.First() }; _logger.LogDebug("Selected AI provider: {ProviderName} using strategy: {Strategy}", selectedProvider.ProviderName, strategy); return selectedProvider; } public async Task> GetAvailableProvidersAsync() { var availableProviders = new List(); foreach (var provider in _providers) { try { if (provider.IsAvailable) { var healthStatus = await provider.CheckHealthAsync(); if (healthStatus.IsHealthy) { availableProviders.Add(provider); } else { _logger.LogWarning("Provider {ProviderName} is not healthy: {Error}", provider.ProviderName, healthStatus.ErrorMessage); } } else { _logger.LogWarning("Provider {ProviderName} is not available", provider.ProviderName); } } catch (Exception ex) { _logger.LogError(ex, "Error checking provider {ProviderName} availability", provider.ProviderName); } } return availableProviders; } public async Task GetProviderByNameAsync(string providerName) { var provider = _providers.FirstOrDefault(p => p.ProviderName.Equals(providerName, StringComparison.OrdinalIgnoreCase)); if (provider != null && provider.IsAvailable) { try { var healthStatus = await provider.CheckHealthAsync(); if (healthStatus.IsHealthy) { return provider; } } catch (Exception ex) { _logger.LogError(ex, "Error checking provider {ProviderName} health", providerName); } } return null; } public async Task CheckAllProvidersHealthAsync() { var report = new ProviderHealthReport { CheckedAt = DateTime.UtcNow, TotalProviders = _providers.Count() }; var healthTasks = _providers.Select(async provider => { try { var healthStatus = await provider.CheckHealthAsync(); var stats = await provider.GetStatsAsync(); return new ProviderHealthInfo { ProviderName = provider.ProviderName, IsHealthy = healthStatus.IsHealthy, ResponseTimeMs = healthStatus.ResponseTimeMs, ErrorMessage = healthStatus.ErrorMessage, Stats = stats }; } catch (Exception ex) { _logger.LogError(ex, "Error checking health for provider {ProviderName}", provider.ProviderName); return new ProviderHealthInfo { ProviderName = provider.ProviderName, IsHealthy = false, ErrorMessage = ex.Message, Stats = new AIProviderStats() }; } }); report.ProviderHealthInfos = (await Task.WhenAll(healthTasks)).ToList(); report.HealthyProviders = report.ProviderHealthInfos.Count(p => p.IsHealthy); return report; } public async Task AnalyzeSentenceAsync(string inputText, AnalysisOptions options, ProviderSelectionStrategy strategy = ProviderSelectionStrategy.Performance) { var provider = await GetBestProviderAsync(strategy); try { var result = await provider.AnalyzeSentenceAsync(inputText, options); _logger.LogInformation("Sentence analyzed successfully using provider: {ProviderName}", provider.ProviderName); return result; } catch (Exception ex) { _logger.LogError(ex, "Error analyzing sentence with provider {ProviderName}, attempting fallback", provider.ProviderName); // 嘗試使用其他可用的提供商 var availableProviders = (await GetAvailableProvidersAsync()) .Where(p => p.ProviderName != provider.ProviderName) .ToList(); foreach (var fallbackProvider in availableProviders) { try { var result = await fallbackProvider.AnalyzeSentenceAsync(inputText, options); _logger.LogWarning("Fallback successful using provider: {ProviderName}", fallbackProvider.ProviderName); return result; } catch (Exception fallbackEx) { _logger.LogError(fallbackEx, "Fallback provider {ProviderName} also failed", fallbackProvider.ProviderName); } } // 如果所有提供商都失敗,重新拋出原始異常 throw; } } #region 私有選擇方法 private async Task SelectByPerformanceAsync(IEnumerable providers) { var providerList = providers.ToList(); var performanceData = new List<(IAIProvider Provider, int ResponseTime)>(); foreach (var provider in providerList) { try { var stats = await provider.GetStatsAsync(); performanceData.Add((provider, stats.AverageResponseTimeMs)); } catch (Exception ex) { _logger.LogWarning(ex, "Could not get stats for provider {ProviderName}", provider.ProviderName); performanceData.Add((provider, int.MaxValue)); // 最低優先級 } } return performanceData .OrderBy(p => p.ResponseTime) .First().Provider; } private IAIProvider SelectByCost(IEnumerable providers) { return providers .OrderBy(p => p.CostPerRequest) .First(); } private async Task SelectByReliabilityAsync(IEnumerable providers) { var providerList = providers.ToList(); var reliabilityData = new List<(IAIProvider Provider, double SuccessRate)>(); foreach (var provider in providerList) { try { var stats = await provider.GetStatsAsync(); reliabilityData.Add((provider, stats.SuccessRate)); } catch (Exception ex) { _logger.LogWarning(ex, "Could not get stats for provider {ProviderName}", provider.ProviderName); reliabilityData.Add((provider, 0.0)); // 最低優先級 } } return reliabilityData .OrderByDescending(p => p.SuccessRate) .First().Provider; } private IAIProvider SelectByLoadBalance(IEnumerable providers) { var providerList = providers.ToList(); var randomIndex = _random.Next(providerList.Count); return providerList[randomIndex]; } private IAIProvider SelectPrimary(IEnumerable providers) { // 使用第一個可用的提供商作為主要提供商 return providers.First(); } #endregion }