import gradio as gr import requests import os # Load API keys securely from environment variables proxycurl_api_key = os.getenv("PROXYCURL_API_KEY") # Proxycurl API key groq_api_key = os.getenv("GROQ_CLOUD_API_KEY") # Groq Cloud API key class EmailAgent: def __init__(self, linkedin_url, company_name, role, word_limit): self.linkedin_url = linkedin_url self.company_name = company_name self.role = role self.word_limit = word_limit self.bio = None self.skills = [] self.experiences = [] self.company_info = None self.role_description = None # Reason: Decide what information is needed def reason_about_data(self): print("Reasoning: I need LinkedIn data, company info, and role description.") if not self.linkedin_url: print("Missing LinkedIn URL. Request from the user.") if not self.company_name: print("Missing company name. Request from the user.") # Action: Fetch LinkedIn data via Proxycurl def fetch_linkedin_data(self): print("Action: Fetching LinkedIn data from 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", []) else: print("Error: Unable to fetch LinkedIn profile.") # Action: Fetch company information via Proxycurl def fetch_company_info(self): print(f"Action: Fetching company info for {self.company_name}.") headers = { "Authorization": f"Bearer {proxycurl_api_key}", } url = f"https://nubela.co/proxycurl/api/v2/linkedin/company?company_name={self.company_name}" response = requests.get(url, headers=headers) if response.status_code == 200: data = response.json() self.company_info = data.get("description", "No detailed company info available.") else: print(f"Error: Unable to fetch company info for {self.company_name}.") # Action: Fetch role description def fetch_role_description(self): print(f"Action: Fetching role description for {self.role}.") self.role_description = f"The role of {self.role} at {self.company_name} involves..." # Reflection: Check if the data is sufficient to generate an email def reflect_on_data(self): print("Reflection: Do I have enough data to generate the email?") if not self.bio or not self.skills or not self.company_info: print("Missing some critical information. Need to gather more data.") return False return True # Action: Generate the email using Groq Cloud LLM def generate_email(self): print("Action: Generating the email with the gathered information.") prompt = f""" Write a professional email applying for the {self.role} position at {self.company_name}. The candidate’s bio is: {self.bio}. The candidate's LinkedIn profile highlights the following skills: {', '.join(self.skills)}. The candidate has the following experiences relevant to the job: {', '.join([exp['title'] for exp in self.experiences])}. The email should be professional, concise, and tailored to the company's culture. Use relevant company details: {self.company_info}. Highlight the candidate’s skills and experiences from LinkedIn, and map them to the job's requirements: {self.role_description}. The email should not exceed {self.word_limit} words. """ url = "https://api.groq.com/openai/v1/chat/completions" headers = { "Authorization": f"Bearer {groq_api_key}", "Content-Type": "application/json", } data = { "messages": [{"role": "user", "content": prompt}], "model": "llama3-8b-8192" } response = requests.post(url, headers=headers, json=data) if response.status_code == 200: return response.json()["choices"][0]["message"]["content"].strip() else: print(f"Error: {response.status_code}, {response.text}") return "Error generating email. Please check your API key or try again later." # Main loop following ReAct pattern def run(self): self.reason_about_data() # Reason self.fetch_linkedin_data() # Action self.fetch_company_info() # Action self.fetch_role_description() # Action if self.reflect_on_data(): # Reflection return self.generate_email() # Final Action else: return "Error: Not enough data to generate the email." # Define the Gradio interface and the main app logic def gradio_ui(): # Input fields 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) # New slider for word limit # Output field email_output = gr.Textbox(label="Generated Email", placeholder="Your generated email will appear here", lines=10) # Function to create and run the email agent def create_email(name, company_name, role, email, phone, linkedin_profile_url, word_limit): agent = EmailAgent(linkedin_profile_url, company_name, role, word_limit) return agent.run() # Gradio interface 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" ) # Launch the Gradio app demo.launch() # Start the Gradio app when running the script if __name__ == "__main__": gradio_ui()