email_assistant / app.py
abdullahtahir's picture
Update app.py
e337c26 verified
import os
import gradio as gr
from crewai import Agent, Task, Crew, LLM
from dotenv import load_dotenv
# Make sure your tools.py file is in the same directory
from tools import SendEmailTool
load_dotenv()
# --- 1. SET UP YOUR CREWAI AGENT AND TASKS WITH FORCED EXECUTION ---
llm = LLM(
model="gemini/gemini-1.5-flash",
verbose=True,
temperature=0.5,
)
email_sender_tool = SendEmailTool()
email_agent = Agent(
role="Portfolio Contact Form Assistant",
goal="Professionally format and send a visitor's message to the portfolio owner.",
backstory=(
"You are an expert assistant responsible for handling the 'Contact Me' form on a professional portfolio. "
"Your job is to take a visitor's message, format it into a proper email, and send it to the portfolio owner. "
"You MUST actually execute the SendEmailTool - do not simulate or fake the execution."
),
verbose=True,
tools=[email_sender_tool],
llm=llm,
# Force real execution settings
allow_delegation=False,
max_execution_time=300, # 5 minutes timeout
)
write_and_send_mail_task = Task(
description=(
"A portfolio visitor has sent a message. Your task is to process it.\n"
"1. Read the visitor's message from the '{topic}' input.\n"
"2. The visitor's email address is provided in the '{sender_email}' input.\n"
"3. Create a professional email body. **Crucially, the first line of the body must be 'Message from: {sender_email}'** followed by the rest of the formatted message.\n"
"4. The email should be professional and well structured.\n"
"5. Create a concise subject line, like 'New Message from Portfolio Visitor'.\n"
"6. Use the SendEmailTool to send this email, passing the visitor's email, your generated subject, and the complete body.\n"
"7. The portfolio belongs to Abdullah Tahir.\n"
"CRITICAL: You MUST actually use the SendEmailTool. Do not simulate, mock, or fabricate the result."
),
expected_output="The actual output from the SendEmailTool execution, including real confirmation details.",
agent=email_agent,
# Force real tool execution
result_as_answer=True,
tools=[email_sender_tool],
)
crew = Crew(
agents=[email_agent],
tasks=[write_and_send_mail_task],
verbose=True,
process="sequential",
full_output=True,
)
# --- 2. ENHANCED WRAPPER FUNCTION WITH VALIDATION ---
def validate_real_execution(result):
"""Check if the result indicates real or simulated execution"""
result_str = str(result).lower()
simulation_keywords = [
'simulated', 'simulation', 'mock', 'fake',
'pretend', 'dummy', 'test-id', '12345-simulated'
]
return not any(keyword in result_str for keyword in simulation_keywords)
def run_crew_and_send_email(message, email_address):
"""
Enhanced function that ensures real tool execution
"""
if not message or not email_address:
return "❌ Error: Please provide both a message and your email address."
# Validate email format
if '@' not in email_address:
return "❌ Error: Please provide a valid email address."
print(f"πŸš€ Processing message from: {email_address}")
inputs = {
"topic": message,
"sender_email": email_address
}
try:
# Step 1: Try CrewAI execution
print("πŸ”„ Attempting CrewAI execution...")
result = crew.kickoff(inputs=inputs)
print(f"πŸ“¨ CrewAI Result: {result}")
# Step 2: Validate if execution was real
if validate_real_execution(result):
print("βœ… Real tool execution detected")
return f"βœ… Message sent successfully!\n\nDetails: {result}"
else:
print("⚠️ Simulated execution detected - trying direct approach...")
# Step 3: Fallback to direct tool execution
professional_message = f"Message from: {email_address}\n\n{message}\n\nBest regards,\nPortfolio Visitor"
direct_result = email_sender_tool._run(
sender_email=email_address,
subject="New Message from Portfolio Visitor",
body=professional_message
)
print(f"πŸ”§ Direct execution result: {direct_result}")
if "βœ…" in str(direct_result) or "successfully" in str(direct_result).lower():
return f"βœ… Message sent successfully (direct execution)!\n\nDetails: {direct_result}"
else:
return f"⚠️ Email sending encountered an issue:\n\n{direct_result}"
except Exception as e:
print(f"❌ CrewAI execution failed: {str(e)}")
# Emergency fallback: Direct tool execution
try:
print("🚨 Using emergency direct execution...")
professional_message = f"Message from: {email_address}\n\n{message}\n\nBest regards,\nPortfolio Visitor"
emergency_result = email_sender_tool._run(
sender_email=email_address,
subject="New Message from Portfolio Visitor",
body=professional_message
)
print(f"πŸ†˜ Emergency result: {emergency_result}")
if "βœ…" in str(emergency_result) or "successfully" in str(emergency_result).lower():
return f"βœ… Message sent successfully (emergency mode)!\n\nDetails: {emergency_result}"
else:
return f"❌ Failed to send message. Error details:\n\n{emergency_result}"
except Exception as emergency_error:
return f"❌ Critical error: Unable to send email. Please contact Abdullah directly.\n\nTechnical details: {str(emergency_error)}"
# --- 3. ENHANCED GRADIO INTERFACE ---
with gr.Blocks(theme=gr.themes.Soft(), title="AI Contact Assistant") as demo:
gr.Markdown(
"""
# πŸ€– AI Contact Assistant for Abdullah Tahir
Welcome! Please leave your message and email address below.
My AI assistant will format your message professionally and send it directly to Abdullah.
**Status indicators:**
- βœ… = Message sent successfully
- ⚠️ = Warning or fallback used
- ❌ = Error occurred
"""
)
with gr.Row():
with gr.Column(scale=2):
# Input components with better validation
message_input = gr.Textbox(
lines=7,
label="Your Message",
placeholder="Hello Abdullah, I came across your portfolio and wanted to connect about...",
info="Please write your message here"
)
email_input = gr.Textbox(
label="Your Email Address",
placeholder="your.name@example.com",
info="This will be used for Abdullah to reply to you"
)
with gr.Row():
send_button = gr.Button("πŸ“§ Send Message", variant="primary", size="lg")
clear_button = gr.Button("πŸ—‘οΈ Clear", variant="secondary")
with gr.Column(scale=1):
# Enhanced output component
status_output = gr.Textbox(
label="πŸ“Š Status & Results",
interactive=False,
lines=8,
info="Real-time status updates will appear here"
)
# Debug info (you can remove this in production)
with gr.Accordion("πŸ”§ Debug Info (for testing)", open=False):
debug_output = gr.Textbox(
label="Environment Check",
interactive=False,
value=f"SendGrid API Key: {'βœ… Found' if os.getenv('SENDGRID_API_KEY') else '❌ Missing'}\n"
f"Verified Sender: {os.getenv('VERIFIED_SENDER_EMAIL', '❌ Missing')}\n"
f"Receiver Email: {os.getenv('RECEIVER_EMAIL', '❌ Missing')}"
)
# Define actions
def clear_form():
return "", "", "Form cleared. Ready for new message."
send_button.click(
fn=run_crew_and_send_email,
inputs=[message_input, email_input],
outputs=status_output
)
clear_button.click(
fn=clear_form,
outputs=[message_input, email_input, status_output]
)
# Add some examples
gr.Markdown(
"""
### πŸ’‘ Example Messages:
- "Hi Abdullah, I'm interested in collaborating on an AI project..."
- "Hello, I saw your portfolio and would like to discuss a job opportunity..."
- "Hi there, I have a question about your CrewAI email assistant project..."
"""
)
# --- 4. LAUNCH THE APP WITH BETTER ERROR HANDLING ---
if __name__ == "__main__":
# Validate environment before launching
required_vars = ['SENDGRID_API_KEY', 'VERIFIED_SENDER_EMAIL', 'RECEIVER_EMAIL']
missing_vars = [var for var in required_vars if not os.getenv(var)]
if missing_vars:
print(f"⚠️ WARNING: Missing environment variables: {missing_vars}")
print("The app will launch but email sending may not work.")
else:
print("βœ… All required environment variables found!")
# Test the email tool on startup
print("\nπŸ§ͺ Testing email tool...")
test_tool = SendEmailTool()
test_result = test_tool._run("test@example.com", "Startup Test", "Testing email tool on startup")
print(f"Test result: {test_result}")
print("\nπŸš€ Launching Gradio app...")
demo.launch(
share=False, # Set to True if you want a public link
show_error=True,
quiet=False
)