""" Health Controller Provides service health and information endpoints """ from fastapi import APIRouter, Response from fastapi.responses import JSONResponse from src.controllers.base import BaseController from src.config import get_settings from src.logging_config import get_logger from src.models.ollama_client import get_ollama_client from src.db import get_database logger = get_logger(__name__) class HealthController(BaseController): """ Controller for service health and information Provides endpoints for: - Service information and status - Health checks """ def __init__(self): super().__init__(prefix="", tags=["Health"]) def create_router(self) -> APIRouter: """Create and configure the router""" router = APIRouter(tags=self.tags) settings = get_settings() @router.get( "/", summary="Service information", response_class=JSONResponse ) async def root(): """ Get service information and health status Returns basic information about the API service and available endpoints. """ logger.debug("Root endpoint accessed") return { "service": settings.app_name, "version": settings.app_version, "status": "healthy", "docs": "/docs" } @router.get( "/health", summary="Health check", response_class=JSONResponse ) async def health_check(): """ Fast health check endpoint for container orchestration Returns a 200 OK immediately if the service is running. Does NOT check backend connectivity (use /health/full for that). Used by Docker, Kubernetes, and load balancers for liveness probes. """ return { "status": "healthy", "version": settings.app_version } @router.get( "/health/full", summary="Fast health check for Docker", ) async def full_health_check(response: Response): """ Fast health check for container orchestration (Docker/K8s). Checks component availability WITHOUT running expensive operations. Returns 200 OK if all components are available, otherwise 503. For detailed diagnostics, use /health/diagnostics instead. """ import time start_time = time.time() # Check 1: Ollama connection + verify agent model is available ollama_client = get_ollama_client() ollama_healthy = False ollama_error = None model_available = False try: # Ping Ollama ollama_healthy = await ollama_client.health_check() # Verify the agent model is pulled and check what's currently loaded models_info = {} if ollama_healthy: try: models_response = await ollama_client.list_models() available_models = [m.get('name', '') for m in models_response.get('models', [])] model_available = settings.agent_model in available_models # Get info about currently loaded models (those with size in memory) loaded_models = [ m.get('name', '') for m in models_response.get('models', []) if m.get('size', 0) > 0 ] models_info = { "configured": settings.agent_model, "available": model_available, "total_in_ollama": len(available_models), "currently_loaded": loaded_models if loaded_models else ["none"] } if not model_available: ollama_error = f"Model '{settings.agent_model}' not found in Ollama. Available: {', '.join(available_models[:3])}" ollama_healthy = False except Exception as e: ollama_error = f"Could not list Ollama models: {str(e)}" ollama_healthy = False except Exception as e: ollama_error = str(e) logger.warning(f"Ollama health check failed: {ollama_error}") # Check 2: Database connection database = get_database() db_healthy = False db_error = None try: db_healthy = await database.health_check() except Exception as e: db_error = str(e) logger.warning(f"Database health check failed: {db_error}") is_healthy = ollama_healthy and db_healthy elapsed_ms = int((time.time() - start_time) * 1000) status_code = 200 if is_healthy else 503 response.status_code = status_code return { "status": "healthy" if is_healthy else "unhealthy", "status_code": status_code, "response_time_ms": elapsed_ms, "components": { "ollama": { "status": "✅ healthy" if ollama_healthy else "❌ unhealthy", "models": models_info if models_info else { "configured": settings.agent_model, "available": False }, "error": ollama_error }, "database": { "status": "✅ healthy" if db_healthy else "❌ unhealthy", "error": db_error } } } @router.get( "/health/diagnostics", summary="Detailed system diagnostics", ) async def diagnostics(deep_test: bool = False): """ Comprehensive system diagnostics with detailed component information. Query Parameters: - deep_test: Set to true to actually test agent generation (slow, ~5-10s) Returns detailed information about all system components. """ import time start_time = time.time() diagnostics = { "timestamp": time.time(), "service": { "name": settings.app_name, "version": settings.app_version, "purpose": "Infrastructure management and tools API" }, "components": {} } # 1. Ollama Connection ollama_client = get_ollama_client() try: ollama_healthy = await ollama_client.health_check() diagnostics["components"]["ollama"] = { "status": "✅ connected", "url": settings.ollama_base_url, "timeout": settings.ollama_timeout, "default_model": settings.default_model } except Exception as e: diagnostics["components"]["ollama"] = { "status": "❌ error", "error": str(e) } # 2. Configuration diagnostics["configuration"] = { "agent_fallback_enabled": settings.agent_fallback_enabled, "memory_tier1_max_turns": settings.memory_tier1_max_turns, "cors_origins": settings.cors_origins[:2] if len(settings.cors_origins) > 2 else settings.cors_origins } elapsed_ms = int((time.time() - start_time) * 1000) diagnostics["response_time_ms"] = elapsed_ms return diagnostics return router # Create controller instance health_controller = HealthController()