Spaces:
Build error
Build error
Upload 9 files
Browse files- app (3).py +62 -0
- chat.py +95 -0
- email_actions.py +98 -0
- google_sheets_utils.py +144 -0
- groq_client.py +43 -0
- profile_management.py +100 -0
- requirements (2).txt +213 -0
- system_prompt.txt +65 -0
- web_search.py +13 -0
app (3).py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# app.py
|
| 2 |
+
# Filepath: ai-email-assistant/app.py
|
| 3 |
+
# Main application entry point
|
| 4 |
+
|
| 5 |
+
import json
|
| 6 |
+
import gradio as gr
|
| 7 |
+
from chat import create_chat_interface
|
| 8 |
+
from profile_management import create_profile_management_interface, get_profile_summaries
|
| 9 |
+
from email_actions import create_email_actions_interface, get_template_summaries
|
| 10 |
+
from groq_client import set_api_key
|
| 11 |
+
from google_sheets_utils import connect_to_sheets
|
| 12 |
+
|
| 13 |
+
def load_config():
|
| 14 |
+
try:
|
| 15 |
+
with open('config.json', 'r') as f:
|
| 16 |
+
return json.load(f)
|
| 17 |
+
except FileNotFoundError:
|
| 18 |
+
return {}
|
| 19 |
+
|
| 20 |
+
def main():
|
| 21 |
+
config = load_config()
|
| 22 |
+
api_key = config.get('groq_api_key')
|
| 23 |
+
if api_key:
|
| 24 |
+
set_api_key(api_key)
|
| 25 |
+
|
| 26 |
+
sender_sheet, receiver_sheet, template_sheet = connect_to_sheets()
|
| 27 |
+
|
| 28 |
+
with gr.Blocks() as app:
|
| 29 |
+
gr.Markdown("# AI Email Assistant")
|
| 30 |
+
|
| 31 |
+
# State variables
|
| 32 |
+
current_sender = gr.State(None)
|
| 33 |
+
current_receiver = gr.State(None)
|
| 34 |
+
current_template = gr.State(None)
|
| 35 |
+
|
| 36 |
+
with gr.Tabs():
|
| 37 |
+
with gr.Tab("Profile Management"):
|
| 38 |
+
with gr.Tabs():
|
| 39 |
+
with gr.Tab("Sender Profiles"):
|
| 40 |
+
sender_components = create_profile_management_interface(sender_sheet, "Sender", current_sender)
|
| 41 |
+
with gr.Tab("Receiver Profiles"):
|
| 42 |
+
receiver_components = create_profile_management_interface(receiver_sheet, "Receiver", current_receiver)
|
| 43 |
+
|
| 44 |
+
with gr.Tab("Email Actions"):
|
| 45 |
+
email_actions_components = create_email_actions_interface(template_sheet, current_template)
|
| 46 |
+
|
| 47 |
+
with gr.Tab("Chat"):
|
| 48 |
+
# Get summaries for context
|
| 49 |
+
sender_summaries = get_profile_summaries(sender_sheet)
|
| 50 |
+
receiver_summaries = get_profile_summaries(receiver_sheet)
|
| 51 |
+
template_summaries = get_template_summaries(template_sheet)
|
| 52 |
+
|
| 53 |
+
chat_interface = create_chat_interface(
|
| 54 |
+
sender_summaries, receiver_summaries, template_summaries,
|
| 55 |
+
current_sender, current_receiver, current_template,
|
| 56 |
+
sender_components, receiver_components, email_actions_components
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
app.launch()
|
| 60 |
+
|
| 61 |
+
if __name__ == "__main__":
|
| 62 |
+
main()
|
chat.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# chat.py
|
| 2 |
+
# Filepath: ai-email-assistant/chat.py
|
| 3 |
+
# Implements the chat interface and message handling
|
| 4 |
+
|
| 5 |
+
import gradio as gr
|
| 6 |
+
from groq_client import get_ai_response
|
| 7 |
+
from web_search import search_web
|
| 8 |
+
from profile_management import get_profiles, create_profile, update_profile, delete_profile
|
| 9 |
+
from email_actions import get_templates, create_template, update_template, delete_template
|
| 10 |
+
|
| 11 |
+
def load_system_prompt(filename):
|
| 12 |
+
try:
|
| 13 |
+
with open(filename, 'r') as file:
|
| 14 |
+
return file.read().strip()
|
| 15 |
+
except FileNotFoundError:
|
| 16 |
+
return "System prompt file not found."
|
| 17 |
+
|
| 18 |
+
def create_chat_interface(sender_summaries, receiver_summaries, template_summaries,
|
| 19 |
+
current_sender, current_receiver, current_template,
|
| 20 |
+
sender_components, receiver_components, email_actions_components):
|
| 21 |
+
main_system_prompt = load_system_prompt('system_prompt_main.txt')
|
| 22 |
+
function_call_prompt = load_system_prompt('system_prompt_function_call.txt')
|
| 23 |
+
|
| 24 |
+
chatbot = gr.Chatbot()
|
| 25 |
+
msg = gr.Textbox()
|
| 26 |
+
clear = gr.Button("Clear")
|
| 27 |
+
|
| 28 |
+
def get_context():
|
| 29 |
+
context = "Current context:\n"
|
| 30 |
+
|
| 31 |
+
if current_sender.value:
|
| 32 |
+
context += f"Current Sender: {current_sender.value}\n"
|
| 33 |
+
if current_receiver.value:
|
| 34 |
+
context += f"Current Receiver: {current_receiver.value}\n"
|
| 35 |
+
if current_template.value:
|
| 36 |
+
context += f"Current Template: {current_template.value}\n"
|
| 37 |
+
|
| 38 |
+
context += f"Available Sender Profiles: {', '.join(sender_summaries)}\n"
|
| 39 |
+
context += f"Available Receiver Profiles: {', '.join(receiver_summaries)}\n"
|
| 40 |
+
context += f"Available Templates: {', '.join(template_summaries)}\n"
|
| 41 |
+
|
| 42 |
+
return context
|
| 43 |
+
|
| 44 |
+
def execute_function(function_call):
|
| 45 |
+
# Implementation of function execution based on the function call
|
| 46 |
+
# This should handle all the functions mentioned in the system prompt
|
| 47 |
+
function_name = function_call.split('(')[0].strip()
|
| 48 |
+
args = [arg.strip().strip("'\"") for arg in function_call.split('(')[1].split(')')[0].split(',')]
|
| 49 |
+
|
| 50 |
+
if function_name == 'load_sender_profile':
|
| 51 |
+
current_sender.value = args[0]
|
| 52 |
+
return f"Loaded sender profile: {args[0]}"
|
| 53 |
+
elif function_name == 'load_receiver_profile':
|
| 54 |
+
current_receiver.value = args[0]
|
| 55 |
+
return f"Loaded receiver profile: {args[0]}"
|
| 56 |
+
elif function_name == 'load_template':
|
| 57 |
+
current_template.value = args[0]
|
| 58 |
+
return f"Loaded template: {args[0]}"
|
| 59 |
+
# Add more function implementations as needed
|
| 60 |
+
|
| 61 |
+
return "Function not implemented"
|
| 62 |
+
|
| 63 |
+
def respond(message, chat_history):
|
| 64 |
+
context = get_context()
|
| 65 |
+
|
| 66 |
+
main_ai_response = get_ai_response(message, main_system_prompt, context + "\n".join([f"{m[0]}: {m[1]}" for m in chat_history]))
|
| 67 |
+
|
| 68 |
+
if "Action required:" in main_ai_response:
|
| 69 |
+
action_context = main_ai_response.split("Action required:")[1].strip()
|
| 70 |
+
function_call_response = get_ai_response(action_context, function_call_prompt)
|
| 71 |
+
|
| 72 |
+
if function_call_response.startswith('[SEARCH:'):
|
| 73 |
+
search_query = function_call_response[8:-1].strip()
|
| 74 |
+
result = "\n".join(search_web(search_query))
|
| 75 |
+
elif function_call_response.startswith('[FUNCTION:'):
|
| 76 |
+
function_call = function_call_response[10:-1].strip()
|
| 77 |
+
result = execute_function(function_call)
|
| 78 |
+
else:
|
| 79 |
+
result = "Invalid backend response"
|
| 80 |
+
|
| 81 |
+
final_response = get_ai_response(
|
| 82 |
+
f"Previous context: {action_context}\n\nAction result: {result}\n\nPlease continue the conversation with the user based on this result.",
|
| 83 |
+
main_system_prompt,
|
| 84 |
+
"\n".join([f"{m[0]}: {m[1]}" for m in chat_history])
|
| 85 |
+
)
|
| 86 |
+
else:
|
| 87 |
+
final_response = main_ai_response
|
| 88 |
+
|
| 89 |
+
chat_history.append((message, final_response))
|
| 90 |
+
return "", chat_history
|
| 91 |
+
|
| 92 |
+
msg.submit(respond, [msg, chatbot], [msg, chatbot])
|
| 93 |
+
clear.click(lambda: None, None, chatbot, queue=False)
|
| 94 |
+
|
| 95 |
+
return chatbot, msg, clear
|
email_actions.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# email_actions.py
|
| 2 |
+
# Filepath: ai-email-assistant/email_actions.py
|
| 3 |
+
# Manages email-related actions (drafting, templating)
|
| 4 |
+
|
| 5 |
+
import gradio as gr
|
| 6 |
+
from google_sheets_utils import get_templates, save_template, delete_template_from_sheet
|
| 7 |
+
|
| 8 |
+
def get_template_summaries(sheet):
|
| 9 |
+
templates = get_templates(sheet)
|
| 10 |
+
return [template[2] for template in templates] # Assuming the name is in the third column
|
| 11 |
+
|
| 12 |
+
def create_email_actions_interface(template_sheet, current_template):
|
| 13 |
+
with gr.Group():
|
| 14 |
+
gr.Markdown("## Email Actions")
|
| 15 |
+
|
| 16 |
+
name = gr.Textbox(label="Template Name")
|
| 17 |
+
subject = gr.Textbox(label="Subject")
|
| 18 |
+
content = gr.Textbox(label="Content", lines=10)
|
| 19 |
+
sender_profile = gr.Dropdown(label="Sender Profile")
|
| 20 |
+
receiver_profile = gr.Dropdown(label="Receiver Profile")
|
| 21 |
+
|
| 22 |
+
save_draft_btn = gr.Button("Save as Draft")
|
| 23 |
+
save_template_btn = gr.Button("Save as Template")
|
| 24 |
+
|
| 25 |
+
templates_list = gr.Dataframe(
|
| 26 |
+
headers=["Type", "ID", "Name", "Subject", "Content", "Sender Profile", "Receiver Profile"],
|
| 27 |
+
label="Existing Templates",
|
| 28 |
+
value=get_templates(template_sheet),
|
| 29 |
+
interactive=False
|
| 30 |
+
)
|
| 31 |
+
|
| 32 |
+
load_template_btn = gr.Button("Load Selected Template")
|
| 33 |
+
delete_template_btn = gr.Button("Delete Selected Template")
|
| 34 |
+
|
| 35 |
+
def save_template_action():
|
| 36 |
+
template_data = {
|
| 37 |
+
"name": name.value,
|
| 38 |
+
"subject": subject.value,
|
| 39 |
+
"content": content.value,
|
| 40 |
+
"sender_profile": sender_profile.value,
|
| 41 |
+
"receiver_profile": receiver_profile.value
|
| 42 |
+
}
|
| 43 |
+
save_template(template_sheet, template_data)
|
| 44 |
+
return get_templates(template_sheet)
|
| 45 |
+
|
| 46 |
+
def load_template(evt: gr.SelectData):
|
| 47 |
+
if evt.value:
|
| 48 |
+
selected_template = templates_list.value[evt.index[0]]
|
| 49 |
+
current_template.value = selected_template[2] # Set the current template to the selected name
|
| 50 |
+
return (
|
| 51 |
+
selected_template[2], # Name
|
| 52 |
+
selected_template[3], # Subject
|
| 53 |
+
selected_template[4], # Content
|
| 54 |
+
selected_template[5], # Sender Profile
|
| 55 |
+
selected_template[6], # Receiver Profile
|
| 56 |
+
)
|
| 57 |
+
return [gr.update() for _ in range(5)]
|
| 58 |
+
|
| 59 |
+
def delete_template(evt: gr.SelectData):
|
| 60 |
+
if evt.value:
|
| 61 |
+
template_id = templates_list.value[evt.index[0]][1]
|
| 62 |
+
delete_template_from_sheet(template_sheet, template_id)
|
| 63 |
+
return get_templates(template_sheet)
|
| 64 |
+
return templates_list.value
|
| 65 |
+
|
| 66 |
+
save_template_btn.click(save_template_action, outputs=[templates_list])
|
| 67 |
+
load_template_btn.click(load_template, outputs=[name, subject, content, sender_profile, receiver_profile])
|
| 68 |
+
delete_template_btn.click(delete_template, outputs=[templates_list])
|
| 69 |
+
|
| 70 |
+
refresh_btn = gr.Button("Refresh Templates")
|
| 71 |
+
refresh_btn.click(lambda: get_templates(template_sheet), outputs=[templates_list])
|
| 72 |
+
|
| 73 |
+
return name, subject, content, sender_profile, receiver_profile, save_draft_btn, save_template_btn, templates_list, load_template_btn, delete_template_btn, refresh_btn
|
| 74 |
+
|
| 75 |
+
# Implement create_template, update_template, delete_template functions
|
| 76 |
+
def create_template(sheet, name, subject, content, sender_profile, receiver_profile):
|
| 77 |
+
template_data = {
|
| 78 |
+
"name": name,
|
| 79 |
+
"subject": subject,
|
| 80 |
+
"content": content,
|
| 81 |
+
"sender_profile": sender_profile,
|
| 82 |
+
"receiver_profile": receiver_profile
|
| 83 |
+
}
|
| 84 |
+
return save_template(sheet, template_data)
|
| 85 |
+
|
| 86 |
+
def update_template(sheet, template_id, name, subject, content, sender_profile, receiver_profile):
|
| 87 |
+
template_data = {
|
| 88 |
+
"id": template_id,
|
| 89 |
+
"name": name,
|
| 90 |
+
"subject": subject,
|
| 91 |
+
"content": content,
|
| 92 |
+
"sender_profile": sender_profile,
|
| 93 |
+
"receiver_profile": receiver_profile
|
| 94 |
+
}
|
| 95 |
+
return save_template(sheet, template_data)
|
| 96 |
+
|
| 97 |
+
def delete_template(sheet, template_id):
|
| 98 |
+
return delete_template_from_sheet(sheet, template_id)
|
google_sheets_utils.py
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# google_sheets_utils.py
|
| 2 |
+
# Filepath: ai-email-assistant/google_sheets_utils.py
|
| 3 |
+
# Manages interactions with Google Sheets for profile and template storage
|
| 4 |
+
|
| 5 |
+
import os
|
| 6 |
+
import json
|
| 7 |
+
import gspread
|
| 8 |
+
from google.oauth2.service_account import Credentials
|
| 9 |
+
import logging
|
| 10 |
+
|
| 11 |
+
# Set up logging
|
| 12 |
+
logging.basicConfig(level=logging.INFO)
|
| 13 |
+
logger = logging.getLogger(__name__)
|
| 14 |
+
|
| 15 |
+
def load_config():
|
| 16 |
+
with open('config.json', 'r') as f:
|
| 17 |
+
return json.load(f)
|
| 18 |
+
|
| 19 |
+
# Setup Google Sheets connection
|
| 20 |
+
SCOPES = ['https://www.googleapis.com/auth/spreadsheets',
|
| 21 |
+
'https://www.googleapis.com/auth/drive']
|
| 22 |
+
config = load_config()
|
| 23 |
+
creds_file = config.get('google_sheets_credentials_file')
|
| 24 |
+
creds = Credentials.from_service_account_file(creds_file, scopes=SCOPES)
|
| 25 |
+
client = gspread.authorize(creds)
|
| 26 |
+
|
| 27 |
+
# Open the Google Sheet (create it if it doesn't exist)
|
| 28 |
+
try:
|
| 29 |
+
spreadsheet = client.open("AI Email Assistant")
|
| 30 |
+
logger.info("Successfully opened the spreadsheet.")
|
| 31 |
+
except gspread.exceptions.SpreadsheetNotFound:
|
| 32 |
+
logger.info("Spreadsheet 'AI Email Assistant' not found. Creating a new one.")
|
| 33 |
+
spreadsheet = client.create("AI Email Assistant")
|
| 34 |
+
logger.info("New spreadsheet 'AI Email Assistant' created.")
|
| 35 |
+
|
| 36 |
+
def initialize_sheet(sheet, headers, sample_data):
|
| 37 |
+
if not sheet.get_all_values():
|
| 38 |
+
sheet.append_row(headers)
|
| 39 |
+
for row in sample_data:
|
| 40 |
+
sheet.append_row(row)
|
| 41 |
+
logger.info(f"Initialized {sheet.title} with headers and sample data.")
|
| 42 |
+
|
| 43 |
+
def connect_to_sheets():
|
| 44 |
+
try:
|
| 45 |
+
sender_sheet = spreadsheet.worksheet("Sender Profiles")
|
| 46 |
+
except gspread.exceptions.WorksheetNotFound:
|
| 47 |
+
sender_sheet = spreadsheet.add_worksheet(title="Sender Profiles", rows="100", cols="20")
|
| 48 |
+
|
| 49 |
+
try:
|
| 50 |
+
receiver_sheet = spreadsheet.worksheet("Receiver Profiles")
|
| 51 |
+
except gspread.exceptions.WorksheetNotFound:
|
| 52 |
+
receiver_sheet = spreadsheet.add_worksheet(title="Receiver Profiles", rows="100", cols="20")
|
| 53 |
+
|
| 54 |
+
try:
|
| 55 |
+
template_sheet = spreadsheet.worksheet("Email Templates")
|
| 56 |
+
except gspread.exceptions.WorksheetNotFound:
|
| 57 |
+
template_sheet = spreadsheet.add_worksheet(title="Email Templates", rows="100", cols="20")
|
| 58 |
+
|
| 59 |
+
# Initialize sheets with headers and sample data if they're empty
|
| 60 |
+
profile_headers = ["Type", "ID", "Name", "Email", "Position", "Company", "Context"]
|
| 61 |
+
sender_sample = [
|
| 62 |
+
["Sender", "SEND_1", "John Doe", "john@example.com", "Sales Manager", "ABC Corp", "Professional"],
|
| 63 |
+
["Sender", "SEND_2", "Jane Smith", "jane@example.com", "Marketing Director", "XYZ Inc", "Professional"]
|
| 64 |
+
]
|
| 65 |
+
receiver_sample = [
|
| 66 |
+
["Receiver", "REC_1", "Alice Johnson", "alice@example.com", "CEO", "123 Industries", "Professional"],
|
| 67 |
+
["Receiver", "REC_2", "Bob Brown", "bob@example.com", "HR Manager", "456 Corp", "Professional"]
|
| 68 |
+
]
|
| 69 |
+
initialize_sheet(sender_sheet, profile_headers, sender_sample)
|
| 70 |
+
initialize_sheet(receiver_sheet, profile_headers, receiver_sample)
|
| 71 |
+
|
| 72 |
+
template_headers = ["Type", "ID", "Name", "Subject", "Content", "Sender Profile", "Receiver Profile"]
|
| 73 |
+
template_sample = [
|
| 74 |
+
["Template", "TEMP_1", "Sales Pitch", "Exciting Offer!", "Dear {receiver_name},\n\nWe have an exciting offer for {receiver_company}...", "John Doe", "Alice Johnson"],
|
| 75 |
+
["Template", "TEMP_2", "Follow-up", "Following up on our meeting", "Hello {receiver_name},\n\nI hope this email finds you well. I wanted to follow up on our recent meeting...", "Jane Smith", "Bob Brown"]
|
| 76 |
+
]
|
| 77 |
+
initialize_sheet(template_sheet, template_headers, template_sample)
|
| 78 |
+
|
| 79 |
+
logger.info("Successfully connected to all sheets.")
|
| 80 |
+
return sender_sheet, receiver_sheet, template_sheet
|
| 81 |
+
|
| 82 |
+
def get_profiles_from_sheet(sheet):
|
| 83 |
+
data = sheet.get_all_values()
|
| 84 |
+
return data[1:] # Exclude header row
|
| 85 |
+
|
| 86 |
+
def save_profile(sheet, profile_data):
|
| 87 |
+
new_row = [
|
| 88 |
+
"Profile",
|
| 89 |
+
f"PROF_{sheet.row_count}",
|
| 90 |
+
profile_data['name'],
|
| 91 |
+
profile_data['email'],
|
| 92 |
+
profile_data['position'],
|
| 93 |
+
profile_data['company'],
|
| 94 |
+
profile_data['context']
|
| 95 |
+
]
|
| 96 |
+
sheet.append_row(new_row)
|
| 97 |
+
|
| 98 |
+
def delete_profile_from_sheet(sheet, profile_id):
|
| 99 |
+
cell = sheet.find(profile_id)
|
| 100 |
+
if cell:
|
| 101 |
+
sheet.delete_row(cell.row)
|
| 102 |
+
|
| 103 |
+
def update_profile(sheet, profile_id, profile_data):
|
| 104 |
+
cell = sheet.find(profile_id)
|
| 105 |
+
if cell:
|
| 106 |
+
row = cell.row
|
| 107 |
+
sheet.update_cell(row, 3, profile_data['name'])
|
| 108 |
+
sheet.update_cell(row, 4, profile_data['email'])
|
| 109 |
+
sheet.update_cell(row, 5, profile_data['position'])
|
| 110 |
+
sheet.update_cell(row, 6, profile_data['company'])
|
| 111 |
+
sheet.update_cell(row, 7, profile_data['context'])
|
| 112 |
+
|
| 113 |
+
def get_templates(sheet):
|
| 114 |
+
data = sheet.get_all_values()
|
| 115 |
+
return data[1:] # Exclude header row
|
| 116 |
+
|
| 117 |
+
def save_template(sheet, template_data):
|
| 118 |
+
new_row = [
|
| 119 |
+
"Template",
|
| 120 |
+
f"TEMP_{sheet.row_count}",
|
| 121 |
+
template_data['name'],
|
| 122 |
+
template_data['subject'],
|
| 123 |
+
template_data['content'],
|
| 124 |
+
template_data['sender_profile'],
|
| 125 |
+
template_data['receiver_profile']
|
| 126 |
+
]
|
| 127 |
+
sheet.append_row(new_row)
|
| 128 |
+
|
| 129 |
+
def delete_template_from_sheet(sheet, template_id):
|
| 130 |
+
cell = sheet.find(template_id)
|
| 131 |
+
if cell:
|
| 132 |
+
sheet.delete_row(cell.row)
|
| 133 |
+
return True
|
| 134 |
+
return False
|
| 135 |
+
|
| 136 |
+
def update_template(sheet, template_id, template_data):
|
| 137 |
+
cell = sheet.find(template_id)
|
| 138 |
+
if cell:
|
| 139 |
+
row = cell.row
|
| 140 |
+
sheet.update_cell(row, 3, template_data['name'])
|
| 141 |
+
sheet.update_cell(row, 4, template_data['subject'])
|
| 142 |
+
sheet.update_cell(row, 5, template_data['content'])
|
| 143 |
+
sheet.update_cell(row, 6, template_data['sender_profile'])
|
| 144 |
+
sheet.update_cell(row, 7, template_data['receiver_profile'])
|
groq_client.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# groq_client.py
|
| 2 |
+
# Filepath: ai-email-assistant/groq_client.py
|
| 3 |
+
# Interfaces with the Groq API for AI-powered responses
|
| 4 |
+
|
| 5 |
+
import os
|
| 6 |
+
import json
|
| 7 |
+
from groq import Groq
|
| 8 |
+
|
| 9 |
+
def load_config():
|
| 10 |
+
try:
|
| 11 |
+
with open('config.json', 'r') as f:
|
| 12 |
+
return json.load(f)
|
| 13 |
+
except FileNotFoundError:
|
| 14 |
+
return {}
|
| 15 |
+
|
| 16 |
+
config = load_config()
|
| 17 |
+
api_key = config.get('groq_api_key')
|
| 18 |
+
client = Groq(api_key=api_key) if api_key else None
|
| 19 |
+
|
| 20 |
+
def set_api_key(api_key):
|
| 21 |
+
global client
|
| 22 |
+
client = Groq(api_key=api_key)
|
| 23 |
+
|
| 24 |
+
def get_ai_response(message, system_prompt, context=""):
|
| 25 |
+
if not client:
|
| 26 |
+
raise ValueError("Groq API key not set. Please check your config.json file.")
|
| 27 |
+
|
| 28 |
+
messages = [
|
| 29 |
+
{"role": "system", "content": system_prompt},
|
| 30 |
+
{"role": "user", "content": message}
|
| 31 |
+
]
|
| 32 |
+
|
| 33 |
+
if context:
|
| 34 |
+
messages.append({"role": "system", "content": context})
|
| 35 |
+
|
| 36 |
+
response = client.chat.completions.create(
|
| 37 |
+
model="mixtral-8x7b-32768",
|
| 38 |
+
messages=messages,
|
| 39 |
+
temperature=0.7,
|
| 40 |
+
max_tokens=1000
|
| 41 |
+
)
|
| 42 |
+
|
| 43 |
+
return response.choices[0].message.content
|
profile_management.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# profile_management.py
|
| 2 |
+
# Filepath: ai-email-assistant/profile_management.py
|
| 3 |
+
# Handles sender and receiver profile management
|
| 4 |
+
|
| 5 |
+
import gradio as gr
|
| 6 |
+
from google_sheets_utils import get_profiles_from_sheet, save_profile, delete_profile_from_sheet
|
| 7 |
+
|
| 8 |
+
def get_profile_summaries(sheet):
|
| 9 |
+
profiles = get_profiles_from_sheet(sheet)
|
| 10 |
+
return [profile[2] for profile in profiles] # Assuming the name is in the third column
|
| 11 |
+
|
| 12 |
+
def create_profile_management_interface(sheet, profile_type, current_profile):
|
| 13 |
+
with gr.Group():
|
| 14 |
+
gr.Markdown(f"## {profile_type} Profile Management")
|
| 15 |
+
|
| 16 |
+
name = gr.Textbox(label="Name")
|
| 17 |
+
email = gr.Textbox(label="Email")
|
| 18 |
+
position = gr.Textbox(label="Position")
|
| 19 |
+
company = gr.Textbox(label="Company")
|
| 20 |
+
context = gr.Dropdown(["Professional", "Personal"], label="Context")
|
| 21 |
+
|
| 22 |
+
create_btn = gr.Button(f"Create {profile_type} Profile")
|
| 23 |
+
|
| 24 |
+
profiles_list = gr.Dataframe(
|
| 25 |
+
headers=["Type", "ID", "Name", "Email", "Position", "Company", "Context"],
|
| 26 |
+
label=f"Existing {profile_type} Profiles",
|
| 27 |
+
value=get_profiles_from_sheet(sheet),
|
| 28 |
+
interactive=False
|
| 29 |
+
)
|
| 30 |
+
|
| 31 |
+
delete_btn = gr.Button(f"Delete Selected {profile_type} Profile")
|
| 32 |
+
load_btn = gr.Button(f"Load Selected {profile_type} Profile")
|
| 33 |
+
|
| 34 |
+
def create_profile_action():
|
| 35 |
+
profile_data = {
|
| 36 |
+
"name": name.value,
|
| 37 |
+
"email": email.value,
|
| 38 |
+
"position": position.value,
|
| 39 |
+
"company": company.value,
|
| 40 |
+
"context": context.value
|
| 41 |
+
}
|
| 42 |
+
save_profile(sheet, profile_data)
|
| 43 |
+
return get_profiles_from_sheet(sheet)
|
| 44 |
+
|
| 45 |
+
def load_profile(evt: gr.SelectData):
|
| 46 |
+
if evt.value:
|
| 47 |
+
selected_profile = profiles_list.value[evt.index[0]]
|
| 48 |
+
current_profile.value = selected_profile[2] # Set the current profile to the selected name
|
| 49 |
+
return (
|
| 50 |
+
selected_profile[2], # Name
|
| 51 |
+
selected_profile[3], # Email
|
| 52 |
+
selected_profile[4], # Position
|
| 53 |
+
selected_profile[5], # Company
|
| 54 |
+
selected_profile[6], # Context
|
| 55 |
+
)
|
| 56 |
+
return [gr.update() for _ in range(5)]
|
| 57 |
+
|
| 58 |
+
def delete_profile(evt: gr.SelectData):
|
| 59 |
+
if evt.value:
|
| 60 |
+
profile_id = profiles_list.value[evt.index[0]][1]
|
| 61 |
+
delete_profile_from_sheet(sheet, profile_id)
|
| 62 |
+
return get_profiles_from_sheet(sheet)
|
| 63 |
+
return profiles_list.value
|
| 64 |
+
|
| 65 |
+
create_btn.click(create_profile_action, outputs=[profiles_list])
|
| 66 |
+
load_btn.click(load_profile, outputs=[name, email, position, company, context])
|
| 67 |
+
delete_btn.click(delete_profile, outputs=[profiles_list])
|
| 68 |
+
|
| 69 |
+
refresh_btn = gr.Button(f"Refresh {profile_type} Profiles")
|
| 70 |
+
refresh_btn.click(lambda: get_profiles_from_sheet(sheet), outputs=[profiles_list])
|
| 71 |
+
|
| 72 |
+
return name, email, position, company, context, create_btn, profiles_list, delete_btn, load_btn, refresh_btn
|
| 73 |
+
|
| 74 |
+
# Implement get_profiles, create_profile, update_profile, delete_profile functions
|
| 75 |
+
def get_profiles(sheet):
|
| 76 |
+
return get_profiles_from_sheet(sheet)
|
| 77 |
+
|
| 78 |
+
def create_profile(sheet, name, email, position, company, context):
|
| 79 |
+
profile_data = {
|
| 80 |
+
"name": name,
|
| 81 |
+
"email": email,
|
| 82 |
+
"position": position,
|
| 83 |
+
"company": company,
|
| 84 |
+
"context": context
|
| 85 |
+
}
|
| 86 |
+
return save_profile(sheet, profile_data)
|
| 87 |
+
|
| 88 |
+
def update_profile(sheet, profile_id, name, email, position, company, context):
|
| 89 |
+
profile_data = {
|
| 90 |
+
"id": profile_id,
|
| 91 |
+
"name": name,
|
| 92 |
+
"email": email,
|
| 93 |
+
"position": position,
|
| 94 |
+
"company": company,
|
| 95 |
+
"context": context
|
| 96 |
+
}
|
| 97 |
+
return save_profile(sheet, profile_data)
|
| 98 |
+
|
| 99 |
+
def delete_profile(sheet, profile_id):
|
| 100 |
+
return delete_profile_from_sheet(sheet, profile_id)
|
requirements (2).txt
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#
|
| 2 |
+
# This file is autogenerated by pip-compile with Python 3.12
|
| 3 |
+
# by the following command:
|
| 4 |
+
#
|
| 5 |
+
# pip-compile
|
| 6 |
+
#
|
| 7 |
+
aiofiles==23.2.1
|
| 8 |
+
# via gradio
|
| 9 |
+
annotated-types==0.7.0
|
| 10 |
+
# via pydantic
|
| 11 |
+
anyio==4.6.0
|
| 12 |
+
# via
|
| 13 |
+
# gradio
|
| 14 |
+
# groq
|
| 15 |
+
# httpx
|
| 16 |
+
# starlette
|
| 17 |
+
cachetools==5.5.0
|
| 18 |
+
# via google-auth
|
| 19 |
+
certifi==2024.8.30
|
| 20 |
+
# via
|
| 21 |
+
# httpcore
|
| 22 |
+
# httpx
|
| 23 |
+
# requests
|
| 24 |
+
charset-normalizer==3.3.2
|
| 25 |
+
# via requests
|
| 26 |
+
click==8.1.7
|
| 27 |
+
# via
|
| 28 |
+
# duckduckgo-search
|
| 29 |
+
# typer
|
| 30 |
+
# uvicorn
|
| 31 |
+
colorama==0.4.6
|
| 32 |
+
# via
|
| 33 |
+
# click
|
| 34 |
+
# tqdm
|
| 35 |
+
contourpy==1.3.0
|
| 36 |
+
# via matplotlib
|
| 37 |
+
cycler==0.12.1
|
| 38 |
+
# via matplotlib
|
| 39 |
+
distro==1.9.0
|
| 40 |
+
# via groq
|
| 41 |
+
duckduckgo-search==6.2.12
|
| 42 |
+
# via -r requirements.in
|
| 43 |
+
fastapi==0.115.0
|
| 44 |
+
# via gradio
|
| 45 |
+
ffmpy==0.4.0
|
| 46 |
+
# via gradio
|
| 47 |
+
filelock==3.16.1
|
| 48 |
+
# via huggingface-hub
|
| 49 |
+
fonttools==4.53.1
|
| 50 |
+
# via matplotlib
|
| 51 |
+
fsspec==2024.9.0
|
| 52 |
+
# via
|
| 53 |
+
# gradio-client
|
| 54 |
+
# huggingface-hub
|
| 55 |
+
google-auth==2.35.0
|
| 56 |
+
# via
|
| 57 |
+
# -r requirements.in
|
| 58 |
+
# google-auth-oauthlib
|
| 59 |
+
# gspread
|
| 60 |
+
google-auth-oauthlib==1.2.1
|
| 61 |
+
# via gspread
|
| 62 |
+
gradio==4.44.0
|
| 63 |
+
# via -r requirements.in
|
| 64 |
+
gradio-client==1.3.0
|
| 65 |
+
# via gradio
|
| 66 |
+
groq==0.11.0
|
| 67 |
+
# via -r requirements.in
|
| 68 |
+
gspread==6.1.2
|
| 69 |
+
# via -r requirements.in
|
| 70 |
+
h11==0.14.0
|
| 71 |
+
# via
|
| 72 |
+
# httpcore
|
| 73 |
+
# uvicorn
|
| 74 |
+
httpcore==1.0.5
|
| 75 |
+
# via httpx
|
| 76 |
+
httpx==0.27.2
|
| 77 |
+
# via
|
| 78 |
+
# gradio
|
| 79 |
+
# gradio-client
|
| 80 |
+
# groq
|
| 81 |
+
huggingface-hub==0.25.1
|
| 82 |
+
# via
|
| 83 |
+
# gradio
|
| 84 |
+
# gradio-client
|
| 85 |
+
idna==3.10
|
| 86 |
+
# via
|
| 87 |
+
# anyio
|
| 88 |
+
# httpx
|
| 89 |
+
# requests
|
| 90 |
+
importlib-resources==6.4.5
|
| 91 |
+
# via gradio
|
| 92 |
+
jinja2==3.1.4
|
| 93 |
+
# via gradio
|
| 94 |
+
kiwisolver==1.4.7
|
| 95 |
+
# via matplotlib
|
| 96 |
+
markdown-it-py==3.0.0
|
| 97 |
+
# via rich
|
| 98 |
+
markupsafe==2.1.5
|
| 99 |
+
# via
|
| 100 |
+
# gradio
|
| 101 |
+
# jinja2
|
| 102 |
+
matplotlib==3.9.2
|
| 103 |
+
# via gradio
|
| 104 |
+
mdurl==0.1.2
|
| 105 |
+
# via markdown-it-py
|
| 106 |
+
numpy==2.1.1
|
| 107 |
+
# via
|
| 108 |
+
# contourpy
|
| 109 |
+
# gradio
|
| 110 |
+
# matplotlib
|
| 111 |
+
# pandas
|
| 112 |
+
oauthlib==3.2.2
|
| 113 |
+
# via requests-oauthlib
|
| 114 |
+
orjson==3.10.7
|
| 115 |
+
# via gradio
|
| 116 |
+
packaging==24.1
|
| 117 |
+
# via
|
| 118 |
+
# gradio
|
| 119 |
+
# gradio-client
|
| 120 |
+
# huggingface-hub
|
| 121 |
+
# matplotlib
|
| 122 |
+
pandas==2.2.3
|
| 123 |
+
# via gradio
|
| 124 |
+
pillow==10.4.0
|
| 125 |
+
# via
|
| 126 |
+
# gradio
|
| 127 |
+
# matplotlib
|
| 128 |
+
primp==0.6.2
|
| 129 |
+
# via duckduckgo-search
|
| 130 |
+
pyasn1==0.6.1
|
| 131 |
+
# via
|
| 132 |
+
# pyasn1-modules
|
| 133 |
+
# rsa
|
| 134 |
+
pyasn1-modules==0.4.1
|
| 135 |
+
# via google-auth
|
| 136 |
+
pydantic==2.9.2
|
| 137 |
+
# via
|
| 138 |
+
# fastapi
|
| 139 |
+
# gradio
|
| 140 |
+
# groq
|
| 141 |
+
pydantic-core==2.23.4
|
| 142 |
+
# via pydantic
|
| 143 |
+
pydub==0.25.1
|
| 144 |
+
# via gradio
|
| 145 |
+
pygments==2.18.0
|
| 146 |
+
# via rich
|
| 147 |
+
pyparsing==3.1.4
|
| 148 |
+
# via matplotlib
|
| 149 |
+
python-dateutil==2.9.0.post0
|
| 150 |
+
# via
|
| 151 |
+
# matplotlib
|
| 152 |
+
# pandas
|
| 153 |
+
python-dotenv==1.0.1
|
| 154 |
+
# via -r requirements.in
|
| 155 |
+
python-multipart==0.0.10
|
| 156 |
+
# via gradio
|
| 157 |
+
pytz==2024.2
|
| 158 |
+
# via pandas
|
| 159 |
+
pyyaml==6.0.2
|
| 160 |
+
# via
|
| 161 |
+
# gradio
|
| 162 |
+
# huggingface-hub
|
| 163 |
+
requests==2.32.3
|
| 164 |
+
# via
|
| 165 |
+
# huggingface-hub
|
| 166 |
+
# requests-oauthlib
|
| 167 |
+
requests-oauthlib==2.0.0
|
| 168 |
+
# via google-auth-oauthlib
|
| 169 |
+
rich==13.8.1
|
| 170 |
+
# via typer
|
| 171 |
+
rsa==4.9
|
| 172 |
+
# via google-auth
|
| 173 |
+
ruff==0.6.7
|
| 174 |
+
# via gradio
|
| 175 |
+
semantic-version==2.10.0
|
| 176 |
+
# via gradio
|
| 177 |
+
shellingham==1.5.4
|
| 178 |
+
# via typer
|
| 179 |
+
six==1.16.0
|
| 180 |
+
# via python-dateutil
|
| 181 |
+
sniffio==1.3.1
|
| 182 |
+
# via
|
| 183 |
+
# anyio
|
| 184 |
+
# groq
|
| 185 |
+
# httpx
|
| 186 |
+
starlette==0.38.6
|
| 187 |
+
# via fastapi
|
| 188 |
+
tomlkit==0.12.0
|
| 189 |
+
# via gradio
|
| 190 |
+
tqdm==4.66.5
|
| 191 |
+
# via huggingface-hub
|
| 192 |
+
typer==0.12.5
|
| 193 |
+
# via gradio
|
| 194 |
+
typing-extensions==4.12.2
|
| 195 |
+
# via
|
| 196 |
+
# fastapi
|
| 197 |
+
# gradio
|
| 198 |
+
# gradio-client
|
| 199 |
+
# groq
|
| 200 |
+
# huggingface-hub
|
| 201 |
+
# pydantic
|
| 202 |
+
# pydantic-core
|
| 203 |
+
# typer
|
| 204 |
+
tzdata==2024.1
|
| 205 |
+
# via pandas
|
| 206 |
+
urllib3==2.2.3
|
| 207 |
+
# via
|
| 208 |
+
# gradio
|
| 209 |
+
# requests
|
| 210 |
+
uvicorn==0.30.6
|
| 211 |
+
# via gradio
|
| 212 |
+
websockets==12.0
|
| 213 |
+
# via gradio-client
|
system_prompt.txt
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
You are an AI-driven email assistant designed to help users generate and refine personalized emails for various scenarios, including professional and personal contexts. Your goal is to gather necessary information through natural conversation and then create tailored emails. Adapt your role based on the email type, switching between casual assistant and professional content generator as needed. Follow these guidelines:
|
| 2 |
+
1. Initial Interaction:
|
| 3 |
+
- Greet the user warmly and briefly explain your purpose.
|
| 4 |
+
- Ask an open-ended question about the type of email they need help with.
|
| 5 |
+
- Example: "Hello! I'm here to help you craft the perfect email. What kind of email are you looking to write today?"
|
| 6 |
+
2. Information Gathering:
|
| 7 |
+
- Based on the user's response, ask relevant follow-up questions to gather necessary details.
|
| 8 |
+
- Adapt your questions to various email scenarios, including:
|
| 9 |
+
a) Professional emails: sales pitch, follow-up, introduction, product launch, partnership proposal
|
| 10 |
+
b) Personal emails: friendly correspondence, family updates
|
| 11 |
+
c) Transactional emails: customer support, shipping information, order confirmation
|
| 12 |
+
d) Formal communications: complaint to a company, job application, resignation letter
|
| 13 |
+
- Keep questions concise and conversational.
|
| 14 |
+
- For professional emails, gather additional relevant information:
|
| 15 |
+
a) Sender's details: name, job title, company, industry
|
| 16 |
+
b) Recipient's details: name, job title, company (if known)
|
| 17 |
+
c) Specific purpose: e.g., introducing a new product, following up on a sales call
|
| 18 |
+
d) Key points: e.g., 3 main features of a product, unique selling propositions
|
| 19 |
+
e) Any prior communication or context
|
| 20 |
+
f) Call-to-action or desired outcome
|
| 21 |
+
- For personal or transactional emails, adjust questions accordingly to gather relevant details.
|
| 22 |
+
3. Background Checklist:
|
| 23 |
+
- Maintain an internal checklist of essential information for different email types.
|
| 24 |
+
- Discreetly check off items as you gather information.
|
| 25 |
+
- If crucial information is missing, naturally work it into the conversation.
|
| 26 |
+
4. Information Confirmation:
|
| 27 |
+
- Summarize the gathered information concisely.
|
| 28 |
+
- Ask if anything is missing or needs changing.
|
| 29 |
+
5. Role Adaptation:
|
| 30 |
+
- Based on the email type, adapt your role:
|
| 31 |
+
a) For professional emails: Take on the role of a professional content generator. Use industry-specific language, maintain a formal tone, and focus on creating compelling, persuasive content.
|
| 32 |
+
b) For personal emails: Maintain a friendly, conversational tone and focus on personal details and emotional context.
|
| 33 |
+
c) For transactional emails: Adopt a clear, concise, and informative tone, ensuring all necessary details are included.
|
| 34 |
+
6. Email Generation:
|
| 35 |
+
- Create a personalized draft based on the confirmed information and your adapted role.
|
| 36 |
+
- Ensure the email is appropriate for the specific scenario and recipient.
|
| 37 |
+
- For professional emails, incorporate:
|
| 38 |
+
a) A strong, attention-grabbing opening
|
| 39 |
+
b) Clear presentation of key points or features
|
| 40 |
+
c) Relevant data or statistics to support your message
|
| 41 |
+
d) A clear call-to-action
|
| 42 |
+
- For personal or transactional emails, focus on clarity, warmth, or informativeness as appropriate.
|
| 43 |
+
7. Refinement and Feedback:
|
| 44 |
+
- Present the draft to the user.
|
| 45 |
+
- Ask for overall impressions and any specific areas needing adjustment.
|
| 46 |
+
- Offer to make changes based on feedback.
|
| 47 |
+
8. Iterative Improvement:
|
| 48 |
+
- Make requested adjustments promptly.
|
| 49 |
+
- Continue refining until the user is satisfied.
|
| 50 |
+
9. Final Touches:
|
| 51 |
+
- Suggest additions like professional signatures, legal disclaimers, or attachment reminders if appropriate.
|
| 52 |
+
- Perform a final check for clarity, grammar, coherence, and appropriateness for the intended audience.
|
| 53 |
+
10. Completion and Next Steps:
|
| 54 |
+
- Present the final version.
|
| 55 |
+
- Offer to save as a template or start a new email if needed.
|
| 56 |
+
11. Adaptability:
|
| 57 |
+
- Remember user preferences within the session.
|
| 58 |
+
- Adjust your approach based on the user's communication style and the email context.
|
| 59 |
+
12. Clarity and Error Handling:
|
| 60 |
+
- If confused, ask for clarification naturally.
|
| 61 |
+
- Guide users clearly if they seem unsure about the process.
|
| 62 |
+
13. Privacy Reminder:
|
| 63 |
+
- Tactfully remind users not to share sensitive personal information.
|
| 64 |
+
- Mention that information isn't stored between sessions.
|
| 65 |
+
Always aim for clear, relevant, and engaging responses. Adapt your personality and language to match the user's tone and the email's purpose. Your goal is to make the email creation process smooth, efficient, and tailored to each unique situation, whether it's a high-stakes professional communication or a casual personal message.
|
web_search.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# web_search.py
|
| 2 |
+
# Filepath: ai-email-assistant/web_search.py
|
| 3 |
+
# Implements web search functionality
|
| 4 |
+
|
| 5 |
+
from duckduckgo_search import DDGS
|
| 6 |
+
|
| 7 |
+
def search_web(query, num_results=5):
|
| 8 |
+
try:
|
| 9 |
+
with DDGS() as ddgs:
|
| 10 |
+
results = list(ddgs.text(query, max_results=num_results))
|
| 11 |
+
return [f"{r['title']}: {r['body']}" for r in results]
|
| 12 |
+
except Exception as e:
|
| 13 |
+
return [f"Error performing web search: {str(e)}"]
|