0Learn commited on
Commit
cf6b61d
·
verified ·
1 Parent(s): 952613e

Upload 9 files

Browse files
Files changed (8) hide show
  1. app.py +157 -18
  2. chat.py +177 -47
  3. email_actions.py +171 -82
  4. google_sheets_utils.py +254 -132
  5. groq_client.py +10 -5
  6. profile_management.py +154 -83
  7. requirements.txt +69 -4
  8. 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 os
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, get_profile_summaries
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 connect_to_sheets
 
 
 
 
 
 
 
 
 
 
13
 
14
- # Load environment variables
15
- load_dotenv()
 
 
 
16
 
17
  def main():
18
- api_key = os.getenv('GROQ_API_KEY')
 
 
 
 
19
  if api_key:
20
  set_api_key(api_key)
 
 
 
 
 
 
 
 
 
21
 
22
- sender_sheet, receiver_sheet, template_sheet = connect_to_sheets()
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.Tab("Sender Profiles"):
36
- sender_components = create_profile_management_interface(sender_sheet, "Sender", current_sender)
37
- with gr.Tab("Receiver Profiles"):
38
- receiver_components = create_profile_management_interface(receiver_sheet, "Receiver", current_receiver)
 
 
 
 
 
 
 
39
 
40
  with gr.Tab("Email Actions"):
41
- email_actions_components = create_email_actions_interface(template_sheet, current_template)
 
 
 
42
 
43
  with gr.Tab("Chat"):
44
  # Get summaries for context
45
- sender_summaries = get_profile_summaries(sender_sheet)
46
- receiver_summaries = get_profile_summaries(receiver_sheet)
47
- template_summaries = get_template_summaries(template_sheet)
 
 
 
 
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
- 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:
@@ -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
- 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
 
@@ -41,53 +149,75 @@ def create_chat_interface(sender_summaries, receiver_summaries, template_summari
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)
 
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
- # 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)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- from google.oauth2.service_account import Credentials
 
10
  import logging
 
 
 
 
 
11
 
12
- # Set up logging
13
  logging.basicConfig(level=logging.INFO)
14
- logger = logging.getLogger(__name__)
15
-
16
- # Load environment variables
17
- load_dotenv()
18
-
19
- # Setup Google Sheets connection
20
- SCOPES = ['https://www.googleapis.com/auth/spreadsheets',
21
- 'https://www.googleapis.com/auth/drive']
22
-
23
- creds_json = os.environ.get('GOOGLE_SHEETS_CREDENTIALS_FILE')
24
- if not creds_json:
25
- raise ValueError("GOOGLE_SHEETS_CREDENTIALS_FILE environment variable is not set")
26
-
27
- creds_dict = json.loads(creds_json)
28
- creds = Credentials.from_service_account_info(creds_dict, scopes=SCOPES)
29
- client = gspread.authorize(creds)
30
-
31
- # Open the Google Sheet (create it if it doesn't exist)
32
- try:
33
- spreadsheet = client.open("AI Email Assistant")
34
- logger.info("Successfully opened the spreadsheet.")
35
- except gspread.exceptions.SpreadsheetNotFound:
36
- logger.info("Spreadsheet 'AI Email Assistant' not found. Creating a new one.")
37
- spreadsheet = client.create("AI Email Assistant")
38
- logger.info("New spreadsheet 'AI Email Assistant' created.")
39
-
40
- def initialize_sheet(sheet, headers, sample_data):
41
- if not sheet.get_all_values():
42
- sheet.append_row(headers)
43
- for row in sample_data:
44
- sheet.append_row(row)
45
- logger.info(f"Initialized {sheet.title} with headers and sample data.")
46
-
47
- def connect_to_sheets():
48
  try:
49
- sender_sheet = spreadsheet.worksheet("Sender Profiles")
50
- except gspread.exceptions.WorksheetNotFound:
51
- sender_sheet = spreadsheet.add_worksheet(title="Sender Profiles", rows="100", cols="20")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  try:
54
- receiver_sheet = spreadsheet.worksheet("Receiver Profiles")
55
- except gspread.exceptions.WorksheetNotFound:
56
- receiver_sheet = spreadsheet.add_worksheet(title="Receiver Profiles", rows="100", cols="20")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  try:
59
- template_sheet = spreadsheet.worksheet("Email Templates")
60
- except gspread.exceptions.WorksheetNotFound:
61
- template_sheet = spreadsheet.add_worksheet(title="Email Templates", rows="100", cols="20")
62
-
63
- # Initialize sheets with headers and sample data if they're empty
64
- profile_headers = ["Type", "ID", "Name", "Email", "Position", "Company", "Context"]
65
- sender_sample = [
66
- ["Sender", "SEND_1", "John Doe", "john@example.com", "Sales Manager", "ABC Corp", "Professional"],
67
- ["Sender", "SEND_2", "Jane Smith", "jane@example.com", "Marketing Director", "XYZ Inc", "Professional"]
68
- ]
69
- receiver_sample = [
70
- ["Receiver", "REC_1", "Alice Johnson", "alice@example.com", "CEO", "123 Industries", "Professional"],
71
- ["Receiver", "REC_2", "Bob Brown", "bob@example.com", "HR Manager", "456 Corp", "Professional"]
72
- ]
73
- initialize_sheet(sender_sheet, profile_headers, sender_sample)
74
- initialize_sheet(receiver_sheet, profile_headers, receiver_sample)
75
-
76
- template_headers = ["Type", "ID", "Name", "Subject", "Content", "Sender Profile", "Receiver Profile"]
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
- sheet.update_cell(row, 3, profile_data['name'])
112
- sheet.update_cell(row, 4, profile_data['email'])
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
- return False
 
 
139
 
140
- def update_template(sheet, template_id, template_data):
141
- cell = sheet.find(template_id)
142
- if cell:
143
- row = cell.row
144
- sheet.update_cell(row, 3, template_data['name'])
145
- sheet.update_cell(row, 4, template_data['subject'])
146
- sheet.update_cell(row, 5, template_data['content'])
147
- sheet.update_cell(row, 6, template_data['sender_profile'])
148
- sheet.update_cell(row, 7, template_data['receiver_profile'])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- from dotenv import load_dotenv
7
  from groq import Groq
 
8
 
9
- # Load environment variables
10
- load_dotenv()
 
 
 
11
 
12
- api_key = os.getenv('GROQ_API_KEY')
 
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 .env file.")
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 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)
 
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 -r requirements.in
 
 
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
- 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
@@ -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-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.
 
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.