File size: 19,198 Bytes
2ea5df0
 
7067897
 
 
2ea5df0
7067897
 
23e9332
7067897
 
 
 
 
e7d0d5d
 
1b088ec
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e7d0d5d
 
 
 
 
 
 
 
 
 
 
1b088ec
 
 
 
e7d0d5d
1b088ec
 
 
e7d0d5d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1b088ec
 
2ea5df0
 
 
 
 
 
 
 
e7d0d5d
 
 
 
2ea5df0
7067897
23e9332
7067897
 
 
 
 
2ea5df0
7067897
 
 
 
 
 
 
2ea5df0
23e9332
91ff4e5
 
23e9332
 
91ff4e5
 
23e9332
2ea5df0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79e8566
2ea5df0
 
 
79e8566
2ea5df0
1b088ec
7067897
 
2ea5df0
23e9332
 
2ea5df0
 
 
 
 
 
 
 
 
 
 
 
 
7067897
 
e7d0d5d
1b088ec
e7d0d5d
1b088ec
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e7d0d5d
 
 
 
1b088ec
e7d0d5d
1b088ec
 
e7d0d5d
 
 
 
1b088ec
 
 
e7d0d5d
 
 
7067897
 
 
 
 
 
 
 
 
1b088ec
e7d0d5d
1b088ec
7067897
 
 
e7d0d5d
1b088ec
 
 
 
 
e7d0d5d
 
1b088ec
e7d0d5d
1b088ec
 
 
e7d0d5d
 
 
 
 
 
 
1b088ec
 
e7d0d5d
 
 
1b088ec
 
 
7067897
 
e7d0d5d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7067897
 
2ea5df0
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
from time import sleep

import gradio as gr
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
from duckduckgo_search import DDGS

# Load the SmolLM model and tokenizer
model_name = "HuggingFaceTB/SmolLM2-360M-Instruct"
model = AutoModelForCausalLM.from_pretrained(model_name)
tokenizer = AutoTokenizer.from_pretrained(model_name)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)

def get_gap_assessment_prompt(job_and_company_info, resume):
    gap_assessment_prompt = f"""

# Do a gap assessment on this job role and resume looking for gaps that can be overcome by strategy in writing the resume and cover letter. For each gap, include a strategy for remediation. Ignore and do not mention any gap that is structural and for which improved writing will not be helpful. Consider things like:

1. Missing Keywords or Phrases

Consider: Does the resume lack important keywords or phrases from the job description?
Strategy: Integrate relevant keywords naturally into the resume and cover letter, especially in the summary, skills, and experience sections.
2. Unclear or Weakly Stated Achievements

Consider: Are the candidate’s accomplishments described in vague or generic terms?
Strategy: Rewrite bullet points to be achievement-oriented and quantify results where possible (e.g., “Increased sales by 20% in six months”).
3. Transferable Skills Not Highlighted

Consider: Are transferable skills from other roles or industries not clearly connected to the target job?
Strategy: Explicitly map transferable skills to job requirements in both the resume and cover letter, using language that mirrors the job posting.
4. Relevant Experience Buried or Underemphasized

Consider: Is relevant experience hidden or not given enough prominence?
Strategy: Reorder sections or bullet points to lead with the most relevant experiences, and use bolding or section headings to draw attention.
5. Lack of Tailoring to the Job

Consider: Is the resume too generic and not tailored to the specific job?
Strategy: Customize the summary/profile, skills, and experience sections to directly address the job requirements and company values.
6. Gaps in Demonstrating Required Soft Skills

Consider: Are important soft skills (e.g., leadership, communication) not clearly demonstrated?
Strategy: Add specific examples or brief stories in the resume and cover letter that illustrate these soft skills in action.
7. Unclear Career Progression or Role Context

Consider: Is it unclear how past roles relate to the target position?
Strategy: Add brief context or explanations in job descriptions or the cover letter to clarify how previous roles prepared the candidate for this job.
8. Formatting or Readability Issues

Consider: Is the resume difficult to scan or visually unappealing?
Strategy: Improve formatting for clarity and readability—use bullet points, clear headings, and consistent formatting.
9. Missing Education or Certification Details (If Present)

Consider: Are relevant credentials present but not highlighted?
Strategy: Move education or certifications to a more prominent position or call them out in the summary if they are key requirements.
10. Absence of Action Verbs

Consider: Are bullet points passive or lacking strong action verbs?
Strategy: Revise statements to start with powerful action verbs that convey impact and initiative.

11. Non-Linear Career Trajectory

Consider: Does the resume reflect a career path with frequent changes in industry, function, or direction, which may appear unfocused to employers?

Remediation Strategies: Craft a strong narrative in the summary section to tie diverse experiences together, emphasizing adaptability, learning agility, and the unique value brought by varied roles

. Use a skills-based or hybrid resume format to foreground transferable skills and relevant achievements rather than strict chronology

. In the cover letter, proactively explain the logic and benefits behind career pivots, highlighting how each transition contributed to your professional growth and how it aligns with the target role


12. Lack of Progression

Consider: Does the resume show little upward movement or increased responsibility over time?

Remediation Strategies: Highlight progression in responsibilities, even within the same job title or company, by emphasizing new projects, leadership roles, or skill development

. Quantify achievements and use action verbs to demonstrate impact and growth, even if formal titles did not change
. In the cover letter, address the context (e.g., company structure, personal choices) and focus on continuous learning or expanded scope within roles

13. Gaps in Employment History

Consider: Are there periods of unemployment or unaccounted time between jobs?

Remediation Strategies: Briefly explain significant gaps in the cover letter, framing them as periods of skill development, education, caregiving, or personal growth

. Include any freelance, contract, volunteer, or project work during gaps to demonstrate ongoing engagement and skill application

. Use a functional or hybrid resume format to de-emphasize strict chronology and spotlight relevant skills and accomplishments

. By strategically reframing these potential red flags, you can present a compelling, cohesive story that emphasizes your adaptability, continuous development, and readiness for the target role—turning perceived weaknesses into strengths

# This is the role description, company information, and results of company research, and 

    Company Name: {job_and_company_info['Company Name']}
    Company URL: {job_and_company_info['Company URL']}
    Job Title: {job_and_company_info['job_title']}
    Role Requirements: {job_and_company_info['Role Requirements']}
    Clean Job Description: {job_and_company_info['Clean Job Description']}
    Company Research:
        Company Values: {job_and_company_info['Company Research']['Company Values Summary']}
        Corporate Culture: {job_and_company_info['Company Research']['Corporate Culture Summary']}
        Leadership Team and Possible Key Opinion Leaders: {job_and_company_info['Company Research']['Leadership Team Summary']}
        Recent Company News: {job_and_company_info['Company Research']['Recent News Summary']}
        Company Competitive Advantages: {job_and_company_info['Company Research']['Competitive Advantages Summary']}


## This is the candidate's resume:

{resume}

# For this task, do not re-write their resume. Simply identify any of these described gaps or other potential gaps and strategies in re-writing and re-framing the resume which are likely to be effective in improving the odds of being offered an interview or job. 
 
"""
    return gap_assessment_prompt


