File size: 4,437 Bytes
3c06ec2
9332f7e
cdf6731
02bafb6
 
f6c9488
b925d82
02bafb6
b925d82
02bafb6
f93c703
b925d82
1221d76
b925d82
 
f6c9488
 
b925d82
f6c9488
 
 
b925d82
f6c9488
 
7c8d169
b925d82
749f56d
f6c9488
 
9332f7e
749f56d
 
 
b925d82
749f56d
9332f7e
 
749f56d
9332f7e
cdf6731
b925d82
38b8d8b
 
fc823d8
38b8d8b
 
 
 
b925d82
38b8d8b
b925d82
 
6e31e76
b925d82
9332f7e
 
b925d82
 
fc823d8
 
 
 
b925d82
9332f7e
 
 
b925d82
9332f7e
50c02b0
cdf6731
 
6e31e76
cdf6731
9332f7e
b925d82
6e31e76
f6c9488
9332f7e
eb3933a
749f56d
38b8d8b
f6c9488
6e31e76
f6c9488
 
b925d82
f6c9488
b925d82
f6c9488
b925d82
f6c9488
 
6e31e76
 
9332f7e
 
cdf6731
b925d82
9332f7e
 
 
 
 
 
 
f8fa1b3
9332f7e
 
cdf6731
f8fa1b3
3c06ec2
9332f7e
1ab7e62
9332f7e
dfb4653
6e31e76
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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()