Spaces:
Sleeping
Sleeping
File size: 5,552 Bytes
a9988a0 |
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 |
from fastapi import FastAPI, HTTPException
from contextlib import asynccontextmanager
import sys
import os
# This ensures that the backend can find your 'src' and 'pipelines' modules and also adds the parent directory to sys.path.
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
if PROJECT_ROOT not in sys.path:
sys.path.append(PROJECT_ROOT)
from backend.models import (ResumeRequest, ApplicantResponse, JobMatch,
RecruiterRequest, RecruiterResponse, ResumeMatch)
from pipelines.core.applicant import run_bert_pipeline, run_tfidf_pipeline, load_job_titles
from pipelines.core.recruiter import rank_with_bert, rank_with_tfidf
from src.feature_engg.bert_embedding_data import load_bert_model, load_faiss_index
from src.feature_engg.tfidf_vectorizing_data import load_tfidf_vectorizer, load_tfidf_matrix
# In memory storage for models (dictionary to hold all loaded models):
ml_models = {}
# Create a lifespan function to handle startup and shutdown events:
@asynccontextmanager
async def lifespan(app: FastAPI):
"""This code runs ONCE when the server starts up."""
print("🚀 Server starting up: Loading ML models...")
# Load Applicant Models
ml_models["bert_model"] = load_bert_model(local_bert_path=None, repo_id="Om-Shandilya/resume-matcher-bert")
ml_models["faiss_index"] = load_faiss_index(local_index_path=None, repo_id="Om-Shandilya/resume-matcher-bert", filename="applicant/jobs.faiss")
ml_models["applicant_vectorizer"] = load_tfidf_vectorizer(local_vectorizer_path=None, repo_id="Om-Shandilya/resume-matcher-tfidf", filename="applicant/job_vectorizer.pkl")
ml_models["applicant_matrix"] = load_tfidf_matrix(local_matrix_path=None, repo_id="Om-Shandilya/resume-matcher-tfidf", filename="applicant/job_matrix.npz")
# Load Recruiter Models
ml_models["recruiter_vectorizer"] = load_tfidf_vectorizer(local_vectorizer_path=None, repo_id="Om-Shandilya/resume-matcher-tfidf", filename="recruiter/combined_vectorizer.pkl")
# Load Job Titles DataFrames
ml_models["tfidf_job_df"] = load_job_titles(repo_id='Om-Shandilya/resume-matcher-tfidf', filename='applicant/tfidf_job_titles.csv')
ml_models["bert_job_df"] = load_job_titles(repo_id='Om-Shandilya/resume-matcher-bert', filename='applicant/bert_job_titles.csv')
print("✅ ML models loaded successfully.")
yield
# This code runs once when the server is shutting down.
print(" shutting down: Clearing ML models...")
ml_models.clear()
# Initializing the FastAPI app
app = FastAPI(
title="Resume-Job Matcher API",
description="An API for matching resumes to jobs and ranking candidates.",
lifespan=lifespan
)
# Creating the API endpoints:
@app.get("/")
def read_root():
return {"status": "Resume Matcher API is running."}
# Applicant side endpoints:
@app.post("/applicant/match/bert", response_model=ApplicantResponse)
async def match_resume_bert(request: ResumeRequest):
try:
matches, message = run_bert_pipeline(
raw_resume=request.raw_text,
model=ml_models["bert_model"],
job_index=ml_models["faiss_index"],
job_df=ml_models["bert_job_df"],
top_k=request.top_k)
response_matches = [JobMatch(job_title=title, match_score=score) for title, score in matches]
return ApplicantResponse(matches=response_matches, message=message)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/applicant/match/tf-idf", response_model=ApplicantResponse)
async def match_resume_tfidf(request: ResumeRequest):
try:
matches, message = run_tfidf_pipeline(
raw_resume=request.raw_text,
vectorizer=ml_models["applicant_vectorizer"],
job_matrix=ml_models["applicant_matrix"],
job_df=ml_models["tfidf_job_df"],
top_k=request.top_k)
response_matches = [JobMatch(job_title=title, match_score=score) for title, score in matches]
return ApplicantResponse(matches=response_matches, message=message)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
# Recruiter side endpoints:
@app.post("/recruiter/rank/bert", response_model=RecruiterResponse)
async def rank_resumes_bert(request: RecruiterRequest):
try:
matches, message = rank_with_bert(
raw_job_text=request.raw_job_text,
raw_resume_texts=request.raw_resume_texts,
model=ml_models["bert_model"],
top_k=request.top_k)
response_matches = [ResumeMatch(resume_filename=fname, match_score=score) for fname, score in matches]
return RecruiterResponse(matches=response_matches, message=message)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/recruiter/rank/tf-idf", response_model=RecruiterResponse)
async def rank_resumes_tfidf(request: RecruiterRequest):
try:
matches, message = rank_with_tfidf(
raw_job_text=request.raw_job_text,
raw_resume_texts=request.raw_resume_texts,
vectorizer=ml_models["recruiter_vectorizer"],
top_k=request.top_k)
response_matches = [ResumeMatch(resume_filename=fname, match_score=score) for fname, score in matches]
return RecruiterResponse(matches=response_matches, message=message)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e)) |