def get_key_accomplishments_prompt(job_and_company_info, resume):

    key_skills_and_accomplaishments = f"""
# Extract relevant 1. 1-5 key accomplishments accomplishments and 2. all transferable skills from the applicant's resume that aligns with the job description.

# This is the role description, company information, and results of company research, and 

    Company Name: {job_and_company_info['Company Name']}
    Company URL: {job_and_company_info['Company URL']}
    Job Title: {job_and_company_info['job_title']}
    Role Requirements: {job_and_company_info['Role Requirements']}
    Clean Job Description: {job_and_company_info['Clean Job Description']}
    Company Research:
        Company Values: {job_and_company_info['Company Research']['Company Values Summary']}
        Corporate Culture: {job_and_company_info['Company Research']['Corporate Culture Summary']}
        Leadership Team and Possible Key Opinion Leaders: {job_and_company_info['Company Research']['Leadership Team Summary']}
        Recent Company News: {job_and_company_info['Company Research']['Recent News Summary']}
        Company Competitive Advantages: {job_and_company_info['Company Research']['Competitive Advantages Summary']}


## This is the candidate's resume:

{resume}

Please provide the following information in a concise and straightforward manner, without any extraneous comments or information that may confuse an LLM that this response will be piped to: Output the results in a simple format, with the key accomplishments first, followed by the transferable skills."
"""

class Role:
    def __init__(self, company_name, company_url, job_description, clean_job_description, job_title):
        self.company_name = company_name
        self.company_url = company_url
        self.job_description = job_description
        self.clean_job_description = clean_job_description
        self.job_title = job_title

class Applicant:
    def __init__(self, resume):
        self.resume = resume

def smol_lm_jd_process(job_description, system_prompt, max_new_tokens=512):
    prompt = f"""<|im_start|>system
{system_prompt}<|im_end|>
<|im_start|>user
{job_description}<|im_end|>
<|im_start|>assistant
"""
    inputs = tokenizer(prompt, return_tensors="pt").to(device)
    output = model.generate(**inputs, max_new_tokens=max_new_tokens, do_sample=True, temperature=0.6, top_k=40, top_p=0.9, repetition_penalty=1.1)
    response = tokenizer.decode(output[0], skip_special_tokens=False)
    start_idx = response.find("<|im_start|>assistant")
    end_idx = response.find("<|im_end|>", start_idx)
    response = response[start_idx + len("<|im_start|>assistant\n"):end_idx].strip()
    return response

