Spaces:
Sleeping
Sleeping
File size: 2,778 Bytes
f9a6bac 8b762d1 f9a6bac 8b762d1 f9a6bac 8b762d1 d98f7ad f9a6bac 1412979 f9a6bac 1412979 f9a6bac 1412979 19c90e8 8b762d1 f9a6bac 8b762d1 f9a6bac 8b762d1 f9a6bac 8b762d1 1412979 d98f7ad 8b762d1 d98f7ad f9a6bac d98f7ad f9a6bac d98f7ad f9a6bac d98f7ad | 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 | #import torch and fast api
import torch
from fastapi import FastAPI
from pathlib import Path
from pydantic import BaseModel
from typing import List
import time
#import transformer modules
from transformers import AutoTokenizer, AutoModelForCausalLM
from peft import PeftModel
#import sentence_transformers
from sentence_transformers import SentenceTransformer
# import modules
from modules.resume_parser import ResumeParser
from modules.comparison import Comparison
app = FastAPI()
@app.on_event("startup")
def load_models():
BASE_DIR = Path(__file__).resolve().parent
# ===== QWEN =====
base_model_name = "Qwen/Qwen2.5-0.5B-Instruct"
adapter_path = BASE_DIR / "models/qwen_final"
tokenizer = AutoTokenizer.from_pretrained(base_model_name)
tokenizer.pad_token = tokenizer.eos_token
base_model = AutoModelForCausalLM.from_pretrained(
base_model_name,
dtype=torch.float32,
device_map="cpu"
)
qwen_model = PeftModel.from_pretrained(base_model, str(adapter_path))
qwen_model.eval()
# ===== MiniLM =====
minilm_model = SentenceTransformer(str(BASE_DIR / "models/mnr_1"))
# ===== Inject =====
app.state.resume_parser = ResumeParser(qwen_model, tokenizer)
app.state.comparison = Comparison(minilm_model)
print("✅ All models loaded once")
# ---------------------------------------------
# HEALTH CHECK
# ---------------------------------------------
@app.get("/")
def home():
return {"status": "ATS API Running 🚀"}
# ---------------------------------------------
# MAIN ENDPOINT
# ---------------------------------------------
class ResumeMatchRequest(BaseModel):
job_description:str
resume: str
@app.post("/match-resumes/")
async def match_resumes(request:ResumeMatchRequest):
start = time.time()
resume = request.resume
job_description = request.job_description
#load parser
parser = app.state.resume_parser
#resumes in parsed format
resume = parser.format_data(resume)
#resumes after generated
resume = parser.parse(resume)
splitted = resume.split("\n")
name = splitted[0].split("name: ").pop()
parsed_resume = "\n".join(splitted[1:])
#load comparison
comparison = app.state.comparison
# use all mini lm to encode
with torch.no_grad():
jd = comparison.encode(job_description)
res = comparison.encode(parsed_resume)
score = comparison.compare(res, jd)
end = time.time()
return {
"scores":score,
"resume":{"name": name, "resume":parsed_resume},
"threshold":0.670,
"process_time": f"{end-start} seconds",
"message": "Candidate Accepted" if score>=0.670 else "Candidate Rejected"
} |