final code changes

This commit is contained in:
Madhu 2024-12-21 11:54:53 +05:30
parent 42cee9621a
commit abc0c10e55

View file

@ -5,6 +5,7 @@ import json
import requests import requests
import PyPDF2 import PyPDF2
from datetime import datetime, timedelta from datetime import datetime, timedelta
import pytz
import streamlit as st import streamlit as st
from phi.agent import Agent from phi.agent import Agent
@ -13,33 +14,14 @@ from phi.tools.email import EmailTools
from phi.tools.zoom import ZoomTool from phi.tools.zoom import ZoomTool
from phi.utils.log import logger from phi.utils.log import logger
from dotenv import load_dotenv from dotenv import load_dotenv
from streamlit_pdf_viewer import pdf_viewer
load_dotenv() load_dotenv()
# Constants
FROM_EMAIL = "ryomensukuna64@gmail.com"
ACCOUNT_ID = os.getenv("ZOOM_ACCOUNT_ID")
CLIENT_ID = os.getenv("ZOOM_CLIENT_ID")
CLIENT_SECRET = os.getenv("ZOOM_CLIENT_SECRET")
class CustomZoomTool(ZoomTool): class CustomZoomTool(ZoomTool):
def __init__( def __init__(self, *, account_id: Optional[str] = None, client_id: Optional[str] = None, client_secret: Optional[str] = None, name: str = "zoom_tool"):
self, super().__init__(account_id=account_id, client_id=client_id, client_secret=client_secret, name=name)
account_id: Optional[str] = None,
client_id: Optional[str] = None,
client_secret: Optional[str] = None,
name: str = "zoom_tool",
):
super().__init__(
account_id=account_id,
client_id=client_id,
client_secret=client_secret,
name=name
)
self.token_url = "https://zoom.us/oauth/token" self.token_url = "https://zoom.us/oauth/token"
self.access_token = None self.access_token = None
self.token_expires_at = 0 self.token_expires_at = 0
@ -47,16 +29,12 @@ class CustomZoomTool(ZoomTool):
def get_access_token(self) -> str: def get_access_token(self) -> str:
if self.access_token and time.time() < self.token_expires_at: if self.access_token and time.time() < self.token_expires_at:
return str(self.access_token) return str(self.access_token)
headers = {"Content-Type": "application/x-www-form-urlencoded"} headers = {"Content-Type": "application/x-www-form-urlencoded"}
data = {"grant_type": "account_credentials", "account_id": self.account_id} data = {"grant_type": "account_credentials", "account_id": self.account_id}
try: try:
response = requests.post( response = requests.post(self.token_url, headers=headers, data=data, auth=(self.client_id, self.client_secret))
self.token_url,
headers=headers,
data=data,
auth=(self.client_id, self.client_secret) # Use basic auth
)
response.raise_for_status() response.raise_for_status()
token_info = response.json() token_info = response.json()
@ -64,7 +42,6 @@ class CustomZoomTool(ZoomTool):
expires_in = token_info["expires_in"] expires_in = token_info["expires_in"]
self.token_expires_at = time.time() + expires_in - 60 self.token_expires_at = time.time() + expires_in - 60
# Update this line to use the helper method
self._set_parent_token(str(self.access_token)) self._set_parent_token(str(self.access_token))
return str(self.access_token) return str(self.access_token)
@ -114,11 +91,9 @@ ROLE_REQUIREMENTS: Dict[str, str] = {
def init_session_state() -> None: def init_session_state() -> None:
"""Initialize only necessary session state variables.""" """Initialize only necessary session state variables."""
defaults = { defaults = {
'candidate_email': "", 'candidate_email': "", 'openai_api_key': "", 'resume_text': "", 'analysis_complete': False,
'openai_api_key': "", 'is_selected': False, 'zoom_account_id': "", 'zoom_client_id': "", 'zoom_client_secret': "",
'resume_text': "", 'email_sender': "", 'email_passkey': "", 'company_name': ""
'analysis_complete': False,
'is_selected': False
} }
for key, value in defaults.items(): for key, value in defaults.items():
if key not in st.session_state: if key not in st.session_state:
@ -155,9 +130,9 @@ def create_email_agent() -> Agent:
), ),
tools=[EmailTools( tools=[EmailTools(
receiver_email=st.session_state.candidate_email, receiver_email=st.session_state.candidate_email,
sender_email=FROM_EMAIL, sender_email=st.session_state.email_sender,
sender_name="AI Recruitment Team", sender_name=st.session_state.company_name,
sender_passkey=os.getenv("EMAIL_PASSKEY") sender_passkey=st.session_state.email_passkey
)], )],
description="You are a professional recruitment coordinator handling email communications.", description="You are a professional recruitment coordinator handling email communications.",
instructions=[ instructions=[
@ -166,7 +141,7 @@ def create_email_agent() -> Agent:
"Maintain a friendly yet professional tone", "Maintain a friendly yet professional tone",
"Always end emails with exactly: 'best,\nthe ai recruiting team'", "Always end emails with exactly: 'best,\nthe ai recruiting team'",
"Never include the sender's or receiver's name in the signature", "Never include the sender's or receiver's name in the signature",
"The name of the company is 'AI Recruiting Team'" f"The name of the company is '{st.session_state.company_name}'"
], ],
markdown=True, markdown=True,
show_tool_calls=True show_tool_calls=True
@ -174,8 +149,11 @@ def create_email_agent() -> Agent:
def create_scheduler_agent() -> Agent: def create_scheduler_agent() -> Agent:
zoom_tools = CustomZoomTool(
zoom_tools = CustomZoomTool(account_id=ACCOUNT_ID, client_id=CLIENT_ID, client_secret=CLIENT_SECRET) account_id=st.session_state.zoom_account_id,
client_id=st.session_state.zoom_client_id,
client_secret=st.session_state.zoom_client_secret
)
return Agent( return Agent(
name="Interview Scheduler", name="Interview Scheduler",
@ -216,8 +194,8 @@ def analyze_resume(
analyzer: Agent analyzer: Agent
) -> Tuple[bool, str]: ) -> Tuple[bool, str]:
try: try:
response = analyzer.run(f""" response = analyzer.run(
Please analyze this resume against the following requirements and provide your response in valid JSON format: f"""Please analyze this resume against the following requirements and provide your response in valid JSON format:
Role Requirements: Role Requirements:
{ROLE_REQUIREMENTS[role]} {ROLE_REQUIREMENTS[role]}
Resume Text: Resume Text:
@ -237,24 +215,16 @@ def analyze_resume(
4. Consider transferable skills from similar technologies 4. Consider transferable skills from similar technologies
5. Look for evidence of continuous learning and adaptability 5. Look for evidence of continuous learning and adaptability
Important: Return ONLY the JSON object without any markdown formatting or backticks. Important: Return ONLY the JSON object without any markdown formatting or backticks.
""") """
)
# Extract the assistant's message content
assistant_message = None
for message in response.messages:
if message.role == 'assistant':
assistant_message = message.content
break
assistant_message = next((msg.content for msg in response.messages if msg.role == 'assistant'), None)
if not assistant_message: if not assistant_message:
raise ValueError("No assistant message found in response.") raise ValueError("No assistant message found in response.")
result = json.loads(assistant_message.strip()) result = json.loads(assistant_message.strip())
if not isinstance(result, dict) or not all(k in result for k in ["selected", "feedback"]):
if not isinstance(result, dict): raise ValueError("Invalid response format")
raise ValueError("Response is not a JSON object")
if "selected" not in result or "feedback" not in result:
raise ValueError("Missing required fields in response")
return result["selected"], result["feedback"] return result["selected"], result["feedback"]
@ -301,29 +271,45 @@ def send_rejection_email(email_agent: Agent, to_email: str, role: str, feedback:
def schedule_interview(scheduler: Agent, candidate_email: str, email_agent: Agent, role: str) -> None: def schedule_interview(scheduler: Agent, candidate_email: str, email_agent: Agent, role: str) -> None:
""" """
Basic interview scheduler that creates a Zoom meeting and sends email details. Schedule interviews during business hours (9 AM - 5 PM IST).
Schedules interviews during business hours (9 AM - 5 PM EST).
""" """
try: try:
# Calculate tomorrow at 2 PM EST (instead of UTC) # Get current time in IST
tomorrow = datetime.now() + timedelta(days=1) ist_tz = pytz.timezone('Asia/Kolkata')
interview_time = tomorrow.replace(hour=14, minute=0, second=0, microsecond=0) current_time_ist = datetime.now(ist_tz)
tomorrow_ist = current_time_ist + timedelta(days=1)
interview_time = tomorrow_ist.replace(hour=11, minute=0, second=0, microsecond=0)
formatted_time = interview_time.strftime('%Y-%m-%dT%H:%M:%S')
# 1. Schedule the meeting with EST timezone specification
meeting_response = scheduler.run( meeting_response = scheduler.run(
f"Schedule a 60-minute meeting titled '{role} Technical Interview' " f"""Schedule a 60-minute technical interview with these specifications:
f"for tomorrow at 2 PM EST (Eastern Time) 2024 with attendee {candidate_email}. " - Title: '{role} Technical Interview'
f"Ensure the meeting is between 9 AM - 5 PM EST only." - Date: {formatted_time}
- Timezone: IST (India Standard Time)
- Attendee: {candidate_email}
Important Notes:
- The meeting must be between 9 AM - 5 PM IST
- Use IST (UTC+5:30) timezone for all communications
- Include timezone information in the meeting details
"""
) )
# 2. Send email notification
email_agent.run( email_agent.run(
f"Send an email to {candidate_email} about their scheduled interview for " f"""Send an interview confirmation email with these details:
f"the {role} position. Include the Zoom meeting link from: {meeting_response}. " - Role: {role} position
f"Make sure to specify that the time is in EST timezone." - Meeting Details: {meeting_response}
Important:
- Clearly specify that the time is in IST (India Standard Time)
- Ask the candidate to join 5 minutes early
- Include timezone conversion link if possible
- Ask him to be confident and not so nervous and prepare well for the interview
"""
) )
st.success("Interview scheduled successfully!") st.success("Interview scheduled successfully! Check your email for details.")
except Exception as e: except Exception as e:
logger.error(f"Error scheduling interview: {str(e)}") logger.error(f"Error scheduling interview: {str(e)}")
@ -331,42 +317,70 @@ def schedule_interview(scheduler: Agent, candidate_email: str, email_agent: Agen
def main() -> None: def main() -> None:
"""Main function to run the Streamlit application."""
st.title("AI Recruitment System") st.title("AI Recruitment System")
# Initialize session state
init_session_state() init_session_state()
# Sidebar for API key
with st.sidebar: with st.sidebar:
st.header("Configuration") st.header("Configuration")
api_key = st.text_input(
"Enter your OpenAI API key",
type="password",
value=st.session_state.openai_api_key,
help="Get your API key from platform.openai.com"
)
if api_key:
st.session_state.openai_api_key = api_key
# Main content # OpenAI Configuration
st.subheader("OpenAI Settings")
api_key = st.text_input("OpenAI API Key", type="password", value=st.session_state.openai_api_key, help="Get your API key from platform.openai.com")
if api_key: st.session_state.openai_api_key = api_key
st.subheader("Zoom Settings")
zoom_account_id = st.text_input("Zoom Account ID", type="password", value=st.session_state.zoom_account_id)
zoom_client_id = st.text_input("Zoom Client ID", type="password", value=st.session_state.zoom_client_id)
zoom_client_secret = st.text_input("Zoom Client Secret", type="password", value=st.session_state.zoom_client_secret)
st.subheader("Email Settings")
email_sender = st.text_input("Sender Email", value=st.session_state.email_sender, help="Email address to send from")
email_passkey = st.text_input("Email App Password", type="password", value=st.session_state.email_passkey, help="App-specific password for email")
company_name = st.text_input("Company Name", value=st.session_state.company_name, help="Name to use in email communications")
if zoom_account_id: st.session_state.zoom_account_id = zoom_account_id
if zoom_client_id: st.session_state.zoom_client_id = zoom_client_id
if zoom_client_secret: st.session_state.zoom_client_secret = zoom_client_secret
if email_sender: st.session_state.email_sender = email_sender
if email_passkey: st.session_state.email_passkey = email_passkey
if company_name: st.session_state.company_name = company_name
required_configs = {'OpenAI API Key': st.session_state.openai_api_key, 'Zoom Account ID': st.session_state.zoom_account_id,
'Zoom Client ID': st.session_state.zoom_client_id, 'Zoom Client Secret': st.session_state.zoom_client_secret,
'Email Sender': st.session_state.email_sender, 'Email Password': st.session_state.email_passkey,
'Company Name': st.session_state.company_name}
missing_configs = [k for k, v in required_configs.items() if not v]
if missing_configs:
st.warning(f"Please configure the following in the sidebar: {', '.join(missing_configs)}")
return
if not st.session_state.openai_api_key: if not st.session_state.openai_api_key:
st.warning("Please enter your OpenAI API key in the sidebar to continue.") st.warning("Please enter your OpenAI API key in the sidebar to continue.")
return return
# Role selection with requirements display role = st.selectbox("Select the role you're applying for:", ["ai_ml_engineer", "frontend_engineer", "backend_engineer"])
role = st.selectbox( with st.expander("View Required Skills", expanded=True): st.markdown(ROLE_REQUIREMENTS[role])
"Select the role you're applying for:",
["ai_ml_engineer", "frontend_engineer", "backend_engineer"]
)
# Display requirements for selected role
with st.expander("View Required Skills", expanded=True):
st.markdown(ROLE_REQUIREMENTS[role])
# Resume upload and processing
resume_file = st.file_uploader("Upload your resume (PDF)", type=["pdf"]) resume_file = st.file_uploader("Upload your resume (PDF)", type=["pdf"])
if resume_file and not st.session_state.resume_text: if resume_file:
st.subheader("Uploaded Resume")
col1, col2 = st.columns([4, 1])
with col1:
import tempfile, os
with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') as tmp_file:
tmp_file.write(resume_file.read())
tmp_file_path = tmp_file.name
resume_file.seek(0)
try: pdf_viewer(tmp_file_path)
finally: os.unlink(tmp_file_path)
with col2:
st.download_button(label="📥 Download", data=resume_file, file_name=resume_file.name, mime="application/pdf")
if st.button("🗑️ Clear"): st.session_state.resume_text = ""; st.rerun()
# Process the resume text
if not st.session_state.resume_text:
with st.spinner("Processing your resume..."): with st.spinner("Processing your resume..."):
resume_text = extract_text_from_pdf(resume_file) resume_text = extract_text_from_pdf(resume_file)
if resume_text: if resume_text:
@ -377,7 +391,7 @@ def main() -> None:
# Email input with session state # Email input with session state
email = st.text_input( email = st.text_input(
"Your email address", "Candidate's email address",
value=st.session_state.candidate_email, value=st.session_state.candidate_email,
key="email_input" key="email_input"
) )