Spaces:
No application file
No application file
| """ | |
| FastAPI backend — optional REST API alongside Gradio UI. | |
| Run: uvicorn main:app --host 0.0.0.0 --port 8000 --reload | |
| """ | |
| import os | |
| import uuid | |
| import io | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| import pandas as pd | |
| from fastapi import FastAPI, UploadFile, File, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from app.models.schemas import EvaluationRequest, EvaluationResponse, Candidate | |
| from app.services.evaluation_service import perform_hybrid_evaluation | |
| app = FastAPI( | |
| title="AI Recruitment Engine", | |
| description="Hybrid 5-stage candidate evaluation pipeline", | |
| version="1.0.0", | |
| ) | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Simple in-memory cache (use Redis/DB in production) | |
| _cache: dict = {} | |
| async def health(): | |
| return {"status": "ok", "service": "AI Recruitment Engine"} | |
| async def upload_csv(file: UploadFile = File(...)): | |
| if not (file.filename or "").endswith(".csv"): | |
| raise HTTPException(status_code=400, detail="Please upload a .csv file.") | |
| try: | |
| content = await file.read() | |
| df = pd.read_csv(io.BytesIO(content)).fillna("") | |
| candidates = [] | |
| for _, row in df.iterrows(): | |
| candidates.append(Candidate( | |
| id=str(uuid.uuid4()), | |
| name=str(row.get("name", "Unknown")), | |
| email=str(row.get("email", "")), | |
| skills=str(row.get("skills", row.get("parsed_skills", ""))), | |
| experience=str(row.get("experience", row.get("parsed_work_experience", ""))), | |
| projects=str(row.get("projects", "")), | |
| education=str(row.get("education", row.get("parsed_metadata_education", ""))), | |
| resume_text=str(row.get("resume_text", row.get("parsed_summary", ""))), | |
| )) | |
| return {"count": len(candidates), "candidates": candidates} | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=f"CSV parse error: {e}") | |
| async def evaluate(request: EvaluationRequest): | |
| if not request.jd: | |
| raise HTTPException(status_code=400, detail="Job Description is required.") | |
| if not request.candidates: | |
| raise HTTPException(status_code=400, detail="At least one candidate is required.") | |
| response = await perform_hybrid_evaluation(request.jd, request.candidates) | |
| for rank in response.shortlist: | |
| _cache[rank.candidate_id] = rank.model_dump() | |
| _cache.update(response.details) | |
| return response | |
| async def get_candidate(candidate_id: str): | |
| if candidate_id not in _cache: | |
| raise HTTPException(status_code=404, detail="Candidate report not found.") | |
| return _cache[candidate_id] | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run( | |
| app, | |
| host=os.getenv("APP_HOST", "0.0.0.0"), | |
| port=int(os.getenv("APP_PORT", "8000")), | |
| ) | |