Spaces:
Sleeping
Sleeping
| 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) |