Initial commit
This commit is contained in:
92
hsbg_ai/backend/api/routes/learning.py
Normal file
92
hsbg_ai/backend/api/routes/learning.py
Normal file
@@ -0,0 +1,92 @@
|
||||
"""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"}
|
||||
Reference in New Issue
Block a user