Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
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() | |