Spaces:
Build error
Build error
Upload 9 files
Browse files- app.py +157 -18
- chat.py +177 -47
- email_actions.py +171 -82
- google_sheets_utils.py +254 -132
- groq_client.py +10 -5
- profile_management.py +154 -83
- requirements.txt +69 -4
- system_prompt.txt +50 -65
app.py
CHANGED
|
@@ -2,24 +2,49 @@
|
|
| 2 |
# Filepath: ai-email-assistant/app.py
|
| 3 |
# Main application entry point
|
| 4 |
|
| 5 |
-
import
|
| 6 |
-
from dotenv import load_dotenv
|
| 7 |
import gradio as gr
|
| 8 |
from chat import create_chat_interface
|
| 9 |
-
from profile_management import create_profile_management_interface
|
| 10 |
from email_actions import create_email_actions_interface, get_template_summaries
|
| 11 |
from groq_client import set_api_key
|
| 12 |
-
from google_sheets_utils import
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
-
|
| 15 |
-
load_dotenv()
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
def main():
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
if api_key:
|
| 20 |
set_api_key(api_key)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
-
|
| 23 |
|
| 24 |
with gr.Blocks() as app:
|
| 25 |
gr.Markdown("# AI Email Assistant")
|
|
@@ -29,28 +54,142 @@ def main():
|
|
| 29 |
current_receiver = gr.State(None)
|
| 30 |
current_template = gr.State(None)
|
| 31 |
|
| 32 |
-
with gr.Tabs():
|
| 33 |
with gr.Tab("Profile Management"):
|
| 34 |
-
with gr.Tabs():
|
| 35 |
-
with gr.
|
| 36 |
-
sender_components = create_profile_management_interface(
|
| 37 |
-
|
| 38 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
|
| 40 |
with gr.Tab("Email Actions"):
|
| 41 |
-
email_actions_components = create_email_actions_interface(
|
|
|
|
|
|
|
|
|
|
| 42 |
|
| 43 |
with gr.Tab("Chat"):
|
| 44 |
# Get summaries for context
|
| 45 |
-
sender_summaries = get_profile_summaries(
|
| 46 |
-
|
| 47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
chat_interface = create_chat_interface(
|
| 50 |
sender_summaries, receiver_summaries, template_summaries,
|
| 51 |
current_sender, current_receiver, current_template,
|
| 52 |
sender_components, receiver_components, email_actions_components
|
| 53 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
|
| 55 |
app.launch()
|
| 56 |
|
|
|
|
| 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
|
| 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 (
|
| 12 |
+
get_or_create_spreadsheet,
|
| 13 |
+
get_profile_summaries,
|
| 14 |
+
)
|
| 15 |
+
import logging
|
| 16 |
+
from dotenv import load_dotenv
|
| 17 |
+
import os
|
| 18 |
+
|
| 19 |
+
# Set up logging
|
| 20 |
+
logging.basicConfig(level=logging.DEBUG)
|
| 21 |
+
logger = logging.getLogger(__name__)
|
| 22 |
|
| 23 |
+
def load_config():
|
| 24 |
+
load_dotenv()
|
| 25 |
+
return {
|
| 26 |
+
'groq_api_key': os.getenv('GROQ_API_KEY')
|
| 27 |
+
}
|
| 28 |
|
| 29 |
def main():
|
| 30 |
+
logger.debug("Starting the application")
|
| 31 |
+
config = load_config()
|
| 32 |
+
logger.debug(f"Loaded config: {config}")
|
| 33 |
+
|
| 34 |
+
api_key = config.get('groq_api_key')
|
| 35 |
if api_key:
|
| 36 |
set_api_key(api_key)
|
| 37 |
+
logger.debug("API key set successfully")
|
| 38 |
+
else:
|
| 39 |
+
logger.warning("No API key found in .env file")
|
| 40 |
+
|
| 41 |
+
spreadsheet = get_or_create_spreadsheet()
|
| 42 |
+
if not spreadsheet:
|
| 43 |
+
logger.error("Failed to connect to Google Sheets")
|
| 44 |
+
gr.Error("Failed to connect to Google Sheets. Please check your credentials and internet connection.")
|
| 45 |
+
return
|
| 46 |
|
| 47 |
+
logger.debug("Connected to Google Sheets successfully")
|
| 48 |
|
| 49 |
with gr.Blocks() as app:
|
| 50 |
gr.Markdown("# AI Email Assistant")
|
|
|
|
| 54 |
current_receiver = gr.State(None)
|
| 55 |
current_template = gr.State(None)
|
| 56 |
|
| 57 |
+
with gr.Tabs() as tabs:
|
| 58 |
with gr.Tab("Profile Management"):
|
| 59 |
+
with gr.Tabs() as profile_tabs:
|
| 60 |
+
with gr.TabItem("Sender Profiles"):
|
| 61 |
+
sender_components = create_profile_management_interface(
|
| 62 |
+
spreadsheet, "Sender Profiles", current_sender
|
| 63 |
+
)
|
| 64 |
+
sender_block = sender_components["block"]
|
| 65 |
+
|
| 66 |
+
with gr.TabItem("Receiver Profiles"):
|
| 67 |
+
receiver_components = create_profile_management_interface(
|
| 68 |
+
spreadsheet, "Receiver Profiles", current_receiver
|
| 69 |
+
)
|
| 70 |
+
receiver_block = receiver_components["block"]
|
| 71 |
|
| 72 |
with gr.Tab("Email Actions"):
|
| 73 |
+
email_actions_components = create_email_actions_interface(
|
| 74 |
+
spreadsheet, current_template
|
| 75 |
+
)
|
| 76 |
+
email_actions_block = email_actions_components["block"]
|
| 77 |
|
| 78 |
with gr.Tab("Chat"):
|
| 79 |
# Get summaries for context
|
| 80 |
+
sender_summaries = get_profile_summaries(
|
| 81 |
+
spreadsheet.worksheet("Sender Profiles"), "Sender Profile"
|
| 82 |
+
)
|
| 83 |
+
receiver_summaries = get_profile_summaries(
|
| 84 |
+
spreadsheet.worksheet("Receiver Profiles"), "Receiver Profile"
|
| 85 |
+
)
|
| 86 |
+
template_summaries = get_template_summaries(spreadsheet)
|
| 87 |
|
| 88 |
chat_interface = create_chat_interface(
|
| 89 |
sender_summaries, receiver_summaries, template_summaries,
|
| 90 |
current_sender, current_receiver, current_template,
|
| 91 |
sender_components, receiver_components, email_actions_components
|
| 92 |
)
|
| 93 |
+
|
| 94 |
+
# Refresh profiles and templates when tabs are selected
|
| 95 |
+
def refresh_data(tab: gr.SelectData):
|
| 96 |
+
if tab.index == 0: # Profile Management tab
|
| 97 |
+
return (
|
| 98 |
+
sender_components["refresh_profiles"](),
|
| 99 |
+
receiver_components["refresh_profiles"](),
|
| 100 |
+
None
|
| 101 |
+
)
|
| 102 |
+
elif tab.index == 1: # Email Actions tab
|
| 103 |
+
return (
|
| 104 |
+
None,
|
| 105 |
+
None,
|
| 106 |
+
email_actions_components["refresh_templates"]()
|
| 107 |
+
)
|
| 108 |
+
return None, None, None # Return None for all other cases
|
| 109 |
+
|
| 110 |
+
tabs.select(
|
| 111 |
+
refresh_data,
|
| 112 |
+
inputs=None,
|
| 113 |
+
outputs=[
|
| 114 |
+
sender_components["profiles_list"],
|
| 115 |
+
receiver_components["profiles_list"],
|
| 116 |
+
email_actions_components["templates_list"]
|
| 117 |
+
]
|
| 118 |
+
)
|
| 119 |
+
|
| 120 |
+
# Refresh data on first load
|
| 121 |
+
app.load(
|
| 122 |
+
lambda: (
|
| 123 |
+
sender_components["refresh_profiles"](),
|
| 124 |
+
receiver_components["refresh_profiles"](),
|
| 125 |
+
email_actions_components["refresh_templates"]()
|
| 126 |
+
),
|
| 127 |
+
inputs=None,
|
| 128 |
+
outputs=[
|
| 129 |
+
sender_components["profiles_list"],
|
| 130 |
+
receiver_components["profiles_list"],
|
| 131 |
+
email_actions_components["templates_list"]
|
| 132 |
+
]
|
| 133 |
+
)
|
| 134 |
+
|
| 135 |
+
# Update the select event handlers for profile lists and template list
|
| 136 |
+
sender_components["profiles_list"].select(
|
| 137 |
+
sender_components["load_profile"],
|
| 138 |
+
inputs=[
|
| 139 |
+
sender_components["name"],
|
| 140 |
+
sender_components["email"],
|
| 141 |
+
sender_components["position"],
|
| 142 |
+
sender_components["company"],
|
| 143 |
+
sender_components["context"],
|
| 144 |
+
sender_components["profile_id"]
|
| 145 |
+
],
|
| 146 |
+
outputs=[
|
| 147 |
+
sender_components["name"],
|
| 148 |
+
sender_components["email"],
|
| 149 |
+
sender_components["position"],
|
| 150 |
+
sender_components["company"],
|
| 151 |
+
sender_components["context"],
|
| 152 |
+
sender_components["profile_id"]
|
| 153 |
+
]
|
| 154 |
+
)
|
| 155 |
+
|
| 156 |
+
receiver_components["profiles_list"].select(
|
| 157 |
+
receiver_components["load_profile"],
|
| 158 |
+
inputs=[
|
| 159 |
+
receiver_components["name"],
|
| 160 |
+
receiver_components["email"],
|
| 161 |
+
receiver_components["position"],
|
| 162 |
+
receiver_components["company"],
|
| 163 |
+
receiver_components["context"],
|
| 164 |
+
receiver_components["profile_id"]
|
| 165 |
+
],
|
| 166 |
+
outputs=[
|
| 167 |
+
receiver_components["name"],
|
| 168 |
+
receiver_components["email"],
|
| 169 |
+
receiver_components["position"],
|
| 170 |
+
receiver_components["company"],
|
| 171 |
+
receiver_components["context"],
|
| 172 |
+
receiver_components["profile_id"]
|
| 173 |
+
]
|
| 174 |
+
)
|
| 175 |
+
|
| 176 |
+
email_actions_components["templates_list"].select(
|
| 177 |
+
email_actions_components["load_template"],
|
| 178 |
+
inputs=[
|
| 179 |
+
email_actions_components["t_name"],
|
| 180 |
+
email_actions_components["t_type"],
|
| 181 |
+
email_actions_components["t_subject"],
|
| 182 |
+
email_actions_components["t_body"],
|
| 183 |
+
email_actions_components["t_id"]
|
| 184 |
+
],
|
| 185 |
+
outputs=[
|
| 186 |
+
email_actions_components["t_name"],
|
| 187 |
+
email_actions_components["t_type"],
|
| 188 |
+
email_actions_components["t_subject"],
|
| 189 |
+
email_actions_components["t_body"],
|
| 190 |
+
email_actions_components["t_id"]
|
| 191 |
+
]
|
| 192 |
+
)
|
| 193 |
|
| 194 |
app.launch()
|
| 195 |
|
chat.py
CHANGED
|
@@ -3,10 +3,33 @@
|
|
| 3 |
# Implements the chat interface and message handling
|
| 4 |
|
| 5 |
import gradio as gr
|
| 6 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
from web_search import search_web
|
| 8 |
-
from
|
| 9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
def load_system_prompt(filename):
|
| 12 |
try:
|
|
@@ -18,13 +41,98 @@ def load_system_prompt(filename):
|
|
| 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 |
-
|
| 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 |
|
|
@@ -41,53 +149,75 @@ def create_chat_interface(sender_summaries, receiver_summaries, template_summari
|
|
| 41 |
|
| 42 |
return context
|
| 43 |
|
| 44 |
-
|
| 45 |
-
|
| 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(
|
| 64 |
context = get_context()
|
| 65 |
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
)
|
| 86 |
-
|
| 87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
-
|
| 90 |
-
return "", chat_history
|
| 91 |
|
| 92 |
msg.submit(respond, [msg, chatbot], [msg, chatbot])
|
| 93 |
clear.click(lambda: None, None, chatbot, queue=False)
|
|
|
|
| 3 |
# Implements the chat interface and message handling
|
| 4 |
|
| 5 |
import gradio as gr
|
| 6 |
+
import os
|
| 7 |
+
import json
|
| 8 |
+
import logging
|
| 9 |
+
from groq import Groq
|
| 10 |
+
from dotenv import load_dotenv
|
| 11 |
+
|
| 12 |
from web_search import search_web
|
| 13 |
+
from google_sheets_utils import (
|
| 14 |
+
get_or_create_spreadsheet,
|
| 15 |
+
get_profiles_from_sheet,
|
| 16 |
+
delete_profile_from_sheet,
|
| 17 |
+
get_profile_summaries,
|
| 18 |
+
get_template_summaries,
|
| 19 |
+
get_profile_by_id,
|
| 20 |
+
get_template_by_id,
|
| 21 |
+
save_profile,
|
| 22 |
+
update_profile_in_sheet,
|
| 23 |
+
delete_template_from_sheet,
|
| 24 |
+
save_template,
|
| 25 |
+
update_template,
|
| 26 |
+
generate_template_id,
|
| 27 |
+
generate_profile_id
|
| 28 |
+
)
|
| 29 |
+
from email_actions import get_templates
|
| 30 |
+
|
| 31 |
+
logging.basicConfig(level=logging.INFO)
|
| 32 |
+
logger = logging.getLogger(__name__)
|
| 33 |
|
| 34 |
def load_system_prompt(filename):
|
| 35 |
try:
|
|
|
|
| 41 |
def create_chat_interface(sender_summaries, receiver_summaries, template_summaries,
|
| 42 |
current_sender, current_receiver, current_template,
|
| 43 |
sender_components, receiver_components, email_actions_components):
|
| 44 |
+
system_prompt = load_system_prompt('system_prompt.txt')
|
|
|
|
| 45 |
|
| 46 |
chatbot = gr.Chatbot()
|
| 47 |
msg = gr.Textbox()
|
| 48 |
clear = gr.Button("Clear")
|
| 49 |
|
| 50 |
+
# Global spreadsheet variable
|
| 51 |
+
global spreadsheet
|
| 52 |
+
spreadsheet = get_or_create_spreadsheet()
|
| 53 |
+
|
| 54 |
+
# Initialize Groq client
|
| 55 |
+
load_dotenv() # Load environment variables from .env file
|
| 56 |
+
groq_api_key = os.getenv('GROQ_API_KEY')
|
| 57 |
+
if not groq_api_key:
|
| 58 |
+
logger.error("GROQ_API_KEY not found in environment variables")
|
| 59 |
+
raise ValueError("GROQ_API_KEY not found in environment variables")
|
| 60 |
+
client = Groq(api_key=groq_api_key)
|
| 61 |
+
model = "llama3-groq-70b-8192-tool-use-preview"
|
| 62 |
+
|
| 63 |
+
# Define available functions (tools) for the assistant
|
| 64 |
+
def get_profile_summaries_tool(profile_type: str):
|
| 65 |
+
"""Get summaries of profiles (Sender or Receiver)"""
|
| 66 |
+
return get_profile_summaries(spreadsheet.worksheet(profile_type), profile_type)
|
| 67 |
+
|
| 68 |
+
def get_template_summaries_tool():
|
| 69 |
+
"""Get summaries of email templates"""
|
| 70 |
+
return get_template_summaries(spreadsheet)
|
| 71 |
+
|
| 72 |
+
def get_profile_by_id_tool(profile_type: str, profile_id: str):
|
| 73 |
+
"""Get a specific profile by ID"""
|
| 74 |
+
return get_profile_by_id(spreadsheet.worksheet(profile_type), profile_id)
|
| 75 |
+
|
| 76 |
+
def get_template_by_id_tool(template_id: str):
|
| 77 |
+
"""Get a specific template by ID"""
|
| 78 |
+
return get_template_by_id(spreadsheet.worksheet("Email Templates"), template_id)
|
| 79 |
+
|
| 80 |
+
def create_profile_tool(profile_type: str, name: str, email: str, position: str, company: str, context: str):
|
| 81 |
+
"""Create a new profile"""
|
| 82 |
+
return save_profile(spreadsheet.worksheet(profile_type), [profile_type, generate_profile_id(), name, email, position, company, context])
|
| 83 |
+
|
| 84 |
+
def update_profile_tool(profile_type: str, profile_id: str, name: str, email: str, position: str, company: str, context: str):
|
| 85 |
+
"""Update an existing profile"""
|
| 86 |
+
return update_profile_in_sheet(spreadsheet.worksheet(profile_type), profile_id, [profile_type, profile_id, name, email, position, company, context])
|
| 87 |
+
|
| 88 |
+
def delete_profile_tool(profile_type: str, profile_id: str):
|
| 89 |
+
"""Delete a profile"""
|
| 90 |
+
return delete_profile_from_sheet(spreadsheet.worksheet(profile_type), profile_id)
|
| 91 |
+
|
| 92 |
+
def create_template_tool(name: str, template_type: str, subject: str, body: str):
|
| 93 |
+
"""Create a new email template"""
|
| 94 |
+
return save_template(spreadsheet.worksheet("Email Templates"), generate_template_id(), name, template_type, subject, body)
|
| 95 |
+
|
| 96 |
+
def update_template_tool(template_id: str, name: str, template_type: str, subject: str, body: str):
|
| 97 |
+
"""Update an existing email template"""
|
| 98 |
+
return update_template(spreadsheet.worksheet("Email Templates"), template_id, name, template_type, subject, body)
|
| 99 |
+
|
| 100 |
+
def delete_template_tool(template_id: str):
|
| 101 |
+
"""Delete an email template"""
|
| 102 |
+
return delete_template_from_sheet(spreadsheet.worksheet("Email Templates"), template_id)
|
| 103 |
+
|
| 104 |
+
# Map function names to actual functions
|
| 105 |
+
available_functions = {
|
| 106 |
+
"get_profile_summaries": get_profile_summaries_tool,
|
| 107 |
+
"get_template_summaries": get_template_summaries_tool,
|
| 108 |
+
"get_profile_by_id": get_profile_by_id_tool,
|
| 109 |
+
"get_template_by_id": get_template_by_id_tool,
|
| 110 |
+
"create_profile": create_profile_tool,
|
| 111 |
+
"update_profile": update_profile_tool,
|
| 112 |
+
"delete_profile": delete_profile_tool,
|
| 113 |
+
"create_template": create_template_tool,
|
| 114 |
+
"update_template": update_template_tool,
|
| 115 |
+
"delete_template": delete_template_tool,
|
| 116 |
+
"search_web": search_web,
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
# Define functions (tools) for the assistant to use
|
| 120 |
+
tools = []
|
| 121 |
+
for func_name, func in available_functions.items():
|
| 122 |
+
tool = {
|
| 123 |
+
"type": "function",
|
| 124 |
+
"function": {
|
| 125 |
+
"name": func_name,
|
| 126 |
+
"description": func.__doc__ or "",
|
| 127 |
+
"parameters": {
|
| 128 |
+
"type": "object",
|
| 129 |
+
"properties": {}, # Parameters will be auto-inferred or can be explicitly defined
|
| 130 |
+
"required": []
|
| 131 |
+
},
|
| 132 |
+
},
|
| 133 |
+
}
|
| 134 |
+
tools.append(tool)
|
| 135 |
+
|
| 136 |
def get_context():
|
| 137 |
context = "Current context:\n"
|
| 138 |
|
|
|
|
| 149 |
|
| 150 |
return context
|
| 151 |
|
| 152 |
+
# Initialize chat history
|
| 153 |
+
chat_history = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
|
| 155 |
+
def respond(user_input, history):
|
| 156 |
context = get_context()
|
| 157 |
|
| 158 |
+
messages = [
|
| 159 |
+
{"role": "system", "content": system_prompt},
|
| 160 |
+
{"role": "system", "content": context}
|
| 161 |
+
]
|
| 162 |
+
# Append conversation history
|
| 163 |
+
for human_msg, ai_msg in history:
|
| 164 |
+
messages.append({"role": "user", "content": human_msg})
|
| 165 |
+
messages.append({"role": "assistant", "content": ai_msg})
|
| 166 |
+
# Append latest user input
|
| 167 |
+
messages.append({"role": "user", "content": user_input})
|
| 168 |
+
|
| 169 |
+
response = client.chat.completions.create(
|
| 170 |
+
model=model,
|
| 171 |
+
messages=messages,
|
| 172 |
+
tools=tools,
|
| 173 |
+
tool_choice="auto",
|
| 174 |
+
max_tokens=512 # Adjust as needed
|
| 175 |
+
)
|
| 176 |
+
|
| 177 |
+
response_message = response.choices[0].message
|
| 178 |
+
|
| 179 |
+
# Check if the assistant made any tool calls
|
| 180 |
+
if hasattr(response_message, 'tool_calls') and response_message.tool_calls:
|
| 181 |
+
# Process each tool call
|
| 182 |
+
for tool_call in response_message.tool_calls:
|
| 183 |
+
function_name = tool_call.function.name
|
| 184 |
+
function_args = json.loads(tool_call.function.arguments)
|
| 185 |
+
function_to_call = available_functions.get(function_name)
|
| 186 |
+
|
| 187 |
+
if function_to_call:
|
| 188 |
+
# Call the function with arguments
|
| 189 |
+
function_response = function_to_call(**function_args)
|
| 190 |
+
|
| 191 |
+
# Add the function response to the messages
|
| 192 |
+
messages.append({
|
| 193 |
+
"role": "tool",
|
| 194 |
+
"content": json.dumps(function_response),
|
| 195 |
+
"tool_call_id": tool_call.id,
|
| 196 |
+
})
|
| 197 |
+
else:
|
| 198 |
+
# If function not found, add an error message
|
| 199 |
+
messages.append({
|
| 200 |
+
"role": "tool",
|
| 201 |
+
"content": json.dumps({"error": f"Function {function_name} not found."}),
|
| 202 |
+
"tool_call_id": tool_call.id,
|
| 203 |
+
})
|
| 204 |
+
|
| 205 |
+
# Send the updated messages back to the model for final response
|
| 206 |
+
response = client.chat.completions.create(
|
| 207 |
+
model=model,
|
| 208 |
+
messages=messages,
|
| 209 |
+
tools=tools,
|
| 210 |
+
tool_choice="auto",
|
| 211 |
+
max_tokens=512
|
| 212 |
)
|
| 213 |
+
response_message = response.choices[0].message
|
| 214 |
+
|
| 215 |
+
assistant_response = response_message.content
|
| 216 |
+
|
| 217 |
+
# Update chat history
|
| 218 |
+
history.append((user_input, assistant_response))
|
| 219 |
|
| 220 |
+
return "", history
|
|
|
|
| 221 |
|
| 222 |
msg.submit(respond, [msg, chatbot], [msg, chatbot])
|
| 223 |
clear.click(lambda: None, None, chatbot, queue=False)
|
email_actions.py
CHANGED
|
@@ -1,98 +1,187 @@
|
|
| 1 |
# email_actions.py
|
| 2 |
# Filepath: ai-email-assistant/email_actions.py
|
| 3 |
-
#
|
| 4 |
|
|
|
|
| 5 |
import gradio as gr
|
| 6 |
-
from google_sheets_utils import
|
| 7 |
|
| 8 |
-
|
| 9 |
-
templates = get_templates(sheet)
|
| 10 |
-
return [template[2] for template in templates] # Assuming the name is in the third column
|
| 11 |
|
| 12 |
-
def
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
gr.Markdown("## Email Actions")
|
| 15 |
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
-
|
| 23 |
-
save_template_btn = gr.Button("Save as Template")
|
| 24 |
|
| 25 |
templates_list = gr.Dataframe(
|
| 26 |
-
headers=
|
| 27 |
-
label="
|
| 28 |
-
|
| 29 |
-
|
| 30 |
)
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
return (
|
| 51 |
-
selected_template[
|
| 52 |
-
selected_template[
|
| 53 |
-
selected_template[
|
| 54 |
-
selected_template[
|
| 55 |
-
selected_template[
|
| 56 |
)
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 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 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
|
| 97 |
-
|
| 98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
# email_actions.py
|
| 2 |
# Filepath: ai-email-assistant/email_actions.py
|
| 3 |
+
# Handles email template management and actions
|
| 4 |
|
| 5 |
+
import logging
|
| 6 |
import gradio as gr
|
| 7 |
+
from google_sheets_utils import get_templates_from_sheet, save_template, delete_template_from_sheet, generate_template_id, update_template_in_sheet
|
| 8 |
|
| 9 |
+
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
| 10 |
|
| 11 |
+
def get_templates(spreadsheet):
|
| 12 |
+
try:
|
| 13 |
+
return get_templates_from_sheet(spreadsheet.worksheet("Email Templates"))
|
| 14 |
+
except Exception as e:
|
| 15 |
+
logger.error(f"Error fetching templates: {str(e)}")
|
| 16 |
+
return []
|
| 17 |
+
|
| 18 |
+
def get_template_summaries(spreadsheet):
|
| 19 |
+
templates = get_templates_from_sheet(spreadsheet.worksheet("Email Templates"))
|
| 20 |
+
return [f"{template['Name']} ({template['Type']})" for template in templates]
|
| 21 |
+
|
| 22 |
+
def create_email_actions_interface(spreadsheet, current_template):
|
| 23 |
+
worksheet = spreadsheet.worksheet("Email Templates")
|
| 24 |
+
|
| 25 |
+
with gr.Blocks() as email_actions_block:
|
| 26 |
gr.Markdown("## Email Actions")
|
| 27 |
|
| 28 |
+
t_name = gr.Textbox(label="Template Name", elem_id="t_name")
|
| 29 |
+
t_type = gr.Dropdown(
|
| 30 |
+
["Cold Email", "Follow-up", "Thank You", "Other"],
|
| 31 |
+
label="Template Type",
|
| 32 |
+
allow_custom_value=True,
|
| 33 |
+
elem_id="t_type"
|
| 34 |
+
)
|
| 35 |
+
t_subject = gr.Textbox(label="Subject", elem_id="t_subject")
|
| 36 |
+
t_body = gr.Textbox(label="Body", lines=5, elem_id="t_body")
|
| 37 |
+
t_id = gr.Textbox(label="Template ID", interactive=False, elem_id="t_id")
|
| 38 |
|
| 39 |
+
headers = ["Type", "ID", "Name", "Subject", "Body"]
|
|
|
|
| 40 |
|
| 41 |
templates_list = gr.Dataframe(
|
| 42 |
+
headers=headers,
|
| 43 |
+
label="Email Templates List",
|
| 44 |
+
interactive=False,
|
| 45 |
+
elem_id="t_templates_list"
|
| 46 |
)
|
| 47 |
+
|
| 48 |
+
save_btn = gr.Button("Save New Template", elem_id="t_save_btn")
|
| 49 |
+
update_btn = gr.Button("Update Existing Template", elem_id="t_update_btn")
|
| 50 |
+
delete_btn = gr.Button("Delete Template", elem_id="t_delete_btn")
|
| 51 |
+
clear_btn = gr.Button("Clear Fields", elem_id="t_clear_btn")
|
| 52 |
+
|
| 53 |
+
def refresh_templates():
|
| 54 |
+
templates = get_templates(spreadsheet)
|
| 55 |
+
return [[template.get(h, "") for h in headers] for template in templates]
|
| 56 |
+
|
| 57 |
+
def load_template(evt: gr.SelectData, current_name, current_type, current_subject, current_body, current_id):
|
| 58 |
+
templates = get_templates(spreadsheet)
|
| 59 |
+
selected_row = evt.index[0]
|
| 60 |
+
selected_template = templates[selected_row]
|
| 61 |
+
|
| 62 |
+
logger.debug(f"Selected Email Template: {selected_template}")
|
| 63 |
+
|
| 64 |
+
# Check if ANY field is non-empty
|
| 65 |
+
fields = [current_name, current_type, current_subject, current_body, current_id]
|
| 66 |
+
non_empty_fields = any(field and field.strip() != "" for field in fields)
|
| 67 |
+
|
| 68 |
+
logger.debug(f"Any email template field non-empty: {non_empty_fields}")
|
| 69 |
+
|
| 70 |
+
if non_empty_fields:
|
| 71 |
+
logger.warning("Email template fields not empty, showing warning")
|
| 72 |
+
gr.Warning("Please clear all fields before loading a new email template.")
|
| 73 |
+
return [gr.update() for _ in range(5)] # Don't update any fields
|
| 74 |
+
|
| 75 |
+
logger.info("Loading selected email template")
|
| 76 |
+
try:
|
| 77 |
return (
|
| 78 |
+
selected_template["Name"],
|
| 79 |
+
selected_template["Type"],
|
| 80 |
+
selected_template["Subject"],
|
| 81 |
+
selected_template["Body"],
|
| 82 |
+
selected_template["ID"]
|
| 83 |
)
|
| 84 |
+
except KeyError as e:
|
| 85 |
+
logger.error(f"Error loading template: missing key {e}")
|
| 86 |
+
gr.Error(f"Error loading template: missing information")
|
| 87 |
+
return [gr.update() for _ in range(5)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
+
def save_template_action(name, template_type, subject, body):
|
| 90 |
+
# Check for empty fields
|
| 91 |
+
if not name or not template_type or not subject or not body:
|
| 92 |
+
message = gr.HTML("<p style='color:red;'>All fields are required. Please fill in all details before saving.</p>")
|
| 93 |
+
return gr.update(), message, refresh_templates()
|
| 94 |
+
|
| 95 |
+
new_id = generate_template_id()
|
| 96 |
+
template_data = [
|
| 97 |
+
"Email Templates", # Type
|
| 98 |
+
new_id, # ID
|
| 99 |
+
name,
|
| 100 |
+
template_type,
|
| 101 |
+
subject,
|
| 102 |
+
body
|
| 103 |
+
]
|
| 104 |
+
result = save_template(worksheet, template_data)
|
| 105 |
+
if result:
|
| 106 |
+
message = gr.HTML("<p style='color:green;'>Template added successfully.</p>")
|
| 107 |
+
else:
|
| 108 |
+
message = gr.HTML("<p style='color:red;'>Failed to add template.</p>")
|
| 109 |
+
return gr.update(), message, refresh_templates()
|
| 110 |
+
|
| 111 |
+
def update_template_action(template_id, name, template_type, subject, body):
|
| 112 |
+
# Check for empty fields
|
| 113 |
+
if not template_id:
|
| 114 |
+
message = gr.HTML("<p style='color:red;'>No template selected. Please select a template to update.</p>")
|
| 115 |
+
return gr.update(), message, refresh_templates()
|
| 116 |
+
if not name or not template_type or not subject or not body:
|
| 117 |
+
message = gr.HTML("<p style='color:red;'>All fields are required. Please fill in all details before updating.</p>")
|
| 118 |
+
return gr.update(), message, refresh_templates()
|
| 119 |
+
|
| 120 |
+
updated_data = [
|
| 121 |
+
"Email Templates", # Type
|
| 122 |
+
template_id,
|
| 123 |
+
name,
|
| 124 |
+
template_type,
|
| 125 |
+
subject,
|
| 126 |
+
body
|
| 127 |
+
]
|
| 128 |
+
result = update_template_in_sheet(worksheet, template_id, updated_data)
|
| 129 |
+
if result:
|
| 130 |
+
message = gr.HTML("<p style='color:green;'>Template updated successfully.</p>")
|
| 131 |
+
else:
|
| 132 |
+
message = gr.HTML("<p style='color:red;'>Failed to update template.</p>")
|
| 133 |
+
return gr.update(), message, refresh_templates()
|
| 134 |
+
|
| 135 |
+
def delete_template_action(template_id):
|
| 136 |
+
if not template_id:
|
| 137 |
+
message = gr.HTML("<p style='color:red;'>No template selected. Please select a template to delete.</p>")
|
| 138 |
+
return message, refresh_templates()
|
| 139 |
+
|
| 140 |
+
result = delete_template_from_sheet(worksheet, template_id)
|
| 141 |
+
if result:
|
| 142 |
+
message = gr.HTML("<p style='color:green;'>Template deleted successfully.</p>")
|
| 143 |
+
else:
|
| 144 |
+
message = gr.HTML("<p style='color:red;'>Failed to delete template.</p>")
|
| 145 |
+
return message, refresh_templates()
|
| 146 |
+
|
| 147 |
+
def clear_fields():
|
| 148 |
+
return [""] * 5 # Clear all 5 fields
|
| 149 |
|
| 150 |
+
save_btn.click(
|
| 151 |
+
save_template_action,
|
| 152 |
+
inputs=[t_name, t_type, t_subject, t_body],
|
| 153 |
+
outputs=[templates_list]
|
| 154 |
+
)
|
| 155 |
+
update_btn.click(
|
| 156 |
+
update_template_action,
|
| 157 |
+
inputs=[t_id, t_name, t_type, t_subject, t_body],
|
| 158 |
+
outputs=[templates_list]
|
| 159 |
+
)
|
| 160 |
+
delete_btn.click(
|
| 161 |
+
delete_template_action,
|
| 162 |
+
inputs=[t_id],
|
| 163 |
+
outputs=[templates_list]
|
| 164 |
+
)
|
| 165 |
+
clear_btn.click(clear_fields, outputs=[t_name, t_type, t_subject, t_body, t_id])
|
| 166 |
+
templates_list.select(
|
| 167 |
+
load_template,
|
| 168 |
+
inputs=[t_name, t_type, t_subject, t_body, t_id],
|
| 169 |
+
outputs=[t_name, t_type, t_subject, t_body, t_id]
|
| 170 |
+
)
|
| 171 |
+
|
| 172 |
+
# Refresh templates on component creation
|
| 173 |
+
templates_list.value = refresh_templates()
|
| 174 |
+
|
| 175 |
+
# Return a dictionary containing the block and required components/functions
|
| 176 |
+
return {
|
| 177 |
+
"block": email_actions_block,
|
| 178 |
+
"templates_list": templates_list,
|
| 179 |
+
"t_name": t_name,
|
| 180 |
+
"t_type": t_type,
|
| 181 |
+
"t_subject": t_subject,
|
| 182 |
+
"t_body": t_body,
|
| 183 |
+
"t_id": t_id,
|
| 184 |
+
"refresh_templates": refresh_templates,
|
| 185 |
+
"load_template": load_template,
|
| 186 |
+
# Add other components or functions you need to access
|
| 187 |
+
}
|
google_sheets_utils.py
CHANGED
|
@@ -2,147 +2,269 @@
|
|
| 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 |
-
from dotenv import load_dotenv
|
| 8 |
import gspread
|
| 9 |
-
|
|
|
|
| 10 |
import logging
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
-
# Set up logging
|
| 13 |
logging.basicConfig(level=logging.INFO)
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
def connect_to_sheets():
|
| 48 |
try:
|
| 49 |
-
|
| 50 |
-
except
|
| 51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
try:
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
try:
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
]
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
template_sample = [
|
| 78 |
-
["Template", "TEMP_1", "Sales Pitch", "Exciting Offer!", "Dear {receiver_name},\n\nWe have an exciting offer for {receiver_company}...", "John Doe", "Alice Johnson"],
|
| 79 |
-
["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"]
|
| 80 |
-
]
|
| 81 |
-
initialize_sheet(template_sheet, template_headers, template_sample)
|
| 82 |
-
|
| 83 |
-
logger.info("Successfully connected to all sheets.")
|
| 84 |
-
return sender_sheet, receiver_sheet, template_sheet
|
| 85 |
-
|
| 86 |
-
def get_profiles_from_sheet(sheet):
|
| 87 |
-
data = sheet.get_all_values()
|
| 88 |
-
return data[1:] # Exclude header row
|
| 89 |
-
|
| 90 |
-
def save_profile(sheet, profile_data):
|
| 91 |
-
new_row = [
|
| 92 |
-
"Profile",
|
| 93 |
-
f"PROF_{sheet.row_count}",
|
| 94 |
-
profile_data['name'],
|
| 95 |
-
profile_data['email'],
|
| 96 |
-
profile_data['position'],
|
| 97 |
-
profile_data['company'],
|
| 98 |
-
profile_data['context']
|
| 99 |
-
]
|
| 100 |
-
sheet.append_row(new_row)
|
| 101 |
-
|
| 102 |
-
def delete_profile_from_sheet(sheet, profile_id):
|
| 103 |
-
cell = sheet.find(profile_id)
|
| 104 |
-
if cell:
|
| 105 |
-
sheet.delete_row(cell.row)
|
| 106 |
-
|
| 107 |
-
def update_profile(sheet, profile_id, profile_data):
|
| 108 |
-
cell = sheet.find(profile_id)
|
| 109 |
-
if cell:
|
| 110 |
row = cell.row
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
sheet.update_cell(row, 5, profile_data['position'])
|
| 114 |
-
sheet.update_cell(row, 6, profile_data['company'])
|
| 115 |
-
sheet.update_cell(row, 7, profile_data['context'])
|
| 116 |
-
|
| 117 |
-
def get_templates(sheet):
|
| 118 |
-
data = sheet.get_all_values()
|
| 119 |
-
return data[1:] # Exclude header row
|
| 120 |
-
|
| 121 |
-
def save_template(sheet, template_data):
|
| 122 |
-
new_row = [
|
| 123 |
-
"Template",
|
| 124 |
-
f"TEMP_{sheet.row_count}",
|
| 125 |
-
template_data['name'],
|
| 126 |
-
template_data['subject'],
|
| 127 |
-
template_data['content'],
|
| 128 |
-
template_data['sender_profile'],
|
| 129 |
-
template_data['receiver_profile']
|
| 130 |
-
]
|
| 131 |
-
sheet.append_row(new_row)
|
| 132 |
-
|
| 133 |
-
def delete_template_from_sheet(sheet, template_id):
|
| 134 |
-
cell = sheet.find(template_id)
|
| 135 |
-
if cell:
|
| 136 |
-
sheet.delete_row(cell.row)
|
| 137 |
return True
|
| 138 |
-
|
|
|
|
|
|
|
| 139 |
|
| 140 |
-
def
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
row = cell.row
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
# Filepath: ai-email-assistant/google_sheets_utils.py
|
| 3 |
# Manages interactions with Google Sheets for profile and template storage
|
| 4 |
|
|
|
|
|
|
|
|
|
|
| 5 |
import gspread
|
| 6 |
+
import json
|
| 7 |
+
import os
|
| 8 |
import logging
|
| 9 |
+
from google.oauth2.service_account import Credentials
|
| 10 |
+
import uuid
|
| 11 |
+
import time
|
| 12 |
+
from google.auth.exceptions import TransportError
|
| 13 |
+
from dotenv import load_dotenv
|
| 14 |
|
|
|
|
| 15 |
logging.basicConfig(level=logging.INFO)
|
| 16 |
+
|
| 17 |
+
# Constants
|
| 18 |
+
SCOPES = ['https://www.googleapis.com/auth/spreadsheets', 'https://www.googleapis.com/auth/drive']
|
| 19 |
+
SPREADSHEET_NAME = "AI Email Assistant Data"
|
| 20 |
+
WORKSHEET_NAMES = ["Sender Profiles", "Receiver Profiles", "Email Templates"]
|
| 21 |
+
HEADERS = {
|
| 22 |
+
"Sender Profiles": ["Type", "ID", "Name", "Email", "Position", "Company", "Context"],
|
| 23 |
+
"Receiver Profiles": ["Type", "ID", "Name", "Email", "Position", "Company", "Context"],
|
| 24 |
+
"Email Templates": ["Type", "ID", "Name", "Subject", "Body"]
|
| 25 |
+
}
|
| 26 |
+
SAMPLE_DATA = {
|
| 27 |
+
"Sender Profiles": ["Sender Profile", "PROF_1", "John Doe", "john.doe@example.com", "Manager", "Example Corp", "Sample context for John Doe"],
|
| 28 |
+
"Receiver Profiles": ["Receiver Profile", "PROF_2", "Jane Smith", "jane.smith@example.com", "Developer", "Example Inc", "Sample context for Jane Smith"],
|
| 29 |
+
"Email Templates": ["Template", "TEMP_1", "Welcome Email", "Welcome to our service", "Hello, welcome to our service. We are glad to have you."]
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
def load_config():
|
| 33 |
+
load_dotenv()
|
| 34 |
+
return {
|
| 35 |
+
'google_sheets_credentials_file': os.getenv('GOOGLE_SHEETS_CREDENTIALS_FILE')
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
def get_credentials():
|
| 39 |
+
config = load_config()
|
| 40 |
+
if not config:
|
| 41 |
+
return None
|
| 42 |
+
|
| 43 |
+
creds_file = config.get('google_sheets_credentials_file')
|
| 44 |
+
if not creds_file:
|
| 45 |
+
logging.error("Google Sheets credentials file path not found in .env file")
|
| 46 |
+
return None
|
| 47 |
+
|
|
|
|
|
|
|
| 48 |
try:
|
| 49 |
+
return Credentials.from_service_account_file(creds_file, scopes=SCOPES)
|
| 50 |
+
except Exception as e:
|
| 51 |
+
logging.error("Error creating credentials: %s", str(e))
|
| 52 |
+
return None
|
| 53 |
+
|
| 54 |
+
def get_client():
|
| 55 |
+
creds = get_credentials()
|
| 56 |
+
if not creds:
|
| 57 |
+
logging.error("Failed to obtain credentials")
|
| 58 |
+
return None
|
| 59 |
+
return gspread.authorize(creds)
|
| 60 |
+
|
| 61 |
+
def get_or_create_spreadsheet():
|
| 62 |
+
gc = get_client()
|
| 63 |
+
if not gc:
|
| 64 |
+
logging.error("Failed to get Google Sheets client")
|
| 65 |
+
return None
|
| 66 |
+
|
| 67 |
+
max_retries = 3
|
| 68 |
+
retry_delay = 5 # seconds
|
| 69 |
+
|
| 70 |
+
for attempt in range(max_retries):
|
| 71 |
+
try:
|
| 72 |
+
sh = gc.open(SPREADSHEET_NAME)
|
| 73 |
+
logging.info(f"Opened existing spreadsheet: {SPREADSHEET_NAME}")
|
| 74 |
+
return sh
|
| 75 |
+
except gspread.SpreadsheetNotFound:
|
| 76 |
+
try:
|
| 77 |
+
sh = gc.create(SPREADSHEET_NAME)
|
| 78 |
+
logging.info(f"Created new spreadsheet: {SPREADSHEET_NAME}")
|
| 79 |
+
return sh
|
| 80 |
+
except Exception as e:
|
| 81 |
+
logging.error(f"Failed to create spreadsheet: {str(e)}")
|
| 82 |
+
return None
|
| 83 |
+
except (gspread.exceptions.APIError, TransportError) as e:
|
| 84 |
+
logging.error(f"API Error (attempt {attempt + 1}/{max_retries}): {str(e)}")
|
| 85 |
+
if attempt < max_retries - 1:
|
| 86 |
+
logging.info(f"Retrying in {retry_delay} seconds...")
|
| 87 |
+
time.sleep(retry_delay)
|
| 88 |
+
else:
|
| 89 |
+
logging.error("Max retries reached. Unable to connect to Google Sheets.")
|
| 90 |
+
return None
|
| 91 |
|
| 92 |
+
# Ensure all required worksheets exist
|
| 93 |
+
existing_worksheets = [worksheet.title for worksheet in sh.worksheets()]
|
| 94 |
+
for worksheet_name in WORKSHEET_NAMES:
|
| 95 |
+
if worksheet_name not in existing_worksheets:
|
| 96 |
+
try:
|
| 97 |
+
worksheet = sh.add_worksheet(title=worksheet_name, rows=1, cols=len(HEADERS[worksheet_name]))
|
| 98 |
+
worksheet.append_row(HEADERS[worksheet_name])
|
| 99 |
+
worksheet.append_row(SAMPLE_DATA[worksheet_name])
|
| 100 |
+
logging.info(f"Created worksheet: {worksheet_name}")
|
| 101 |
+
except Exception as e:
|
| 102 |
+
logging.error(f"Failed to create worksheet {worksheet_name}: {str(e)}")
|
| 103 |
+
|
| 104 |
+
return sh
|
| 105 |
+
|
| 106 |
+
def get_profiles_from_sheet(worksheet):
|
| 107 |
+
try:
|
| 108 |
+
records = worksheet.get_all_records()
|
| 109 |
+
return records
|
| 110 |
+
except Exception as e:
|
| 111 |
+
logging.error("Error getting profiles from worksheet: %s", str(e))
|
| 112 |
+
return []
|
| 113 |
+
|
| 114 |
+
def save_profile(worksheet, profile_data):
|
| 115 |
+
try:
|
| 116 |
+
worksheet.append_row(profile_data)
|
| 117 |
+
logging.info("Profile saved successfully")
|
| 118 |
+
return True
|
| 119 |
+
except Exception as e:
|
| 120 |
+
logging.error("Error saving profile: %s", str(e))
|
| 121 |
+
return False
|
| 122 |
+
|
| 123 |
+
def delete_profile_from_sheet(worksheet, profile_id):
|
| 124 |
+
try:
|
| 125 |
+
cell = worksheet.find(profile_id)
|
| 126 |
+
worksheet.delete_rows(cell.row) # Use delete_rows instead of delete_row
|
| 127 |
+
logging.info(f"Profile {profile_id} deleted successfully")
|
| 128 |
+
return True
|
| 129 |
+
except Exception as e:
|
| 130 |
+
logging.error(f"Error deleting profile {profile_id}: {str(e)}")
|
| 131 |
+
return False
|
| 132 |
+
|
| 133 |
+
def get_templates(worksheet):
|
| 134 |
+
try:
|
| 135 |
+
records = worksheet.get_all_records()
|
| 136 |
+
return records
|
| 137 |
+
except Exception as e:
|
| 138 |
+
logging.error(f"Error getting templates: {str(e)}")
|
| 139 |
+
return []
|
| 140 |
+
|
| 141 |
+
def save_template(worksheet, template_id, name, template_type, subject, body):
|
| 142 |
+
try:
|
| 143 |
+
template_data = [template_type, template_id, name, subject, body]
|
| 144 |
+
worksheet.append_row(template_data)
|
| 145 |
+
logging.info(f"Template saved successfully with ID: {template_id}")
|
| 146 |
+
return f"Template saved successfully with ID: {template_id}"
|
| 147 |
+
except Exception as e:
|
| 148 |
+
logging.error(f"Error saving template: {str(e)}")
|
| 149 |
+
return f"Error saving template: {str(e)}"
|
| 150 |
+
|
| 151 |
+
def delete_template_from_sheet(worksheet, template_id):
|
| 152 |
+
try:
|
| 153 |
+
cell = worksheet.find(template_id)
|
| 154 |
+
worksheet.delete_rows(cell.row) # Use delete_rows instead of delete_row
|
| 155 |
+
logging.info(f"Template {template_id} deleted successfully")
|
| 156 |
+
return f"Template {template_id} deleted successfully"
|
| 157 |
+
except Exception as e:
|
| 158 |
+
logging.error(f"Error deleting template {template_id}: {str(e)}")
|
| 159 |
+
return f"Error deleting template {template_id}: {str(e)}"
|
| 160 |
+
|
| 161 |
+
def update_profile_in_sheet(worksheet, profile_id, updated_data):
|
| 162 |
try:
|
| 163 |
+
cell = worksheet.find(profile_id)
|
| 164 |
+
for idx, value in enumerate(updated_data):
|
| 165 |
+
worksheet.update_cell(cell.row, idx + 1, value)
|
| 166 |
+
logging.info(f"Profile {profile_id} updated successfully")
|
| 167 |
+
return True
|
| 168 |
+
except Exception as e:
|
| 169 |
+
logging.error(f"Error updating profile {profile_id}: {str(e)}")
|
| 170 |
+
return False
|
| 171 |
+
|
| 172 |
+
def get_templates_from_sheet(worksheet):
|
| 173 |
+
try:
|
| 174 |
+
# Get all records from the worksheet
|
| 175 |
+
records = worksheet.get_all_records()
|
| 176 |
+
|
| 177 |
+
# Convert records to a list of dictionaries
|
| 178 |
+
templates = []
|
| 179 |
+
for record in records:
|
| 180 |
+
template = {
|
| 181 |
+
"Name": record.get("Name", ""),
|
| 182 |
+
"Type": record.get("Type", ""),
|
| 183 |
+
"ID": record.get("ID", ""),
|
| 184 |
+
"Subject": record.get("Subject", ""),
|
| 185 |
+
"Body": record.get("Body", "")
|
| 186 |
+
}
|
| 187 |
+
templates.append(template)
|
| 188 |
+
|
| 189 |
+
return templates
|
| 190 |
|
| 191 |
+
except Exception as e:
|
| 192 |
+
print(f"An error occurred while fetching templates: {str(e)}")
|
| 193 |
+
return []
|
| 194 |
+
|
| 195 |
+
def get_profile_summaries(worksheet, profile_type):
|
| 196 |
+
try:
|
| 197 |
+
profiles = worksheet.get_all_records()
|
| 198 |
+
return [f"{profile['Name']} ({profile_type})" for profile in profiles]
|
| 199 |
+
except Exception as e:
|
| 200 |
+
logging.error(f"Error getting profile summaries: {str(e)}")
|
| 201 |
+
return []
|
| 202 |
+
|
| 203 |
+
def get_template_summaries(worksheet):
|
| 204 |
try:
|
| 205 |
+
records = worksheet.get_all_records()
|
| 206 |
+
summaries = [{"Name": record["Name"], "ID": record["ID"]} for record in records]
|
| 207 |
+
return summaries
|
| 208 |
+
except Exception as e:
|
| 209 |
+
logging.error(f"Error getting template summaries: {str(e)}")
|
| 210 |
+
return []
|
| 211 |
+
|
| 212 |
+
# Additional utility functions can be added here as needed
|
| 213 |
+
|
| 214 |
+
def generate_template_id():
|
| 215 |
+
return f"TEMP_{uuid.uuid4().hex[:8].upper()}"
|
| 216 |
+
|
| 217 |
+
def generate_profile_id():
|
| 218 |
+
return f"PROF_{uuid.uuid4().hex[:8].upper()}"
|
| 219 |
+
|
| 220 |
+
def update_template(worksheet, template_id, name, template_type, subject, body):
|
| 221 |
+
try:
|
| 222 |
+
cell = worksheet.find(template_id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
row = cell.row
|
| 224 |
+
worksheet.update(f'A{row}:E{row}', [[template_type, template_id, name, subject, body]])
|
| 225 |
+
logging.info(f"Template {template_id} updated successfully")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
return True
|
| 227 |
+
except Exception as e:
|
| 228 |
+
logging.error(f"Error updating template {template_id}: {str(e)}")
|
| 229 |
+
return False
|
| 230 |
|
| 231 |
+
def get_profile_by_id(worksheet, profile_id):
|
| 232 |
+
try:
|
| 233 |
+
cell = worksheet.find(profile_id)
|
| 234 |
+
row = worksheet.row_values(cell.row)
|
| 235 |
+
profile_data = dict(zip(HEADERS[worksheet.title], row))
|
| 236 |
+
return profile_data
|
| 237 |
+
except Exception as e:
|
| 238 |
+
logging.error(f"Error fetching profile {profile_id}: {str(e)}")
|
| 239 |
+
return None
|
| 240 |
+
|
| 241 |
+
def get_template_by_id(worksheet, template_id):
|
| 242 |
+
try:
|
| 243 |
+
cell = worksheet.find(template_id)
|
| 244 |
+
row = worksheet.row_values(cell.row)
|
| 245 |
+
template_data = dict(zip(HEADERS[worksheet.title], row))
|
| 246 |
+
return template_data
|
| 247 |
+
except Exception as e:
|
| 248 |
+
logging.error(f"Error fetching template {template_id}: {str(e)}")
|
| 249 |
+
return None
|
| 250 |
+
|
| 251 |
+
def update_template_in_sheet(worksheet, template_id, updated_data):
|
| 252 |
+
try:
|
| 253 |
+
cell = worksheet.find(template_id)
|
| 254 |
+
for idx, value in enumerate(updated_data):
|
| 255 |
+
worksheet.update_cell(cell.row, idx + 1, value)
|
| 256 |
+
logging.info(f"Template {template_id} updated successfully")
|
| 257 |
+
return True
|
| 258 |
+
except Exception as e:
|
| 259 |
+
logging.error(f"Error updating template {template_id}: {str(e)}")
|
| 260 |
+
return False
|
| 261 |
+
|
| 262 |
+
if __name__ == "__main__":
|
| 263 |
+
# Test the functions
|
| 264 |
+
print("Testing Google Sheets integration...")
|
| 265 |
+
sh = get_or_create_spreadsheet()
|
| 266 |
+
if sh:
|
| 267 |
+
print(f"Successfully connected to spreadsheet: {sh.title}")
|
| 268 |
+
print("Worksheets:", [worksheet.title for worksheet in sh.worksheets()])
|
| 269 |
+
else:
|
| 270 |
+
print("Failed to connect to Google Sheets")
|
groq_client.py
CHANGED
|
@@ -3,13 +3,18 @@
|
|
| 3 |
# Interfaces with the Groq API for AI-powered responses
|
| 4 |
|
| 5 |
import os
|
| 6 |
-
|
| 7 |
from groq import Groq
|
|
|
|
| 8 |
|
| 9 |
-
|
| 10 |
-
load_dotenv()
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
-
|
|
|
|
| 13 |
client = Groq(api_key=api_key) if api_key else None
|
| 14 |
|
| 15 |
def set_api_key(api_key):
|
|
@@ -18,7 +23,7 @@ def set_api_key(api_key):
|
|
| 18 |
|
| 19 |
def get_ai_response(message, system_prompt, context=""):
|
| 20 |
if not client:
|
| 21 |
-
raise ValueError("Groq API key not set. Please check your .
|
| 22 |
|
| 23 |
messages = [
|
| 24 |
{"role": "system", "content": system_prompt},
|
|
|
|
| 3 |
# Interfaces with the Groq API for AI-powered responses
|
| 4 |
|
| 5 |
import os
|
| 6 |
+
import json
|
| 7 |
from groq import Groq
|
| 8 |
+
from dotenv import load_dotenv
|
| 9 |
|
| 10 |
+
def load_config():
|
| 11 |
+
load_dotenv()
|
| 12 |
+
return {
|
| 13 |
+
'groq_api_key': os.getenv('GROQ_API_KEY')
|
| 14 |
+
}
|
| 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):
|
|
|
|
| 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},
|
profile_management.py
CHANGED
|
@@ -3,98 +3,169 @@
|
|
| 3 |
# Handles sender and receiver profile management
|
| 4 |
|
| 5 |
import gradio as gr
|
| 6 |
-
from google_sheets_utils import
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
return [profile[2] for profile in profiles] # Assuming the name is in the third column
|
| 11 |
|
| 12 |
-
def create_profile_management_interface(
|
| 13 |
-
with gr.
|
| 14 |
-
gr.Markdown(f"## {profile_type}
|
| 15 |
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
-
create_btn = gr.Button(f"Create {profile_type} Profile")
|
| 23 |
-
|
| 24 |
profiles_list = gr.Dataframe(
|
| 25 |
-
headers=
|
| 26 |
-
label=f"
|
| 27 |
-
|
| 28 |
-
|
| 29 |
)
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
return (
|
| 50 |
-
selected_profile[
|
| 51 |
-
selected_profile[
|
| 52 |
-
selected_profile[
|
| 53 |
-
selected_profile[
|
| 54 |
-
selected_profile[
|
|
|
|
| 55 |
)
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
"id": profile_id,
|
| 91 |
"name": name,
|
| 92 |
"email": email,
|
| 93 |
"position": position,
|
| 94 |
"company": company,
|
| 95 |
-
"context": context
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
|
|
|
| 3 |
# Handles sender and receiver profile management
|
| 4 |
|
| 5 |
import gradio as gr
|
| 6 |
+
from google_sheets_utils import (
|
| 7 |
+
get_profiles_from_sheet,
|
| 8 |
+
save_profile,
|
| 9 |
+
delete_profile_from_sheet,
|
| 10 |
+
update_profile_in_sheet,
|
| 11 |
+
generate_profile_id,
|
| 12 |
+
)
|
| 13 |
+
import logging
|
| 14 |
|
| 15 |
+
logging.basicConfig(level=logging.DEBUG)
|
| 16 |
+
logger = logging.getLogger(__name__)
|
|
|
|
| 17 |
|
| 18 |
+
def create_profile_management_interface(spreadsheet, profile_type, current_profile):
|
| 19 |
+
with gr.Blocks() as profile_management_block:
|
| 20 |
+
gr.Markdown(f"## {profile_type} Management")
|
| 21 |
|
| 22 |
+
prefix = "s_" if profile_type == "Sender Profiles" else "r_"
|
| 23 |
+
|
| 24 |
+
name = gr.Textbox(label="Name", elem_id=f"{prefix}name")
|
| 25 |
+
email = gr.Textbox(label="Email", elem_id=f"{prefix}email")
|
| 26 |
+
position = gr.Textbox(label="Position", elem_id=f"{prefix}position")
|
| 27 |
+
company = gr.Textbox(label="Company", elem_id=f"{prefix}company")
|
| 28 |
+
context = gr.Dropdown(["Professional", "Personal", "Other"], label="Context", allow_custom_value=True, elem_id=f"{prefix}context")
|
| 29 |
+
profile_id = gr.Textbox(label="Profile ID", interactive=False, elem_id=f"{prefix}profile_id")
|
| 30 |
+
|
| 31 |
+
worksheet = spreadsheet.worksheet(profile_type)
|
| 32 |
+
|
| 33 |
+
headers = ["Type", "ID", "Name", "Email", "Position", "Company", "Context"]
|
| 34 |
|
|
|
|
|
|
|
| 35 |
profiles_list = gr.Dataframe(
|
| 36 |
+
headers=headers,
|
| 37 |
+
label=f"{profile_type} List",
|
| 38 |
+
interactive=False,
|
| 39 |
+
elem_id=f"{prefix}profiles_list"
|
| 40 |
)
|
| 41 |
+
|
| 42 |
+
add_btn = gr.Button("Add New Profile", elem_id=f"{prefix}add_btn")
|
| 43 |
+
update_btn = gr.Button("Update Existing Profile", elem_id=f"{prefix}update_btn")
|
| 44 |
+
delete_btn = gr.Button("Delete Profile", elem_id=f"{prefix}delete_btn")
|
| 45 |
+
clear_btn = gr.Button("Clear Fields", elem_id=f"{prefix}clear_btn")
|
| 46 |
+
|
| 47 |
+
message_output = gr.HTML(elem_id=f"{prefix}message_output")
|
| 48 |
+
|
| 49 |
+
def refresh_profiles():
|
| 50 |
+
profiles = get_profiles_from_sheet(worksheet)
|
| 51 |
+
return [[profile.get(h, "") for h in headers] for profile in profiles]
|
| 52 |
+
|
| 53 |
+
def add_profile(name, email, position, company, context):
|
| 54 |
+
# Check for empty fields
|
| 55 |
+
if not name or not email or not position or not company or not context:
|
| 56 |
+
gr.Error("All fields are required. Please fill in all details before saving.")
|
| 57 |
+
return refresh_profiles()
|
| 58 |
+
new_id = generate_profile_id()
|
| 59 |
+
profile_data = [
|
| 60 |
+
profile_type, # Type
|
| 61 |
+
new_id, # ID
|
| 62 |
+
name,
|
| 63 |
+
email,
|
| 64 |
+
position,
|
| 65 |
+
company,
|
| 66 |
+
context
|
| 67 |
+
]
|
| 68 |
+
result = save_profile(worksheet, profile_data)
|
| 69 |
+
message = gr.Info("Profile added successfully.") if result else gr.Error("Failed to add profile.")
|
| 70 |
+
return gr.update(), message, refresh_profiles()
|
| 71 |
+
|
| 72 |
+
def update_profile(profile_id, name, email, position, company, context):
|
| 73 |
+
# Check for empty fields
|
| 74 |
+
if not profile_id:
|
| 75 |
+
return gr.update(), gr.Error("No profile selected. Please select a profile to update."), refresh_profiles()
|
| 76 |
+
if not name or not email or not position or not company or not context:
|
| 77 |
+
return gr.update(), gr.Error("All fields are required. Please fill in all details before updating."), refresh_profiles()
|
| 78 |
+
updated_data = [
|
| 79 |
+
profile_type, # Type
|
| 80 |
+
profile_id,
|
| 81 |
+
name,
|
| 82 |
+
email,
|
| 83 |
+
position,
|
| 84 |
+
company,
|
| 85 |
+
context
|
| 86 |
+
]
|
| 87 |
+
result = update_profile_in_sheet(worksheet, profile_id, updated_data)
|
| 88 |
+
message = gr.Info("Profile updated successfully.") if result else gr.Error("Failed to update profile.")
|
| 89 |
+
return gr.update(), message, refresh_profiles()
|
| 90 |
+
|
| 91 |
+
def delete_profile(profile_id):
|
| 92 |
+
result = delete_profile_from_sheet(worksheet, profile_id)
|
| 93 |
+
gr.Info("Profile deleted successfully" if result else "Failed to delete profile")
|
| 94 |
+
return refresh_profiles()
|
| 95 |
+
|
| 96 |
+
def clear_fields():
|
| 97 |
+
return [""] * 6 # Clear all 6 fields
|
| 98 |
+
|
| 99 |
+
def load_profile(evt: gr.SelectData, current_name, current_email, current_position,
|
| 100 |
+
current_company, current_context, current_profile_id):
|
| 101 |
+
profiles = get_profiles_from_sheet(worksheet)
|
| 102 |
+
selected_row = evt.index[0]
|
| 103 |
+
selected_profile = profiles[selected_row]
|
| 104 |
+
|
| 105 |
+
logger.debug(f"Selected {profile_type} Profile: {selected_profile}")
|
| 106 |
+
|
| 107 |
+
# Check if ANY field (including profile_id) is non-empty
|
| 108 |
+
fields = [current_name, current_email, current_position, current_company, current_context, current_profile_id]
|
| 109 |
+
non_empty_fields = any(field and field.strip() != "" for field in fields)
|
| 110 |
+
|
| 111 |
+
logger.debug(f"Any {profile_type} field non-empty: {non_empty_fields}")
|
| 112 |
+
|
| 113 |
+
if non_empty_fields:
|
| 114 |
+
logger.warning(f"{profile_type} fields not empty, showing warning")
|
| 115 |
+
gr.Warning(f"Please clear all fields before loading a {profile_type[:-1]} profile.")
|
| 116 |
+
return [gr.update() for _ in range(6)] # Don't update any fields
|
| 117 |
+
|
| 118 |
+
logger.info(f"Loading selected {profile_type} profile")
|
| 119 |
+
try:
|
| 120 |
return (
|
| 121 |
+
selected_profile["Name"],
|
| 122 |
+
selected_profile["Email"],
|
| 123 |
+
selected_profile["Position"],
|
| 124 |
+
selected_profile["Company"],
|
| 125 |
+
selected_profile["Context"],
|
| 126 |
+
selected_profile["ID"]
|
| 127 |
)
|
| 128 |
+
except KeyError as e:
|
| 129 |
+
logger.error(f"Error loading profile: missing key {e}")
|
| 130 |
+
gr.Error(f"Error loading profile: missing information")
|
| 131 |
+
return [gr.update() for _ in range(6)]
|
| 132 |
+
|
| 133 |
+
# Attach event handlers
|
| 134 |
+
add_btn.click(
|
| 135 |
+
add_profile,
|
| 136 |
+
inputs=[name, email, position, company, context],
|
| 137 |
+
outputs=[message_output, profiles_list]
|
| 138 |
+
)
|
| 139 |
+
update_btn.click(
|
| 140 |
+
update_profile,
|
| 141 |
+
inputs=[profile_id, name, email, position, company, context],
|
| 142 |
+
outputs=[message_output, profiles_list]
|
| 143 |
+
)
|
| 144 |
+
delete_btn.click(
|
| 145 |
+
delete_profile,
|
| 146 |
+
inputs=[profile_id],
|
| 147 |
+
outputs=[profiles_list]
|
| 148 |
+
)
|
| 149 |
+
clear_btn.click(clear_fields, outputs=[name, email, position, company, context, profile_id])
|
| 150 |
+
profiles_list.select(
|
| 151 |
+
load_profile,
|
| 152 |
+
inputs=[name, email, position, company, context, profile_id],
|
| 153 |
+
outputs=[name, email, position, company, context, profile_id]
|
| 154 |
+
)
|
| 155 |
+
|
| 156 |
+
# Refresh profiles on component creation
|
| 157 |
+
profiles_list.value = refresh_profiles()
|
| 158 |
+
|
| 159 |
+
# Return a dictionary containing the block and required components/functions
|
| 160 |
+
return {
|
| 161 |
+
"block": profile_management_block,
|
|
|
|
| 162 |
"name": name,
|
| 163 |
"email": email,
|
| 164 |
"position": position,
|
| 165 |
"company": company,
|
| 166 |
+
"context": context,
|
| 167 |
+
"profile_id": profile_id,
|
| 168 |
+
"profiles_list": profiles_list,
|
| 169 |
+
"refresh_profiles": refresh_profiles,
|
| 170 |
+
"load_profile": load_profile, # Add other components/functions as needed
|
| 171 |
+
}
|
requirements.txt
CHANGED
|
@@ -2,10 +2,16 @@
|
|
| 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
|
|
@@ -14,6 +20,8 @@ anyio==4.6.0
|
|
| 14 |
# groq
|
| 15 |
# httpx
|
| 16 |
# starlette
|
|
|
|
|
|
|
| 17 |
cachetools==5.5.0
|
| 18 |
# via google-auth
|
| 19 |
certifi==2024.8.30
|
|
@@ -48,6 +56,10 @@ 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
|
|
@@ -63,8 +75,12 @@ 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
|
|
|
|
|
|
|
| 68 |
gspread==6.1.2
|
| 69 |
# via -r requirements.in
|
| 70 |
h11==0.14.0
|
|
@@ -78,6 +94,7 @@ httpx==0.27.2
|
|
| 78 |
# gradio
|
| 79 |
# gradio-client
|
| 80 |
# groq
|
|
|
|
| 81 |
huggingface-hub==0.25.1
|
| 82 |
# via
|
| 83 |
# gradio
|
|
@@ -87,12 +104,32 @@ idna==3.10
|
|
| 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
|
|
@@ -103,21 +140,29 @@ matplotlib==3.9.2
|
|
| 103 |
# via gradio
|
| 104 |
mdurl==0.1.2
|
| 105 |
# via markdown-it-py
|
| 106 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
|
|
|
|
|
|
| 116 |
packaging==24.1
|
| 117 |
# via
|
| 118 |
# gradio
|
| 119 |
# gradio-client
|
| 120 |
# huggingface-hub
|
|
|
|
| 121 |
# matplotlib
|
| 122 |
pandas==2.2.3
|
| 123 |
# via gradio
|
|
@@ -138,6 +183,9 @@ pydantic==2.9.2
|
|
| 138 |
# fastapi
|
| 139 |
# gradio
|
| 140 |
# groq
|
|
|
|
|
|
|
|
|
|
| 141 |
pydantic-core==2.23.4
|
| 142 |
# via pydantic
|
| 143 |
pydub==0.25.1
|
|
@@ -160,12 +208,19 @@ 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
|
|
@@ -183,8 +238,14 @@ sniffio==1.3.1
|
|
| 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
|
|
@@ -198,8 +259,10 @@ typing-extensions==4.12.2
|
|
| 198 |
# gradio-client
|
| 199 |
# groq
|
| 200 |
# huggingface-hub
|
|
|
|
| 201 |
# pydantic
|
| 202 |
# pydantic-core
|
|
|
|
| 203 |
# typer
|
| 204 |
tzdata==2024.1
|
| 205 |
# via pandas
|
|
@@ -211,3 +274,5 @@ uvicorn==0.30.6
|
|
| 211 |
# via gradio
|
| 212 |
websockets==12.0
|
| 213 |
# via gradio-client
|
|
|
|
|
|
|
|
|
| 2 |
# This file is autogenerated by pip-compile with Python 3.12
|
| 3 |
# by the following command:
|
| 4 |
#
|
| 5 |
+
# pip-compile --output-file=requirements.txt requirements.in
|
| 6 |
#
|
| 7 |
aiofiles==23.2.1
|
| 8 |
# via gradio
|
| 9 |
+
aiohappyeyeballs==2.4.3
|
| 10 |
+
# via aiohttp
|
| 11 |
+
aiohttp==3.10.9
|
| 12 |
+
# via langchain
|
| 13 |
+
aiosignal==1.3.1
|
| 14 |
+
# via aiohttp
|
| 15 |
annotated-types==0.7.0
|
| 16 |
# via pydantic
|
| 17 |
anyio==4.6.0
|
|
|
|
| 20 |
# groq
|
| 21 |
# httpx
|
| 22 |
# starlette
|
| 23 |
+
attrs==24.2.0
|
| 24 |
+
# via aiohttp
|
| 25 |
cachetools==5.5.0
|
| 26 |
# via google-auth
|
| 27 |
certifi==2024.8.30
|
|
|
|
| 56 |
# via huggingface-hub
|
| 57 |
fonttools==4.53.1
|
| 58 |
# via matplotlib
|
| 59 |
+
frozenlist==1.4.1
|
| 60 |
+
# via
|
| 61 |
+
# aiohttp
|
| 62 |
+
# aiosignal
|
| 63 |
fsspec==2024.9.0
|
| 64 |
# via
|
| 65 |
# gradio-client
|
|
|
|
| 75 |
# via -r requirements.in
|
| 76 |
gradio-client==1.3.0
|
| 77 |
# via gradio
|
| 78 |
+
greenlet==3.1.1
|
| 79 |
+
# via sqlalchemy
|
| 80 |
groq==0.11.0
|
| 81 |
+
# via
|
| 82 |
+
# -r requirements.in
|
| 83 |
+
# langchain-groq
|
| 84 |
gspread==6.1.2
|
| 85 |
# via -r requirements.in
|
| 86 |
h11==0.14.0
|
|
|
|
| 94 |
# gradio
|
| 95 |
# gradio-client
|
| 96 |
# groq
|
| 97 |
+
# langsmith
|
| 98 |
huggingface-hub==0.25.1
|
| 99 |
# via
|
| 100 |
# gradio
|
|
|
|
| 104 |
# anyio
|
| 105 |
# httpx
|
| 106 |
# requests
|
| 107 |
+
# yarl
|
| 108 |
importlib-resources==6.4.5
|
| 109 |
# via gradio
|
| 110 |
jinja2==3.1.4
|
| 111 |
# via gradio
|
| 112 |
+
jsonpatch==1.33
|
| 113 |
+
# via langchain-core
|
| 114 |
+
jsonpointer==3.0.0
|
| 115 |
+
# via jsonpatch
|
| 116 |
kiwisolver==1.4.7
|
| 117 |
# via matplotlib
|
| 118 |
+
langchain==0.3.0
|
| 119 |
+
# via -r requirements.in
|
| 120 |
+
langchain-core==0.3.9
|
| 121 |
+
# via
|
| 122 |
+
# langchain
|
| 123 |
+
# langchain-groq
|
| 124 |
+
# langchain-text-splitters
|
| 125 |
+
langchain-groq==0.2.0
|
| 126 |
+
# via -r requirements.in
|
| 127 |
+
langchain-text-splitters==0.3.0
|
| 128 |
+
# via langchain
|
| 129 |
+
langsmith==0.1.131
|
| 130 |
+
# via
|
| 131 |
+
# langchain
|
| 132 |
+
# langchain-core
|
| 133 |
markdown-it-py==3.0.0
|
| 134 |
# via rich
|
| 135 |
markupsafe==2.1.5
|
|
|
|
| 140 |
# via gradio
|
| 141 |
mdurl==0.1.2
|
| 142 |
# via markdown-it-py
|
| 143 |
+
multidict==6.1.0
|
| 144 |
+
# via
|
| 145 |
+
# aiohttp
|
| 146 |
+
# yarl
|
| 147 |
+
numpy==1.26.4
|
| 148 |
# via
|
| 149 |
# contourpy
|
| 150 |
# gradio
|
| 151 |
+
# langchain
|
| 152 |
# matplotlib
|
| 153 |
# pandas
|
| 154 |
oauthlib==3.2.2
|
| 155 |
# via requests-oauthlib
|
| 156 |
orjson==3.10.7
|
| 157 |
+
# via
|
| 158 |
+
# gradio
|
| 159 |
+
# langsmith
|
| 160 |
packaging==24.1
|
| 161 |
# via
|
| 162 |
# gradio
|
| 163 |
# gradio-client
|
| 164 |
# huggingface-hub
|
| 165 |
+
# langchain-core
|
| 166 |
# matplotlib
|
| 167 |
pandas==2.2.3
|
| 168 |
# via gradio
|
|
|
|
| 183 |
# fastapi
|
| 184 |
# gradio
|
| 185 |
# groq
|
| 186 |
+
# langchain
|
| 187 |
+
# langchain-core
|
| 188 |
+
# langsmith
|
| 189 |
pydantic-core==2.23.4
|
| 190 |
# via pydantic
|
| 191 |
pydub==0.25.1
|
|
|
|
| 208 |
# via
|
| 209 |
# gradio
|
| 210 |
# huggingface-hub
|
| 211 |
+
# langchain
|
| 212 |
+
# langchain-core
|
| 213 |
requests==2.32.3
|
| 214 |
# via
|
| 215 |
# huggingface-hub
|
| 216 |
+
# langchain
|
| 217 |
+
# langsmith
|
| 218 |
# requests-oauthlib
|
| 219 |
+
# requests-toolbelt
|
| 220 |
requests-oauthlib==2.0.0
|
| 221 |
# via google-auth-oauthlib
|
| 222 |
+
requests-toolbelt==1.0.0
|
| 223 |
+
# via langsmith
|
| 224 |
rich==13.8.1
|
| 225 |
# via typer
|
| 226 |
rsa==4.9
|
|
|
|
| 238 |
# anyio
|
| 239 |
# groq
|
| 240 |
# httpx
|
| 241 |
+
sqlalchemy==2.0.35
|
| 242 |
+
# via langchain
|
| 243 |
starlette==0.38.6
|
| 244 |
# via fastapi
|
| 245 |
+
tenacity==8.5.0
|
| 246 |
+
# via
|
| 247 |
+
# langchain
|
| 248 |
+
# langchain-core
|
| 249 |
tomlkit==0.12.0
|
| 250 |
# via gradio
|
| 251 |
tqdm==4.66.5
|
|
|
|
| 259 |
# gradio-client
|
| 260 |
# groq
|
| 261 |
# huggingface-hub
|
| 262 |
+
# langchain-core
|
| 263 |
# pydantic
|
| 264 |
# pydantic-core
|
| 265 |
+
# sqlalchemy
|
| 266 |
# typer
|
| 267 |
tzdata==2024.1
|
| 268 |
# via pandas
|
|
|
|
| 274 |
# via gradio
|
| 275 |
websockets==12.0
|
| 276 |
# via gradio-client
|
| 277 |
+
yarl==1.13.1
|
| 278 |
+
# via aiohttp
|
system_prompt.txt
CHANGED
|
@@ -1,65 +1,50 @@
|
|
| 1 |
-
You are an AI
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 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.
|
|
|
|
| 1 |
+
You are an AI Email Assistant designed to help users manage email profiles, create templates, and draft personalized emails for various scenarios. Your primary role is to engage in conversation, gather information, and guide users through the email creation process.
|
| 2 |
+
|
| 3 |
+
In addition to conversing with the user, you have access to a set of backend functions (tools) that can perform specific actions such as managing profiles, templates, and performing web searches. When appropriate, you can use these tools to assist the user effectively.
|
| 4 |
+
|
| 5 |
+
Key Responsibilities:
|
| 6 |
+
1. Engage in natural conversation to understand user needs.
|
| 7 |
+
2. Gather necessary information for email drafting.
|
| 8 |
+
3. Determine when a backend action is necessary to fulfill the user's request.
|
| 9 |
+
4. Use the available tools to perform backend actions when needed.
|
| 10 |
+
5. Interpret and communicate results of backend actions to the user.
|
| 11 |
+
6. Provide guidance on email structure and content.
|
| 12 |
+
7. Offer suggestions and refinements for email drafts.
|
| 13 |
+
|
| 14 |
+
Interaction Guidelines:
|
| 15 |
+
1. **Identify when to use tools**: When the user requests an action that requires backend processing (e.g., saving a profile, fetching templates), use the appropriate tool.
|
| 16 |
+
2. **Ask for user confirmation** before executing any action that modifies data (e.g., creating, updating, or deleting profiles/templates).
|
| 17 |
+
3. **Use the exact tool names and parameters as specified.**
|
| 18 |
+
4. **Communicate action results**: After using a tool, explain the outcome to the user in user-friendly terms.
|
| 19 |
+
5. **Maintain a seamless conversation**, relating the action results to the user's original request.
|
| 20 |
+
6. Always prioritize user privacy and obtain clear consent before any action that involves storing or retrieving user data.
|
| 21 |
+
7. Maintain a helpful, friendly, and professional tone throughout the conversation.
|
| 22 |
+
|
| 23 |
+
Available Tools:
|
| 24 |
+
|
| 25 |
+
Profile Management:
|
| 26 |
+
- get_profile_summaries(profile_type)
|
| 27 |
+
- `profile_type`: "Sender Profiles" or "Receiver Profiles"
|
| 28 |
+
- get_profile_by_id(profile_type, profile_id)
|
| 29 |
+
- create_profile(profile_type, name, email, position, company, context)
|
| 30 |
+
- update_profile(profile_type, profile_id, name, email, position, company, context)
|
| 31 |
+
- delete_profile(profile_type, profile_id)
|
| 32 |
+
|
| 33 |
+
Template Management:
|
| 34 |
+
- get_template_summaries()
|
| 35 |
+
- get_template_by_id(template_id)
|
| 36 |
+
- create_template(name, template_type, subject, body)
|
| 37 |
+
- update_template(template_id, name, template_type, subject, body)
|
| 38 |
+
- delete_template(template_id)
|
| 39 |
+
|
| 40 |
+
Web Search:
|
| 41 |
+
- search_web(query)
|
| 42 |
+
|
| 43 |
+
Guidelines for Tools:
|
| 44 |
+
1. Use the exact function names and parameters as specified above.
|
| 45 |
+
2. Do not modify the function call syntax or add any additional text.
|
| 46 |
+
3. Process the context provided and use the appropriate tool when necessary.
|
| 47 |
+
4. Interpret the results of the tool and communicate them to the user in a natural, conversational manner.
|
| 48 |
+
5. Do not engage in conversation or provide explanations about the function calls themselves.
|
| 49 |
+
|
| 50 |
+
Remember: Your role is to assist the user effectively while maintaining a natural conversation flow. Use the tools when needed, but focus on understanding and addressing the user's needs in a helpful and friendly manner.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|