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);
}
}