Files
hsbg-ai/hsbg_ai/backend/api/routes/learning.py
2026-03-31 13:10:46 +02:00

93 lines
2.8 KiB
Python

"""Routes API — Mode Apprentissage."""
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, desc
from typing import Literal
from backend.database.db import get_db
from backend.database.models import AIDecision, LearningFeedback
router = APIRouter()
_processor = None
def _get_processor():
global _processor
if not _processor:
from backend.config.settings import get_settings
from backend.ai.learning.feedback_processor import FeedbackProcessor
_processor = FeedbackProcessor(get_settings())
return _processor
class FeedbackIn(BaseModel):
decision_id: int
rating: Literal["good", "bad", "neutral"]
better_action: dict | None = None
comment: str | None = None
@router.post("/feedback")
async def submit_feedback(req: FeedbackIn, db: AsyncSession = Depends(get_db)):
"""Soumet un retour utilisateur sur une décision IA."""
proc = _get_processor()
try:
fb = await proc.record_feedback(
db,
decision_id=req.decision_id,
rating=req.rating,
better_action=req.better_action,
comment=req.comment,
)
return {
"feedback_id": fb.id,
"rating": fb.rating,
"message": "Feedback enregistré — merci pour votre contribution!",
}
except ValueError as e:
raise HTTPException(404, str(e))
@router.get("/stats")
async def get_stats(db: AsyncSession = Depends(get_db)):
"""Statistiques globales du système d'apprentissage."""
return await _get_processor().get_stats(db)
@router.get("/decisions")
async def get_decisions(
session_id: int | None = None,
limit: int = 20,
db: AsyncSession = Depends(get_db),
):
"""Historique des décisions IA avec leurs feedbacks."""
q = select(AIDecision).order_by(desc(AIDecision.created_at)).limit(limit)
if session_id:
q = q.where(AIDecision.session_id == session_id)
result = await db.execute(q)
decisions = result.scalars().all()
return [
{
"id": d.id,
"session_id": d.session_id,
"turn": d.turn,
"phase": d.phase,
"recommendation": d.recommendation,
"reasoning": d.reasoning,
"confidence": d.confidence,
"outcome_rating": d.outcome_rating,
"user_feedback": d.user_feedback,
"model_used": d.model_used,
"processing_ms": d.processing_ms,
"created_at": d.created_at.isoformat(),
}
for d in decisions
]
@router.post("/flush")
async def flush_buffer():
"""Force l'export du buffer d'apprentissage."""
await _get_processor().force_flush()
return {"status": "flushed"}