Spaces:
Running
Running
import gradio as gr | |
from transformers import pipeline | |
import re | |
import os | |
from huggingface_hub import login | |
import spacy | |
from sklearn.feature_extraction.text import ENGLISH_STOP_WORDS | |
# Authenticate with Hugging Face | |
login(token=os.environ.get("HUGGINGFACEHUB_API_TOKEN")) | |
# Load summarization model | |
summarizer = pipeline("text2text-generation", model="declare-lab/flan-alpaca-base") | |
# Load SpaCy English model | |
nlp = spacy.load("en_core_web_sm") | |
# π Use SpaCy to extract nouns and proper nouns (contextually relevant keywords) | |
def extract_relevant_keywords(text): | |
doc = nlp(text.lower()) | |
return set( | |
token.text for token in doc | |
if token.pos_ in {"NOUN", "PROPN"} and not token.is_stop and len(token.text) > 2 | |
) | |
# Compare keywords with semantic filtering | |
def compare_keywords(resume_text, job_desc): | |
resume_words = extract_relevant_keywords(resume_text) | |
job_words = extract_relevant_keywords(job_desc) | |
matched = resume_words & job_words | |
missing = job_words - resume_words | |
return matched, missing | |
# Highlight matched keywords in the resume | |
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 | |
# LLM-based missing keyword extraction | |
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] | |
return result.get('generated_text', result.get('summary_text', str(result))).strip() | |
# Resume improvement prompt | |
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 | |
# Generate analysis result | |
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: | |
result = summarizer(user_prompt, max_new_tokens=512, do_sample=True)[0] | |
response_text = result.get('generated_text', result.get('summary_text', str(result))).strip() | |
if analyze_with_jd and job_desc: | |
matched, missing = 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 | |
{highlighted_resume} | |
--- | |
** Matched Keywords (Semantic Comparison):** | |
{', '.join(sorted(matched)) or 'None'} | |
** Missing Keywords (Semantic Comparison):** | |
{', '.join(sorted(missing)) or 'None'} | |
** LLM-Inferred Missing Keywords:** | |
{llm_missing_keywords} | |
--- | |
{response_text}""" | |
return response_text | |
except Exception as e: | |
return f"β Error: {str(e)}" | |
# Gradio Interface | |
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...") | |
analyze_button = gr.Button("Run Analysis") | |
with gr.Column(): | |
output_analysis = gr.Markdown(label="Resume Analysis and Suggestions") | |
analyze_button.click(fn=analyze_resume, inputs=[job_desc, resume_text, analyze_checkbox], outputs=output_analysis) | |
return demo | |
if __name__ == '__main__': | |
create_ui().launch() | |