Update app.py
Browse files
app.py
CHANGED
@@ -7,7 +7,7 @@ proxycurl_api_key = os.getenv("PROXYCURL_API_KEY") # Proxycurl API key
|
|
7 |
groq_api_key = os.getenv("GROQ_CLOUD_API_KEY") # Groq Cloud API key
|
8 |
firecrawl_api_key = os.getenv("FIRECRAWL_API_KEY") # Firecrawl API key
|
9 |
|
10 |
-
class
|
11 |
def __init__(self, linkedin_url, company_name, role, word_limit, user_name, email, phone, linkedin):
|
12 |
self.linkedin_url = linkedin_url
|
13 |
self.company_name = company_name
|
@@ -23,38 +23,33 @@ class EmailAgent:
|
|
23 |
self.company_info = None
|
24 |
self.role_description = None
|
25 |
|
26 |
-
#
|
27 |
-
def
|
28 |
-
print("Reasoning:
|
29 |
|
30 |
-
# LLM reasoning prompt that evaluates the current data and
|
31 |
reasoning_prompt = f"""
|
32 |
-
You are
|
33 |
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
Candidate's Skills: {', '.join(self.skills)}
|
45 |
-
Candidate's Experiences: {', '.join([exp['title'] for exp in self.experiences])}
|
46 |
-
|
47 |
-
Company Information: {self.company_info}
|
48 |
-
Role Description: {self.role_description}
|
49 |
-
|
50 |
-
Evaluate the completeness of the data. If some key data is missing, determine whether we should:
|
51 |
-
- Scrape for more data (e.g., company info, role descriptions).
|
52 |
-
- Proceed with the available information and generate the email using default logic.
|
53 |
-
|
54 |
-
Reflect on whether we need more data or if the current information is sufficient to proceed.
|
55 |
-
"""
|
56 |
|
57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
url = "https://api.groq.com/openai/v1/chat/completions"
|
59 |
headers = {
|
60 |
"Authorization": f"Bearer {groq_api_key}",
|
@@ -70,12 +65,30 @@ class EmailAgent:
|
|
70 |
if response.status_code == 200:
|
71 |
reasoning_output = response.json()["choices"][0]["message"]["content"].strip()
|
72 |
print("LLM Reasoning Output:", reasoning_output)
|
73 |
-
|
|
|
|
|
74 |
else:
|
75 |
print(f"Error: {response.status_code}, {response.text}")
|
76 |
return "Error: Unable to complete reasoning."
|
77 |
|
78 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
def fetch_linkedin_data(self):
|
80 |
if not self.linkedin_url:
|
81 |
print("Action: No LinkedIn URL provided, using default bio.")
|
@@ -121,11 +134,10 @@ class EmailAgent:
|
|
121 |
print(f"Error: Unable to fetch company info via Firecrawl. Using default info.")
|
122 |
self.company_info = "A leading company in its field."
|
123 |
|
124 |
-
# Final Action: Generate the email using Groq Cloud LLM
|
125 |
def generate_email(self):
|
126 |
print("Action: Generating the email with the gathered information.")
|
127 |
|
128 |
-
# Use the LinkedIn profile link dynamically in the body
|
129 |
linkedin_text = f"Please find my LinkedIn profile at {self.linkedin}" if self.linkedin else ""
|
130 |
|
131 |
# Dynamic LLM prompt
|
@@ -175,13 +187,9 @@ class EmailAgent:
|
|
175 |
|
176 |
# Main loop following ReAct pattern
|
177 |
def run(self):
|
178 |
-
reasoning_output = self.reason_with_llm() # LLM performs reasoning and reflection
|
179 |
-
print("LLM Reflection:", reasoning_output)
|
180 |
-
|
181 |
self.fetch_linkedin_data() # Fetch LinkedIn data
|
182 |
-
|
183 |
-
|
184 |
-
return self.generate_email() # Final action: generate email
|
185 |
|
186 |
# Define the Gradio interface and the main app logic
|
187 |
def gradio_ui():
|
@@ -197,3 +205,24 @@ def gradio_ui():
|
|
197 |
# Output field
|
198 |
email_output = gr.Textbox(label="Generated Email", placeholder="Your generated email will appear here", lines=10)
|
199 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
groq_api_key = os.getenv("GROQ_CLOUD_API_KEY") # Groq Cloud API key
|
8 |
firecrawl_api_key = os.getenv("FIRECRAWL_API_KEY") # Firecrawl API key
|
9 |
|
10 |
+
class AutonomousEmailAgent:
|
11 |
def __init__(self, linkedin_url, company_name, role, word_limit, user_name, email, phone, linkedin):
|
12 |
self.linkedin_url = linkedin_url
|
13 |
self.company_name = company_name
|
|
|
23 |
self.company_info = None
|
24 |
self.role_description = None
|
25 |
|
26 |
+
# Reason and Act via LLM: Let the LLM control reasoning and actions dynamically
|
27 |
+
def autonomous_reasoning(self):
|
28 |
+
print("Autonomous Reasoning: Letting the LLM fully reason and act on available data...")
|
29 |
|
30 |
+
# LLM reasoning prompt that evaluates the current data and issues actions autonomously
|
31 |
reasoning_prompt = f"""
|
32 |
+
You are an autonomous agent responsible for generating a job application email.
|
33 |
|
34 |
+
Here's the current data:
|
35 |
+
|
36 |
+
- LinkedIn profile: {self.linkedin_url}
|
37 |
+
- Company Name: {self.company_name}
|
38 |
+
- Role: {self.role}
|
39 |
+
- Candidate's Bio: {self.bio}
|
40 |
+
- Candidate's Skills: {', '.join(self.skills)}
|
41 |
+
- Candidate's Experiences: {', '.join([exp['title'] for exp in self.experiences])}
|
42 |
+
- Company Information: {self.company_info}
|
43 |
+
- Role Description: {self.role_description}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
|
45 |
+
Based on this data, decide if it is sufficient to generate the email. If some information is missing or insufficient:
|
46 |
+
- Decide whether to scrape the company's website for more information or use a fallback.
|
47 |
+
- If the scraping fails, decide what next steps to take.
|
48 |
+
|
49 |
+
Once the information is complete, proceed to generate the email. After generating the email, reflect on whether the content aligns with the role and company and whether any improvements are needed.
|
50 |
+
"""
|
51 |
+
|
52 |
+
# Send the reasoning prompt to the LLM
|
53 |
url = "https://api.groq.com/openai/v1/chat/completions"
|
54 |
headers = {
|
55 |
"Authorization": f"Bearer {groq_api_key}",
|
|
|
65 |
if response.status_code == 200:
|
66 |
reasoning_output = response.json()["choices"][0]["message"]["content"].strip()
|
67 |
print("LLM Reasoning Output:", reasoning_output)
|
68 |
+
|
69 |
+
# Now the LLM takes action based on the reflection
|
70 |
+
return self.act_on_llm_instructions(reasoning_output)
|
71 |
else:
|
72 |
print(f"Error: {response.status_code}, {response.text}")
|
73 |
return "Error: Unable to complete reasoning."
|
74 |
|
75 |
+
# Function to act on the LLM's instructions
|
76 |
+
def act_on_llm_instructions(self, reasoning_output):
|
77 |
+
# Parse the LLM's instructions (assume it's structured with action steps)
|
78 |
+
if "scrape the company's website" in reasoning_output:
|
79 |
+
# Action: Scrape the company's website for more information
|
80 |
+
self.fetch_company_info_with_firecrawl()
|
81 |
+
# Reflect again by invoking the LLM to reassess
|
82 |
+
return self.autonomous_reasoning()
|
83 |
+
|
84 |
+
elif "generate the email" in reasoning_output:
|
85 |
+
# Action: Proceed to generate the email
|
86 |
+
return self.generate_email()
|
87 |
+
|
88 |
+
else:
|
89 |
+
return "Error: Unrecognized instruction from LLM."
|
90 |
+
|
91 |
+
# Action: Fetch LinkedIn data via Proxycurl
|
92 |
def fetch_linkedin_data(self):
|
93 |
if not self.linkedin_url:
|
94 |
print("Action: No LinkedIn URL provided, using default bio.")
|
|
|
134 |
print(f"Error: Unable to fetch company info via Firecrawl. Using default info.")
|
135 |
self.company_info = "A leading company in its field."
|
136 |
|
137 |
+
# Final Action: Generate the email using Groq Cloud LLM
|
138 |
def generate_email(self):
|
139 |
print("Action: Generating the email with the gathered information.")
|
140 |
|
|
|
141 |
linkedin_text = f"Please find my LinkedIn profile at {self.linkedin}" if self.linkedin else ""
|
142 |
|
143 |
# Dynamic LLM prompt
|
|
|
187 |
|
188 |
# Main loop following ReAct pattern
|
189 |
def run(self):
|
|
|
|
|
|
|
190 |
self.fetch_linkedin_data() # Fetch LinkedIn data
|
191 |
+
# Let LLM autonomously decide and act
|
192 |
+
return self.autonomous_reasoning()
|
|
|
193 |
|
194 |
# Define the Gradio interface and the main app logic
|
195 |
def gradio_ui():
|
|
|
205 |
# Output field
|
206 |
email_output = gr.Textbox(label="Generated Email", placeholder="Your generated email will appear here", lines=10)
|
207 |
|
208 |
+
# Function to create and run the email agent
|
209 |
+
def create_email(name, company_name, role, email, phone, linkedin_url, word_limit):
|
210 |
+
agent = AutonomousEmailAgent(linkedin_url, company_name, role, word_limit, name, email, phone, linkedin_url)
|
211 |
+
return agent.run()
|
212 |
+
|
213 |
+
# Gradio interface
|
214 |
+
demo = gr.Interface(
|
215 |
+
fn=create_email,
|
216 |
+
inputs=[name_input, company_input, role_input, email_input, phone_input, linkedin_input, word_limit_slider],
|
217 |
+
outputs=[email_output],
|
218 |
+
title="Email Writing AI Agent with ReAct",
|
219 |
+
description="Generate a professional email for a job application using LinkedIn data, company info, and role description.",
|
220 |
+
allow_flagging="never"
|
221 |
+
)
|
222 |
+
|
223 |
+
# Launch the Gradio app
|
224 |
+
demo.launch()
|
225 |
+
|
226 |
+
# Start the Gradio app when running the script
|
227 |
+
if __name__ == "__main__":
|
228 |
+
gradio_ui()
|