|
import gradio as gr |
|
import requests |
|
import os |
|
import json |
|
|
|
class AutonomousEmailAgent: |
|
def __init__(self, linkedin_url, company_name, role, word_limit, user_name, email, phone, linkedin): |
|
self.linkedin_url = linkedin_url |
|
self.company_name = company_name |
|
self.role = role |
|
self.word_limit = word_limit |
|
self.user_name = user_name |
|
self.email = email |
|
self.phone = phone |
|
self.linkedin = linkedin |
|
self.bio = None |
|
self.skills = [] |
|
self.experiences = [] |
|
self.company_info = None |
|
self.role_description = None |
|
self.attempts = 0 |
|
|
|
def fetch_linkedin_data(self): |
|
proxycurl_api_key = os.getenv("PROXYCURL_API_KEY") |
|
if not self.linkedin_url: |
|
print("Action: No LinkedIn URL provided, using default bio.") |
|
self.bio = "A professional with diverse experience." |
|
self.skills = ["Adaptable", "Hardworking"] |
|
self.experiences = ["Worked across various industries"] |
|
else: |
|
print("Action: Fetching LinkedIn data via Proxycurl.") |
|
headers = {"Authorization": f"Bearer {proxycurl_api_key}"} |
|
url = f"https://nubela.co/proxycurl/api/v2/linkedin?url={self.linkedin_url}" |
|
response = requests.get(url, headers=headers) |
|
if response.status_code == 200: |
|
data = response.json() |
|
self.bio = data.get("summary", "No bio available") |
|
self.skills = data.get("skills", []) |
|
self.experiences = data.get("experiences", []) |
|
print("LinkedIn data fetched successfully.") |
|
else: |
|
print("Error: Unable to fetch LinkedIn profile. Status Code:", response.status_code) |
|
self.use_default_profile() |
|
|
|
def use_default_profile(self): |
|
print("Using default profile values.") |
|
self.bio = "A professional with a versatile background and extensive experience." |
|
self.skills = ["Leadership", "Communication", "Problem-solving"] |
|
self.experiences = [{"title": "Project Manager"}, {"title": "Team Leader"}] |
|
|
|
def run(self): |
|
self.fetch_linkedin_data() |
|
return self.autonomous_reasoning() |
|
|
|
def autonomous_reasoning(self): |
|
print("Autonomous Reasoning: Letting the LLM fully reason and act on available data...") |
|
|
|
reasoning_prompt = f""" |
|
You are an AI agent tasked with generating a job application email using Simon Sinek's Start with Why model. |
|
The email must begin with why the candidate is passionate about the role, then explain how their skills and |
|
experience align with the company and role, and finally describe specific achievements that demonstrate their |
|
capabilities. The email must not exceed {self.word_limit} words but should remain coherent and complete. |
|
|
|
Here’s the current data: |
|
- LinkedIn profile: {self.linkedin_url} |
|
- Company Name: {self.company_name} |
|
- Role: {self.role} |
|
- Candidate's Bio: {self.bio} |
|
- Candidate's Skills: {', '.join(self.skills)} |
|
- Candidate's Experiences: {', '.join([exp['title'] for exp in self.experiences])} |
|
|
|
Generate a fully coherent and complete email that fits within the word limit. |
|
""" |
|
|
|
return self.send_request_to_llm(reasoning_prompt) |
|
|
|
def send_request_to_llm(self, prompt): |
|
print("Sending request to Groq Cloud LLM...") |
|
api_key = os.getenv("GROQ_API_KEY") |
|
if not api_key: |
|
print("Error: API key not found. Please set the GROQ_API_KEY environment variable.") |
|
return "Error: API key not found." |
|
|
|
headers = { |
|
"Authorization": f"Bearer {api_key}", |
|
"Content-Type": "application/json" |
|
} |
|
data = { |
|
"model": "llama-3.1-70b-versatile", |
|
"messages": [{"role": "user", "content": prompt}] |
|
} |
|
response = requests.post("https://api.groq.com/openai/v1/chat/completions", headers=headers, json=data) |
|
|
|
print(f"Status Code: {response.status_code}") |
|
if response.status_code == 200: |
|
try: |
|
result = response.json() |
|
print(f"LLM Response: {json.dumps(result, indent=2)}") |
|
choices = result.get("choices", []) |
|
if choices and "message" in choices[0]: |
|
content = choices[0]["message"]["content"] |
|
print(f"Content: {content}") |
|
return self.format_email(content) |
|
else: |
|
print("Error: Unrecognized format in LLM response.") |
|
return "Error: Unrecognized response format." |
|
except json.JSONDecodeError: |
|
print("Error: Response from Groq Cloud LLM is not valid JSON.") |
|
return "Error: Response is not in JSON format." |
|
else: |
|
print(f"Error: Unable to connect to Groq Cloud LLM. Status Code: {response.status_code}") |
|
return "Error: Unable to generate response." |
|
|
|
def format_email(self, llm_response): |
|
|
|
paragraphs = [line.strip() for line in llm_response.split("\n") if line.strip()] |
|
formatted_email = "\n\n".join(paragraphs) |
|
|
|
|
|
closing_section = ( |
|
"\n\nI would appreciate the opportunity to discuss how my background, skills, and passion align with the goals " |
|
f"of {self.company_name}. I am eager to contribute to your mission and support the development of future leaders.\n\n" |
|
"Thank you for considering my application. I look forward to the possibility of discussing this role further.\n" |
|
) |
|
|
|
|
|
signature = ( |
|
f"Best regards,\n" |
|
f"{self.user_name}\n" |
|
f"Email: {self.email}\n" |
|
f"Phone: {self.phone}\n" |
|
f"LinkedIn: {self.linkedin}" |
|
) |
|
|
|
|
|
if "Best regards" in formatted_email: |
|
formatted_email = formatted_email.split("Best regards")[0].strip() |
|
|
|
return f"{formatted_email}{closing_section}\n{signature}" |
|
|
|
|
|
def gradio_ui(): |
|
name_input = gr.Textbox(label="Your Name", placeholder="Enter your name") |
|
company_input = gr.Textbox(label="Company Name or URL", placeholder="Enter the company name or website URL") |
|
role_input = gr.Textbox(label="Role Applying For", placeholder="Enter the role you are applying for") |
|
email_input = gr.Textbox(label="Your Email Address", placeholder="Enter your email address") |
|
phone_input = gr.Textbox(label="Your Phone Number", placeholder="Enter your phone number") |
|
linkedin_input = gr.Textbox(label="Your LinkedIn URL", placeholder="Enter your LinkedIn profile URL") |
|
word_limit_slider = gr.Slider(minimum=50, maximum=300, step=10, label="Email Word Limit", value=150) |
|
|
|
email_output = gr.Textbox(label="Generated Email", placeholder="Your generated email will appear here", lines=10) |
|
|
|
def create_email(name, company_name, role, email, phone, linkedin_url, word_limit): |
|
agent = AutonomousEmailAgent(linkedin_url, company_name, role, word_limit, name, email, phone, linkedin_url) |
|
return agent.run() |
|
|
|
demo = gr.Interface( |
|
fn=create_email, |
|
inputs=[name_input, company_input, role_input, email_input, phone_input, linkedin_input, word_limit_slider], |
|
outputs=[email_output], |
|
title="Email Writing AI Agent with ReAct", |
|
description="Generate a professional email for a job application using LinkedIn data, company info, and role description.", |
|
allow_flagging="never" |
|
) |
|
|
|
demo.launch() |
|
|
|
if __name__ == "__main__": |
|
gradio_ui() |
|
|