Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
|
@@ -43,21 +43,33 @@ db = SQLDatabase.from_uri(f"sqlite:///{db_loc}")
|
|
| 43 |
database_schema = db.get_table_info()
|
| 44 |
|
| 45 |
|
| 46 |
-
#=================================Logging=====================================#
|
| 47 |
|
| 48 |
|
| 49 |
log_file = Path("logs/") / f"data_{uuid.uuid4()}.json"
|
| 50 |
log_folder = log_file.parent
|
| 51 |
|
| 52 |
-
|
| 53 |
-
repo_id="chatbot-logs",
|
| 54 |
repo_type="dataset",
|
| 55 |
folder_path=log_folder,
|
| 56 |
path_in_repo="data",
|
| 57 |
-
every=
|
| 58 |
)
|
| 59 |
|
| 60 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
#=================================SQL_AGENT=====================================#
|
| 62 |
|
| 63 |
# Define the system message for the agent, including instructions and available tables
|
|
@@ -108,6 +120,7 @@ sqlite_agent = create_sql_agent(
|
|
| 108 |
@tool
|
| 109 |
def sql_tool(user_input):
|
| 110 |
"""
|
|
|
|
| 111 |
Executes a SQL query using the sqlite_agent and returns the result.
|
| 112 |
Args:
|
| 113 |
user_input (str): a natural language query string explaining what information is required while also providing the necessary details to get the information.
|
|
@@ -128,148 +141,126 @@ def sql_tool(user_input):
|
|
| 128 |
# Return the result or the exception message
|
| 129 |
return prediction
|
| 130 |
|
| 131 |
-
#===================================
|
|
|
|
|
|
|
| 132 |
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
# Save the log to the file
|
| 137 |
-
with scheduler.lock:
|
| 138 |
-
# Open the log file in append mode
|
| 139 |
-
with log_file.open("a") as f:
|
| 140 |
-
f.write(json.dumps({
|
| 141 |
-
"task": task,
|
| 142 |
-
"details": details,
|
| 143 |
-
"timestamp": str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
| 144 |
-
}))
|
| 145 |
|
| 146 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
|
| 148 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
|
| 150 |
-
|
| 151 |
-
def cancel_order(order_id, customer_id):
|
| 152 |
-
"""
|
| 153 |
-
Cancels the order, processes refund and updates database.
|
| 154 |
-
Logs the action of canceling an order.
|
| 155 |
-
Args:
|
| 156 |
-
order_id (int): The unique ID of the order to be canceled.
|
| 157 |
-
customer_id (int): The unique ID of the customer requesting the cancellation.
|
| 158 |
-
Returns:
|
| 159 |
-
str: If something fails this function will return a string stating what's wrong.
|
| 160 |
-
"""
|
| 161 |
-
details = {
|
| 162 |
-
"order_id": order_id,
|
| 163 |
-
"customer_id": customer_id,
|
| 164 |
-
"action": "Order Canceled"
|
| 165 |
-
}
|
| 166 |
-
try:
|
| 167 |
-
# Connect to the database
|
| 168 |
-
connection = sqlite3.connect("ecomm_copy.db") # We will use a dummy database just to protect our original data during testing
|
| 169 |
-
cursor = connection.cursor()
|
| 170 |
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
UPDATE Orders
|
| 174 |
-
SET status = 'Canceled'
|
| 175 |
-
WHERE order_id = ? AND customer_id = ?;
|
| 176 |
-
"""
|
| 177 |
-
cursor.execute(query_order, (order_id, customer_id))
|
| 178 |
|
| 179 |
-
|
| 180 |
-
|
|
|
|
| 181 |
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
WHERE order_id = ? AND customer_id = ?;
|
| 187 |
-
"""
|
| 188 |
-
cursor.execute(query_shipping, (order_id, customer_id))
|
| 189 |
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
return f"No matching order found for Order ID {order_id} and Customer ID {customer_id}."
|
| 194 |
-
else:
|
| 195 |
-
cancel_fee = float(row[0])
|
| 196 |
-
price = float(row[1])
|
| 197 |
-
|
| 198 |
-
refund_amount = price - cancel_fee
|
| 199 |
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
"""
|
| 205 |
-
cursor.execute(query_refund, (order_id, cancel_fee, refund_amount))
|
| 206 |
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
|
|
|
|
|
|
| 210 |
|
| 211 |
-
query = """select * from refund where order_id = ?;"""
|
| 212 |
-
cursor.execute(query,(order_id,))
|
| 213 |
-
st.write(f"Updated details: {cursor.fetchall()}")
|
| 214 |
-
|
| 215 |
-
return f"Order ID {order_id} for Customer ID {customer_id} has been canceled successfully. Shipping status updated."
|
| 216 |
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
|
|
|
|
|
|
| 220 |
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
connection.close()
|
| 224 |
|
|
|
|
|
|
|
| 225 |
|
| 226 |
-
@tool
|
| 227 |
-
def change_address(order_id, new_address):
|
| 228 |
-
"""
|
| 229 |
-
Updates the shipping address for a specific order in the database.
|
| 230 |
-
Args:
|
| 231 |
-
order_id (int): The unique ID of the order for which the address is being changed.
|
| 232 |
-
new_address (str): The new shipping address to be updated.
|
| 233 |
-
Returns:
|
| 234 |
-
str: A confirmation message indicating the update status.
|
| 235 |
"""
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 241 |
|
| 242 |
try:
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
|
|
|
| 246 |
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
SET shipping_address = ?
|
| 251 |
-
WHERE order_id = ?;
|
| 252 |
-
"""
|
| 253 |
-
|
| 254 |
-
# Execute the query with placeholders
|
| 255 |
-
cursor.execute(query, (new_address, order_id))
|
| 256 |
-
connection.commit() # Commit the changes
|
| 257 |
|
| 258 |
-
|
| 259 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 260 |
|
| 261 |
-
log_action("change_address",details)
|
| 262 |
-
st.write(f"Updated details: {cursor.fetchall()}")
|
| 263 |
-
|
| 264 |
-
return f"Update successful for Order ID {order_id} with new address: {new_address}"
|
| 265 |
-
|
| 266 |
-
except sqlite3.Error as e:
|
| 267 |
-
st.write(f"An error occurred: {e}")
|
| 268 |
-
return f"An error occurred: {e}"
|
| 269 |
-
|
| 270 |
-
finally:
|
| 271 |
-
if connection:
|
| 272 |
-
connection.close()
|
| 273 |
|
| 274 |
@tool
|
| 275 |
def register_feedback(intent, customer_id, feedback, rating):
|
|
@@ -317,9 +308,9 @@ def defer_to_human(customer_id, query, intent, reason):
|
|
| 317 |
|
| 318 |
|
| 319 |
@tool
|
| 320 |
-
def days_since(delivered_date):
|
| 321 |
"""
|
| 322 |
-
Calculates the number of days since the product was delivered.
|
| 323 |
Args:
|
| 324 |
delivered_date (str): The date when the product was delivered in the format 'YYYY-MM-DD'.
|
| 325 |
"""
|
|
@@ -331,7 +322,7 @@ def days_since(delivered_date):
|
|
| 331 |
# Calculate the difference in days
|
| 332 |
days_difference = (today - delivered_date).days
|
| 333 |
|
| 334 |
-
return days_difference
|
| 335 |
except ValueError as e:
|
| 336 |
return f"Error: {e}"
|
| 337 |
|
|
@@ -339,70 +330,41 @@ def build_prompt(df):
|
|
| 339 |
|
| 340 |
system_message = f"""
|
| 341 |
|
| 342 |
-
You are an intelligent e-commerce chatbot designed to assist users with post-order queries.
|
| 343 |
-
|
| 344 |
-
|
|
|
|
|
|
|
| 345 |
You are only allowed to provide information relevant to the particular customer and the customer information is provided below. you can provide information of this customer only. Following is the information about the customer from the last 2 weeks:
|
| 346 |
|
| 347 |
{df}
|
| 348 |
|
| 349 |
-
If this information is not enough to answer question, identify the customer from data above and fetch necessary information usign the sql_tool - do not fetch information of other customers.
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
- Get necessary details and learn why customer wants to cancel.
|
| 356 |
-
- check product status, if it's already cancelled - tell the customer the same - you can cancel a product or refund a product if it's already cancelled.
|
| 357 |
-
- Check cancellation fee, validity period, and amount for the product.
|
| 358 |
-
- if it doesn't meet the criteria you do not have the clearance to process refund or process return. defer to human while providing a reason to customer on why you cannot process it or why you are defering it to a human.
|
| 359 |
-
- Check If the cancellation meets the criteria: i.e,
|
| 360 |
-
- if the product is not delivered yet then the cancellation fee doesn't apply and you can proceed with cancellation and refund directly by calling cancel_order tool.
|
| 361 |
-
- if the order is delivered and within the validity period by checking days since delivery date - use the days_since tool to calculate the number of days since delivery
|
| 362 |
-
- If the product is delivered, within the validity period and if the customer is okay with the cancellation fee cancel the order and process the refund.
|
| 363 |
-
- do not proceed to cancel if the customer is not okay with the cancellation fee
|
| 364 |
-
- Call `cancel_order` which will cancel the order and process the refund. refund amount should be product price - cancellation fee.
|
| 365 |
-
|
| 366 |
-
#### 2. **Check Cancellation Fee**
|
| 367 |
-
- Check the cancellation fee.
|
| 368 |
-
#### 3. **Check Refund Policy**
|
| 369 |
-
- Provide the refund policy details.
|
| 370 |
-
#### 4. **Get Invoice**
|
| 371 |
-
- Retrieve necessary details.
|
| 372 |
-
- Invoice is only generated for orders that are shipped.
|
| 373 |
-
- Provide the invoice information to the customer.
|
| 374 |
-
#### 5. **Track Order/Track Refund**
|
| 375 |
-
- Get necessary information from user
|
| 376 |
-
- Retrieve the order or refund status.
|
| 377 |
-
- Provide the information to the customer.
|
| 378 |
-
#### 6. **Change Shipping Address**
|
| 379 |
-
- You cannot change shipping orders of orders that are already delivered or cancelled.
|
| 380 |
-
- Retrieve necessary details.
|
| 381 |
-
- Update the shipping address with `change_address`.
|
| 382 |
-
### 7. Provide Information
|
| 383 |
-
- If customer asks for his own information, provide accurate information by checking the information provided to you above or if that's not enough use the sql_tool.
|
| 384 |
-
|
| 385 |
MANDATORY STEP:
|
| 386 |
After helping the customer with their concern,
|
| 387 |
- Ask if the customer needs help with anything else. If they ask for anything from the above list help them and along with that,
|
| 388 |
1. Ask for their feedback and rating out of 5.
|
| 389 |
2. then, Use the `register_feedback` tool to log it. - you MUST ask customer feedback along with asking customer what else they need help with.
|
|
|
|
| 390 |
|
| 391 |
---
|
| 392 |
### **Handling Out-of-Scope Queries:**
|
| 393 |
If the user's query, at any point is not covered by the workflows above:
|
| 394 |
- Respond:
|
| 395 |
> "This is beyond my skill. Let me connect you to a customer service agent" and get necessary details from the customer and use the defer_to_human tool.
|
| 396 |
-
- End the conversation.
|
| 397 |
---
|
| 398 |
### **IMPORTANT Notes for the Model:**
|
| 399 |
- Always fetch additional required details from the database and do not blindly believe details provided by the customer like customer id, email and phone number. You should get the customer id from the system prompt. Cross check with the database and stay loyal to the database.
|
| 400 |
- Be empathetic to the customer but loyal to the instructions provided to you. Try to deescalate a situation before deferring it to human and defer to human only once.
|
| 401 |
-
- Always aim to minimize the number of questions asked by retrieving as much information as possible from
|
| 402 |
- Follow the exact workflows for each query category.
|
| 403 |
-
- You will always confirm the order id even if the customer has only one order before you
|
| 404 |
-
- Ensure smooth transitions between steps, asking the customer if they need further assistance after resolving their issue.
|
| 405 |
-
- Log every interaction properly to maintain an audit trail for customer support sessions.
|
| 406 |
"""
|
| 407 |
|
| 408 |
#st.write(system_message)
|
|
@@ -417,9 +379,41 @@ def build_prompt(df):
|
|
| 417 |
|
| 418 |
#===============================================Streamlit=========================================#
|
| 419 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 420 |
def fetch_details(email):
|
| 421 |
try:
|
| 422 |
-
|
| 423 |
# Connect to the SQLite database
|
| 424 |
connection = sqlite3.connect("ecomm.db") # Replace with your .db file path
|
| 425 |
cursor = connection.cursor()
|
|
@@ -473,50 +467,19 @@ def fetch_details(email):
|
|
| 473 |
cursor.close()
|
| 474 |
connection.close()
|
| 475 |
|
| 476 |
-
|
| 477 |
-
def authenticate_user(email, phone):
|
| 478 |
-
connection = sqlite3.connect("ecomm.db") # Replace with your .db file path
|
| 479 |
-
cursor = connection.cursor()
|
| 480 |
-
|
| 481 |
-
query = "SELECT first_name FROM customers WHERE email = ? AND phone = ?"
|
| 482 |
-
cursor.execute(query, (email, phone))
|
| 483 |
-
user = cursor.fetchone()
|
| 484 |
-
|
| 485 |
-
if user:
|
| 486 |
-
return True # Login successful
|
| 487 |
-
return False # Login failed
|
| 488 |
-
|
| 489 |
-
def login_page():
|
| 490 |
-
st.title("Login Page")
|
| 491 |
-
|
| 492 |
-
email = st.text_input("Email")
|
| 493 |
-
password = st.text_input("Password", type="password")
|
| 494 |
-
|
| 495 |
-
login_button = st.button("Login")
|
| 496 |
-
|
| 497 |
-
if login_button:
|
| 498 |
-
if authenticate_user(email, password):
|
| 499 |
-
st.session_state.logged_in = True
|
| 500 |
-
st.session_state.email = email
|
| 501 |
-
st.success("Login successful! Redirecting to Chatbot...")
|
| 502 |
-
st.rerun()
|
| 503 |
-
else:
|
| 504 |
-
st.error("Invalid email or password.")
|
| 505 |
-
|
| 506 |
# Function to process user input and generate a chatbot response
|
| 507 |
def chatbot_interface():
|
| 508 |
st.title("E-Commerce Chatbot")
|
| 509 |
-
st.write("
|
| 510 |
|
| 511 |
|
| 512 |
if 'conversation_history' not in st.session_state:
|
| 513 |
-
st.session_state.conversation_history = [{"role": "assistant", "content": "
|
| 514 |
|
| 515 |
|
| 516 |
-
details = fetch_details(st.session_state.email)
|
| 517 |
-
#st.write("chatbot: ",st.session_state.email)
|
| 518 |
prompt = build_prompt(details)
|
| 519 |
-
tools = [sql_tool,defer_to_human,
|
| 520 |
|
| 521 |
chatbot = AzureChatOpenAI(
|
| 522 |
model_name=model_name,
|
|
@@ -554,7 +517,9 @@ def chatbot_interface():
|
|
| 554 |
# Add the chatbot's response to the history
|
| 555 |
chatbot_response = response['output']
|
| 556 |
st.session_state.conversation_history.append({"role": "assistant", "content": chatbot_response})
|
| 557 |
-
|
|
|
|
|
|
|
| 558 |
# Display the chatbot's response
|
| 559 |
with st.chat_message("assistant"):
|
| 560 |
st.markdown(chatbot_response)
|
|
|
|
| 43 |
database_schema = db.get_table_info()
|
| 44 |
|
| 45 |
|
| 46 |
+
#=================================Setup Logging=====================================#
|
| 47 |
|
| 48 |
|
| 49 |
log_file = Path("logs/") / f"data_{uuid.uuid4()}.json"
|
| 50 |
log_folder = log_file.parent
|
| 51 |
|
| 52 |
+
log_scheduler = CommitScheduler(
|
| 53 |
+
repo_id="chatbot-logs", #Dataset name where we want to save the logs.
|
| 54 |
repo_type="dataset",
|
| 55 |
folder_path=log_folder,
|
| 56 |
path_in_repo="data",
|
| 57 |
+
every=5 # Saves data every x minute
|
| 58 |
)
|
| 59 |
|
| 60 |
|
| 61 |
+
|
| 62 |
+
history_file = Path("history/")/f"data_{uuid.uuid4()}.json"
|
| 63 |
+
history_folder = history_file.parent
|
| 64 |
+
|
| 65 |
+
history_scheduler = CommitScheduler(
|
| 66 |
+
repo_id="chatbot-history", #Dataset name where we want to save the logs.
|
| 67 |
+
repo_type="dataset",
|
| 68 |
+
folder_path=history_folder,
|
| 69 |
+
path_in_repo="data",
|
| 70 |
+
every=5 # Saves data every x minute
|
| 71 |
+
)
|
| 72 |
+
|
| 73 |
#=================================SQL_AGENT=====================================#
|
| 74 |
|
| 75 |
# Define the system message for the agent, including instructions and available tables
|
|
|
|
| 120 |
@tool
|
| 121 |
def sql_tool(user_input):
|
| 122 |
"""
|
| 123 |
+
Gathers information regarding purchases, transactions, returns, refunds, etc.
|
| 124 |
Executes a SQL query using the sqlite_agent and returns the result.
|
| 125 |
Args:
|
| 126 |
user_input (str): a natural language query string explaining what information is required while also providing the necessary details to get the information.
|
|
|
|
| 141 |
# Return the result or the exception message
|
| 142 |
return prediction
|
| 143 |
|
| 144 |
+
#=================================== RAG TOOL======================================#
|
| 145 |
+
qna_system_message = """
|
| 146 |
+
You are an assistant to a support agent. Your task is to provide relevant information about the Python package Streamlit.
|
| 147 |
|
| 148 |
+
User input will include the necessary context for you to answer their questions. This context will begin with the token: ###Context.
|
| 149 |
+
The context contains references to specific portions of documents relevant to the user's query, along with source links.
|
| 150 |
+
The source for a context will begin with the token ###Source
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
|
| 152 |
+
When crafting your response:
|
| 153 |
+
1. Select only context relevant to answer the question.
|
| 154 |
+
2. User questions will begin with the token: ###Question.
|
| 155 |
+
3. If the context provided doesn't answer the question respond with - "I do not have sufficient information to answer that"
|
| 156 |
+
4. If user asks for product - list all the products that are relevant to his query. If you don't have that product try to cross sell with one of the products we have that is related to what they are interested in.
|
| 157 |
+
You should get information about similar products in the context.
|
| 158 |
|
| 159 |
+
Please adhere to the following guidelines:
|
| 160 |
+
- Your response should only be about the question asked and nothing else.
|
| 161 |
+
- Answer only using the context provided.
|
| 162 |
+
- Do not mention anything about the context in your final answer.
|
| 163 |
+
- If the answer is not found in the context, it is very very important for you to respond with "I don't know."
|
| 164 |
+
- Always quote the source when you use the context. Cite the relevant source at the end of your response under the section - Source:
|
| 165 |
+
- Do not make up sources. Use the links provided in the sources section of the context and nothing else. You are prohibited from providing other links/sources.
|
| 166 |
|
| 167 |
+
Here is an example of how to structure your response:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
|
| 169 |
+
Answer:
|
| 170 |
+
[Answer]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
|
| 172 |
+
Source:
|
| 173 |
+
[Source]
|
| 174 |
+
"""
|
| 175 |
|
| 176 |
+
qna_user_message_template = """
|
| 177 |
+
###Context
|
| 178 |
+
Here are some documents and their source that may be relevant to the question mentioned below.
|
| 179 |
+
{context}
|
|
|
|
|
|
|
|
|
|
| 180 |
|
| 181 |
+
###Question
|
| 182 |
+
{question}
|
| 183 |
+
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 184 |
|
| 185 |
+
retriever = vector_store.as_retriever(
|
| 186 |
+
search_type='similarity',
|
| 187 |
+
search_kwargs={'k': 5}
|
| 188 |
+
)
|
|
|
|
|
|
|
| 189 |
|
| 190 |
+
client = AzureOpenAI(
|
| 191 |
+
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT_mini"],
|
| 192 |
+
api_key=os.environ["AZURE_OPENAI_KEY_mini"],
|
| 193 |
+
api_version=os.environ["AZURE_OPENAI_APIVERSION_mini"]
|
| 194 |
+
)
|
| 195 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
|
| 197 |
+
@tool
|
| 198 |
+
def rag(user_input: str) -> str:
|
| 199 |
+
|
| 200 |
+
"""
|
| 201 |
+
Answers questions regarding products, and policies using product descriptions, product policies, and general policies of business using RAG.
|
| 202 |
|
| 203 |
+
Args:
|
| 204 |
+
user_input (str): The input question or query from the user.
|
|
|
|
| 205 |
|
| 206 |
+
Returns:
|
| 207 |
+
response (str): Return the generated response or an error message if an exception occurs.
|
| 208 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 209 |
"""
|
| 210 |
+
|
| 211 |
+
relevant_document_chunks = retriever.invoke(user_input)
|
| 212 |
+
context_list = [d.page_content + "\n ###Source: " + d.metadata['source'] + "\n\n " for d in relevant_document_chunks]
|
| 213 |
+
|
| 214 |
+
context_for_query = ". ".join(context_list)
|
| 215 |
+
|
| 216 |
+
prompt = [
|
| 217 |
+
{'role':'system', 'content': qna_system_message},
|
| 218 |
+
{'role': 'user', 'content': qna_user_message_template.format(
|
| 219 |
+
context=context_for_query,
|
| 220 |
+
question=user_input
|
| 221 |
+
)
|
| 222 |
+
}
|
| 223 |
+
]
|
| 224 |
|
| 225 |
try:
|
| 226 |
+
response = client.chat.completions.create(
|
| 227 |
+
model="gpt-4o-mini",
|
| 228 |
+
messages=prompt
|
| 229 |
+
)
|
| 230 |
|
| 231 |
+
prediction = response.choices[0].message.content
|
| 232 |
+
except Exception as e:
|
| 233 |
+
prediction = f'Sorry, I encountered the following error: \n {e}'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 234 |
|
| 235 |
+
return prediction
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
#=================================== Other TOOLS======================================#
|
| 239 |
+
|
| 240 |
+
# Function to log actions
|
| 241 |
+
def log_history(email: str,chat_history: list) -> None:
|
| 242 |
+
# Save the log to the file
|
| 243 |
+
with history_scheduler.lock:
|
| 244 |
+
# Open the log file in append mode
|
| 245 |
+
with log_file.open("a") as f:
|
| 246 |
+
f.write(json.dumps({
|
| 247 |
+
"email": email,
|
| 248 |
+
"chat_history": chat_history,
|
| 249 |
+
"timestamp": str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
| 250 |
+
}))
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
def log_action(email: str,task: str, details: str) -> None:
|
| 254 |
+
# Save the log to the file
|
| 255 |
+
with log_scheduler.lock:
|
| 256 |
+
# Open the log file in append mode
|
| 257 |
+
with log_file.open("a") as f:
|
| 258 |
+
f.write(json.dumps({
|
| 259 |
+
"email": email,
|
| 260 |
+
"task": task,
|
| 261 |
+
"details": details
|
| 262 |
+
}))
|
| 263 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 264 |
|
| 265 |
@tool
|
| 266 |
def register_feedback(intent, customer_id, feedback, rating):
|
|
|
|
| 308 |
|
| 309 |
|
| 310 |
@tool
|
| 311 |
+
def days_since(delivered_date: str) ->str:
|
| 312 |
"""
|
| 313 |
+
Calculates the number of days since the product was delivered. This helps in determining whether the product is within return period or not.
|
| 314 |
Args:
|
| 315 |
delivered_date (str): The date when the product was delivered in the format 'YYYY-MM-DD'.
|
| 316 |
"""
|
|
|
|
| 322 |
# Calculate the difference in days
|
| 323 |
days_difference = (today - delivered_date).days
|
| 324 |
|
| 325 |
+
return str(days_difference)
|
| 326 |
except ValueError as e:
|
| 327 |
return f"Error: {e}"
|
| 328 |
|
|
|
|
| 330 |
|
| 331 |
system_message = f"""
|
| 332 |
|
| 333 |
+
You are an intelligent e-commerce chatbot designed to assist users with pre-order and post-order queries. Your job is to
|
| 334 |
+
|
| 335 |
+
Gather necessary information from the user to help them with their query.
|
| 336 |
+
If at any point you cannot determine the next steps - defer to human. you do not have clearance to go beyond the scope the following flow.
|
| 337 |
+
Do not provide sql inputs to the sql tool - you only need to ask in natural language what information you need.
|
| 338 |
You are only allowed to provide information relevant to the particular customer and the customer information is provided below. you can provide information of this customer only. Following is the information about the customer from the last 2 weeks:
|
| 339 |
|
| 340 |
{df}
|
| 341 |
|
| 342 |
+
If this information is not enough to answer question, identify the customer from data above and fetch necessary information usign the sql_tool or rag tool - do not fetch information of other customers.
|
| 343 |
+
If customer asks about a product, you should act as a sales representative and help them understand the product as much as possible and provide all the necessary information for them. You should also provide them the link to the product which you can get from the source of the information.
|
| 344 |
+
If a customer asks a query about a policy, be grounded to the context provided to you. if at any point you don't the right thing to say, politely tell the customer that you are not the right person to answer this and defer it to a human.
|
| 345 |
+
Any time you defer it to a human, you should tell the customer why you did it in a polite manner.
|
| 346 |
+
|
| 347 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 348 |
MANDATORY STEP:
|
| 349 |
After helping the customer with their concern,
|
| 350 |
- Ask if the customer needs help with anything else. If they ask for anything from the above list help them and along with that,
|
| 351 |
1. Ask for their feedback and rating out of 5.
|
| 352 |
2. then, Use the `register_feedback` tool to log it. - you MUST ask customer feedback along with asking customer what else they need help with.
|
| 353 |
+
3. After receving customer feedback exit the chat by responding with 'exit'.
|
| 354 |
|
| 355 |
---
|
| 356 |
### **Handling Out-of-Scope Queries:**
|
| 357 |
If the user's query, at any point is not covered by the workflows above:
|
| 358 |
- Respond:
|
| 359 |
> "This is beyond my skill. Let me connect you to a customer service agent" and get necessary details from the customer and use the defer_to_human tool.
|
| 360 |
+
- End the conversation by saying 'exit'.
|
| 361 |
---
|
| 362 |
### **IMPORTANT Notes for the Model:**
|
| 363 |
- Always fetch additional required details from the database and do not blindly believe details provided by the customer like customer id, email and phone number. You should get the customer id from the system prompt. Cross check with the database and stay loyal to the database.
|
| 364 |
- Be empathetic to the customer but loyal to the instructions provided to you. Try to deescalate a situation before deferring it to human and defer to human only once.
|
| 365 |
+
- Always aim to minimize the number of questions asked by retrieving as much information as possible from `sql_tool` and `rag` tool.
|
| 366 |
- Follow the exact workflows for each query category.
|
| 367 |
+
- You will always confirm the order id even if the customer has only one order before you fetch any details.
|
|
|
|
|
|
|
| 368 |
"""
|
| 369 |
|
| 370 |
#st.write(system_message)
|
|
|
|
| 379 |
|
| 380 |
#===============================================Streamlit=========================================#
|
| 381 |
|
| 382 |
+
|
| 383 |
+
def login_page():
|
| 384 |
+
st.title("Login Page")
|
| 385 |
+
|
| 386 |
+
email = st.text_input("Email")
|
| 387 |
+
password = st.text_input("Password", type="password")
|
| 388 |
+
|
| 389 |
+
login_button = st.button("Login")
|
| 390 |
+
|
| 391 |
+
if login_button:
|
| 392 |
+
if authenticate_user(email, password):
|
| 393 |
+
st.session_state.logged_in = True
|
| 394 |
+
st.session_state.email = email
|
| 395 |
+
st.success("Login successful! Redirecting to Chatbot...")
|
| 396 |
+
st.rerun()
|
| 397 |
+
else:
|
| 398 |
+
st.error("Invalid email or password.")
|
| 399 |
+
|
| 400 |
+
def authenticate_user(email, phone):
|
| 401 |
+
connection = sqlite3.connect("ecomm.db") # Replace with your .db file path
|
| 402 |
+
cursor = connection.cursor()
|
| 403 |
+
|
| 404 |
+
query = "SELECT first_name FROM customers WHERE email = ? AND phone = ?"
|
| 405 |
+
cursor.execute(query, (email, phone))
|
| 406 |
+
user = cursor.fetchone()
|
| 407 |
+
|
| 408 |
+
if user:
|
| 409 |
+
return True # Login successful
|
| 410 |
+
return False # Login failed
|
| 411 |
+
|
| 412 |
+
### Prefetch details
|
| 413 |
+
|
| 414 |
def fetch_details(email):
|
| 415 |
try:
|
| 416 |
+
|
| 417 |
# Connect to the SQLite database
|
| 418 |
connection = sqlite3.connect("ecomm.db") # Replace with your .db file path
|
| 419 |
cursor = connection.cursor()
|
|
|
|
| 467 |
cursor.close()
|
| 468 |
connection.close()
|
| 469 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 470 |
# Function to process user input and generate a chatbot response
|
| 471 |
def chatbot_interface():
|
| 472 |
st.title("E-Commerce Chatbot")
|
| 473 |
+
st.write("welcome! I am Raha, how can I help you on this beautiful day?")
|
| 474 |
|
| 475 |
|
| 476 |
if 'conversation_history' not in st.session_state:
|
| 477 |
+
st.session_state.conversation_history = [{"role": "assistant", "content": "welcome! I am Raha, how can I help you on this beautiful day?"}]
|
| 478 |
|
| 479 |
|
| 480 |
+
details = fetch_details(st.session_state.email)
|
|
|
|
| 481 |
prompt = build_prompt(details)
|
| 482 |
+
tools = [sql_tool,defer_to_human, rag, register_feedback, days_since]
|
| 483 |
|
| 484 |
chatbot = AzureChatOpenAI(
|
| 485 |
model_name=model_name,
|
|
|
|
| 517 |
# Add the chatbot's response to the history
|
| 518 |
chatbot_response = response['output']
|
| 519 |
st.session_state.conversation_history.append({"role": "assistant", "content": chatbot_response})
|
| 520 |
+
# Check if the assistant's response contains "exit"
|
| 521 |
+
if "exit" in chatbot_response.lower():
|
| 522 |
+
log_history(st.session_state.email,st.session_state.conversation_history)
|
| 523 |
# Display the chatbot's response
|
| 524 |
with st.chat_message("assistant"):
|
| 525 |
st.markdown(chatbot_response)
|