Spaces:
Sleeping
Sleeping
| import logging | |
| import os | |
| import requests # Ajout de la librairie pour les requêtes HTTP | |
| from fastapi import FastAPI, HTTPException, Body, UploadFile, File | |
| from pydantic import BaseModel | |
| from typing import List, Dict, Any | |
| from dotenv import load_dotenv | |
| from fastapi.concurrency import run_in_threadpool | |
| # --- Import de VOS modules de travail --- | |
| from src.cv_parsing_agents import CVParser | |
| from src.interview_simulator.entretient_version_prod import InterviewProcessor | |
| from src.config import Config | |
| # --- Celery n'est plus importé ici --- | |
| load_dotenv() | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| # --- Récupération de l'URL du nouveau service --- | |
| # Vous devrez ajouter cette variable à votre environnement sur la plateforme où vous déploierez cette API. | |
| CELERY_SERVICE_URL = os.environ.get("CELERY_SERVICE_URL") | |
| if not CELERY_SERVICE_URL: | |
| logger.warning("La variable d'environnement CELERY_SERVICE_URL n'est pas définie. Les analyses asynchrones échoueront.") | |
| app = FastAPI( | |
| title="AIrh Main API", | |
| description="API principale gérant le parsing de CV et la simulation interactive.", | |
| version="2.1.0" | |
| ) | |
| # --- Modèles de données Pydantic --- | |
| class ParsedCVResponse(BaseModel): | |
| candidat: Dict[str, Any] | |
| class InterviewRequest(BaseModel): | |
| cv_document: Dict[str, Any] | |
| job_offer: Dict[str, Any] | |
| messages: List[Dict[str, Any]] | |
| conversation_history: List[Dict[str, Any]] | |
| class InterviewResponse(BaseModel): | |
| response: str | |
| class AnalysisRequest(BaseModel): | |
| conversation_history: List[Dict[str, Any]] | |
| job_description_text: str | |
| class TaskStatusResponse(BaseModel): | |
| task_id: str | |
| status: str | |
| result: Any = None | |
| # --- Endpoints de l'API --- | |
| async def read_root(): | |
| return {"message": "AIrh Main API est opérationnelle."} | |
| # --- SECTION ORIGINALE (INCHANGÉE) --- | |
| async def parse_cv(file: UploadFile = File(...)): | |
| logger.info(f"Réception du fichier CV: {file.filename}") | |
| cv_content = await file.read() | |
| if not cv_content: | |
| raise HTTPException(status_code=400, detail="Le fichier CV est vide.") | |
| try: | |
| parser = CVParser() | |
| parsed_data = await run_in_threadpool(parser.parse, cv_content) | |
| if not parsed_data or "candidat" not in parsed_data: | |
| raise HTTPException(status_code=422, detail="Impossible d'extraire les données structurées du CV.") | |
| return parsed_data | |
| except Exception as e: | |
| logger.error(f"Erreur critique lors du parsing du CV: {e}", exc_info=True) | |
| raise HTTPException(status_code=500, detail=f"Erreur interne du serveur lors du parsing: {str(e)}") | |
| async def simulate_interview(request: InterviewRequest): | |
| logger.info("Réception d'une requête pour la simulation d'entretien.") | |
| try: | |
| processor = InterviewProcessor( | |
| cv_document=request.cv_document, | |
| job_offer=request.job_offer, | |
| conversation_history=request.conversation_history | |
| ) | |
| ai_response_object = await run_in_threadpool(processor.run, messages=request.messages) | |
| last_message = ai_response_object["messages"][-1].content | |
| return {"response": last_message} | |
| except Exception as e: | |
| logger.error(f"Erreur lors de la simulation d'entretien: {e}", exc_info=True) | |
| raise HTTPException(status_code=500, detail=f"Erreur interne du serveur lors de la simulation: {str(e)}") | |
| # --- SECTION MODIFIÉE POUR APPELER LE SERVICE EXTERNE --- | |
| async def trigger_analysis(request: AnalysisRequest): | |
| """ | |
| Déclenche l'analyse en appelant le service Celery externe. | |
| """ | |
| logger.info("Redirection de la demande d'analyse vers le service Celery externe.") | |
| if not CELERY_SERVICE_URL: | |
| raise HTTPException(status_code=503, detail="Le service d'analyse est actuellement indisponible.") | |
| try: | |
| # On fait une requête POST à notre service Celery sur Render | |
| api_url = f"{CELERY_SERVICE_URL}/trigger-analysis" | |
| logger.info(f"Appel de l'API externe : {api_url}") | |
| response = requests.post(api_url, json=request.dict()) | |
| response.raise_for_status() # Lève une exception si le statut n'est pas 2xx | |
| return response.json() | |
| except requests.exceptions.RequestException as e: | |
| logger.error(f"Erreur de communication avec le service Celery: {e}") | |
| raise HTTPException(status_code=502, detail="Erreur de communication avec le service d'analyse.") | |
| async def get_analysis_status(task_id: str): | |
| """ | |
| Vérifie le statut d'une tâche en interrogeant le service Celery externe. | |
| """ | |
| logger.info(f"Vérification du statut de la tâche externe: {task_id}") | |
| if not CELERY_SERVICE_URL: | |
| raise HTTPException(status_code=503, detail="Le service d'analyse est actuellement indisponible.") | |
| try: | |
| # On fait une requête GET à notre service Celery sur Render | |
| api_url = f"{CELERY_SERVICE_URL}/analysis-status/{task_id}" | |
| logger.info(f"Appel de l'API externe : {api_url}") | |
| response = requests.get(api_url) | |
| response.raise_for_status() | |
| return response.json() | |
| except requests.exceptions.RequestException as e: | |
| logger.error(f"Erreur de communication avec le service Celery: {e}") | |
| raise HTTPException(status_code=502, detail="Erreur de communication avec le service d'analyse.") |