Noo88ear's picture
πŸš€ Initial deployment of Multi-Agent Job Application Assistant
7498f2c
from __future__ import annotations
from typing import List, Tuple, Optional
import textwrap
import re
from .text import normalize_whitespace
ACTION_VERBS = [
"Led", "Built", "Improved", "Optimized", "Delivered", "Designed", "Implemented", "Automated",
"Reduced", "Increased", "Analyzed", "Developed", "Launched", "Managed", "Resolved", "Created",
# Reed-recommended action words
"Achieved", "Formulated", "Planned", "Generated", "Represented", "Completed",
]
# Weak openers to avoid on bullets and suggested stronger replacements
_WEAK_TO_STRONG = [
(re.compile(r"^\s*-\s*responsible for\s+", re.IGNORECASE), "- Led "),
(re.compile(r"^\s*-\s*tasked with\s+", re.IGNORECASE), "- Executed "),
(re.compile(r"^\s*-\s*worked on\s+", re.IGNORECASE), "- Delivered "),
(re.compile(r"^\s*-\s*helped\s+", re.IGNORECASE), "- Supported "),
(re.compile(r"^\s*-\s*assisted with\s+", re.IGNORECASE), "- Supported "),
(re.compile(r"^\s*-\s*handled\s+", re.IGNORECASE), "- Managed "),
]
def strengthen_action_verbs(text: str) -> str:
"""Promote weak bullet openers to stronger action verbs (The Muse guidance)."""
if not text:
return text
lines = text.splitlines()
out: List[str] = []
for line in lines:
new_line = line
for pattern, repl in _WEAK_TO_STRONG:
if pattern.search(new_line):
new_line = pattern.sub(repl, new_line)
break
out.append(new_line)
return "\n".join(out)
def make_bullets(lines: List[str]) -> str:
clean_lines = [f"- {normalize_whitespace(l)}" for l in lines if l and l.strip()]
return "\n".join(clean_lines)
def ensure_keywords(text: str, keywords: List[str], max_new: int = 30, allowed_keywords: Optional[set] = None) -> Tuple[str, List[str]]:
used = []
missing = []
lower_text = text.lower()
for k in keywords:
if k.lower() in lower_text:
used.append(k)
else:
missing.append(k)
if missing:
additions = []
actually_added = []
for k in missing:
if len(actually_added) >= max_new:
break
if allowed_keywords is not None and k.lower() not in allowed_keywords:
continue
additions.append(f"Experience with {k}.")
actually_added.append(k)
if additions:
text = text.rstrip() + "\n\nKeywords: " + ", ".join(actually_added) + "\n" + make_bullets(additions)
used.extend(actually_added)
return text, used
def format_resume_header(full_name: str, headline: str, email: str | None, phone: str | None, location: str | None, links: dict) -> str:
contact_parts = [p for p in [email, phone, location] if p]
links_str = " | ".join([f"{k}: {v}" for k, v in links.items()]) if links else ""
top_line = f"{full_name} β€” {headline}" if headline else full_name
contact_line = " | ".join(filter(None, [" | ".join(contact_parts), links_str]))
return "\n".join([top_line, contact_line]).strip() + "\n"
def format_experience_section(experiences: List[dict]) -> str:
sections: List[str] = []
for exp in experiences:
header = f"{exp.get('title','')} β€” {exp.get('company','')} ({exp.get('start_date','')} – {exp.get('end_date','Present')})"
bullets = exp.get("achievements") or []
if not bullets:
bullets = [
f"{ACTION_VERBS[0]} key outcomes relevant to the role.",
"Collaborated cross-functionally to deliver results.",
"Drove measurable impact with data-informed decisions.",
]
sections.append("\n".join([header, make_bullets(bullets)]))
return "\n\n".join(sections)
def format_skills_section(skills: List[str]) -> str:
if not skills:
return ""
return "Skills: " + ", ".join(skills)
def basic_resume_template(header: str, summary: str | None, skills: str, experience: str, education: str | None) -> str:
parts = [header]
if summary:
parts.append("\nSummary\n" + textwrap.fill(summary, width=100))
if skills:
parts.append("\n" + skills)
if experience:
parts.append("\n\nExperience\n" + experience)
if education:
parts.append("\n\nEducation\n" + education)
return "\n".join(parts).strip() + "\n"
def basic_cover_letter_template(greeting: str, body_paragraphs: List[str], closing: str, signature: str) -> str:
body = "\n\n".join(textwrap.fill(p, width=100) for p in body_paragraphs)
return "\n".join([greeting, "", body, "", closing, "", signature]).strip() + "\n"