import os import sys import traceback import logging from pathlib import Path # Setup logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) logger.info("=" * 80) logger.info("šŸš€ Job Application Agent - Startup") logger.info("=" * 80) # Add src to Python path current_dir = os.path.dirname(os.path.abspath(__file__)) src_path = os.path.join(current_dir, 'src') if src_path not in sys.path: sys.path.insert(0, src_path) logger.info(f"šŸ“ Current directory: {current_dir}") logger.info(f"šŸ“ Source directory: {src_path}") logger.info(f"āœ“ Python path updated") # Import Gradio try: import gradio as gr gradio_version = gr.__version__ logger.info(f"āœ“ Gradio imported successfully (version: {gradio_version})") except ImportError as e: logger.error(f"āœ— Failed to import Gradio: {e}") logger.error("Install with: pip install gradio") sys.exit(1) # Import PyTorch try: import torch logger.info(f"āœ“ PyTorch imported successfully (GPU available: {torch.cuda.is_available()})") except ImportError as e: logger.error(f"āœ— Failed to import PyTorch: {e}") logger.error("Install with: pip install torch") sys.exit(1) # Import application components logger.info("\nšŸ“¦ Attempting to import application modules...") mayini_model = None vocab = None customizer = None classifier = None scraper = None agent = None # Try to import actual modules try: logger.info(" - Importing MAYINI model...") from mayini_model import MAYINIModel, MAYINIVocabulary logger.info(" āœ“ MAYINI model imported") except ImportError as e: logger.warning(f" āš ļø Could not import MAYINI: {e}") logger.warning(" Creating mock MAYINI classes...") class MAYINIVocabulary: def __init__(self, vocab_size=5000): self.vocab_size = vocab_size def encode(self, text, max_len=512): return torch.zeros(1, max_len, dtype=torch.long) def get_embeddings(self): return torch.randn(5000, 256) class MAYINIModel: def __init__(self, **kwargs): self.config = kwargs def eval(self): return self def get_embeddings(self, input_ids): return torch.randn(1, 512, 256) def forward(self, input_ids): return torch.randn(1, 5000) def count_parameters(self): return 3500000 logger.info(" āœ“ Mock MAYINI classes created") try: logger.info(" - Importing Job Scraper...") from scraper import JobScraper logger.info(" āœ“ Scraper imported") except ImportError as e: logger.warning(f" āš ļø Could not import Scraper: {e}") logger.warning(" Creating mock Scraper class...") class JobScraper: def __init__(self): self.jobs = [ { 'title': 'Senior Python Developer', 'company': 'Tech Giants Inc', 'location': 'Remote', 'description': 'We are looking for a Senior Python Developer', 'requirements': ['Python', 'Docker', 'AWS'], 'salary_range': '$120k - $160k', 'experience_required': 5 }, { 'title': 'ML Engineer', 'company': 'AI Solutions', 'location': 'San Francisco', 'description': 'Machine Learning Engineer role', 'requirements': ['Python', 'PyTorch', 'TensorFlow'], 'salary_range': '$150k - $180k', 'experience_required': 4 } ] def get_all_jobs(self): return self.jobs def search_jobs(self, keywords=None, location=None, limit=10): return self.jobs[:limit] logger.info(" āœ“ Mock Scraper class created") try: logger.info(" - Importing Resume Customizer...") from customizer import ResumeCustomizer logger.info(" āœ“ Customizer imported") except ImportError as e: logger.warning(f" āš ļø Could not import Customizer: {e}") logger.warning(" Creating mock Customizer class...") class ResumeCustomizer: def __init__(self, model, vocab): self.model = model self.vocab = vocab def customize_for_job(self, job): return { 'summary': f"Experienced professional ready for {job.get('title', 'N/A')} role", 'skills': ['Python', 'Docker', 'AWS', 'Git', 'REST API'], 'customized_for': {'match_score': 0.85} } logger.info(" āœ“ Mock Customizer class created") try: logger.info(" - Importing Job Classifier...") from classifier import JobRelevanceClassifier logger.info(" āœ“ Classifier imported") except ImportError as e: logger.warning(f" āš ļø Could not import Classifier: {e}") logger.warning(" Creating mock Classifier class...") class JobRelevanceClassifier: def __init__(self, **kwargs): pass def classify_job(self, job, skills): return 0.75 def get_match_details(self, job, skills): return { 'relevance_score': 0.75, 'recommendation': 'Consider applying', 'matching_skills': ['Python', 'Docker'], 'missing_skills': ['Kubernetes'] } def rank_jobs(self, jobs, skills): return [(j, 0.7 + i*0.05) for i, j in enumerate(jobs)] logger.info(" āœ“ Mock Classifier class created") try: logger.info(" - Importing Application Agent...") from agent import JobApplicationAgent logger.info(" āœ“ Agent imported") except ImportError as e: logger.warning(f" āš ļø Could not import Agent: {e}") logger.warning(" Creating mock Agent class...") class JobApplicationAgent: def __init__(self, scraper, customizer, classifier): self.scraper = scraper self.customizer = customizer self.classifier = classifier def search_and_apply(self, keywords=None, location=None, num_jobs=5): jobs = self.scraper.search_jobs(limit=num_jobs) return { 'total_jobs_found': len(jobs), 'relevant_jobs': len(jobs), 'pass_rate': 0.85, 'applications': [ {'job': j, 'relevance_score': 0.8, 'match_details': { 'matching_skills': ['Python', 'Docker'], 'missing_skills': ['Kubernetes'] }} for j in jobs ] } logger.info(" āœ“ Mock Agent class created") logger.info("\n" + "=" * 80) logger.info("āœ… All components loaded") logger.info("=" * 80) # ============================================================================ # INITIALIZATION # ============================================================================ logger.info("\nšŸ”§ Initializing components...") try: vocab = MAYINIVocabulary(vocab_size=5000) logger.info("āœ“ MAYINI Vocabulary initialized") mayini_model = MAYINIModel(vocab_size=5000, hidden_dim=256, num_heads=8, num_layers=4) mayini_model.eval() logger.info("āœ“ MAYINI Model initialized") scraper = JobScraper() logger.info("āœ“ Job Scraper initialized") customizer = ResumeCustomizer(mayini_model, vocab) logger.info("āœ“ Resume Customizer initialized") classifier = JobRelevanceClassifier() logger.info("āœ“ Job Classifier initialized") agent = JobApplicationAgent(scraper, customizer, classifier) logger.info("āœ“ Application Agent initialized") except Exception as e: logger.error(f"Error during initialization: {e}") logger.error(traceback.format_exc()) logger.info("\n" + "=" * 80) logger.info("āœ… INITIALIZATION COMPLETE - Ready to serve!") logger.info("=" * 80 + "\n") # ============================================================================ # INTERFACE FUNCTIONS # ============================================================================ def search_jobs_interface(keywords: str, location: str, num_jobs: int) -> str: """Search and rank jobs""" try: if not keywords or not keywords.strip(): return "āŒ **Error:** Please enter keywords" results = agent.search_and_apply( keywords=keywords.strip(), location=location.strip() if location else "Remote", num_jobs=int(num_jobs) ) output = f"āœ… **Search Results**\n\n" output += f"- Found: {results.get('total_jobs_found', 0)} jobs\n" output += f"- Relevant: {results.get('relevant_jobs', 0)} jobs\n" output += f"- Pass Rate: {results.get('pass_rate', 0):.1%}\n\n" output += "---\n\n" for i, app in enumerate(results.get('applications', [])[:5], 1): job = app.get('job', {}) score = app.get('relevance_score', 0) output += f"**{i}. {job.get('title', 'N/A')}**\n" output += f"- Company: {job.get('company', 'N/A')}\n" output += f"- Location: {job.get('location', 'N/A')}\n" output += f"- šŸŽÆ Relevance: **{score:.0%}**\n" output += f"- šŸ’° Salary: {job.get('salary_range', 'Not specified')}\n" output += f"- šŸ“Š Experience: {job.get('experience_required', 'N/A')} years\n\n" return output except Exception as e: logger.error(f"Error in search: {e}\n{traceback.format_exc()}") return f"āŒ **Error:** {str(e)}\n\nPlease try again." def customize_resume_interface(job_title: str, company: str, requirements: str) -> str: """Customize resume for job""" try: if not job_title or not job_title.strip(): return "āŒ **Error:** Please enter job title" job = { 'title': job_title.strip(), 'company': company.strip(), 'requirements': [r.strip() for r in requirements.split(',') if r.strip()] if requirements else [] } customized = customizer.customize_for_job(job) output = f"āœ… **Customized Resume**\n\n" output += f"**Job:** {job_title} @ {company}\n\n" output += f"**Summary:**\n{customized.get('summary', 'N/A')}\n\n" output += f"**Top Skills:**\n" for skill in customized.get('skills', [])[:10]: output += f"• {skill}\n" return output except Exception as e: logger.error(f"Error in customization: {e}\n{traceback.format_exc()}") return f"āŒ **Error:** {str(e)}\n\nPlease check your inputs." def classify_job_interface(job_title: str, requirements: str) -> str: """Classify job relevance""" try: if not job_title or not job_title.strip(): return "āŒ **Error:** Please enter job title" job = { 'title': job_title.strip(), 'requirements': [r.strip() for r in requirements.split(',') if r.strip()] if requirements else [], 'location': 'Remote', 'company': 'Unknown', 'description': job_title.strip(), 'experience_required': 5, 'salary_range': 'Unknown' } resume_skills = [ "Python", "Docker", "AWS", "PostgreSQL", "REST API", "Microservices", "Git", "Kubernetes", "Machine Learning" ] score = classifier.classify_job(job, resume_skills) details = classifier.get_match_details(job, resume_skills) output = f"āœ… **Job Classification**\n\n" output += f"**Job:** {job_title}\n\n" score_percent = score * 100 if score >= 0.8: emoji = "🟢" level = "EXCELLENT" elif score >= 0.6: emoji = "🟔" level = "GOOD" elif score >= 0.4: emoji = "🟠" level = "FAIR" else: emoji = "šŸ”“" level = "POOR" output += f"{emoji} **Score:** {score_percent:.1f}% ({level})\n\n" output += f"āœ“ **Matching:** {', '.join(details.get('matching_skills', []))}\n" output += f"āœ— **Missing:** {', '.join(details.get('missing_skills', []))}\n" return output except Exception as e: logger.error(f"Error in classification: {e}\n{traceback.format_exc()}") return f"āŒ **Error:** {str(e)}\n\nPlease check your inputs." # ============================================================================ # GRADIO INTERFACE # ============================================================================ logger.info("šŸŽØ Building Gradio interface...") with gr.Blocks(title="Job Application Agent", theme=gr.themes.Soft()) as demo: gr.Markdown(""" # šŸ¤– Job Application Agent ### AI-Powered Job Search & Resume Customization **Powered by MAYINI Framework - Custom Transformer ML Model** """) with gr.Tab("šŸ” Search & Match Jobs"): gr.Markdown("Find jobs matching your skills using AI-powered matching.") with gr.Row(): with gr.Column(): search_keywords = gr.Textbox( label="Keywords", placeholder="python docker aws", value="python" ) search_location = gr.Textbox( label="Location", placeholder="Remote", value="Remote" ) search_num = gr.Slider( minimum=1, maximum=20, value=5, step=1, label="Number of Jobs" ) search_btn = gr.Button("šŸ” Search", variant="primary") with gr.Column(): search_output = gr.Markdown(value="### Results will appear here...") search_btn.click( fn=search_jobs_interface, inputs=[search_keywords, search_location, search_num], outputs=search_output ) with gr.Tab("šŸ“„ Customize Resume"): gr.Markdown("Tailor your resume for specific job opportunities.") with gr.Row(): with gr.Column(): customize_title = gr.Textbox( label="Job Title", placeholder="Senior Python Developer" ) customize_company = gr.Textbox( label="Company", placeholder="Tech Inc" ) customize_req = gr.Textbox( label="Requirements", placeholder="Python, Docker, AWS", lines=2 ) customize_btn = gr.Button("✨ Customize", variant="primary") with gr.Column(): customize_output = gr.Markdown(value="### Customized resume will appear here...") customize_btn.click( fn=customize_resume_interface, inputs=[customize_title, customize_company, customize_req], outputs=customize_output ) with gr.Tab("šŸŽÆ Classify Job"): gr.Markdown("Check job relevance to your skills.") with gr.Row(): with gr.Column(): classify_title = gr.Textbox( label="Job Title", placeholder="ML Engineer" ) classify_req = gr.Textbox( label="Requirements", placeholder="Python, PyTorch", lines=2 ) classify_btn = gr.Button("šŸŽÆ Classify", variant="primary") with gr.Column(): classify_output = gr.Markdown(value="### Results will appear here...") classify_btn.click( fn=classify_job_interface, inputs=[classify_title, classify_req], outputs=classify_output ) with gr.Tab("ā„¹ļø About"): gr.Markdown(""" ## Job Application Agent Powered by **MAYINI Framework** - a custom Transformer-based ML model. **Features:** - šŸ” AI-powered job search - šŸ“„ Resume customization - šŸŽÆ Job relevance scoring **MAYINI Specs:** - Vocabulary: 5,000 tokens - Hidden Dims: 256 - Heads: 8 - Layers: 4 - Parameters: ~3.5M **Repository:** [GitHub](https://github.com/907-bot/Job-Application-Agent) """) logger.info("āœ“ Gradio interface built successfully\n") # ============================================================================ # LAUNCH - FIXED FOR GRADIO COMPATIBILITY # ============================================================================ if __name__ == "__main__": logger.info("šŸš€ LAUNCHING APPLICATION") logger.info("Access at: http://0.0.0.0:7860\n") try: # FIXED: Removed invalid 'concurrency_count' parameter # Using only valid parameters for Gradio 4.0+ demo.launch( server_name="0.0.0.0", server_port=7860, show_error=True, share=False ) except Exception as e: logger.error(f"Launch failed: {e}\n{traceback.format_exc()}") sys.exit(1)