Spaces:
Sleeping
Sleeping
import gradio as gr | |
from transformers import pipeline | |
import re | |
import os | |
from huggingface_hub import login | |
# β Authenticate using HF token stored in secret | |
login(token=os.environ.get("HUGGINGFACEHUB_API_TOKEN")) | |
# β Use a lightweight instruction-tuned model compatible with CPU | |
summarizer = pipeline("text2text-generation", model="declare-lab/flan-alpaca-base") | |
# β Highlight matching and find missing keywords | |
from sklearn.feature_extraction.text import ENGLISH_STOP_WORDS | |
def compare_keywords(resume_text, job_desc): | |
resume_words = set(re.findall(r"\b\w{3,}\b", resume_text.lower())) - ENGLISH_STOP_WORDS | |
job_words = set(re.findall(r"\b\w{3,}\b", job_desc.lower())) - ENGLISH_STOP_WORDS | |
matched = resume_words & job_words | |
missing = job_words - resume_words | |
return matched, missing | |
def highlight_keywords(resume_text, matched): | |
highlighted = resume_text | |
for word in sorted(matched, key=len, reverse=True): | |
highlighted = re.sub(rf"\b({re.escape(word)})\b", r"**\1**", highlighted, flags=re.IGNORECASE) | |
return highlighted | |
# π Use LLM to extract missing keywords contextually from JD | |
def extract_missing_keywords_with_llm(job_desc, resume_text): | |
prompt = f""" | |
Given the following job description and resume, list the important skills, tools, and concepts from the job description that are missing or weakly represented in the resume. | |
Job Description: | |
{job_desc} | |
Resume: | |
{resume_text} | |
Only list the missing keywords as bullet points. | |
""" | |
result = summarizer(prompt, max_new_tokens=300, do_sample=True)[0]['generated_text'] | |
return result.strip() | |
# π Prompt for dynamic section classification and feedback | |
def build_dynamic_prompt(job_desc, resume_text, analyze_with_jd): | |
prompt = f""" | |
Analyze the resume below and organize it into meaningful categories (e.g., Skills, Education, Work Experience, etc.). | |
If a job description is provided, compare it against the resume and suggest improvements section by section. | |
Job Description: | |
{job_desc if analyze_with_jd else '[None provided]'} | |
Resume: | |
{resume_text} | |
Return structured Markdown with headers for each section and improvement suggestions. | |
""" | |
return prompt | |
# π§ Function to call Hugging Face model and get structured resume feedback | |
def analyze_resume(job_desc, resume_text, analyze_with_jd): | |
if not resume_text.strip(): | |
return "β οΈ Please paste your resume text." | |
user_prompt = build_dynamic_prompt(job_desc, resume_text, analyze_with_jd) | |
try: | |
response = summarizer(user_prompt, max_new_tokens=512, do_sample=True)[0]['generated_text'] | |
if analyze_with_jd and job_desc: | |
matched, _ = compare_keywords(resume_text, job_desc) | |
highlighted_resume = highlight_keywords(resume_text, matched) | |
llm_missing_keywords = extract_missing_keywords_with_llm(job_desc, resume_text) | |
return f"### π Resume with Highlighted Matches\n\n{highlighted_resume}\n\n---\n**β Matched Keywords:** {', '.join(sorted(matched)) or 'None'}\n\n**β LLM-Inferred Missing Keywords:**\n{llm_missing_keywords}\n\n---\n{response.strip()}" | |
return response.strip() | |
except Exception as e: | |
return f"β Error: {str(e)}" | |
# ποΈ Gradio UI | |
def create_ui(): | |
with gr.Blocks() as demo: | |
with gr.Row(): | |
with gr.Column(): | |
analyze_checkbox = gr.Checkbox(label="Analyze with Job Description", value=True) | |
job_desc = gr.Textbox(label="Job Description", lines=6, placeholder="Paste job description here...") | |
resume_text = gr.Textbox(label="Resume Text", lines=16, placeholder="Paste resume content here...") | |
with gr.Column(): | |
output_analysis = gr.Markdown(label="Resume Analysis and Suggestions") | |
resume_text.change(fn=analyze_resume, inputs=[job_desc, resume_text, analyze_checkbox], outputs=output_analysis) | |
job_desc.change(fn=analyze_resume, inputs=[job_desc, resume_text, analyze_checkbox], outputs=output_analysis) | |
return demo | |
if __name__ == '__main__': | |
create_ui().launch() |