File size: 4,614 Bytes
3c06ec2
9332f7e
cdf6731
02bafb6
 
 
 
 
f93c703
9332f7e
d78fa13
726a7dd
749f56d
9332f7e
7c8d169
749f56d
9332f7e
 
 
749f56d
 
 
 
9332f7e
 
749f56d
9332f7e
cdf6731
38b8d8b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9332f7e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50c02b0
cdf6731
 
9332f7e
cdf6731
9332f7e
 
 
 
38b8d8b
749f56d
38b8d8b
 
9332f7e
 
 
cdf6731
9332f7e
 
 
 
 
 
 
 
 
 
cdf6731
9332f7e
 
3c06ec2
9332f7e
1ab7e62
9332f7e
02bafb6
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
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"))

# โœ… 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()