Spaces:
Runtime error
Runtime error
| # Imports | |
| import requests | |
| from bs4 import BeautifulSoup | |
| import pandas as pd | |
| from time import sleep | |
| import random | |
| from datetime import datetime | |
| import json | |
| import os | |
| from pathlib import Path | |
| import re | |
| from docx import Document | |
| import logging | |
| from typing import List, Dict, Any | |
| from openai import OpenAI | |
| import tiktoken | |
| from dotenv import load_dotenv | |
| import streamlit as st | |
| # OpenAI model | |
| model = "gpt-4o-mini" | |
| class LLMJobAssistant: | |
| def __init__(self): | |
| self.headers = { | |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' | |
| } | |
| self.jobs = [] | |
| self.setup_logging() | |
| self.load_config() | |
| self.client = OpenAI(api_key=os.getenv('OPENAI_API_KEY')) | |
| with open('templates/base_resume.txt', 'r') as f: | |
| self.resume_text = f.read() | |
| def analyze_job_posting(self, job_description: str) -> Dict[str, Any]: | |
| """Use LLM to analyze job posting and extract key information""" | |
| prompt = f""" \ | |
| Analyze this job posting and extract key information: \ | |
| {job_description} \ | |
| Return a JSON object with: | |
| 1. Required skills | |
| 2. Required experience | |
| 3. Estimated salary range | |
| 4. Key responsibilities | |
| 5. Match score (0-100) with this resume: | |
| {self.resume_text} \ | |
| Also include a boolean 'should_apply' based on match score > 70% \ | |
| """ | |
| response = self.client.chat.completions.create( | |
| model=model, | |
| messages=[ | |
| {"role": "user", "content": prompt} | |
| ], | |
| response_format={"type": "json_object"} | |
| ) | |
| return json.loads(response.choices[0].message.content) | |
| def generate_custom_cover_letter(self, job_info: Dict[str, Any], company_name: str) -> str: | |
| """Generate a customized cover letter using an LLM""" | |
| prompt = f""" \ | |
| Write a professional cover letter for a {job_info['title']} position at {company_name} \ | |
| Use these details from my resume: | |
| {self.resume_text} \ | |
| And these job requirements: | |
| {json.dumps(job_info['requirements'], indent=2)} \ | |
| Focus on: | |
| 1. Specific matching experiences | |
| 2. Relevant projects and achievements | |
| 3. Why I'm interested in this role and companhy | |
| 4. My background in languages and AI/ML | |
| Tone should be professional but conversational. | |
| """ | |
| response = self.client.chat.completions.create( | |
| model=model, | |
| messages= [ | |
| {"role": "user", "content": prompt} | |
| ] | |
| ) | |
| return response.choices[0].message.content | |
| def tailor_resume(self, job_info: Dict[str, Any]) -> str: | |
| """Use LLMs to suggest resume tailoring for a specific job""" | |
| prompt = f""" \ | |
| Suggest specific modifications to this resume for a {job_info['title']} position. \ | |
| Current resume: | |
| {self.resume_text} \ | |
| Job requirements: \ | |
| {json.dumps(job_info['requirements'], indent=2)} \ | |
| Return specific suggestions for: | |
| 1. Skills to emphasize | |
| 2. Experiences to highlight | |
| 3. Projects to feature | |
| 4. Keywords to add | |
| """ | |
| response = self.client.chat.completions.create( | |
| model=model, | |
| messages= [ | |
| {"role": "user", "content": prompt} | |
| ] | |
| ) | |
| return response.choices[0].message.content | |
| def scrape_job_description(self, url: str) -> str: | |
| """Scrape full job description from posting URL""" | |
| try: | |
| response = requests.get(url, headers=self.headers) | |
| soup = BeautifulSoup(response.text, "html.parser") | |
| # Detect job board from URL | |
| if 'linkedin.com' in url: | |
| description = soup.find('div', class_='description__text') | |
| elif 'indeed.com' in url: | |
| description = soup.find('div', id='jobDescriptionText') | |
| elif 'glassdoor.com' in url: | |
| description = soup.find('div', class_='jobDescriptionContent') | |
| else: | |
| # Default fallback - look for common job description containers | |
| description = ( | |
| soup.find('div', class_='job-description') or | |
| soup.find('div', class_='job_description') or | |
| soup.find('div', {'class': lambda x: x and 'description' in x.lower()}) | |
| ) | |
| return description.text.strip() if description else "" | |
| except Exception as e: | |
| logging.error(f"Error scraping job description: {str(e)}") | |
| return "" | |
| def process_job_posting(self, job: Dict[str, Any]): | |
| """Process a single job posting with LLM analysis""" | |
| # Scrape full job description | |
| full_description = self.scrape_job_description(job['url']) | |
| if not full_description: | |
| return None | |
| # Analyze job posting | |
| analysis = self.analyze_job_posting(full_description) | |
| # If there is a good match, generate materials | |
| if analysis.get('should_apply', False): | |
| cover_letter = self.generate_custom_cover_letter(analysis, job['company']) | |
| resume_suggestions = self.tailor_resume(analysis) | |
| return { | |
| **job, | |
| 'analysis': analysis, | |
| 'cover_letter': cover_letter, | |
| 'resume_suggestions': resume_suggestions, | |
| 'full_description': full_description | |
| } | |
| return None | |
| def run_enhanced_job_search(self): | |
| """Run job search with LLM enhancements""" | |
| # First run basic job search | |
| jobs_df = self.run_job_search() | |
| # Process each job with the LLM | |
| enhanced_jobs = [] | |
| for _, job in jobs_df.iterrows(): | |
| processed_job = self.process_job_posting(job.to_dict()) | |
| if processed_job: | |
| enhanced_jobs.append(processed_job) | |
| sleep(random.uniform(1, 2)) # Rate limiting | |
| # Convert to DataFrame | |
| enhanced_df = pd.DataFrame(enhanced_jobs) | |
| # Save detailed results | |
| enhanced_df.to_pickle('enhanced_jobs.pkl') # Save full data | |
| return enhanced_df | |
| def generate_application_strategy(self, job_data: Dict[str, Any]) -> str: | |
| """Generate application strategy using an LLM""" | |
| prompt = f""" \ | |
| Create an application strategy for this job: | |
| Job Title: {job_data['title']} \ | |
| Company: {job_data['company']} \ | |
| Match Score: {job_data['analysis']['match_score']} \ | |
| Include: | |
| 1. Best approach for application (direct, referral, etc.) | |
| 2. Key points to emphasize in interview | |
| 3. Potential questions to ask | |
| 4. Company research suggestions | |
| 5. Follow-up strategy | |
| """ | |
| response = self.client.chat.completions.create( | |
| model=model, | |
| messages = [ | |
| {"role": "user", "content": prompt} | |
| ] | |
| ) | |
| return response.choices[0].message.content | |
| def main(): | |
| # Load environment variables | |
| os.environ['PATH'] += f':{os.path.expanduser("~/.cargo/bin")}' | |
| load_dotenv() | |
| # Initialize assistant | |
| assistant = LLMJobAssistant() | |
| st.title("LLM-Enhanced Job Application Assistant") | |
| st.spinner("Running enhanced job search...") | |
| jobs_df = assistant.run_enhanced_job_search() | |
| st.write("Job Search Summary:") | |
| st.write(f"Total matching jobs found: {len(jobs_df)}") | |
| st.write("Top matching positions:") | |
| top_matches = jobs_df.nlargest(5, 'analysis.match_score') | |
| for _, job in top_matches.iterrows(): | |
| st.write(f"\n{job['title']} at {job['company']}") | |
| st.write(f"Match Score: {job['analysis']['match_score']}") | |
| st.write(f"Estimated Salary: {job['analysis']['estimated_salary_range']}") | |
| # Generate application strategies for top matches | |
| st.write("Generating application strategies for top matches...") | |
| for _, job in top_matches.iterrows(): | |
| strategy = assistant.generate_application_strategy(job.to_dict()) | |
| # Save strategy to file | |
| filename = f"strategies/{job['company']}_{job['title']}.txt".replace(' ', '_') | |
| os.makedirs('strategies', exist_ok=True) | |
| with open(filename, 'w') as f: | |
| f.write(strategy) | |
| st.dataframe(jobs_df) | |
| if __name__ == 'main': | |
| main() |