feat: first IA page

This commit is contained in:
2026-02-13 12:18:24 +01:00
parent 5c9a8c9b5e
commit 4fe6a4b2c9
10 changed files with 910 additions and 3 deletions

View File

@@ -15,6 +15,7 @@ from .routes import (
analytics_router,
dashboard_router,
revenus_router,
ia_router,
)
app = FastAPI(
@@ -41,6 +42,7 @@ app.include_router(tags_router)
app.include_router(analytics_router)
app.include_router(dashboard_router)
app.include_router(revenus_router)
app.include_router(ia_router)
# Health check endpoints (keep in main app)

View File

@@ -6,6 +6,7 @@ from .tags import router as tags_router
from .analytics import router as analytics_router
from .dashboard import router as dashboard_router
from .revenus import router as revenus_router
from .ia import router as ia_router
__all__ = [
"extraction_router",
@@ -14,4 +15,5 @@ __all__ = [
"analytics_router",
"dashboard_router",
"revenus_router",
"ia_router",
]

View File

@@ -0,0 +1,88 @@
"""Routes IA — Requêtes en langage naturel via Ollama."""
import logging
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
import httpx
from ...services.ollama_service import ask_ollama, check_ollama_health
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/ia", tags=["ia"])
# ============================================================
# Request / Response models
# ============================================================
class AskRequest(BaseModel):
question: str
chart_type: str = "auto"
class AskResponse(BaseModel):
answer: str | None = None
data: dict | None = None
chart_config: dict | None = None
sql_query: str | None = None
error: str | None = None
class HealthResponse(BaseModel):
ollama_available: bool
model: str
# ============================================================
# Endpoints
# ============================================================
@router.post("/ask", response_model=AskResponse)
async def ask_ia(request: AskRequest) -> AskResponse:
"""Pose une question en langage naturel et obtient une réponse via Ollama.
Le LLM génère les requêtes SQL nécessaires, exécute les outils,
et retourne une réponse avec données et graphique optionnel.
"""
if not request.question.strip():
raise HTTPException(status_code=400, detail="La question ne peut pas être vide.")
try:
result = await ask_ollama(request.question, request.chart_type)
return AskResponse(
answer=result.get("answer"),
data=result.get("data"),
chart_config=result.get("chart_config"),
sql_query=result.get("sql_query"),
)
except httpx.TimeoutException:
raise HTTPException(
status_code=504,
detail="Ollama n'a pas répondu dans le délai imparti.",
)
except httpx.ConnectError:
raise HTTPException(
status_code=503,
detail="Impossible de se connecter à Ollama. Vérifiez qu'il est lancé.",
)
except httpx.HTTPStatusError as e:
logger.error(f"Ollama HTTP error: {e}")
raise HTTPException(
status_code=503,
detail=f"Erreur Ollama : {e.response.status_code}",
)
except Exception as e:
logger.error(f"IA ask error: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=str(e))
@router.get("/health", response_model=HealthResponse)
async def ia_health() -> HealthResponse:
"""Vérifie la disponibilité d'Ollama et du modèle configuré."""
result = await check_ollama_health()
return HealthResponse(**result)