Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d2926dc6b | ||
|
|
31370f053c | ||
|
|
9370c70311 |
+5
-5
@@ -22,11 +22,11 @@ PORT=8083
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
# Portainer API (required for container/stack management)
|
# Portainer API (required for container/stack management)
|
||||||
PORTAINER_URL=http://portainer:9000
|
PORTAINER_URL=http://localhost:8001
|
||||||
PORTAINER_API_KEY=ptr_your-api-key-here
|
PORTAINER_API_KEY=ptr_your-api-key-here
|
||||||
|
|
||||||
# Nginx Proxy Manager API
|
# Nginx Proxy Manager API
|
||||||
NPM_URL=http://npm:81
|
NPM_URL=http://localhost:81
|
||||||
NPM_EMAIL=admin@example.com
|
NPM_EMAIL=admin@example.com
|
||||||
NPM_PASSWORD=your-npm-password
|
NPM_PASSWORD=your-npm-password
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ NPM_PASSWORD=your-npm-password
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
# Home Assistant API
|
# Home Assistant API
|
||||||
HOMEASSISTANT_URL=http://homeassistant:8123
|
HOMEASSISTANT_URL=http://localhost:8123
|
||||||
HOMEASSISTANT_TOKEN=your-long-lived-access-token
|
HOMEASSISTANT_TOKEN=your-long-lived-access-token
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -43,10 +43,10 @@ HOMEASSISTANT_TOKEN=your-long-lived-access-token
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
# Ollama API
|
# Ollama API
|
||||||
OLLAMA_BASE_URL=http://ollama:11434
|
OLLAMA_BASE_URL=http://localhost:11434
|
||||||
|
|
||||||
# SearXNG (self-hosted search)
|
# SearXNG (self-hosted search)
|
||||||
SEARXNG_URL=http://searxng:8080
|
SEARXNG_URL=http://localhost:8080
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Vector Database
|
# Vector Database
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "core-api"
|
name = "core-api"
|
||||||
version = "1.3.1"
|
version = "1.3.3"
|
||||||
description = "Core Code API - Infrastructure management and tools API"
|
description = "Core Code API - Infrastructure management and tools API"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
|
|||||||
@@ -1,197 +0,0 @@
|
|||||||
"""
|
|
||||||
Core-AI HTTP Client
|
|
||||||
|
|
||||||
Provides interface to Core-AI service for AI performance metrics.
|
|
||||||
"""
|
|
||||||
import httpx
|
|
||||||
from typing import Optional, Dict, List, Any
|
|
||||||
from src.logging_config import get_logger
|
|
||||||
from src.config import get_settings
|
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
|
||||||
settings = get_settings()
|
|
||||||
|
|
||||||
|
|
||||||
class CoreAIClient:
|
|
||||||
"""
|
|
||||||
HTTP client for Core-AI service
|
|
||||||
|
|
||||||
Provides access to AI performance metrics, tool execution stats,
|
|
||||||
and memory system monitoring.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
base_url: Optional[str] = None,
|
|
||||||
timeout: int = 10
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Initialize Core-AI client
|
|
||||||
|
|
||||||
Args:
|
|
||||||
base_url: Core-AI base URL (default from settings)
|
|
||||||
timeout: Request timeout in seconds
|
|
||||||
"""
|
|
||||||
self.base_url = (base_url or getattr(settings, 'core_ai_base_url', 'http://core-ai:8086')).rstrip("/")
|
|
||||||
self.timeout = timeout
|
|
||||||
self.client = httpx.AsyncClient(timeout=self.timeout)
|
|
||||||
|
|
||||||
async def close(self):
|
|
||||||
"""Close the HTTP client"""
|
|
||||||
await self.client.aclose()
|
|
||||||
|
|
||||||
async def health_check(self) -> bool:
|
|
||||||
"""
|
|
||||||
Check if Core-AI service is accessible
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
True if accessible, False otherwise
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
response = await self.client.get(f"{self.base_url}/health")
|
|
||||||
return response.status_code == 200
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Core-AI health check failed: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
async def get_metrics(self) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
Get comprehensive AI performance metrics
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dict with agent performance, tool execution, memory stats
|
|
||||||
|
|
||||||
Example:
|
|
||||||
{
|
|
||||||
"uptime_seconds": 3600,
|
|
||||||
"timestamp": "2025-12-03T20:00:00Z",
|
|
||||||
"agent": {
|
|
||||||
"total_requests": 100,
|
|
||||||
"avg_response_time_ms": 1250.5,
|
|
||||||
"p95_response_time_ms": 3200.0,
|
|
||||||
...
|
|
||||||
},
|
|
||||||
"tools": {
|
|
||||||
"total_calls": 250,
|
|
||||||
"success_rate": 0.98,
|
|
||||||
"top_tools": {...}
|
|
||||||
},
|
|
||||||
"memory": {
|
|
||||||
"tier1_hit_rate": 0.85,
|
|
||||||
...
|
|
||||||
},
|
|
||||||
...
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
response = await self.client.get(f"{self.base_url}/metrics")
|
|
||||||
response.raise_for_status()
|
|
||||||
return response.json()
|
|
||||||
except httpx.HTTPStatusError as e:
|
|
||||||
logger.error(f"Failed to get metrics: HTTP {e.response.status_code}")
|
|
||||||
raise
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to get metrics: {e}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
async def get_recent_errors(self, limit: int = 20) -> List[Dict[str, Any]]:
|
|
||||||
"""
|
|
||||||
Get recent request errors
|
|
||||||
|
|
||||||
Args:
|
|
||||||
limit: Maximum number of errors to return
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
List of error records with timestamps
|
|
||||||
|
|
||||||
Example:
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"timestamp": "2025-12-03T19:45:12Z",
|
|
||||||
"agent_type": "pydantic",
|
|
||||||
"error": "Connection timeout",
|
|
||||||
"duration_ms": 5000
|
|
||||||
},
|
|
||||||
...
|
|
||||||
]
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
response = await self.client.get(
|
|
||||||
f"{self.base_url}/metrics/errors",
|
|
||||||
params={"limit": limit}
|
|
||||||
)
|
|
||||||
response.raise_for_status()
|
|
||||||
data = response.json()
|
|
||||||
return data.get("errors", [])
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to get recent errors: {e}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
async def get_tool_failures(self, limit: int = 20) -> List[Dict[str, Any]]:
|
|
||||||
"""
|
|
||||||
Get recent tool execution failures
|
|
||||||
|
|
||||||
Args:
|
|
||||||
limit: Maximum number of failures to return
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
List of tool failure records
|
|
||||||
|
|
||||||
Example:
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"timestamp": "2025-12-03T19:50:30Z",
|
|
||||||
"tool_name": "list_containers",
|
|
||||||
"error": "Connection refused",
|
|
||||||
"duration_ms": 150
|
|
||||||
},
|
|
||||||
...
|
|
||||||
]
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
response = await self.client.get(
|
|
||||||
f"{self.base_url}/metrics/tool-failures",
|
|
||||||
params={"limit": limit}
|
|
||||||
)
|
|
||||||
response.raise_for_status()
|
|
||||||
data = response.json()
|
|
||||||
return data.get("failures", [])
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to get tool failures: {e}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
async def reset_metrics(self) -> bool:
|
|
||||||
"""
|
|
||||||
Reset all metrics (admin operation)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
True if successful
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
response = await self.client.post(f"{self.base_url}/metrics/reset")
|
|
||||||
response.raise_for_status()
|
|
||||||
logger.info("Successfully reset Core-AI metrics")
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to reset metrics: {e}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
async def __aenter__(self):
|
|
||||||
"""Async context manager entry"""
|
|
||||||
return self
|
|
||||||
|
|
||||||
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
||||||
"""Async context manager exit"""
|
|
||||||
await self.close()
|
|
||||||
|
|
||||||
|
|
||||||
# Singleton instance
|
|
||||||
_ai_client: Optional[CoreAIClient] = None
|
|
||||||
|
|
||||||
|
|
||||||
def get_ai_client() -> CoreAIClient:
|
|
||||||
"""Get singleton Core-AI client instance"""
|
|
||||||
global _ai_client
|
|
||||||
if _ai_client is None:
|
|
||||||
_ai_client = CoreAIClient()
|
|
||||||
return _ai_client
|
|
||||||
+9
-12
@@ -46,7 +46,7 @@ class Settings(BaseSettings):
|
|||||||
log_level: str = "DEBUG"
|
log_level: str = "DEBUG"
|
||||||
|
|
||||||
# Ollama Configuration (for AI orchestration)
|
# Ollama Configuration (for AI orchestration)
|
||||||
ollama_base_url: str = "http://ollama:11434"
|
ollama_base_url: str # Required - set OLLAMA_BASE_URL in .env
|
||||||
ollama_timeout: int = 300 # 5 minutes
|
ollama_timeout: int = 300 # 5 minutes
|
||||||
|
|
||||||
# Model Configuration
|
# Model Configuration
|
||||||
@@ -90,25 +90,22 @@ class Settings(BaseSettings):
|
|||||||
|
|
||||||
# Search Configuration
|
# Search Configuration
|
||||||
search_provider: str = "searxng"
|
search_provider: str = "searxng"
|
||||||
searxng_url: str = "http://searxng:8080"
|
searxng_url: str # Required - set SEARXNG_URL in .env
|
||||||
|
|
||||||
# Infrastructure Management (Portainer)
|
# Infrastructure Management (Portainer)
|
||||||
portainer_url: str = "http://portainer:9000"
|
portainer_url: str # Required - set PORTAINER_URL in .env
|
||||||
portainer_api_key: str = ""
|
portainer_api_key: str # Required - set PORTAINER_API_KEY in .env
|
||||||
|
|
||||||
# Infrastructure Management (Nginx Proxy Manager)
|
# Infrastructure Management (Nginx Proxy Manager)
|
||||||
npm_url: str = "http://npm:81"
|
npm_url: str # Required - set NPM_URL in .env
|
||||||
npm_email: str = ""
|
npm_email: str # Required - set NPM_EMAIL in .env
|
||||||
npm_password: str = ""
|
npm_password: str # Required - set NPM_PASSWORD in .env
|
||||||
|
|
||||||
# Home Assistant Configuration
|
# Home Assistant Configuration
|
||||||
homeassistant_url: str = "http://homeassistant:8123"
|
homeassistant_url: str # Required - set HOMEASSISTANT_URL in .env
|
||||||
homeassistant_token: str = ""
|
homeassistant_token: str # Required - set HOMEASSISTANT_TOKEN in .env
|
||||||
homeassistant_timeout: int = 30
|
homeassistant_timeout: int = 30
|
||||||
|
|
||||||
# Core-AI Service (AI performance metrics)
|
|
||||||
core_ai_base_url: str = "http://core-ai:8086"
|
|
||||||
|
|
||||||
# OIDC Authentication (Authentik)
|
# OIDC Authentication (Authentik)
|
||||||
oidc_enabled: bool = False # Set to True to require authentication
|
oidc_enabled: bool = False # Set to True to require authentication
|
||||||
oidc_issuer: str = "https://auth.schweitz.net/application/o/core-api/"
|
oidc_issuer: str = "https://auth.schweitz.net/application/o/core-api/"
|
||||||
|
|||||||
@@ -1,219 +0,0 @@
|
|||||||
"""
|
|
||||||
AI Metrics Proxy Controller
|
|
||||||
|
|
||||||
Provides proxy endpoints to Core-AI service metrics.
|
|
||||||
Allows external access to AI performance stats via core-api.
|
|
||||||
"""
|
|
||||||
from fastapi import APIRouter, HTTPException
|
|
||||||
from typing import Dict, List, Any
|
|
||||||
from src.clients.ai_client import get_ai_client
|
|
||||||
from src.logging_config import get_logger
|
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
|
||||||
|
|
||||||
# Create router
|
|
||||||
router = APIRouter(
|
|
||||||
prefix="/ai",
|
|
||||||
tags=["AI Metrics"]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get(
|
|
||||||
"/health",
|
|
||||||
summary="Check Core-AI service health",
|
|
||||||
description="Verify that the Core-AI service is accessible and responding"
|
|
||||||
)
|
|
||||||
async def ai_health_check():
|
|
||||||
"""
|
|
||||||
Check if Core-AI service is healthy
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Health status and availability
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
ai_client = get_ai_client()
|
|
||||||
is_healthy = await ai_client.health_check()
|
|
||||||
|
|
||||||
return {
|
|
||||||
"service": "core-ai",
|
|
||||||
"status": "healthy" if is_healthy else "unhealthy",
|
|
||||||
"accessible": is_healthy
|
|
||||||
}
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"AI health check failed: {e}")
|
|
||||||
return {
|
|
||||||
"service": "core-ai",
|
|
||||||
"status": "error",
|
|
||||||
"accessible": False,
|
|
||||||
"error": str(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@router.get(
|
|
||||||
"/metrics",
|
|
||||||
response_model=Dict[str, Any],
|
|
||||||
summary="Get comprehensive AI performance metrics",
|
|
||||||
description="Returns detailed metrics including agent performance, tool execution stats, memory system metrics, and user activity"
|
|
||||||
)
|
|
||||||
async def get_ai_metrics():
|
|
||||||
"""
|
|
||||||
Proxy endpoint for Core-AI metrics
|
|
||||||
|
|
||||||
Returns comprehensive AI performance data:
|
|
||||||
- Agent request statistics (total, by type, response times)
|
|
||||||
- Response time percentiles (p50, p95, p99)
|
|
||||||
- Tool execution metrics (calls, success rates, durations)
|
|
||||||
- Memory system statistics (cache hits, consolidations)
|
|
||||||
- User activity tracking
|
|
||||||
- Concurrency metrics
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dict with all collected metrics
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
HTTPException: If Core-AI is unreachable or returns error
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
ai_client = get_ai_client()
|
|
||||||
metrics = await ai_client.get_metrics()
|
|
||||||
return metrics
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to fetch AI metrics: {e}")
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=503,
|
|
||||||
detail=f"Core-AI service unavailable: {str(e)}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get(
|
|
||||||
"/metrics/errors",
|
|
||||||
response_model=Dict[str, Any],
|
|
||||||
summary="Get recent request errors",
|
|
||||||
description="Returns recent AI agent request errors with timestamps and details"
|
|
||||||
)
|
|
||||||
async def get_ai_errors(limit: int = 20):
|
|
||||||
"""
|
|
||||||
Get recent AI request errors
|
|
||||||
|
|
||||||
Args:
|
|
||||||
limit: Maximum number of errors to return (default: 20)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dict with error list and total count
|
|
||||||
|
|
||||||
Example response:
|
|
||||||
{
|
|
||||||
"errors": [
|
|
||||||
{
|
|
||||||
"timestamp": "2025-12-03T19:45:12Z",
|
|
||||||
"agent_type": "pydantic",
|
|
||||||
"error": "Connection timeout",
|
|
||||||
"duration_ms": 5000
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"total": 1
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
ai_client = get_ai_client()
|
|
||||||
errors = await ai_client.get_recent_errors(limit=limit)
|
|
||||||
|
|
||||||
return {
|
|
||||||
"errors": errors,
|
|
||||||
"total": len(errors)
|
|
||||||
}
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to fetch AI errors: {e}")
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=503,
|
|
||||||
detail=f"Core-AI service unavailable: {str(e)}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get(
|
|
||||||
"/metrics/tool-failures",
|
|
||||||
response_model=Dict[str, Any],
|
|
||||||
summary="Get recent tool execution failures",
|
|
||||||
description="Returns recent tool execution failures with error details"
|
|
||||||
)
|
|
||||||
async def get_ai_tool_failures(limit: int = 20):
|
|
||||||
"""
|
|
||||||
Get recent tool execution failures
|
|
||||||
|
|
||||||
Args:
|
|
||||||
limit: Maximum number of failures to return (default: 20)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dict with failure list and total count
|
|
||||||
|
|
||||||
Example response:
|
|
||||||
{
|
|
||||||
"failures": [
|
|
||||||
{
|
|
||||||
"timestamp": "2025-12-03T19:50:30Z",
|
|
||||||
"tool_name": "list_containers",
|
|
||||||
"error": "Connection refused",
|
|
||||||
"duration_ms": 150
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"total": 1
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
ai_client = get_ai_client()
|
|
||||||
failures = await ai_client.get_tool_failures(limit=limit)
|
|
||||||
|
|
||||||
return {
|
|
||||||
"failures": failures,
|
|
||||||
"total": len(failures)
|
|
||||||
}
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to fetch tool failures: {e}")
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=503,
|
|
||||||
detail=f"Core-AI service unavailable: {str(e)}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.post(
|
|
||||||
"/metrics/reset",
|
|
||||||
summary="Reset all AI metrics (admin)",
|
|
||||||
description="Clear all collected metrics. This is an administrative operation that resets all counters and history."
|
|
||||||
)
|
|
||||||
async def reset_ai_metrics():
|
|
||||||
"""
|
|
||||||
Reset all AI metrics (admin operation)
|
|
||||||
|
|
||||||
Clears all collected metrics including:
|
|
||||||
- Request history
|
|
||||||
- Tool execution stats
|
|
||||||
- Memory system metrics
|
|
||||||
- Error logs
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Success confirmation
|
|
||||||
|
|
||||||
Note:
|
|
||||||
This is an administrative operation that should be used carefully.
|
|
||||||
All historical data will be lost.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
ai_client = get_ai_client()
|
|
||||||
await ai_client.reset_metrics()
|
|
||||||
|
|
||||||
logger.info("AI metrics reset successfully")
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"message": "AI metrics reset successfully"
|
|
||||||
}
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to reset AI metrics: {e}")
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=503,
|
|
||||||
detail=f"Core-AI service unavailable: {str(e)}"
|
|
||||||
)
|
|
||||||
@@ -11,9 +11,6 @@ from src.config import get_settings
|
|||||||
from src.logging_config import get_logger
|
from src.logging_config import get_logger
|
||||||
from src.models.ollama_client import get_ollama_client
|
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__)
|
logger = get_logger(__name__)
|
||||||
@@ -52,20 +49,7 @@ class HealthController(BaseController):
|
|||||||
"service": settings.app_name,
|
"service": settings.app_name,
|
||||||
"version": settings.app_version,
|
"version": settings.app_version,
|
||||||
"status": "healthy",
|
"status": "healthy",
|
||||||
"documentation": {
|
"docs": "/docs"
|
||||||
"swagger_ui": "/docs",
|
|
||||||
"redoc": "/redoc",
|
|
||||||
"openapi_spec": "/openapi.json"
|
|
||||||
},
|
|
||||||
"endpoints": {
|
|
||||||
"chat_completions": "/v1/chat/completions",
|
|
||||||
"models": "/v1/models",
|
|
||||||
"conversations": "/v1/conversations",
|
|
||||||
"dns_lookup": "/tools/dns/lookup",
|
|
||||||
"infrastructure": "/infrastructure",
|
|
||||||
"health": "/health",
|
|
||||||
"health_full": "/health/full"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
@@ -147,11 +131,6 @@ class HealthController(BaseController):
|
|||||||
ollama_error = str(e)
|
ollama_error = str(e)
|
||||||
logger.warning(f"Ollama health check failed: {ollama_error}")
|
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
|
is_healthy = ollama_healthy
|
||||||
|
|
||||||
elapsed_ms = int((time.time() - start_time) * 1000)
|
elapsed_ms = int((time.time() - start_time) * 1000)
|
||||||
@@ -170,8 +149,7 @@ class HealthController(BaseController):
|
|||||||
"available": False
|
"available": False
|
||||||
},
|
},
|
||||||
"error": ollama_error
|
"error": ollama_error
|
||||||
},
|
}
|
||||||
"note": "AI agent functionality available in separate core-ai service (port 8086)"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,20 +195,7 @@ class HealthController(BaseController):
|
|||||||
"error": str(e)
|
"error": str(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
# 2. Agent Stack - Moved to separate core-ai service
|
# 2. Configuration
|
||||||
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"] = {
|
diagnostics["configuration"] = {
|
||||||
"agent_fallback_enabled": settings.agent_fallback_enabled,
|
"agent_fallback_enabled": settings.agent_fallback_enabled,
|
||||||
"memory_tier1_max_turns": settings.memory_tier1_max_turns,
|
"memory_tier1_max_turns": settings.memory_tier1_max_turns,
|
||||||
|
|||||||
+7
-80
@@ -13,7 +13,6 @@ from src.controllers.infrastructure_controller import infrastructure_controller
|
|||||||
from src.controllers.tools_controller import tools_controller
|
from src.controllers.tools_controller import tools_controller
|
||||||
from src.controllers.health_controller import health_controller
|
from src.controllers.health_controller import health_controller
|
||||||
from src.controllers.static_controller import static_controller
|
from src.controllers.static_controller import static_controller
|
||||||
from src.controllers.ai_controller import router as ai_router
|
|
||||||
from src.controllers.housekeeping_controller import housekeeping_controller
|
from src.controllers.housekeeping_controller import housekeeping_controller
|
||||||
from src.security import initialize_oidc
|
from src.security import initialize_oidc
|
||||||
|
|
||||||
@@ -64,89 +63,18 @@ app = FastAPI(
|
|||||||
title=settings.app_name,
|
title=settings.app_name,
|
||||||
version=settings.app_version,
|
version=settings.app_version,
|
||||||
description="""
|
description="""
|
||||||
Core Code API provides OpenAPI-compatible functions and AI orchestration for Open WebUI.
|
Core Code API - Infrastructure management and home automation API.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
### OpenAI-Compatible API (v1)
|
- **Infrastructure Management** - Container and stack management via Portainer
|
||||||
- `/v1/chat/completions` - Chat completions with streaming support
|
- **Home Automation** - Device control via Home Assistant
|
||||||
- `/v1/models` - List available models
|
- **Tools** - DNS lookup and utilities
|
||||||
Compatible with OpenAI client libraries and Open WebUI.
|
|
||||||
|
|
||||||
### Conversation Memory (Phase 2)
|
See `/docs` for the full API reference.
|
||||||
- `/v1/conversations/{id}` - Get conversation history
|
|
||||||
- `/v1/conversations/{id}/search` - Semantic search within conversation
|
|
||||||
- `/v1/conversations/search` - Search across all conversations
|
|
||||||
- `/v1/conversations/{id}/stats` - Get conversation statistics
|
|
||||||
- `/v1/conversations/{id}/consolidate` - Manual consolidation
|
|
||||||
- `DELETE /v1/conversations/{id}` - Delete conversation
|
|
||||||
|
|
||||||
Multi-tier memory system:
|
|
||||||
- **Tier 1**: Fast in-memory buffer (last 10 turns)
|
|
||||||
- **Tier 2/3**: Unified Qdrant storage (persistent + semantic search)
|
|
||||||
|
|
||||||
### Infrastructure Management
|
|
||||||
**Read Endpoints:**
|
|
||||||
- `GET /infrastructure/health` - Check Portainer & NPM connectivity
|
|
||||||
- `GET /infrastructure/services` - List all deployed services
|
|
||||||
- `GET /infrastructure/services/{name}` - Get service details
|
|
||||||
- `GET /infrastructure/ports` - List allocated ports
|
|
||||||
- `GET /infrastructure/domains` - List configured domains
|
|
||||||
|
|
||||||
**Write Endpoints (Admin Only):**
|
|
||||||
- `POST /infrastructure/services` - Deploy new service from compose YAML
|
|
||||||
- `PUT /infrastructure/services/{name}` - Update existing service
|
|
||||||
- `DELETE /infrastructure/services/{name}` - Remove service and stack
|
|
||||||
- `POST /infrastructure/proxy` - Create proxy host with optional SSL
|
|
||||||
|
|
||||||
Automates infrastructure operations via Portainer and Nginx Proxy Manager APIs.
|
|
||||||
|
|
||||||
### Home Automation (Housekeeping)
|
|
||||||
**Read Endpoints:**
|
|
||||||
- `GET /housekeeping/health` - Check Home Assistant connectivity
|
|
||||||
- `GET /housekeeping/devices` - List devices (filter by domain, area)
|
|
||||||
- `GET /housekeeping/devices/{entity_id}` - Get device details
|
|
||||||
- `GET /housekeeping/areas` - List areas/rooms
|
|
||||||
- `GET /housekeeping/scenes` - List scenes
|
|
||||||
- `GET /housekeeping/scripts` - List scripts
|
|
||||||
- `GET /housekeeping/automations` - List automations
|
|
||||||
- `GET /housekeeping/history` - Get state history
|
|
||||||
|
|
||||||
**Write Endpoints (Admin Only):**
|
|
||||||
- `POST /housekeeping/devices/{entity_id}/control` - Control device
|
|
||||||
- `POST /housekeeping/scenes/{scene_id}/activate` - Activate scene
|
|
||||||
- `POST /housekeeping/scripts/{script_id}/run` - Run script
|
|
||||||
- `POST /housekeeping/automations/{automation_id}/toggle` - Enable/disable automation
|
|
||||||
|
|
||||||
Abstracts Home Assistant for the Tatlock Housekeeper agent and other consumers.
|
|
||||||
|
|
||||||
### Web Scraper
|
|
||||||
Intelligent web scraping with main content extraction.
|
|
||||||
Perfect for extracting articles, documentation, and blog posts for LLM consumption.
|
|
||||||
|
|
||||||
## Authentication
|
|
||||||
|
|
||||||
When OIDC authentication is enabled (oidc_enabled=true in config):
|
|
||||||
- Infrastructure write endpoints require authentication
|
|
||||||
- Use OAuth2/OIDC bearer token from Authentik
|
|
||||||
- Admin group membership required for infrastructure operations
|
|
||||||
|
|
||||||
## Integration
|
|
||||||
|
|
||||||
This API is designed to integrate with:
|
|
||||||
- **Open WebUI**: Direct OpenAI API compatibility
|
|
||||||
- **Open WebUI Functions**: Import via OpenAPI spec
|
|
||||||
- **Open WebUI Pipelines**: Use as data source
|
|
||||||
- **LangChain**: Compatible with standard HTTP tools
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
- **OpenAPI Spec**: `/openapi.json`
|
|
||||||
- **Swagger UI**: `/docs`
|
|
||||||
- **ReDoc**: `/redoc`
|
|
||||||
""",
|
""",
|
||||||
docs_url="/docs",
|
docs_url="/docs",
|
||||||
redoc_url="/redoc",
|
redoc_url=None,
|
||||||
openapi_url="/openapi.json",
|
openapi_url="/openapi.json",
|
||||||
lifespan=lifespan,
|
lifespan=lifespan,
|
||||||
debug=settings.debug,
|
debug=settings.debug,
|
||||||
@@ -172,7 +100,6 @@ app.include_router(tools_controller.router) # /tools/*
|
|||||||
app.include_router(infrastructure_controller.router) # /infrastructure/*
|
app.include_router(infrastructure_controller.router) # /infrastructure/*
|
||||||
app.include_router(housekeeping_controller.router) # /housekeeping/*
|
app.include_router(housekeeping_controller.router) # /housekeeping/*
|
||||||
app.include_router(static_controller.router) # /static/*
|
app.include_router(static_controller.router) # /static/*
|
||||||
app.include_router(ai_router) # /ai/*
|
|
||||||
|
|
||||||
|
|
||||||
# Global exception handler
|
# Global exception handler
|
||||||
|
|||||||
Reference in New Issue
Block a user