1qwsd's picture
Update app.py
c89ab4a verified
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)