Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from crewai import Agent, Task, Crew, Process
|
3 |
+
from langchain_groq import ChatGroq # Import ChatGroq
|
4 |
+
import os
|
5 |
+
import fitz # PyMuPDF for PDF handling
|
6 |
+
import tempfile
|
7 |
+
from dotenv import load_dotenv
|
8 |
+
import textwrap
|
9 |
+
|
10 |
+
# Load environment variables (API Keys, etc.)
|
11 |
+
load_dotenv()
|
12 |
+
|
13 |
+
# --- Helper Functions ---
|
14 |
+
|
15 |
+
def read_pdf(file):
|
16 |
+
"""Reads text from a PDF file."""
|
17 |
+
try:
|
18 |
+
with fitz.open(stream=file.read(), filetype="pdf") as doc:
|
19 |
+
text = ""
|
20 |
+
for page in doc:
|
21 |
+
text += page.get_text()
|
22 |
+
return text
|
23 |
+
except Exception as e:
|
24 |
+
st.error(f"Error reading PDF: {e}")
|
25 |
+
return None
|
26 |
+
|
27 |
+
def wrap_text(text, width=80):
|
28 |
+
"""Wraps text to a specified width."""
|
29 |
+
lines = text.split('\n')
|
30 |
+
wrapped_lines = []
|
31 |
+
for line in lines:
|
32 |
+
wrapped_lines.extend(textwrap.wrap(line, width=width))
|
33 |
+
return '\n'.join(wrapped_lines)
|
34 |
+
|
35 |
+
|
36 |
+
# --- CrewAI Agents and Tasks ---
|
37 |
+
|
38 |
+
def create_crew(job_description, resume_text):
|
39 |
+
"""Creates the CrewAI crew with agents and tasks."""
|
40 |
+
|
41 |
+
# Groq LLM configuration using ChatGroq
|
42 |
+
llm = ChatGroq(
|
43 |
+
model_name="gemma-2b-it",#change this to the model you want
|
44 |
+
groq_api_key=os.environ.get("GROQ_API_KEY"), # Get API key from environment
|
45 |
+
temperature=0.7,
|
46 |
+
)
|
47 |
+
|
48 |
+
# 1. Resume Analyzer Agent
|
49 |
+
resume_analyzer = Agent(
|
50 |
+
role='Resume Analysis Expert',
|
51 |
+
goal='Thoroughly analyze the provided resume and identify its strengths and weaknesses.',
|
52 |
+
backstory="""You are a seasoned resume analyst with years of experience
|
53 |
+
in helping job seekers optimize their resumes. You are adept at
|
54 |
+
identifying key skills, experiences, and formatting issues.""",
|
55 |
+
verbose=True,
|
56 |
+
allow_delegation=False,
|
57 |
+
llm=llm # Use the ChatGroq instance
|
58 |
+
)
|
59 |
+
|
60 |
+
# 2. Job Description Analyzer Agent
|
61 |
+
job_analyzer = Agent(
|
62 |
+
role='Job Description Expert',
|
63 |
+
goal='Analyze the job description and extract key requirements, skills, and keywords.',
|
64 |
+
backstory="""You are a highly skilled job description analyst.
|
65 |
+
You excel at identifying the core requirements, desired skills, and
|
66 |
+
important keywords from any job posting.""",
|
67 |
+
verbose=True,
|
68 |
+
allow_delegation=False,
|
69 |
+
llm=llm
|
70 |
+
)
|
71 |
+
|
72 |
+
# 3. Resume Improvement Suggestor Agent
|
73 |
+
improvement_suggestor = Agent(
|
74 |
+
role='Resume Improvement Specialist',
|
75 |
+
goal='Provide specific, actionable suggestions to improve the resume based on the job description.',
|
76 |
+
backstory="""You are a master resume writer and career coach. You
|
77 |
+
are known for your ability to craft compelling resumes that highlight
|
78 |
+
a candidate's strengths and align them perfectly with job requirements.
|
79 |
+
You provide concrete, easy-to-implement suggestions.""",
|
80 |
+
verbose=True,
|
81 |
+
allow_delegation=False,
|
82 |
+
llm=llm
|
83 |
+
)
|
84 |
+
|
85 |
+
# --- Tasks ---
|
86 |
+
|
87 |
+
# Task 1: Analyze the Resume
|
88 |
+
task_analyze_resume = Task(
|
89 |
+
description=f"""Analyze the following resume content and identify key skills, experiences,
|
90 |
+
and potential areas for improvement. Focus on the overall structure, clarity,
|
91 |
+
and impact of the resume. Output should be a structured report, not just raw thoughts.
|
92 |
+
Resume:
|
93 |
+
--------------
|
94 |
+
{resume_text}
|
95 |
+
--------------
|
96 |
+
""",
|
97 |
+
agent=resume_analyzer
|
98 |
+
)
|
99 |
+
|
100 |
+
# Task 2: Analyze the Job Description
|
101 |
+
task_analyze_job_description = Task(
|
102 |
+
description=f"""Analyze the following job description and extract the key requirements,
|
103 |
+
desired skills, preferred qualifications, and any important keywords.
|
104 |
+
Be specific and comprehensive in your analysis. Output a structured summary.
|
105 |
+
Job Description:
|
106 |
+
--------------
|
107 |
+
{job_description}
|
108 |
+
--------------
|
109 |
+
""",
|
110 |
+
agent=job_analyzer
|
111 |
+
)
|
112 |
+
|
113 |
+
# Task 3: Suggest Improvements
|
114 |
+
task_suggest_improvements = Task(
|
115 |
+
description=f"""Based on the analysis of the resume and the job description,
|
116 |
+
provide specific and actionable suggestions to improve the resume. Address:
|
117 |
+
1. **Content:** Suggest additions, deletions, or modifications to the resume content to better match the job requirements.
|
118 |
+
2. **Keywords:** Identify keywords from the job description that should be incorporated into the resume.
|
119 |
+
3. **Formatting:** Suggest any formatting changes to improve readability and impact.
|
120 |
+
4. **Overall Strategy:** Provide an overall strategy for tailoring the resume to the specific job.
|
121 |
+
|
122 |
+
The resume analysis is: {task_analyze_resume.output}
|
123 |
+
The job description analysis is: {task_analyze_job_description.output}
|
124 |
+
""",
|
125 |
+
agent=improvement_suggestor
|
126 |
+
)
|
127 |
+
|
128 |
+
# --- Crew ---
|
129 |
+
crew = Crew(
|
130 |
+
agents=[resume_analyzer, job_analyzer, improvement_suggestor],
|
131 |
+
tasks=[task_analyze_resume, task_analyze_job_description, task_suggest_improvements],
|
132 |
+
verbose=True, # You can set this to 1 or 2 for different levels of verbosity
|
133 |
+
process=Process.sequential # Tasks are executed in the defined order
|
134 |
+
)
|
135 |
+
|
136 |
+
return crew
|
137 |
+
|
138 |
+
# --- Streamlit App ---
|
139 |
+
|
140 |
+
st.title("Resume Tailoring Assistant")
|
141 |
+
|
142 |
+
# Input: Job Description
|
143 |
+
job_description = st.text_area("Paste the Job Description Here:", height=200)
|
144 |
+
|
145 |
+
# Input: Resume (PDF Upload)
|
146 |
+
resume_file = st.file_uploader("Upload Your Resume (PDF)", type="pdf")
|
147 |
+
|
148 |
+
if st.button("Tailor Resume"):
|
149 |
+
if job_description and resume_file:
|
150 |
+
with st.spinner("Analyzing and generating suggestions..."):
|
151 |
+
resume_text = read_pdf(resume_file)
|
152 |
+
if resume_text:
|
153 |
+
# Create and run the Crew
|
154 |
+
crew = create_crew(job_description, resume_text)
|
155 |
+
result = crew.kickoff()
|
156 |
+
st.subheader("Suggested Improvements:")
|
157 |
+
st.write(wrap_text(result)) # Wrap the output for better readability
|
158 |
+
else:
|
159 |
+
st.error("Failed to read the resume content.")
|
160 |
+
|
161 |
+
else:
|
162 |
+
st.warning("Please provide both the job description and your resume.")
|