using GermanApp.Domain.Interfaces; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Logging; namespace GermanApp.Infrastructure.Services; /// /// Health check for AI services (Mistral, Vosk, Coqui TTS). /// This is part of the Infrastructure layer. /// public class AiServicesHealthCheck : IHealthCheck { private readonly IMistralService? _mistralService; private readonly IVoskService? _voskService; private readonly ITtsService? _ttsService; private readonly ILogger _logger; /// /// Creates a new AI services health check. /// /// The Mistral text generation service (optional, may be null in some environments) /// The Vosk speech recognition service (optional, may be null in some environments) /// The Coqui TTS service (optional, may be null in some environments) /// Logger for health check operations public AiServicesHealthCheck( IMistralService? mistralService, IVoskService? voskService, ITtsService? ttsService, ILogger logger) { _mistralService = mistralService; _voskService = voskService; _ttsService = ttsService; _logger = logger; } /// /// Executes the health check for all AI services. /// public async Task CheckHealthAsync( HealthCheckContext context, CancellationToken cancellationToken = default) { var checks = new Dictionary(); var exceptions = new Dictionary(); // Check Mistral service if (_mistralService != null) { try { var isHealthy = await _mistralService.TestConnectionAsync(cancellationToken); checks["Mistral"] = isHealthy ? HealthStatus.Healthy : HealthStatus.Unhealthy; if (!isHealthy) { _logger.LogWarning("Mistral service health check failed"); } } catch (Exception ex) { checks["Mistral"] = HealthStatus.Unhealthy; exceptions["Mistral"] = ex; _logger.LogError(ex, "Mistral service health check failed with exception"); } } else { checks["Mistral"] = HealthStatus.Degraded; _logger.LogWarning("Mistral service is not registered"); } // Check Vosk service if (_voskService != null) { try { var isHealthy = await _voskService.TestModelAsync(cancellationToken); checks["Vosk"] = isHealthy ? HealthStatus.Healthy : HealthStatus.Unhealthy; if (!isHealthy) { _logger.LogWarning("Vosk service health check failed"); } } catch (Exception ex) { checks["Vosk"] = HealthStatus.Unhealthy; exceptions["Vosk"] = ex; _logger.LogError(ex, "Vosk service health check failed with exception"); } } else { checks["Vosk"] = HealthStatus.Degraded; _logger.LogWarning("Vosk service is not registered"); } // Check Coqui TTS service if (_ttsService != null) { try { var isHealthy = await _ttsService.TestModelAsync(cancellationToken); checks["Coqui TTS"] = isHealthy ? HealthStatus.Healthy : HealthStatus.Unhealthy; if (!isHealthy) { _logger.LogWarning("Coqui TTS service health check failed"); } } catch (Exception ex) { checks["Coqui TTS"] = HealthStatus.Unhealthy; exceptions["Coqui TTS"] = ex; _logger.LogError(ex, "Coqui TTS service health check failed with exception"); } } else { checks["Coqui TTS"] = HealthStatus.Degraded; _logger.LogWarning("Coqui TTS service is not registered"); } // Determine overall status var allHealthy = checks.Values.All(s => s == HealthStatus.Healthy); var anyUnhealthy = checks.Values.Any(s => s == HealthStatus.Unhealthy); var anyDegraded = checks.Values.Any(s => s == HealthStatus.Degraded); HealthStatus overallStatus = allHealthy ? HealthStatus.Healthy : anyUnhealthy ? HealthStatus.Unhealthy : HealthStatus.Degraded; // Build data dictionary with details var data = new Dictionary(); foreach (var check in checks) { data[check.Key] = new { Status = check.Value.ToString(), Exception = exceptions.TryGetValue(check.Key, out var ex) ? ex.Message : null }; } return new HealthCheckResult( overallStatus, "AI Services health check", data: data); } }