def process_job_description(company_name, company_url, job_description):
    
    # Step 2: Extract key qualifications, skills, and requirements
    system_prompt_requirements = "Extract key qualifications, skills, and requirements from this job description. Output as bullet points. Remove benefits/salary, bragging about the company, and other fluff not relevant to the skills, qualifications, and job requirements. ONLY INCLUDE INFORMATION THAT TELLS THE USER WHAT SKILLS THE EMPLOYER SEEKS."
    role_requirements = smol_lm_jd_process(job_description, system_prompt_requirements)

    # Step 3: Create a concise summary of the job description
    system_prompt_summary = "Create a concise 150-200 word summary of this job description. Remove company bragging bragging about the company, and other fluff not relevant to the position and what is desired from the candidate. FOCUS ON ASPECTS THAT POINT THE USER IN WHAT THE EMPLOYER WANTS FROM A CANDIDATE IN TERMS OF SKILLS, ACCOMPLISHMENTS, AND SUCH"
    clean_job_description = smol_lm_jd_process(job_description, system_prompt_summary)

    system_prompt_get_job_title = "Extract only the job title from the following job description. Respond with nothing but the job title—no labels, no comments, no summaries, no locations, or extra text. If the title is unusually long or nonstandard, replace it with the most common, concise, and widely recognized job title for the role. Your answer must be 7 words or fewer, with no punctuation, newlines, or additional information. Acceptable examples may look like: 'Systems Analyst', 'marketing director', 'patient advocate III', ..."
    job_title = smol_lm_jd_process(job_description, system_prompt_get_job_title, max_new_tokens=150)[:50].lower().replace("job","").replace("title","").replace("\n","").replace(":","")

    role = Role(company_name, company_url, job_description, clean_job_description, job_title)

    # Step 4: Company Research
    searches = {
        "company_values": f"{role.company_name} company values",
        "corporate_culture": f"{role.company_name} corporate culture",
        "leadership_team": f"{role.company_name} leadership team members relevant to {role.job_title} role",
        "recent_news": f"{role.company_name} recent news relevant to {role.job_title} role",
        "competitive_advantages": f"{role.company_name} competitive advantages in {role.job_title} market"
    }
    search_client = DDGS()
    search_results = {}
    for key, query in searches.items():
        print(f"searching {query}")
        try:
            results = search_client.text(query,
                                         max_results=3)
            print(f"searching {query} successful")
            search_results[key] = results
        except Exception as exc:
            print(f"Rate limit error, will wait and retry searching {query}.")
            sleep(5)
            results = search_client.text(query,
                                         max_results=3)
            print(f"searching {query} successful")
            search_results[key] = results
        sleep(3)


    # Summarize search results using SmolLM
    summaries = {}
    system_prompt_summary_search = "Summarize the following search results in 150 tokens or less."
    for key, results in search_results.items():
        print(f"Summarizing {key}")
        search_result_text = "\n".join([result['body'] for result in results])
        summary = smol_lm_jd_process(search_result_text, system_prompt_summary_search, max_new_tokens=150)
        summaries[key] = summary
        print(f"Summarizing {key} successful")

    job_and_company_info = {
        "Company Name": company_name,
        "Company URL": company_url,
        "job_title": job_title,
        "Original Job Description": job_description,
        "Role Requirements": role_requirements,
        "Clean Job Description": clean_job_description,
        "Company Research": {
            "Company Values Search Results": search_results['company_values'],
            "Company Values Summary": summaries['company_values'],
            "Corporate Culture Search Results": search_results['corporate_culture'],
            "Corporate Culture Summary": summaries['corporate_culture'],
            "Leadership Team Search Results": search_results['leadership_team'],
            "Leadership Team Summary": summaries['leadership_team'],
            "Recent News Search Results": search_results['recent_news'],
            "Recent News Summary": summaries['recent_news'],
            "Competitive Advantages Search Results": search_results['competitive_advantages'],
            "Competitive Advantages Summary": summaries['competitive_advantages'],
        }
    }

    return job_and_company_info, role

