93 lines
2.8 KiB
Python
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"}
|