""" 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