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()