import os import gradio as gr import requests import PyPDF2 import spacy from transformers import pipeline # Load spaCy for NER tasks nlp = spacy.load("en_core_web_sm") # Summarization model summarizer = pipeline("summarization", model="facebook/bart-large-cnn") # Sentiment analysis model sentiment_analyzer = pipeline("sentiment-analysis", model="distilbert-base-uncased-finetuned-sst-2-english") # Set up your Groq API endpoint and API key GROQ_API_URL = "https://api.groq.com/v1/models/llama" # Update this if needed GROQ_API_KEY = "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" # Replace with your actual API key def extract_text_from_pdf(file): """Extract text from uploaded PDF file.""" if file is None: return "" try: pdf_reader = PyPDF2.PdfReader(file) text = "" for page in pdf_reader.pages: page_text = page.extract_text() or "" text += page_text return text except Exception as e: return f"Error extracting PDF text: {str(e)}" def extract_text_from_file(file): """Extract text from uploaded file (PDF or TXT).""" if file is None: return "" if file.name.endswith('.pdf'): return extract_text_from_pdf(file) elif file.name.endswith('.txt'): return file.read().decode('utf-8') else: return "Unsupported file format. Please upload PDF or TXT files only." def extract_skills(text): """Extract skills from text using a pre-trained NER model.""" doc = nlp(text) skills = [ent.text for ent in doc.ents if ent.label_ == "SKILL"] return list(set(skills)) def extract_education_and_experience(text): """Extract education and experience information from text using NER.""" doc = nlp(text) education = [ent.text for ent in doc.ents if ent.label_ in ["EDUCATION", "DEGREE"]] experience = [ent.text for ent in doc.ents if ent.label_ == "EXPERIENCE"] return { 'education': list(set(education)), 'experience': list(set(experience)) } def calculate_match_percentage(resume_skills, job_skills): """Calculate the match percentage between resume skills and job requirements.""" if not job_skills: return 0 matching_skills = set(resume_skills).intersection(set(job_skills)) return (len(matching_skills) / len(job_skills)) * 100 def call_groq_api(prompt): """Call the Groq API with the prompt and return the response.""" headers = { "Authorization": f"Bearer {GROQ_API_KEY}", "Content-Type": "application/json" } payload = { "model": "llama3-8b-8192", # Use the specified LLaMA model "prompt": prompt, "max_tokens": 150 # Adjust as needed } try: response = requests.post(GROQ_API_URL, headers=headers, json=payload) if response.status_code == 200: return response.json().get("output", "No output received.") else: return f"API call failed with status {response.status_code}: {response.text}" except requests.exceptions.RequestException as e: return f"Request failed: {str(e)}" def summarize_text(text, max_length=100): """Summarize the input text using the summarization model.""" return summarizer(text, max_length=max_length, min_length=30, do_sample=False)[0]['summary_text'] def sentiment_analysis(text): """Perform sentiment analysis on the given text.""" return sentiment_analyzer(text)[0] def analyze_resume_and_job(resume_file, job_desc_file): """Main function to analyze resume and job description.""" try: # Extract text from files resume_text = extract_text_from_file(resume_file) job_desc_text = extract_text_from_file(job_desc_file) if not resume_text or not job_desc_text: return { "error": "Could not extract text from one or both files" } # Summarize resume and job description resume_summary = summarize_text(resume_text) job_desc_summary = summarize_text(job_desc_text) # Perform sentiment analysis on the resume summary resume_sentiment = sentiment_analysis(resume_summary) # Extract information from resume resume_skills = extract_skills(resume_text) resume_info = extract_education_and_experience(resume_text) # Extract information from job description job_skills = extract_skills(job_desc_text) job_info = extract_education_and_experience(job_desc_text) # Calculate match percentages skills_match = calculate_match_percentage(resume_skills, job_skills) # Prepare input for LLaMA via Groq API input_prompt = f"Analyze the following resume: {resume_text[:300]} and job description: {job_desc_text[:300]}." # Call Groq API to analyze using LLaMA llama_analysis = call_groq_api(input_prompt) # Prepare analysis results summary = f""" ### Summary Analysis - Overall Skills Match: {skills_match:.1f}% - Experience Found: {', '.join(resume_info['experience'])} - Education Found: {', '.join(resume_info['education'])} """ skills = f""" ### Skills Analysis Resume Skills: {', '.join(resume_skills)} Required Skills: {', '.join(job_skills)} Missing Skills: {', '.join(set(job_skills) - set(resume_skills))} """ qualifications = f""" ### Qualifications Education Found: {', '.join(resume_info['education'])} Required Education: {', '.join(job_info['education'])} """ sentiment = f""" ### Sentiment Analysis Resume Sentiment: {resume_sentiment['label']} (Confidence: {resume_sentiment['score']:.2f}) """ # Generate recommendation based on skills match recommendation = "Recommendation based on skills match and experience." if skills_match >= 70: recommendation = "Strong Match - Recommended for interview." elif skills_match >= 50: recommendation = "Moderate Match - Consider for interview with focus on missing skills." else: recommendation = "Low Match - May not meet core requirements." recommendation = f""" ### Recommendation {recommendation} """ return { "summary": summary.strip(), "skills": skills.strip(), "qualifications": qualifications.strip(), "recommendation": recommendation.strip(), "llama_analysis": llama_analysis.strip(), "sentiment": sentiment.strip(), "resume_summary": resume_summary.strip(), "job_summary": job_desc_summary.strip() } except Exception as e: return { "error": f"Analysis failed: {str(e)}" } # Create Gradio interface def create_interface(): with gr.Blocks(title="Resume Analyzer", theme=gr.themes.Soft()) as demo: gr.Markdown("# Smart Resume Analyzer") gr.Markdown("Upload your resume and job description for instant analysis") with gr.Row(): resume_input = gr.File(label="Upload Resume (PDF/TXT)") job_desc_input = gr.File(label="Upload Job Description (PDF/TXT)") analyze_button = gr.Button("Analyze") with gr.Tabs(): with gr.TabItem("Summary"): summary_output = gr.Markdown() with gr.TabItem("Skills Analysis"): skills_output = gr.Markdown() with gr.TabItem("Qualifications"): qualifications_output = gr.Markdown() with gr.TabItem("Recommendation"): recommendation_output = gr.Markdown() with gr.TabItem("LLaMA Analysis"): llama_output = gr.Markdown() with gr.TabItem("Sentiment Analysis"): sentiment_output = gr.Markdown() with gr.TabItem("Resume Summary"): resume_summary_output = gr.Markdown() with gr.TabItem("Job Description Summary"): job_summary_output = gr.Markdown() def analyze(resume_file, job_desc_file): if not resume_file or not job_desc_file: return "Please upload both resume and job description." analysis_results = analyze_resume_and_job(resume_file, job_desc_file) summary_output.update(analysis_results.get("summary", "")) skills_output.update(analysis_results.get("skills", "")) qualifications_output.update(analysis_results.get("qualifications", "")) recommendation_output.update(analysis_results.get("recommendation", "")) llama_output.update(analysis_results.get("llama_analysis", "")) sentiment_output.update(analysis_results.get("sentiment", "")) resume_summary_output.update(analysis_results.get("resume_summary", "")) job_summary_output.update(analysis_results.get("job_summary", "")) analyze_button.click(analyze, inputs=[resume_input, job_desc_input]) return demo # Launch Gradio app if __name__ == "__main__": create_interface().launch()