Spaces:
Sleeping
Sleeping
import streamlit as st | |
import os | |
import re | |
from groq import Groq | |
import json | |
# Set up Groq client | |
client = Groq(api_key="") | |
# Define main function | |
def main(): | |
st.title("AI-powered Resume Scanner") | |
# File upload | |
uploaded_file = st.file_uploader("Upload a resume", type=["pdf", "docx"]) | |
# Job role input | |
job_role = st.text_input("Enter the job role") | |
if uploaded_file is not None and job_role: | |
# Process resume and get results | |
resume_text = parse_resume(uploaded_file) | |
if resume_text: | |
#st.write("Extracted Resume Text:") | |
#st.write(resume_text) | |
# Get resume analysis from the Groq model | |
name, degree, cgpa, skills, experience_score, ats_score = analyze_resume(resume_text, job_role) | |
# Display the results | |
st.write(f"**Candidate Name:** {name}") | |
st.write(f"**Degree:** {degree}") | |
st.write(f"**Latest CGPA/Percentage:** {cgpa}") | |
if skills: | |
st.write("**Skills:**") | |
for skill in skills: | |
st.write(f"- {skill}") | |
st.write(f"**Experience Score out of 10:** {experience_score}") | |
st.write(f"**ATS Score for {job_role} out of 10:** {ats_score}") | |
# Function to parse PDF/Word file | |
def parse_resume(uploaded_file): | |
# If PDF | |
if uploaded_file.type == "application/pdf": | |
from PyPDF2 import PdfReader | |
reader = PdfReader(uploaded_file) | |
text = "" | |
for page in reader.pages: | |
text += page.extract_text() | |
return text | |
# If Word | |
elif uploaded_file.type == "application/vnd.openxmlformats-officedocument.wordprocessingml.document": | |
from docx import Document | |
doc = Document(uploaded_file) | |
text = "\n".join([para.text for para in doc.paragraphs]) | |
return text | |
else: | |
st.error("Unsupported file type!") | |
return None | |
# Function to analyze resume using Groq API and LLaMA 3.1 | |
# Function to analyze resume using Groq API and LLaMA 3.1 | |
import json | |
# Function to analyze resume using Groq API and LLaMA 3.1 | |
def analyze_resume(text, job_role): | |
# Construct prompt for Groq API | |
prompt = ( | |
f"Extract the following details from the given resume text: \n" | |
f"1. Candidate's Name \n" | |
f"2. Latest Education CGPA or Percentage \n" | |
f"3. List of Skills \n" | |
f"4. Rate experience (projects, internships) on a scale from 0 to 10 \n" | |
f"5. Provide an ATS score for the job role: {job_role}\n" | |
f"Resume Text: {text}\n" | |
f"Format your response in a JSON object with the following structure: \n" | |
f"{{\n" | |
f' "name": "Candidate Name",\n' | |
f' "Degree": "Latest Education qualification or grade:",\n' | |
f' "cgpa": "Latest Education CGPA or Percentage",\n' | |
f' "skills": ["List of skills"],\n' | |
f' "experience_score": "Experience Score (0 to 10)",\n' | |
f' "ats_score": "ATS Score for the job role"\n' | |
f"}}" | |
) | |
# Call Groq API for chat completion | |
chat_completion = client.chat.completions.create( | |
messages=[{"role": "user", "content": prompt}], | |
model="llama3-8b-8192", | |
temperature=0.7, | |
max_tokens=1024, | |
top_p=1, | |
stream=False | |
) | |
# Get the raw output from the LLM | |
output = chat_completion.choices[0].message.content | |
# Clean up the output to avoid any parsing issues | |
cleaned_output = re.search(r'{[^}]*}', output).group() | |
# Try parsing the cleaned JSON | |
try: | |
response_data = json.loads(cleaned_output) | |
except json.JSONDecodeError: | |
st.error("Failed to parse response from model.") | |
st.write("Model Output:") | |
st.write(cleaned_output) | |
return None, None, None, None, None | |
# Extract information from the parsed JSON | |
name = response_data.get("name", "Name not found") | |
degree = response_data.get("Degree", "Degree not found") | |
cgpa = response_data.get("cgpa", "CGPA/Percentage not found") | |
skills = response_data.get("skills", "Skills not found") | |
experience_score = response_data.get("experience_score", "Experience score not found") | |
ats_score = response_data.get("ats_score", "ATS score not found") | |
return name, degree, cgpa, skills, experience_score, ats_score | |
# Function to extract the candidate's name | |
def extract_name(text): | |
name_pattern = re.compile(r"(Name:?\s*)([A-Z][a-z]+(?:\s[A-Z][a-z]+)*)") | |
match = name_pattern.search(text) | |
if match: | |
return match.group(2) | |
return "Name not found" | |
# Function to extract CGPA or Percentage | |
def extract_cgpa(text): | |
cgpa_pattern = re.compile(r"(\bCGPA\b|\bGPA\b|\bPercentage\b):?\s*(\d+\.?\d*)") | |
match = cgpa_pattern.search(text) | |
if match: | |
return match.group(2) | |
return "CGPA/Percentage not found" | |
# Function to extract skills | |
def extract_skills(text): | |
skills_pattern = re.compile(r"Skills:?\s*(.*?)(?:Experience|Education|$)", re.DOTALL) | |
match = skills_pattern.search(text) | |
if match: | |
skills = match.group(1) | |
return [skill.strip() for skill in skills.split(",")] | |
return "Skills not found" | |
# Function to extract experience score | |
def extract_experience_score(text): | |
experience_pattern = re.compile(r"Experience Score:?\s*(\d{1,2})") | |
match = experience_pattern.search(text) | |
if match: | |
return int(match.group(1)) | |
# Heuristic: If no explicit experience score is given, infer it based on keywords | |
experience_keywords = ["internship", "project", "work experience", "employment"] | |
experience_count = sum(text.lower().count(keyword) for keyword in experience_keywords) | |
return min(10, experience_count) # Cap at 10 | |
# Function to extract ATS score | |
def extract_ats_score(text): | |
ats_pattern = re.compile(r"ATS Score:?\s*(\d+\.?\d*)") | |
match = ats_pattern.search(text) | |
if match: | |
return float(match.group(1)) | |
# Heuristic to generate a score based on skill-job match | |
return generate_ats_score(text) | |
# Heuristic function to generate ATS score | |
def generate_ats_score(text): | |
# Just a dummy heuristic for now | |
skills = extract_skills(text) | |
if not skills: | |
return 0 | |
required_skills = ["Python", "Machine Learning", "Data Analysis"] # Add job role specific required skills | |
match_count = sum(1 for skill in required_skills if skill.lower() in [s.lower() for s in skills]) | |
return round((match_count / len(required_skills)) * 10, 2) | |
if __name__ == "__main__": | |
main() | |