def generate_gap_assessment_prompt(job_and_company_info, resume, role):
    job_and_company_info_str = f"""
    Company Name: {job_and_company_info['Company Name']}
    Company URL: {job_and_company_info['Company URL']}
    Job Title: {job_and_company_info['job_title']}
    Role Requirements: {job_and_company_info['Role Requirements']}
    Clean Job Description: {job_and_company_info['Clean Job Description']}
    Company Research:
        Company Values: {job_and_company_info['Company Research']['Company Values Summary']}
        Corporate Culture: {job_and_company_info['Company Research']['Corporate Culture Summary']}
        Leadership Team and Possible Key Opinion Leaders: {job_and_company_info['Company Research']['Leadership Team Summary']}
        Recent Company News: {job_and_company_info['Company Research']['Recent News Summary']}
        Company Competitive Advantages: {job_and_company_info['Company Research']['Competitive Advantages Summary']}
    """

    prompt = GAP_ASSESSMENT_PROMPT.replace("{JOB_AND_COMPANY_INFO}", job_and_company_info_str).replace("{RESUME}", resume)

    return prompt

def generate_key_accomplishments_and_skills_prompt(role, applicant):
    prompt = KEY_ACCOMPLISHMENTS_AND_SKILLS.replace("{Role.clean_job_description}", role.clean_job_description).replace("{Applicant.resume}", applicant.resume)
    return prompt

def gap_assessment_interface(job_and_company_info, resume):
    prompt = get_gap_assessment_prompt(job_and_company_info, resume)
    return gr.TextArea(label="Gap Assessment Prompt", value=prompt, interactive=False)

def key_accomplishments_and_skills_interface(job_and_company_info, resume):
    prompt = get_key_accomplishments_prompt(job_and_company_info, resume)
    return gr.TextArea(label="Key Accomplishments and Skills Prompt", value=prompt, interactive=False)

def submit_gap_assessment_result(gap_assessment_result):
    return gap_assessment_result

def submit_key_accomplishments_and_skills_result(key_accomplishments_and_skills_result):
    return key_accomplishments_and_skills_result

# Create the Gradio app
demo = gr.Blocks()

with demo:
    gr.Markdown("# Job Description Input")
    company_name = gr.Textbox(label="Company Name")
    company_url = gr.Textbox(label="Company URL")
    job_description = gr.TextArea(label="Paste Job Description")

    job_and_company_info_output = gr.JSON(label="Job and Company Info")
    role_output = gr.State()

    gr.Button("Submit").click(
        process_job_description,
        inputs=[company_name, company_url, job_description],
        outputs=[job_and_company_info_output, role_output]
    )

    gr.Markdown("# Resume Input")
    resume = gr.TextArea(label="Paste Resume")

    gap_assessment_prompt_output = gr.TextArea(label="Gap Assessment Prompt", visible=False)
    key_accomplishments_and_skills_prompt_output = gr.TextArea(label="Key Accomplishments and Skills Prompt", visible=False)

    gr.Button("Generate Prompts").click(
        gap_assessment_interface,
        inputs=[job_and_company_info_output, resume],
        outputs=gap_assessment_prompt_output
    ).then(
        key_accomplishments_and_skills_interface,
        inputs=[job_and_company_info_output, resume],
        outputs=key_accomplishments_and_skills_prompt_output
    ).then(
        lambda: [gr.update(visible=True), gr.update(visible=True)],
        outputs=[gap_assessment_prompt_output, key_accomplishments_and_skills_prompt_output]
    )

    gr.Markdown("# Gap Assessment Result", visible=False)
    gap_assessment_result = gr.TextArea(label="Paste Gap Assessment Result", visible=False)
    gr.Button("Submit Gap Assessment Result", visible=False).click(
        submit_gap_assessment_result,
        inputs=[gap_assessment_result],
        outputs=gr.TextArea(label="Gap Assessment Result")
    )

    gr.Markdown("# Key Accomplishments and Skills Result", visible=False)
    key_accomplishments_and_skills_result = gr.TextArea(label="Paste Key Accomplishments and Skills Result", visible=False)
    gr.Button("Submit Key Accomplishments and Skills Result", visible=False).click(
        submit_key_accomplishments_and_skills_result,
        inputs=[key_accomplishments_and_skills_result],
        outputs=gr.TextArea(label="Key Accomplishments and Skills Result")
    )

    def show_results():
        return [gr.update(visible=True), gr.update(visible=True), gr.update(visible=True), gr.update(visible=True)]

    gr.Button("Generate Prompts").click(
        show_results,
        outputs=[gr.Markdown("# Gap Assessment Result", visible=False),
                 gap_assessment_result,
                 gr.Button("Submit Gap Assessment Result", visible=False),
                 gr.Markdown("# Key Accomplishments and Skills Result", visible=False),
                 key_accomplishments_and_skills_result,
                 gr.Button("Submit Key Accomplishments and Skills Result", visible=False)]
    )

if __name__ == "__main__":
    demo.launch()