Spaces:
Sleeping
Sleeping
| import json | |
| import re | |
| from typing import Dict, List, Optional, Tuple | |
| from dataclasses import dataclass | |
| from abc import ABC, abstractmethod | |
| import google.generativeai as genai | |
| from datetime import datetime | |
| # Configure Gemini API | |
| genai.configure(api_key="YOUR_GEMINI_API_KEY") | |
| class ResumeData: | |
| """Data structure to hold resume information""" | |
| personal_info: Dict | |
| summary: str | |
| experiences: List[Dict] | |
| skills: List[str] | |
| education: List[Dict] | |
| raw_text: str | |
| class JobDescription: | |
| """Data structure for job descriptions""" | |
| title: str | |
| company: str | |
| description: str | |
| requirements: List[str] | |
| keywords: List[str] | |
| class Agent(ABC): | |
| """Base agent class""" | |
| def __init__(self, model_name: str = "gemini-1.5-flash"): | |
| self.model = genai.GenerativeModel(model_name) | |
| def execute(self, *args, **kwargs): | |
| pass | |
| def generate_response(self, prompt: str) -> str: | |
| """Generate response using Gemini""" | |
| try: | |
| response = self.model.generate_content(prompt) | |
| return response.text | |
| except Exception as e: | |
| return f"Error generating response: {str(e)}" | |
| class SummaryAgent(Agent): | |
| """Agent responsible for creating compelling professional summaries""" | |
| def execute(self, resume_data: ResumeData, job_desc: Optional[JobDescription] = None) -> str: | |
| context = f""" | |
| Personal Info: {resume_data.personal_info} | |
| Experience: {resume_data.experiences} | |
| Skills: {resume_data.skills} | |
| Education: {resume_data.education} | |
| """ | |
| job_context = "" | |
| if job_desc: | |
| job_context = f""" | |
| Target Job: {job_desc.title} at {job_desc.company} | |
| Job Requirements: {job_desc.requirements} | |
| """ | |
| prompt = f""" | |
| Create a compelling professional summary (2-3 sentences) based on this resume information: | |
| {context} | |
| {job_context} | |
| Guidelines: | |
| - Highlight unique value proposition | |
| - Use action-oriented language | |
| - Focus on achievements and impact | |
| - Keep it concise and engaging | |
| - If job description provided, align with role requirements | |
| Return only the professional summary text. | |
| """ | |
| return self.generate_response(prompt) | |
| class ExperienceMatchingAgent(Agent): | |
| """Agent for matching experiences to job descriptions""" | |
| def execute(self, resume_data: ResumeData, job_desc: JobDescription) -> List[Dict]: | |
| experiences_text = json.dumps(resume_data.experiences, indent=2) | |
| prompt = f""" | |
| Analyze these work experiences and rank them by relevance to the target job: | |
| EXPERIENCES: | |
| {experiences_text} | |
| TARGET JOB: | |
| Title: {job_desc.title} | |
| Company: {job_desc.company} | |
| Description: {job_desc.description} | |
| Requirements: {job_desc.requirements} | |
| For each experience, provide: | |
| 1. Relevance score (1-10) | |
| 2. Key matching points | |
| 3. Suggested improvements for better alignment | |
| 4. Recommended order for resume | |
| Return as JSON format: | |
| {{ | |
| "ranked_experiences": [ | |
| {{ | |
| "original_experience": {{...}}, | |
| "relevance_score": 8, | |
| "matching_points": ["point1", "point2"], | |
| "suggested_improvements": ["improvement1", "improvement2"], | |
| "recommended_position": 1 | |
| }} | |
| ] | |
| }} | |
| """ | |
| response = self.generate_response(prompt) | |
| try: | |
| return json.loads(response) | |
| except json.JSONDecodeError: | |
| return {"error": "Failed to parse experience matching results"} | |
| class KeywordOptimizationAgent(Agent): | |
| """Agent for optimizing ATS keywords""" | |
| def execute(self, resume_data: ResumeData, job_desc: JobDescription) -> Dict: | |
| prompt = f""" | |
| Analyze the resume and job description to optimize ATS keywords: | |
| RESUME CONTENT: | |
| Summary: {resume_data.summary} | |
| Skills: {resume_data.skills} | |
| Experiences: {json.dumps(resume_data.experiences)} | |
| JOB DESCRIPTION: | |
| {job_desc.description} | |
| Requirements: {job_desc.requirements} | |
| Provide: | |
| 1. Missing critical keywords from job description | |
| 2. Keyword density analysis | |
| 3. Suggested keyword placements | |
| 4. Industry-specific terms to include | |
| 5. ATS optimization score (1-100) | |
| Return as JSON: | |
| {{ | |
| "missing_keywords": ["keyword1", "keyword2"], | |
| "current_keyword_density": {{"keyword": "frequency"}}, | |
| "suggested_placements": [ | |
| {{ | |
| "keyword": "Python", | |
| "sections": ["skills", "experience"], | |
| "context": "Add to technical skills section" | |
| }} | |
| ], | |
| "industry_terms": ["term1", "term2"], | |
| "ats_score": 75, | |
| "recommendations": ["rec1", "rec2"] | |
| }} | |
| """ | |
| response = self.generate_response(prompt) | |
| try: | |
| return json.loads(response) | |
| except json.JSONDecodeError: | |
| return {"error": "Failed to parse keyword optimization results"} | |
| class DesignAgent(Agent): | |
| """Agent for design and formatting suggestions""" | |
| def execute(self, resume_data: ResumeData, job_desc: Optional[JobDescription] = None) -> Dict: | |
| industry = job_desc.title.split()[0] if job_desc else "General" | |
| prompt = f""" | |
| Suggest design and formatting improvements for a {industry} professional's resume: | |
| CURRENT RESUME STRUCTURE: | |
| - Personal Info: {len(resume_data.personal_info)} fields | |
| - Experiences: {len(resume_data.experiences)} positions | |
| - Skills: {len(resume_data.skills)} skills listed | |
| - Education: {len(resume_data.education)} entries | |
| Consider: | |
| 1. Industry standards for {industry} | |
| 2. ATS-friendly formatting | |
| 3. Visual hierarchy and readability | |
| 4. Professional appearance | |
| 5. Length optimization | |
| Return JSON with: | |
| {{ | |
| "recommended_template": "template_name", | |
| "layout_suggestions": ["suggestion1", "suggestion2"], | |
| "formatting_rules": ["rule1", "rule2"], | |
| "color_scheme": "color_description", | |
| "typography": "font_recommendations", | |
| "sections_order": ["section1", "section2", "section3"], | |
| "design_tips": ["tip1", "tip2"] | |
| }} | |
| """ | |
| response = self.generate_response(prompt) | |
| try: | |
| return json.loads(response) | |
| except json.JSONDecodeError: | |
| return {"error": "Failed to parse design suggestions"} | |
| class EditingAgent(Agent): | |
| """Agent for grammar, punctuation, and content improvement""" | |
| def execute(self, text: str) -> Dict: | |
| prompt = f""" | |
| Analyze this resume text for improvements: | |
| TEXT TO REVIEW: | |
| {text} | |
| Check for: | |
| 1. Grammar and punctuation errors | |
| 2. Clarity and conciseness | |
| 3. Action verb usage | |
| 4. Quantifiable achievements | |
| 5. Professional tone | |
| 6. Consistency in formatting | |
| Return JSON: | |
| {{ | |
| "grammar_errors": [ | |
| {{ | |
| "original": "original text", | |
| "corrected": "corrected text", | |
| "explanation": "reason for change" | |
| }} | |
| ], | |
| "clarity_improvements": [ | |
| {{ | |
| "original": "original text", | |
| "improved": "improved text", | |
| "reason": "why it's better" | |
| }} | |
| ], | |
| "action_verb_suggestions": ["verb1", "verb2"], | |
| "quantification_opportunities": ["opportunity1", "opportunity2"], | |
| "overall_score": 85, | |
| "summary_feedback": "Overall assessment" | |
| }} | |
| """ | |
| response = self.generate_response(prompt) | |
| try: | |
| return json.loads(response) | |
| except json.JSONDecodeError: | |
| return {"error": "Failed to parse editing suggestions"} | |
| class ResumeAgent: | |
| """Main orchestrating agent that coordinates all sub-agents""" | |
| def __init__(self): | |
| self.summary_agent = SummaryAgent() | |
| self.experience_agent = ExperienceMatchingAgent() | |
| self.keyword_agent = KeywordOptimizationAgent() | |
| self.design_agent = DesignAgent() | |
| self.editing_agent = EditingAgent() | |
| def parse_resume(self, resume_text: str) -> ResumeData: | |
| """Simple resume parsing - can be enhanced with proper NLP""" | |
| # This is a simplified parser - in production, you'd use more sophisticated parsing | |
| lines = resume_text.split('\n') | |
| # Extract basic sections (this is a simplified implementation) | |
| personal_info = {"name": "John Doe", "email": "john@email.com"} # Placeholder | |
| summary = "" | |
| experiences = [] | |
| skills = [] | |
| education = [] | |
| # Simple pattern matching (enhance as needed) | |
| current_section = None | |
| for line in lines: | |
| line = line.strip() | |
| if re.match(r'(summary|profile|objective)', line.lower()): | |
| current_section = 'summary' | |
| elif re.match(r'(experience|work|employment)', line.lower()): | |
| current_section = 'experience' | |
| elif re.match(r'(skills|technical)', line.lower()): | |
| current_section = 'skills' | |
| elif re.match(r'(education|academic)', line.lower()): | |
| current_section = 'education' | |
| elif line and current_section: | |
| if current_section == 'summary': | |
| summary += line + " " | |
| elif current_section == 'skills': | |
| skills.extend([skill.strip() for skill in line.split(',')]) | |
| return ResumeData( | |
| personal_info=personal_info, | |
| summary=summary.strip(), | |
| experiences=experiences, | |
| skills=skills, | |
| education=education, | |
| raw_text=resume_text | |
| ) | |
| def optimize_resume(self, resume_text: str, job_description: Optional[str] = None) -> Dict: | |
| """Main method to optimize resume using all agents""" | |
| # Parse resume | |
| resume_data = self.parse_resume(resume_text) | |
| # Parse job description if provided | |
| job_desc = None | |
| if job_description: | |
| job_desc = JobDescription( | |
| title="Target Position", | |
| company="Target Company", | |
| description=job_description, | |
| requirements=[req.strip() for req in job_description.split('.') if req.strip()], | |
| keywords=[] | |
| ) | |
| results = { | |
| "timestamp": datetime.now().isoformat(), | |
| "original_resume": resume_data.__dict__, | |
| } | |
| # Generate new summary | |
| print("π Generating compelling summary...") | |
| results["new_summary"] = self.summary_agent.execute(resume_data, job_desc) | |
| # Match experiences to job | |
| if job_desc: | |
| print("π Analyzing experience relevance...") | |
| results["experience_matching"] = self.experience_agent.execute(resume_data, job_desc) | |
| print("π Optimizing keywords for ATS...") | |
| results["keyword_optimization"] = self.keyword_agent.execute(resume_data, job_desc) | |
| # Design suggestions | |
| print("π Generating design recommendations...") | |
| results["design_suggestions"] = self.design_agent.execute(resume_data, job_desc) | |
| # Edit and improve | |
| print("π Analyzing content for improvements...") | |
| results["editing_suggestions"] = self.editing_agent.execute(resume_text) | |
| return results | |
| # File handling utilities | |
| def read_file(file_path: str) -> str: | |
| """Read content from a file""" | |
| try: | |
| with open(file_path, 'r', encoding='utf-8') as file: | |
| return file.read() | |
| except FileNotFoundError: | |
| print(f"β File not found: {file_path}") | |
| return "" | |
| except Exception as e: | |
| print(f"β Error reading file: {str(e)}") | |
| return "" | |
| def get_sample_resume() -> str: | |
| """Return sample resume text""" | |
| return """ | |
| John Doe | |
| Software Engineer | |
| john.doe@email.com | |
| (555) 123-4567 | |
| SUMMARY | |
| Experienced software developer with 5 years in web development and system design. | |
| EXPERIENCE | |
| Software Developer at TechCorp (2019-2024) | |
| - Developed web applications using Python and JavaScript | |
| - Worked with databases and APIs | |
| - Collaborated with team members on agile projects | |
| - Maintained code quality and performed code reviews | |
| Senior Developer Intern at StartupXYZ (2018-2019) | |
| - Built responsive web interfaces using React | |
| - Integrated third-party APIs and services | |
| - Participated in daily standups and sprint planning | |
| SKILLS | |
| Python, JavaScript, React, SQL, Git, Docker, AWS, REST APIs | |
| EDUCATION | |
| BS Computer Science, University XYZ (2019) | |
| GPA: 3.7/4.0 | |
| """ | |
| def get_sample_job_description() -> str: | |
| """Return sample job description""" | |
| return """ | |
| Senior Python Developer position at InnovaTech | |
| We are looking for an experienced Python developer with expertise in Django, | |
| REST APIs, database optimization, and cloud technologies. The ideal candidate | |
| should have 3+ years of experience, strong problem-solving skills, and | |
| experience with AWS or Azure. | |
| Requirements: | |
| - 3+ years of Python development experience | |
| - Strong knowledge of Django framework | |
| - Experience with REST API development | |
| - Database design and optimization skills | |
| - Cloud platform experience (AWS/Azure) | |
| - Git version control | |
| - Agile development methodology | |
| - Strong communication skills | |
| """ | |
| # Example usage and testing | |
| def main(): | |
| """Main function with file upload capability""" | |
| print("π AI Resume Optimization Agent") | |
| print("=" * 50) | |
| # Get resume content | |
| resume_file = input("π Enter resume file path (or press Enter for sample): ").strip() | |
| if resume_file and resume_file != "": | |
| resume_text = read_file(resume_file) | |
| if not resume_text: | |
| print("π Using sample resume instead...") | |
| resume_text = get_sample_resume() | |
| else: | |
| print("π Using sample resume...") | |
| resume_text = get_sample_resume() | |
| # Get job description | |
| job_file = input("πΌ Enter job description file path (or press Enter for sample): ").strip() | |
| if job_file and job_file != "": | |
| job_description = read_file(job_file) | |
| if not job_description: | |
| print("πΌ Using sample job description instead...") | |
| job_description = get_sample_job_description() | |
| else: | |
| print("πΌ Using sample job description...") | |
| job_description = get_sample_job_description() | |
| # Initialize the agent | |
| agent = ResumeAgent() | |
| print("\nπ Starting Resume Optimization...") | |
| print("=" * 50) | |
| # Optimize resume | |
| results = agent.optimize_resume(resume_text, job_description) | |
| print("\nβ Optimization Complete!") | |
| print("=" * 50) | |
| # Display results | |
| print(f"\nπ NEW SUMMARY:") | |
| print(results.get("new_summary", "")) | |
| if "keyword_optimization" in results: | |
| keyword_data = results["keyword_optimization"] | |
| if isinstance(keyword_data, dict) and "ats_score" in keyword_data: | |
| print(f"\nπ― ATS SCORE: {keyword_data['ats_score']}/100") | |
| if "design_suggestions" in results: | |
| design_data = results["design_suggestions"] | |
| if isinstance(design_data, dict) and "recommended_template" in design_data: | |
| print(f"\nπ¨ RECOMMENDED TEMPLATE: {design_data['recommended_template']}") | |
| print(f"\nπ ANALYSIS COMPLETE") | |
| print(f"Full results saved with timestamp: {results['timestamp']}") | |
| # Save results to file | |
| output_file = f"resume_optimization_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json" | |
| try: | |
| with open(output_file, 'w', encoding='utf-8') as f: | |
| json.dump(results, f, indent=2, default=str) | |
| print(f"πΎ Results saved to: {output_file}") | |
| except Exception as e: | |
| print(f"β Error saving results: {str(e)}") | |
| return results | |
| if __name__ == "__main__": | |
| # Note: Replace "YOUR_GEMINI_API_KEY" with your actual API key | |
| main() | |