File size: 5,025 Bytes
84c7ce0 2e32ddd 2b16a80 84c7ce0 57af3d0 84c7ce0 8165461 2e32ddd c42e6f5 84c7ce0 2b16a80 84c7ce0 2d06d01 84c7ce0 037629b 84c7ce0 2b16a80 037629b 84c7ce0 a8ee0db 2e32ddd 2b16a80 2e32ddd a8ee0db 2e32ddd c42e6f5 84c7ce0 2d06d01 6fd206b 84c7ce0 a8ee0db 84c7ce0 2b16a80 8165461 2b16a80 8165461 c42e6f5 2e32ddd 2b16a80 84c7ce0 2e32ddd 84c7ce0 037629b ae434c1 84c7ce0 037629b 84c7ce0 ae434c1 84c7ce0 ae434c1 84c7ce0 f95f21f 84c7ce0 037629b ae434c1 037629b ae434c1 037629b 84c7ce0 2e32ddd 84c7ce0 c42e6f5 2e32ddd 84c7ce0 2d06d01 84c7ce0 2d06d01 3181519 84c7ce0 2b16a80 2d06d01 c42e6f5 84c7ce0 c42e6f5 2e32ddd 84c7ce0 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
import logging
import sys
import os
import json
import tempfile
from datetime import datetime
# CORRECTION : Assurez-vous que 'Request' est bien présent dans cette ligne d'import
from fastapi import FastAPI, Request, HTTPException, UploadFile, File, BackgroundTasks
from fastapi.responses import JSONResponse
from fastapi.concurrency import run_in_threadpool
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field
from typing import List, Dict, Any, Optional
from bson import ObjectId
# --- Imports de vos services et classes ---
from src.models import load_all_models
from src.services.cv_service import CVParsingService
from src.services.analysis_service import AnalysisService
from services.graph_service import GraphInterviewProcessor
# --- Configuration du logging ---
logging.basicConfig(level=logging.INFO)
# Le logger global est utilisé pour les messages au démarrage de l'application.
logger = logging.getLogger(__name__)
# --- Configuration de l'environnement pour les fichiers temporaires ---
os.makedirs('/tmp/feedbacks', exist_ok=True)
# --- Initialisation de l'application FastAPI ---
app = FastAPI(
title="AIrh Interview Assistant",
description="API pour l'analyse de CV et la simulation d'entretiens d'embauche avec analyse asynchrone.",
version="2.0.0",
docs_url="/docs",
redoc_url="/redoc"
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# --- Initialisation des services ---
logger.info("Chargement des modèles et initialisation des services...")
models = load_all_models()
cv_service = CVParsingService(models)
logger.info("Services initialisés.")
# --- Définition des modèles Pydantic ---
class Feedback(BaseModel):
status: str
feedback_data: Optional[Dict[str, Any]] = None
class HealthCheck(BaseModel):
status: str = "ok"
# --- Endpoint de santé ---
@app.get("/", response_model=HealthCheck, tags=["Status"])
async def health_check():
return HealthCheck()
# --- Endpoint principal pour la simulation d'entretien ---
@app.post("/api/v1/simulate-interview/")
async def simulate_interview(request: Request):
"""
Ce endpoint reçoit les données de l'entretien, instancie le processeur de graphe
et lance la conversation.
"""
# CORRECTION : Récupérer l'instance du logger pour garantir sa disponibilité dans le scope de la fonction.
logger = logging.getLogger(__name__)
try:
payload = await request.json()
if not all(k in payload for k in ["user_id", "job_offer_id", "cv_document", "job_offer"]):
raise HTTPException(status_code=400, detail="Données manquantes dans le payload (user_id, job_offer_id, cv_document, job_offer).")
logger.info(f"Début de la simulation pour l'utilisateur : {payload['user_id']}")
processor = GraphInterviewProcessor(payload)
result = processor.invoke(payload.get("messages", []))
return JSONResponse(content=result)
except ValueError as ve:
logger.error(f"Erreur de validation des données : {ve}", exc_info=True)
return JSONResponse(content={"error": str(ve)}, status_code=400)
except Exception as e:
logger.error(f"Erreur interne dans le endpoint simulate-interview: {e}", exc_info=True)
return JSONResponse(
content={"error": "Une erreur interne est survenue sur le serveur de l'assistant."},
status_code=500
)
# --- Endpoint pour l'analyse de CV ---
@app.post("/parse-cv/", tags=["CV Parsing"])
async def parse_cv(file: UploadFile = File(...)):
"""
Analyse un fichier CV (PDF) et retourne les données extraites.
"""
if file.content_type != "application/pdf":
raise HTTPException(status_code=400, detail="Fichier PDF requis")
contents = await file.read()
with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
tmp.write(contents)
tmp_path = tmp.name
try:
result = await run_in_threadpool(cv_service.parse_cv, tmp_path)
finally:
if os.path.exists(tmp_path):
os.remove(tmp_path)
if not result:
raise HTTPException(status_code=500, detail="Échec de l'extraction des données du CV.")
return result
# --- Endpoint pour récupérer le feedback ---
@app.get("/get-feedback/{user_id}", response_model=Feedback, tags=["Analysis"])
async def get_feedback(user_id: str):
feedback_path = f"/tmp/feedbacks/{user_id}.json"
if not os.path.exists(feedback_path):
raise HTTPException(status_code=404, detail="Feedback non trouvé ou non encore traité.")
with open(feedback_path, "r", encoding="utf-8") as f:
data = json.load(f)
return Feedback(**data)
# --- Démarrage de l'application (pour un test local) ---
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000) |