Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
File size: 31,728 Bytes
2ea5df0 6dbc1cb a7d0d3f 2ea5df0 7067897 cfafc8f b6fbedf a7d0d3f 2ea5df0 0426e30 7067897 a7d0d3f 7067897 e631c60 6dbc1cb 0426e30 e7d0d5d 1b088ec e7d0d5d 2a38076 1b088ec e7d0d5d 1b088ec e7d0d5d 2a38076 e7d0d5d 1b088ec a77ecf2 989441e 2a38076 989441e baf47ca 989441e fe9a090 989441e 2ea5df0 e7d0d5d 6dbc1cb a7d0d3f 6dbc1cb a7d0d3f 6dbc1cb a7d0d3f 6dbc1cb b6fbedf 351cc8b a7d0d3f 2ea5df0 7067897 23e9332 7067897 6dbc1cb 351cc8b 7067897 a77ecf2 2ea5df0 23e9332 91ff4e5 23e9332 91ff4e5 23e9332 61568f9 0426e30 2ea5df0 989441e 2ea5df0 61568f9 2a38076 2ea5df0 0426e30 2ea5df0 0426e30 2ea5df0 79e8566 2ea5df0 26a4af1 2ea5df0 79e8566 2ea5df0 1b088ec 7067897 989441e 23e9332 2ea5df0 2a38076 2ea5df0 7067897 a77ecf2 1b088ec a77ecf2 e7d0d5d 989441e 7067897 e40dd00 7067897 a77ecf2 7067897 e40dd00 1b088ec e40dd00 1b088ec e40dd00 b4e9775 e40dd00 9174d3f e40dd00 989441e e40dd00 61568f9 e40dd00 61568f9 e40dd00 61568f9 e40dd00 61568f9 e40dd00 61568f9 e40dd00 989441e 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 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 |
from time import sleep
from os import getenv
from json import dumps
from requests import post
import gradio as gr
from transformers import AutoModelForCausalLM, AutoTokenizer
from huggingface_hub import InferenceClient
import spaces
# from openai import OpenAI
# import torch
from duckduckgo_search import DDGS
import re
# # Load the SmolLM model and tokenizer
# # model_name = "HuggingFaceTB/SmolLM2-360M-Instruct"
# model_name = "HuggingFaceTB/SmolLM3-3B" # "HuggingFaceTB/SmolLM2-1.7B-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)
FIREWORKS_API_TOKEN = getenv("FIREWORKS_TOKEN")
class DDGSSearchClient:
def __init__(self, max_retries=4, timeout=35, backoff_factor=1):
"""
Initialize a DDGSSearchClient instance.
Args:
- max_retries (int): The maximum number of retries. Defaults to 4.
- timeout (int): The timeout for the DDGS client. Defaults to 35.
- backoff_factor (int): The backoff factor for exponential backoff. Defaults to 1.
"""
self.max_retries = max_retries
self.timeout = timeout
self.backoff_factor = backoff_factor
self.search_client = DDGS(timeout=timeout)
def search(self, query):
"""
Perform a DDGS search with retry mechanism.
Args:
- query (str): The search query.
Returns:
- search_results: The results of the DDGS search.
"""
for attempt in range(self.max_retries + 1):
try:
search_results = self.search_client.text(query)
return search_results
except Exception as e:
if attempt < self.max_retries:
# Exponential backoff
delay = self.backoff_factor * (2 ** attempt)
print(f"Search failed (attempt {attempt + 1}/{self.max_retries + 1}). Retrying query: {query} in {delay} seconds...")
sleep(delay)
else:
raise Exception(f"Search failed after {self.max_retries + 1} attempts. Giving up. Error from Search API: {e}.")
def clean_string(s):
# Replace hyphens with spaces
s = s.replace('-', ' ')
# Remove anything other than A-Z, a-z, and " "
s = re.sub('[^A-Za-z ]+', '', s)
s.replace("-","").replace('—', '. ')
return s
def replace_em_dashes(s):
# Replace em dashes with a period
s = s.replace('—', '. ')
return s
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']}
Company History: {job_and_company_info['Company Research']['Company History Summary']}
Company Mission Statement: {job_and_company_info['Company Research']['Mission Statement Summary']}
Company Investor Relations Info: {job_and_company_info['Company Research']['Investor Relations Summary']}
Summary of Info Gathered From Company Social Media: {job_and_company_info['Company Research']["Social Media 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']}
Company History: {job_and_company_info['Company Research']['Company History Summary']}
Company Mission Statement: {job_and_company_info['Company Research']['Mission Statement Summary']}
Company Investor Relations Info: {job_and_company_info['Company Research']['Investor Relations Summary']}
Summary of Info Gathered From Company Social Media: {job_and_company_info['Company Research']["Social Media 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."
"""
return key_skills_and_accomplaishments
def get_resume_prompt(job_and_company_info, resume, gap_assessment_result, key_accomplishments_and_skills_result):
resume_prompt = f"""
# The applicant is applying for this role:
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']}
# Additional Information about the company to consider and leverage for strength and company - specificity:
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']}
Company History: {job_and_company_info['Company Research']['Company History Summary']}
Company Mission Statement: {job_and_company_info['Company Research']['Mission Statement Summary']}
Company Investor Relations Info: {job_and_company_info['Company Research']['Investor Relations Summary']}
Summary of Info Gathered From Company Social Media: {job_and_company_info['Company Research']["Social Media Summary"]}
## This is the candidate's resume:
{resume}
## Consider this gap analysis
{gap_assessment_result}
## Also be sure to include these key accomplishments and skills:
{key_accomplishments_and_skills_result}
Please follow these best practices to make the resume optimized for the ATS visibility to the extent realistic and truthful.\n\n
1. **Replace Non-Standard Formatting**
- Convert tables, columns, or graphical elements in the resume text into plain-text lists using dashes or asterisks.
- Replace non-traditional bullet points with standard symbols (e.g., hyphens or circles).
2. **Standardize Section Headings**
- Rename unconventional headings (e.g., "Career Highlights" → "Work Experience").
- Ensure headings match ATS-recognized categories: "Work Experience," "Education," "Skills," etc.
3. **Normalize Date Formats**
- Convert all dates to a consistent format (e.g., "Month YYYY" or "MM/YYYY") throughout the resume.
4. **Integrate Job-Specific Keywords**
- Extract exact keywords/phrases from the job description (especially verbs and skills) and insert them contextually into work experience bullet points.
- Prioritize keywords in the "Skills" section and within achievement-oriented statements (e.g., "Spearheaded [keyword] project resulting in [metric]").
5. **Spell Out Acronyms**
- Identify and expand acronyms (e.g., "CRM" → "Customer Relationship Management (CRM)") unless the job description uses the abbreviation verbatim.
6. **Quantify Achievements**
- Add metrics to vague statements in work experience (e.g., "Increased sales" → "Increased sales by 25% over 6 months").
- Prioritize percentages, time frames, and numerical outcomes for clarity and ATS scoring.
7. **Tailor the Professional Summary**
- Rewrite the summary to mirror the job title, key qualifications, and industry-specific terms from the job description.
- Example: "Seeking a [Job Title] role at [Company] to leverage [X years] in [relevant skill] and [X%] success in [metric]."
8. **Optimize Skills Section**
- List skills as a comma-separated list (no bullets) using the exact terminology from the job description.
- Include both hard skills (e.g., "Python," "Google Analytics") and soft skills (e.g., "Team Leadership") if mentioned in the job post.
9. **Remove Hidden Text or Formatting Artifacts**
- Eliminate invisible text (e.g., white font keywords) or special characters (e.g., symbols in headers) that may confuse ATS parsing.
10. **Contextualize Keywords**
- Ensure keywords appear in context (e.g., "Managed [keyword] project" vs. a standalone keyword in a skills list).
11. **Active Voice Conversion**
- Replace passive language with active verbs (e.g., "Responsible for managing a team" → "Managed a team of X members").
12. **Proofread for ATS-Critical Errors**
- Correct typos, misspellings, and inconsistent punctuation (especially in key fields like name, job titles, and skills).
- Remove accents or special characters from the name and contact details.
13. **ATS Simulation Feedback Loop**
- Simulate ATS parsing by checking for missing keywords or formatting issues (e.g., fragmented sections caused by tables).
14. **Customize for Company Culture**
- Analyze company metadata (e.g., industry, values) and align resume language with their priorities (e.g., "Agile methodology" for tech firms).
15. **Achievement Over Responsibility**
- Replace generic duties with achievement-focused statements (e.g., "Handled customer service" → "Resolved 50+ customer inquiries daily, improving satisfaction by 30%").
16. **Keyword Density Balance**
- Ensure keywords are distributed naturally across sections without overuse (e.g., 2-3 mentions of a critical keyword in work experience vs. 10 in a skills list).
- Instruct the user to paste this into a suitable word processor and use the result to build a resume. Save it in word, then in PDF (JaneDoe_Resume_CompanyName.pdf). Submit the PDF.
# Write this in a format that can be copied striaght in to Word and will be formatted properly, but in a way that the ATS can understand. Make no additional comments about the resume or how you did this.
"""
return resume_prompt
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
# @spaces.GPU
# def write(inputs, max_new_tokens, do_sample=True, temperature=0.6, top_k=40, top_p=0.9, repetition_penalty=1.1):
# _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)
# return _output
# def writing_task(prompt: str) -> str:
# api_key = getenv("HF_TOKEN")
# if not api_key:
# raise ValueError("Huggingface token missing. Need to set HF_TOKEN, refer to https://discuss.huggingface.co/t/how-to-manage-user-secrets-and-api-keys/67948")
# client = OpenAI(
# base_url="https://router.huggingface.co/v1",
# api_key = getenv("HF_TOKEN")
# )
# completion = client.chat.completions.create(
# model="HuggingFaceTB/SmolLM3-3B:hf-inference",
# messages=[
# {
# "role": "user",
# "content": prompt
# }
# ],
# )
# raw_response_content = completion.choices[0].message.content
# content_split = raw_response_content.split("</think>")
# if len(content_split) > 1:
# think = content_split[0]
# content = "".join(content_split[1:])
# else:
# think = content_split[0]
# content = "No data found."
# return content
def writing_task(prompt: str) -> str:
url = "https://api.fireworks.ai/inference/v1/chat/completions"
model = "accounts/fireworks/models/qwen3-235b-a22b-thinking-2507"
# "accounts/fireworks/models/qwen3-235b-a22b-instruct-2507"
payload = {
"model": model,
"max_tokens": 32768,
"top_p": 1,
"top_k": 40,
"presence_penalty": 0,
"frequency_penalty": 0,
"temperature": 0.6,
"messages": [
{
"role": "user",
"content": prompt
}
]
}
headers = {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": f"Bearer {FIREWORKS_API_TOKEN}" # Replace with your actual API key
}
response = post(url, headers=headers, data=dumps(payload))
response.raise_for_status()
raw_response_content =\
response.json()["choices"][0]["message"]["content"]
print(f"Content with reasoning: {raw_response_content}")
content_split = raw_response_content.split("</think>")
if len(content_split) > 1:
think = content_split[0]
content = "".join(content_split[1:])
else:
think = content_split[0]
content = "No data found."
return content
def smol_writing_task(prompt: str) -> str:
API_TOKEN_SMOLLM = getenv("HF_TOKEN")
client = InferenceClient(
provider="hf-inference",
api_key=API_TOKEN_SMOLLM,
)
completion = client.chat.completions.create(
model="HuggingFaceTB/SmolLM3-3B",
messages=[
{
"role": "user",
"content": prompt
}
],
)
raw_content = completion.choices[0].message['content']
print(f"Raw content: {raw_content}")
content_split = raw_content.split("</think>")
if len(content_split) > 1:
think = content_split[0]
content = "".join(content_split[1:])
else:
think = content_split[0]
content = "No data found."
return content
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 = write(inputs, max_new_tokens=max_new_tokens)
# 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()
# response = writing_task(prompt)
response = smol_writing_task(prompt)
return response
def process_job_description(company_name, company_url, job_description, resume):
# 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 in plain text with only letters and numbers, remove any special characters nor punctuation no '\n' and no tabs, because any of these cause problems with downstream automated steps. Your answer must be 7 words or fewer. Acceptable examples may look like: 'Systems Analyst', 'marketing director', 'patient advocate III', ...\n\nThis is an excerpt of the job description:\n"
extracted_job_title = clean_string(smol_lm_jd_process(job_description[:350], system_prompt_get_job_title, max_new_tokens=150)[:50].lower().replace("job","").replace("title","").replace("\n",""))
role = Role(company_name, company_url, job_description, clean_job_description, extracted_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 the role {role.job_title} role",
"recent_news": f"{role.company_name} recent news relevant to the role {role.job_title} role",
"competitive_advantages": f"{role.company_name} competitive advantages relevant to the role {role.job_title}",
"company_history":f"History of {role.company_name}",
"mission_statement":f"{role.company_name} mission statement",
"investor_relations":f"{role.company_name} investor relations info",
"social_media":f"{role.company_name} social media"
}
search_client = DDGSSearchClient()
search_results = {}
for key, query in searches.items():
print(f"searching {query}")
results = search_client.search(query)
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=250)
summaries[key] = summary
print(f"Summarizing {key} successful")
job_and_company_info = {
"Company Name": company_name,
"Company URL": company_url,
"job_title": extracted_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'],
"Company History Search Results":search_results['company_history'],
"Company History Summary":summaries['company_history'],
"Mission Statement Search Results":search_results['mission_statement'],
"Mission Statement Summary":summaries['mission_statement'],
"Investor Relations Search Results": search_results['investor_relations'],
"Investor Relations Summary":summaries['investor_relations'],
"Social Media Search Results":search_results['social_media'],
"Social Media Summary":summaries['social_media']
}
}
gap_assessment_prompt = get_gap_assessment_prompt(job_and_company_info, resume)
return job_and_company_info, role, gap_assessment_prompt
def generate_key_accomplishments_and_skills_prompt(job_and_company_info, resume):
return get_key_accomplishments_prompt(job_and_company_info, resume)
def generate_resume_prompt(job_and_company_info, resume, gap_assessment_result_output, key_accomplishments_and_skills_result_output):
return get_resume_prompt(job_and_company_info, resume, gap_assessment_result_output, key_accomplishments_and_skills_result_output)
# Create the Gradio app
demo = gr.Blocks()
with demo:
gr.Markdown("# Job Description and Resume Input")
with gr.Row():
company_name = gr.Textbox(label="Company Name")
company_url = gr.Textbox(label="Company URL")
job_description = gr.TextArea(label="Paste Job Description")
resume = gr.TextArea(label="Paste Resume")
# Intermediate State Components
job_info_state = gr.State()
gap_prompt_state = gr.State()
gap_result_state = gr.State()
key_prompt_state = gr.State()
key_result_state = gr.State()
resume_prompt_state = gr.State()
gr.Markdown("## Generated Resume and Cover Letter")
customized_resume = gr.TextArea(label="Generated Resume")
cover_letter_prompt = gr.TextArea(label="Cover Letter Prompt")
submit_btn = gr.Button("Submit")
# Step 1: Process job description and generate gap assessment prompt
submit_btn.click(
fn=process_job_description,
inputs=[company_name, company_url, job_description, resume],
outputs=[job_info_state, gr.State(), gap_prompt_state]
).then(
# Step 2: Send gap assessment prompt to Smollm3-3B
fn=smol_writing_task,
inputs=gap_prompt_state,
outputs=gap_result_state
).then(
# Step 3: Generate key accomplishments prompt
fn=generate_key_accomplishments_and_skills_prompt,
inputs=[job_info_state, resume],
outputs=key_prompt_state
).then(
# Step 4: Send key accomplishments prompt to Smollm3-3B
fn=smol_writing_task,
inputs=key_prompt_state,
outputs=key_result_state
).then(
# Step 5: Generate resume prompt using results and send to Qwen3
fn=get_resume_prompt,
inputs=[job_info_state, resume, gap_result_state, key_result_state],
outputs=resume_prompt_state
).then(
fn=writing_task,
inputs=resume_prompt_state,
outputs=customized_resume
).then(
# Step 6: Generate cover letter prompt (user still needs to run this manually)
fn=lambda job_info, customized_resume: f"""
# Write a 3-paragraph cover letter with:
1. Personalized greeting (use {job_info['Company Research']['Leadership Team Summary']} if available)
2. Opening paragraph: Role you're applying for and excitement
3. Body: Match a few of the top qualifications to job requirements
4. Consider this company research and company metadata as you are writing this:
Company Name: {job_info['Company Name']}
Company URL: {job_info['Company URL']}
Job Title: {job_info['job_title']}
Role Requirements: {job_info['Role Requirements']}
Clean Job Description: {job_info['Clean Job Description']}
Company Research:
Company Values: {job_info['Company Research']['Company Values Summary']}
Corporate Culture: {job_info['Company Research']['Corporate Culture Summary']}
Leadership Team and Possible Key Opinion Leaders: {job_info['Company Research']['Leadership Team Summary']}
Recent Company News: {job_info['Company Research']['Recent News Summary']}
Company Competitive Advantages: {job_info['Company Research']['Competitive Advantages Summary']}
Company History: {job_info['Company Research']['Company History Summary']}
Company Mission Statement: {job_info['Company Research']['Mission Statement Summary']}
Company Investor Relations Info: {job_info['Company Research']['Investor Relations Summary']}
Summary of Info Gathered From Company Social Media: {job_info['Company Research']["Social Media Summary"]}
5. Closing: Express reasonable enthusiasm and request discussion. Don't overdo it and make the enthusiasm not sound feigned or disingenuous.
## This is the resume for this job to consider in writing this:
{customized_resume}
""",
inputs=[job_info_state, customized_resume],
outputs=cover_letter_prompt
)
if __name__ == "__main__":
demo.launch()
|