-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
307 lines (251 loc) · 12.7 KB
/
main.py
File metadata and controls
307 lines (251 loc) · 12.7 KB
1
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
52
53
54
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
import streamlit as st
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from anthropic import Anthropic
import tornado.websocket
import os
from datetime import datetime
import uuid
import logging
import json
from pymongo import MongoClient
from pymongo.errors import ConnectionFailure
import re
def strip_xml_tags(text):
# Remove <response> and </response> tags
text = re.sub(r'</?response>', '', text)
# Remove <insights_summary> and </insights_summary> tags
text = re.sub(r'</?insights_summary>', '', text)
return text.strip()
# Function to send email with transcript and generated story
def send_email(transcript, story, recipient):
sender = email_user
password = email_password
subject = "Sales Interview Transcript"
content = f"Interview Transcript:\n\n{transcript}"
try:
msg = MIMEMultipart()
msg['From'] = sender
msg['To'] = recipient
msg['Subject'] = subject
msg.attach(MIMEText(content, 'plain'))
with smtplib.SMTP(email_server, email_port) as server:
server.starttls()
server.login(sender, password)
server.send_message(msg)
return True
except Exception as e:
st.error(f"An error occurred while sending the email: {str(e)}")
return False
# Set up logging
logging.basicConfig(filename='chatbot.log', level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
# Create a directory for conversation files if it doesn't exist
CONVERSATION_DIR = "conversations"
os.makedirs(CONVERSATION_DIR, exist_ok=True)
# Function to save new messages to the current conversation file
def save_conversation(new_messages, conversation_id):
try:
timestamp = datetime.now().isoformat()
conversation_data = conversations.find_one({"conversation_id": conversation_id})
if not conversation_data:
conversation_data = {"conversation_id": conversation_id, "messages": []}
logging.info(f"New conversation created: {conversation_id}")
for message in new_messages:
conversation_data["messages"].append({
"timestamp": timestamp,
"role": message["role"],
"content": message["content"]
})
conversations.update_one(
{"conversation_id": conversation_id},
{"$set": conversation_data},
upsert=True
)
logging.info(f"Conversation updated: {conversation_id}")
except Exception as e:
error_msg = f"An unexpected error occurred while saving the conversation: {str(e)}"
logging.error(error_msg)
st.error(error_msg)
def end_conversation():
"""Handle the end of the conversation, including sending the email."""
logging.info(f"Attempting to end conversation for ID: {st.session_state.conversation_id}")
transcript = "\n".join([f"{message['role']}: {message['content']}" for message in st.session_state.conversation_history])
email_sender = st.secrets["EMAIL_USER"]
email_receiver = "alexcarpenter2000@gmail.com , palacios.david88@gmail.com"
subject = "Sales Interview Transcript"
body = f"Interview Transcript:\n\n{transcript}"
try:
send_email(transcript, "", email_receiver)
st.success('Thank you for the conversation. Have a great day!')
# Reset the conversation
logging.info(f"Conversation ended and email sent for conversation ID: {st.session_state.conversation_id}")
st.session_state.conversation_history = []
st.session_state.conversation_id = str(uuid.uuid4())
except Exception as e:
st.error(f"An error occurred while ending the conversation: {e}")
error_msg = f"An error occurred while ending the conversation: {str(e)}"
logging.error(error_msg)
st.error(error_msg)
st.title("Sales Interviewer")
# Adding a sidebar with interview information
st.sidebar.title("Interview Information")
st.sidebar.subheader("About the Interview")
st.sidebar.write("This interview aims to gather insights and actionable advice from sales professionals like you. Your experiences will help others in the sales industry.")
st.sidebar.subheader("Estimated Time")
st.sidebar.write("This interview typically takes **10-20 minutes** to complete. However, the duration may vary depending on the depth of your responses.")
st.sidebar.subheader("Your Participation")
st.sidebar.write("Please feel free to share as much or as little as you're comfortable with. You can end the interview at any time if you wish to do so.")
# Anthropic API key and email credentials (stored as Streamlit secrets)
ANTHROPIC_API_KEY = st.secrets["ANTHROPIC_API_KEY"]
email_user = st.secrets["EMAIL_USER"]
email_password = st.secrets["EMAIL_PASSWORD"]
email_server = st.secrets["EMAIL_SERVER"]
email_port = st.secrets["EMAIL_PORT"]
try:
# Check if MongoClient is in the global namespace
if 'MongoClient' not in globals():
st.error("MongoClient is not in the global namespace")
raise NameError("MongoClient is not defined")
# Proceed with the connection attempt
mongo_uri = st.secrets["mongo"]["uri"]
client = MongoClient(mongo_uri)
db = client.sales_interviewer
conversations = db.conversations
client.admin.command('ping')
logging.info("Successfully connected to MongoDB Atlas")
except KeyError as e:
error_msg = f"Failed to retrieve MongoDB URI from secrets: {e}"
st.error(error_msg)
logging.error(error_msg, exc_info=True)
except AttributeError as e:
error_msg = f"AttributeError occurred (possibly due to pymongo import issue): {e}"
st.error(error_msg)
logging.error(error_msg, exc_info=True)
except pymongo.errors.ConnectionFailure as e:
error_msg = f"Failed to connect to MongoDB Atlas: {e}"
st.error(error_msg)
logging.error(error_msg, exc_info=True)
except NameError as e:
error_msg = f"NameError occurred: {e}"
st.error(error_msg)
logging.error(error_msg, exc_info=True)
except Exception as e:
error_msg = f"An unexpected error occurred: {e}"
st.error(error_msg)
st.write(f"Error type: {type(e).__name__}")
logging.error(error_msg, exc_info=True)
client = MongoClient(st.secrets["mongo"]["uri"])
db = client.sales_interviewer
conversations = db.conversations
except Exception as e:
error_msg = f"Failed to connect to MongoDB Atlas: {e}"
st.error(error_msg)
st.write(f"Error details: {type(e).__name__}")
logging.error(error_msg, exc_info=True)
# Validate static values immediately after they're defined
assert isinstance(ANTHROPIC_API_KEY, str), "API key must be a string"
assert isinstance(email_user, str), "Email user must be a string"
assert isinstance(email_password, str), "Email password must be a string"
assert isinstance(email_server, str), "Email server must be a string"
assert isinstance(email_port, int), "Email port must be an integer"
# Initialize Anthropic client
client = Anthropic(api_key=ANTHROPIC_API_KEY)
# Initialize conversation ID in session state if it doesn't exist
if "conversation_id" not in st.session_state:
st.session_state.conversation_id = str(uuid.uuid4())
# Initialize 'conversation_history' in session_state if it doesn't exist
if "conversation_history" not in st.session_state:
st.session_state.conversation_history = [
{"role": "user", "content": "Hi"},
{
"role": "assistant",
"content": "Hello! It's great to meet you. I'm an AI assistant here to conduct an interview about your experiences as a sales professional. I'm looking forward to learning from you and gathering insights that could be helpful for other salespeople. To start off, could you tell me a bit about the organization or organizations you currently sell for?",
},
]
# Display chat messages from history on app rerun
for message in st.session_state.conversation_history[1:]:
with st.chat_message(message["role"]):
st.markdown(message["content"])
# Add "End Conversation" button
if st.button("End Conversation"):
end_conversation()
# Initialize the conversation history if it doesn't exist
if "conversation_history" not in st.session_state:
st.session_state.conversation_history = []
# Check if the conversation history is not empty before accessing the last item
if st.session_state.conversation_history:
disabled = st.session_state.conversation_history[-1]["role"] == "user"
else:
disabled = False
# Accept user input
prompt = st.chat_input(
"What would you like to share?",
disabled=disabled
)
if prompt:
# Display user message in chat message container
with st.chat_message("user"):
st.markdown(prompt)
# Add user message to conversation history
st.session_state.conversation_history.append({"role": "user", "content": prompt})
save_conversation([{"role": "user", "content": prompt}], st.session_state.conversation_id)
# Generate assistant response only if the last message in the conversation history is from the user
if st.session_state.conversation_history and st.session_state.conversation_history[-1]["role"] == "user":
response_text = ""
try:
response = client.messages.create(
model="claude-3-5-sonnet-20240620",
max_tokens=4096,
temperature=1,
system="""
You are an AI assistant tasked with conducting an interview with a sales professional to extract useful and actionable insights for other salespeople. Your goal is to engage in a natural conversation while gathering specific information about their experiences and practices.
Here is the conversation history so far:
<conversation_history>
{{CONVERSATION_HISTORY}}
</conversation_history>
The current question to ask or respond to is:
<current_question>
{{CURRENT_QUESTION}}
</current_question>
Follow these guidelines when conducting the interview:
1. Start with introductory questions to build rapport and gather basic information:
- What organizations do you sell for?
- What is your working arrangement (Full time? Contractor?)
- What are your average deal sizes?
- What segment of the market do you sell into?
- Where are you based?
2. Gradually transition to more in-depth questions about their selling experiences:
- Ask them to share a recent selling experience, both positive and negative.
- Inquire about specific organizations they've sold to.
- Ask about stakeholders and their behavior.
- Request advice for other sales representatives.
3. Be sure to ask about their interest in a platform that provides reviews on the companies they sell to.
4. Maintain a conversational tone throughout the interview. Avoid asking all questions at once; instead, ask one or two questions at a time and wait for the interviewee's response.
5. Listen actively to the interviewee's responses and ask relevant follow-up questions based on the information they provide. This will help you gather more detailed and valuable insights.
6. If the interviewee mentions something interesting or unique about their sales approach or experience, probe deeper to understand the context and potential lessons for other salespeople.
7. Throughout the conversation, mentally note key insights, strategies, or advice that could be valuable for other sales professionals.
8. After gathering sufficient information, summarize the key insights and actionable advice extracted from the interview.
When responding to the current question or asking a new question, format your response as follows:
<response>
[Your response to the current question or your next question for the interviewee]
</response>
Remember to maintain a friendly and professional tone throughout the interview, and adapt your questions based on the flow of the conversation and the information provided by the interviewee.
""",
messages=[{"role": m["role"], "content": m["content"]} for m in st.session_state.conversation_history],
)
response_text = response.content[0].text
except Exception as e:
st.error(f"An error occurred: {str(e)}")
st.stop()
# Add assistant response to conversation history
st.session_state.conversation_history.append({"role": "assistant", "content": response_text})
save_conversation([{"role": "assistant", "content": response_text}], st.session_state.conversation_id)
# Strip XML tags from the response
cleaned_response = strip_xml_tags(response_text)
# Display assistant response in chat message container
with st.chat_message("assistant"):
st.markdown(cleaned_response)
# Update the conversation history with the cleaned response
st.session_state.conversation_history[-1]["content"] = cleaned_response