Initial commit: core-api service extraction from portainer-core
Build and Push / build (release) Successful in 43s

This commit is contained in:
2025-12-11 15:52:59 +01:00
commit 488a4e8a91
49 changed files with 9478 additions and 0 deletions
+248
View File
@@ -0,0 +1,248 @@
"""
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
# Note: Agent functionality moved to separate core-ai service (Dec 2025)
# This service (core-api) only provides infrastructure management and tools
AGENT_AVAILABLE = False
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",
"documentation": {
"swagger_ui": "/docs",
"redoc": "/redoc",
"openapi_spec": "/openapi.json"
},
"endpoints": {
"chat_completions": "/v1/chat/completions",
"models": "/v1/models",
"conversations": "/v1/conversations",
"web_scraper": "/web-scraper/scrape",
"infrastructure": "/infrastructure",
"health": "/health",
"health_full": "/health/full"
}
}
@router.get(
"/health",
summary="Health check",
response_class=JSONResponse
)
async def health_check():
"""
Simple health check endpoint for container orchestration
Returns a 200 OK status when the service is running properly.
Used by Docker, Kubernetes, and load balancers.
"""
ollama_client = get_ollama_client()
ollama_healthy = await ollama_client.health_check()
return {
"status": "healthy",
"ollama_connected": ollama_healthy
}
@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}")
# Note: Agent functionality moved to separate core-ai service
# This service only needs Ollama for embeddings (infrastructure tools)
# Agent health is checked separately in core-ai service
# Determine overall status (only Ollama required for core-api)
is_healthy = ollama_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
},
"note": "AI agent functionality available in separate core-ai service (port 8086)"
}
}
@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. Agent Stack - Moved to separate core-ai service
diagnostics["components"]["agent"] = {
"status": "N/A",
"note": "AI agent functionality moved to separate core-ai service (port 8086)",
"check_url": "http://core-ai:8086/health"
}
# 3. Memory System (Qdrant) - Moved to core-ai service
diagnostics["components"]["qdrant"] = {
"status": "N/A",
"note": "Memory system managed by core-ai service (port 8086)"
}
# 4. 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()