Spaces:
Sleeping
Sleeping
File size: 5,556 Bytes
4703cd3 | 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 | """
generator.py — AI Resume Improvement Generator.
Uses google/flan-t5-base (free, open-source) to generate:
1. An improved version of the resume text
2. A polished professional summary
FLAN-T5 is instruction-tuned, making it ideal for text rewriting tasks
without requiring fine-tuning.
Model size: ~250 MB. Downloaded once and cached automatically by
HuggingFace transformers.
"""
# ---------------------------------------------------------------------------
# Lazy model loading — keeps Streamlit startup fast
# ---------------------------------------------------------------------------
_pipeline = None
def _get_pipeline():
"""Load and cache the FLAN-T5 text2text pipeline."""
global _pipeline
if _pipeline is None:
from transformers import pipeline
_pipeline = pipeline(
"text2text-generation",
model="google/flan-t5-base",
max_new_tokens=512,
)
return _pipeline
# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------
def generate_improved_resume(
resume_text: str,
job_description: str = "",
missing_sections: list = None,
) -> str:
"""
Generate an improved, ATS-friendly version of the resume.
Rewrites the resume with:
- Professional tone
- Bullet-point format
- Stronger action verbs
- Explicit mention of any missing sections
Args:
resume_text : original extracted resume text
job_description : optional job description to tailor the rewrite
missing_sections: list of sections the model should add
Returns:
Improved resume text as a string.
"""
if not resume_text:
return "No resume text provided for improvement."
if missing_sections is None:
missing_sections = []
pipe = _get_pipeline()
# Build a focused, instruction-style prompt
# FLAN-T5 works best with clear task instructions
truncated_resume = _truncate(resume_text, 400)
jd_hint = f"\nThe target job requires: {_truncate(job_description, 100)}" if job_description else ""
missing_hint = (
f"\nMake sure to include sections for: {', '.join(missing_sections)}."
if missing_sections
else ""
)
prompt = (
f"Rewrite this resume to be professional, ATS-friendly, and impactful. "
f"Use bullet points, strong action verbs, and quantify achievements. "
f"Keep all original facts.{jd_hint}{missing_hint}\n\n"
f"Original Resume:\n{truncated_resume}\n\n"
f"Improved Resume:"
)
try:
result = pipe(prompt, max_new_tokens=512, do_sample=False)
improved = result[0]["generated_text"].strip()
return improved if improved else "⚠️ Could not generate improvement. Please try again."
except Exception as e:
return f"⚠️ Generation error: {str(e)}"
def generate_professional_summary(
name: str,
skills: list,
experience_present: bool,
job_title: str = "",
) -> str:
"""
Generate a short professional summary / objective paragraph.
Args:
name : candidate name (or empty string)
skills : list of extracted skill strings
experience_present: whether work experience was detected
job_title : optional target job title
Returns:
A 2–3 sentence professional summary.
"""
pipe = _get_pipeline()
skills_str = ", ".join(skills[:8]) if skills else "various technical skills"
exp_phrase = (
"with hands-on professional experience"
if experience_present
else "seeking to start a professional career"
)
target = f" targeting a {job_title} role" if job_title else ""
prompt = (
f"Write a 2-3 sentence professional resume summary for a candidate "
f"{exp_phrase} in {skills_str}{target}. "
f"Keep it concise, first-person, and ATS-friendly."
)
try:
result = pipe(prompt, max_new_tokens=120, do_sample=False)
return result[0]["generated_text"].strip()
except Exception as e:
return f"⚠️ Summary generation error: {str(e)}"
def generate_section_content(section_name: str, context: str) -> str:
"""
Generate content for a specific missing resume section.
Args:
section_name: e.g., 'Projects', 'Skills', 'Experience'
context : relevant resume text to base the generation on
Returns:
Generated section content as a string.
"""
pipe = _get_pipeline()
prompt = (
f"Based on the following resume context, write a professional "
f"'{section_name}' section in bullet-point format:\n\n"
f"{_truncate(context, 200)}\n\n"
f"{section_name} Section:"
)
try:
result = pipe(prompt, max_new_tokens=200, do_sample=False)
return result[0]["generated_text"].strip()
except Exception as e:
return f"⚠️ Section generation error: {str(e)}"
# ---------------------------------------------------------------------------
# Internal helpers
# ---------------------------------------------------------------------------
def _truncate(text: str, max_words: int) -> str:
"""Truncate text to max_words words to stay within model context limit."""
words = text.split()
if len(words) > max_words:
return " ".join(words[:max_words]) + "..."
return text |