import gradio as gr from transformers import pipeline import re # βœ… Load Hugging Face summarization/chat model summarizer = pipeline("text-generation", model="mistralai/Mistral-7B-Instruct-v0.1") # βœ… 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""" You are an expert resume evaluator. Given the job description and the resume, identify important skills, tools, and concepts in the job description that are missing or weakly represented in the resume. List only the missing or insufficiently addressed keywords in bullet format. Job Description: {job_desc} Resume: {resume_text} """ result = summarizer(prompt, max_new_tokens=300, do_sample=True, temperature=0.7)[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""" You are an expert resume analyst. Classify the content of the resume into meaningful categories based on the text (e.g., Technical Skills, Soft Skills, Certifications, Projects, Work Experience, Education, Personal Information). You do not need a fixed list of section namesβ€”choose the most suitable ones based on the content. Then: - For each section, summarize its contents. - If a job description is provided, compare each section against it. - Highlight missing or weak areas relevant to the job. - Provide smart, actionable suggestions to improve each section. - Output in structured Markdown with headings for each section. """ if analyze_with_jd and job_desc: prompt += f"\nJob Description:\n{job_desc}\n" prompt += f"\nResume:\n{resume_text}" 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=768, do_sample=True, temperature=0.7)[0]['generated_text'] cleaned = re.sub(rf".*?{re.escape(resume_text)}", "", response, flags=re.DOTALL).strip() 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{cleaned}" return cleaned 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()