masadonline commited on
Commit
ea3a27c
·
verified ·
1 Parent(s): 9342de7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +147 -57
app.py CHANGED
@@ -18,6 +18,8 @@ st.set_page_config(page_title="RAG Customer Support Chatbot", layout="wide")
18
  DEFAULT_TWILIO_ACCOUNT_SID_FALLBACK = ""
19
  DEFAULT_TWILIO_AUTH_TOKEN_FALLBACK = ""
20
  DEFAULT_GROQ_API_KEY_FALLBACK = ""
 
 
21
  DEFAULT_TWILIO_BOT_WHATSAPP_IDENTITY = st.secrets.get("TWILIO_PHONE_NUMBER", "whatsapp:+14155238886")
22
  DEFAULT_EMBEDDING_MODEL_NAME = "sentence-transformers/all-MiniLM-L6-v2"
23
  DEFAULT_POLLING_INTERVAL_S = 30
@@ -31,8 +33,11 @@ FAQ_PDF_FILE = os.path.join(DOCS_FOLDER, "FAQ.pdf")
31
  APP_TWILIO_ACCOUNT_SID = st.secrets.get("TWILIO_ACCOUNT_SID")
32
  APP_TWILIO_AUTH_TOKEN = st.secrets.get("TWILIO_AUTH_TOKEN")
33
  APP_GROQ_API_KEY = st.secrets.get("GROQ_API_KEY")
 
 
34
  APP_TWILIO_BOT_WHATSAPP_IDENTITY_SECRET = st.secrets.get("TWILIO_BOT_WHATSAPP_IDENTITY")
35
 
 
36
  # --- RAG Processing Utilities ---
37
  def load_json_data(file_path):
38
  try:
@@ -197,6 +202,7 @@ def generate_response_groq(_groq_client, query, context, model="llama3-8b-8192",
197
 
198
  item_description = item_name if item_name else "the ordered item(s)"
199
 
 
200
  core_info_parts = [
201
  f"your order {order_id}",
202
  f"for {item_description}",
@@ -214,9 +220,10 @@ def generate_response_groq(_groq_client, query, context, model="llama3-8b-8192",
214
  core_info_parts.append("(delivery date not specified)")
215
 
216
  core_information_to_include = ", ".join(core_info_parts[:-1]) + (f" {core_info_parts[-1]}" if len(core_info_parts) > 1 else "")
217
- if not order_status.lower() == "delivered" and len(core_info_parts) > 1:
218
  core_information_to_include = f"your order {order_id} for {item_description} has a status of '{order_status}'"
219
 
 
220
  user_prompt = (
221
  f"Customer: {customer_name}\n"
222
  f"Order ID: {order_id}\n"
@@ -237,8 +244,10 @@ def generate_response_groq(_groq_client, query, context, model="llama3-8b-8192",
237
  f"Ensure the sentence flows naturally and uses the details you've been given.\n"
238
  f"Respond now with ONLY that single sentence."
239
  )
 
 
240
 
241
- else:
242
  system_message = "You are a helpful customer support assistant."
243
  user_prompt = f"""Use the following context to answer the user's question.
244
  If the context doesn't contain the answer, state that you don't have enough information or ask clarifying questions.
@@ -258,16 +267,17 @@ Assistant Answer:
258
  {"role": "user", "content": user_prompt}
259
  ],
260
  model=model,
261
- temperature=0.5,
262
  max_tokens=1024,
263
  top_p=1
264
  )
265
- response = chat_completion.choices[0].message.content.strip()
266
  return response
267
  except Exception as e:
268
  st.error(f"Error calling GROQ API: {e}")
269
  return "Sorry, I encountered an error while trying to generate a response."
270
 
 
271
  def initialize_groq_client(api_key_val):
272
  if not api_key_val:
273
  st.warning("GROQ API Key is missing.")
@@ -291,53 +301,62 @@ def initialize_twilio_client(acc_sid, auth_tkn):
291
  st.error(f"Failed to initialize Twilio client: {e}")
292
  return None
293
 
294
- def get_new_whatsapp_messages(twilio_client, bot_start_time_utc, processed_message_sids, bot_whatsapp_identity_val):
 
295
  if not twilio_client:
296
  st.warning("Twilio client not initialized.")
297
  return []
 
 
 
298
  if not bot_whatsapp_identity_val:
299
  st.warning("Twilio Bot WhatsApp Identity not provided.")
300
  return []
301
 
302
  new_messages_to_process = []
303
  try:
304
- # Get all conversations (not limited to a specific service)
305
- conversations = twilio_client.conversations.v1.conversations.list(limit=50)
 
 
306
 
307
  for conv in conversations:
308
  if conv.date_updated and conv.date_updated > bot_start_time_utc:
309
  messages = twilio_client.conversations.v1 \
 
310
  .conversations(conv.sid) \
311
  .messages \
312
- .list(order='desc', limit=10)
313
 
314
  for msg in messages:
315
  if msg.sid in processed_message_sids:
316
- continue
317
 
318
- # Check if message is from WhatsApp and not from the bot
319
  if msg.author and msg.author.lower() != bot_whatsapp_identity_val.lower() and \
320
- msg.date_created and msg.date_created > bot_start_time_utc and \
321
- msg.author.startswith('whatsapp:'):
322
  new_messages_to_process.append({
323
  "conversation_sid": conv.sid, "message_sid": msg.sid,
324
  "author_identity": msg.author, "message_body": msg.body,
325
  "timestamp_utc": msg.date_created
326
  })
327
- break
328
  except Exception as e:
329
  st.error(f"Error fetching Twilio messages: {e}")
330
- return sorted(new_messages_to_process, key=lambda m: m['timestamp_utc'])
331
 
332
- def send_whatsapp_message(twilio_client, conversation_sid, message_body, bot_identity_val):
333
  if not twilio_client:
334
  st.error("Twilio client not initialized for sending message.")
335
  return False
 
 
 
336
  if not bot_identity_val:
337
  st.error("Bot identity not provided for sending message.")
338
  return False
339
  try:
340
  twilio_client.conversations.v1 \
 
341
  .conversations(conversation_sid) \
342
  .messages \
343
  .create(author=bot_identity_val, body=message_body)
@@ -347,27 +366,6 @@ def send_whatsapp_message(twilio_client, conversation_sid, message_body, bot_ide
347
  st.error(f"Error sending Twilio message to {conversation_sid}: {e}")
348
  return False
349
 
350
- # --- Simple Intent Classifier ---
351
- def simple_intent_classifier(query):
352
- query_lower = query.lower()
353
- order_keywords = ["order", "status", "track", "delivery"]
354
- order_id_match = re.search(r'\b(ord\d{3,})\b', query_lower, re.IGNORECASE)
355
-
356
- if any(k in query_lower for k in order_keywords):
357
- if order_id_match:
358
- return "ORDER_STATUS", order_id_match.group(1).upper()
359
- return "ORDER_STATUS", None
360
-
361
- product_keywords = ["product", "item", "buy", "price", "feature", "stock"]
362
- product_id_match = re.search(r'\b(prd\d{3,})\b', query_lower, re.IGNORECASE)
363
- if any(k in query_lower for k in product_keywords) or product_id_match:
364
- return "PRODUCT_INFO", None
365
-
366
- if any(k in query_lower for k in ["return", "policy", "refund", "exchange", "faq", "question", "how to", "support"]):
367
- return "GENERAL_POLICY_FAQ", None
368
-
369
- return "UNKNOWN", None
370
-
371
  # --- Main Application Logic & UI ---
372
  st.title("🤖 RAG-Based Customer Support Chatbot")
373
  st.markdown("Powered by Streamlit, Twilio, GROQ LLaMA3, and FAISS.")
@@ -396,10 +394,16 @@ else:
396
  st.sidebar.warning("Secret 'GROQ_API_KEY' not found.")
397
  groq_api_key_to_use = st.sidebar.text_input("GROQ API Key (Enter Manually)", value=DEFAULT_GROQ_API_KEY_FALLBACK, type="password")
398
 
 
 
 
 
 
 
399
  twilio_bot_whatsapp_identity_to_use = st.sidebar.text_input(
400
  "Twilio Bot WhatsApp Identity",
401
  value=APP_TWILIO_BOT_WHATSAPP_IDENTITY_SECRET or DEFAULT_TWILIO_BOT_WHATSAPP_IDENTITY,
402
- help="e.g., 'whatsapp:+1234567890'"
403
  )
404
  embedding_model_name_to_use = st.sidebar.text_input(
405
  "Embedding Model Name",
@@ -421,6 +425,27 @@ if "bot_start_time_utc" not in st.session_state: st.session_state.bot_start_time
421
  if "processed_message_sids" not in st.session_state: st.session_state.processed_message_sids = set()
422
  if "manual_chat_history" not in st.session_state: st.session_state.manual_chat_history = []
423
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
424
  # --- Main Application Controls ---
425
  col1, col2, col3, col4 = st.columns(4)
426
  with col1:
@@ -481,8 +506,8 @@ with col2:
481
  st.rerun()
482
  with col3:
483
  if st.button("💬 Start WhatsApp Bot", disabled=not st.session_state.app_started or st.session_state.bot_started, use_container_width=True):
484
- if not all([twilio_account_sid_to_use, twilio_auth_token_to_use, twilio_bot_whatsapp_identity_to_use]):
485
- st.error("Twilio Account SID, Auth Token, and Bot WhatsApp Identity are all required.")
486
  else:
487
  st.session_state.twilio_client = initialize_twilio_client(twilio_account_sid_to_use, twilio_auth_token_to_use)
488
  if st.session_state.twilio_client:
@@ -535,6 +560,7 @@ if st.session_state.get("app_started") and st.session_state.get("rag_pipeline_re
535
  extracted_customer_name, extracted_item_name, extracted_shipping_address, \
536
  extracted_delivery_date, extracted_order_id, extracted_order_status = [None] * 6
537
 
 
538
  if intent == "ORDER_STATUS":
539
  order_id_to_check = None
540
  if potential_oid_from_intent:
@@ -546,6 +572,9 @@ if st.session_state.get("app_started") and st.session_state.get("rag_pipeline_re
546
 
547
  if order_id_to_check:
548
  raw_context_data = get_order_details(order_id_to_check, st.session_state.customer_orders_data)
 
 
 
549
  context_for_llm = raw_context_data
550
 
551
  if isinstance(raw_context_data, str) and not raw_context_data.startswith("No order found") and not raw_context_data.startswith("Customer order data is not loaded"):
@@ -556,23 +585,23 @@ if st.session_state.get("app_started") and st.session_state.get("rag_pipeline_re
556
  if items and len(items) > 0 and isinstance(items[0], dict):
557
  extracted_item_name = items[0].get("name", "your item(s)")
558
  else:
559
- extracted_item_name = "your item(s)"
560
  extracted_shipping_address = order_data_dict.get("shipping_address")
561
  extracted_delivery_date = order_data_dict.get("delivered_on")
562
  extracted_order_status = order_data_dict.get("status")
563
- extracted_order_id = order_data_dict.get("order_id")
564
  except json.JSONDecodeError:
565
  st.warning(f"Could not parse order details JSON for {order_id_to_check} for personalization.")
566
  context_for_llm = f"Error parsing order details for {order_id_to_check}. Raw data: {raw_context_data}"
567
- elif isinstance(raw_context_data, str):
568
- context_for_llm = raw_context_data
569
  else:
570
  context_for_llm = "To check an order status, please provide a valid Order ID (e.g., ORD123)."
571
  raw_context_data = {"message": "Order ID needed or not found in query."}
572
 
573
  elif intent == "PRODUCT_INFO":
574
  raw_context_data = get_product_info(user_query_manual, st.session_state.products_data)
575
- context_for_llm = raw_context_data
576
 
577
  elif intent == "GENERAL_POLICY_FAQ" or intent == "UNKNOWN":
578
  if st.session_state.faiss_index_pdfs and st.session_state.embedding_model and st.session_state.indexed_pdf_chunks:
@@ -598,7 +627,7 @@ if st.session_state.get("app_started") and st.session_state.get("rag_pipeline_re
598
  item_name=extracted_item_name,
599
  shipping_address=extracted_shipping_address,
600
  delivery_date=extracted_delivery_date,
601
- order_id=extracted_order_id,
602
  order_status=extracted_order_status
603
  )
604
 
@@ -617,7 +646,7 @@ if st.session_state.get("app_started") and st.session_state.get("rag_pipeline_re
617
  except (json.JSONDecodeError, TypeError):
618
  st.text(str(raw_context_data))
619
  st.session_state.manual_chat_history.append({"role": "assistant", "content": llm_response, "context": raw_context_data})
620
- st.rerun()
621
 
622
  # --- Twilio Bot Polling Logic ---
623
  if st.session_state.get("bot_started") and st.session_state.get("rag_pipeline_ready"):
@@ -628,17 +657,18 @@ if st.session_state.get("bot_started") and st.session_state.get("rag_pipeline_re
628
  if (current_time - st.session_state.last_twilio_poll_time) > polling_interval_to_use:
629
  st.session_state.last_twilio_poll_time = current_time
630
 
631
- if not st.session_state.get("twilio_client") or not twilio_bot_whatsapp_identity_to_use or not st.session_state.get("bot_start_time_utc"):
632
- st.warning("Twilio client/config missing for polling. Ensure bot is started and WhatsApp identity is set.")
 
 
 
633
  else:
634
  with st.spinner(f"Checking WhatsApp messages (last poll: {datetime.fromtimestamp(st.session_state.last_twilio_poll_time).strftime('%H:%M:%S')})..."):
635
- new_messages = get_new_whatsapp_messages(
636
- st.session_state.twilio_client,
637
- st.session_state.bot_start_time_utc,
638
- st.session_state.processed_message_sids,
639
- twilio_bot_whatsapp_identity_to_use
640
- )
641
-
642
  if new_messages:
643
  st.info(f"Found {len(new_messages)} new WhatsApp message(s) to process.")
644
  for msg_data in new_messages:
@@ -658,6 +688,7 @@ if st.session_state.get("bot_started") and st.session_state.get("rag_pipeline_re
658
  wa_customer_name, wa_item_name, wa_shipping_address, \
659
  wa_delivery_date, wa_order_id, wa_order_status = [None] * 6
660
 
 
661
  if intent_whatsapp == "ORDER_STATUS":
662
  order_id_to_check_whatsapp = None
663
  if potential_oid_whatsapp:
@@ -669,7 +700,7 @@ if st.session_state.get("bot_started") and st.session_state.get("rag_pipeline_re
669
 
670
  if order_id_to_check_whatsapp:
671
  raw_context_data_whatsapp = get_order_details(order_id_to_check_whatsapp, st.session_state.customer_orders_data)
672
- context_for_llm_whatsapp = raw_context_data_whatsapp
673
 
674
  if isinstance(raw_context_data_whatsapp, str) and not raw_context_data_whatsapp.startswith("No order found") and not raw_context_data_whatsapp.startswith("Customer order data is not loaded"):
675
  try:
@@ -687,4 +718,63 @@ if st.session_state.get("bot_started") and st.session_state.get("rag_pipeline_re
687
  except json.JSONDecodeError:
688
  st.warning(f"Could not parse order details JSON for {order_id_to_check_whatsapp} (WhatsApp) for personalization.")
689
  context_for_llm_whatsapp = f"Error parsing order details for {order_id_to_check_whatsapp}. Raw data: {raw_context_data_whatsapp}"
690
- elif isinstance(raw_context_data_whats
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  DEFAULT_TWILIO_ACCOUNT_SID_FALLBACK = ""
19
  DEFAULT_TWILIO_AUTH_TOKEN_FALLBACK = ""
20
  DEFAULT_GROQ_API_KEY_FALLBACK = ""
21
+
22
+ #DEFAULT_TWILIO_CONVERSATION_SERVICE_SID = ""
23
  DEFAULT_TWILIO_BOT_WHATSAPP_IDENTITY = st.secrets.get("TWILIO_PHONE_NUMBER", "whatsapp:+14155238886")
24
  DEFAULT_EMBEDDING_MODEL_NAME = "sentence-transformers/all-MiniLM-L6-v2"
25
  DEFAULT_POLLING_INTERVAL_S = 30
 
33
  APP_TWILIO_ACCOUNT_SID = st.secrets.get("TWILIO_ACCOUNT_SID")
34
  APP_TWILIO_AUTH_TOKEN = st.secrets.get("TWILIO_AUTH_TOKEN")
35
  APP_GROQ_API_KEY = st.secrets.get("GROQ_API_KEY")
36
+
37
+ #APP_TWILIO_CONVERSATION_SERVICE_SID_SECRET = st.secrets.get("TWILIO_CONVERSATION_SERVICE_SID")
38
  APP_TWILIO_BOT_WHATSAPP_IDENTITY_SECRET = st.secrets.get("TWILIO_BOT_WHATSAPP_IDENTITY")
39
 
40
+
41
  # --- RAG Processing Utilities ---
42
  def load_json_data(file_path):
43
  try:
 
202
 
203
  item_description = item_name if item_name else "the ordered item(s)"
204
 
205
+ # Construct the core information string that the LLM needs to build upon
206
  core_info_parts = [
207
  f"your order {order_id}",
208
  f"for {item_description}",
 
220
  core_info_parts.append("(delivery date not specified)")
221
 
222
  core_information_to_include = ", ".join(core_info_parts[:-1]) + (f" {core_info_parts[-1]}" if len(core_info_parts) > 1 else "")
223
+ if not order_status.lower() == "delivered" and len(core_info_parts) > 1 : # for non-delivered, avoid 'and' before status
224
  core_information_to_include = f"your order {order_id} for {item_description} has a status of '{order_status}'"
225
 
226
+
227
  user_prompt = (
228
  f"Customer: {customer_name}\n"
229
  f"Order ID: {order_id}\n"
 
244
  f"Ensure the sentence flows naturally and uses the details you've been given.\n"
245
  f"Respond now with ONLY that single sentence."
246
  )
247
+ # For LLM's deeper reference, though the primary instruction is above:
248
+ # user_prompt += f"\n\nFull database context for your reference if needed: {context}"
249
 
250
+ else: # Default prompt structure for other intents or if details are missing
251
  system_message = "You are a helpful customer support assistant."
252
  user_prompt = f"""Use the following context to answer the user's question.
253
  If the context doesn't contain the answer, state that you don't have enough information or ask clarifying questions.
 
267
  {"role": "user", "content": user_prompt}
268
  ],
269
  model=model,
270
+ temperature=0.5, # Slightly lower temperature might help with stricter adherence
271
  max_tokens=1024,
272
  top_p=1
273
  )
274
+ response = chat_completion.choices[0].message.content.strip() # Added strip()
275
  return response
276
  except Exception as e:
277
  st.error(f"Error calling GROQ API: {e}")
278
  return "Sorry, I encountered an error while trying to generate a response."
279
 
280
+
281
  def initialize_groq_client(api_key_val):
282
  if not api_key_val:
283
  st.warning("GROQ API Key is missing.")
 
301
  st.error(f"Failed to initialize Twilio client: {e}")
302
  return None
303
 
304
+ def get_new_whatsapp_messages(twilio_client, conversation_service_sid_val, bot_start_time_utc,
305
+ processed_message_sids, bot_whatsapp_identity_val):
306
  if not twilio_client:
307
  st.warning("Twilio client not initialized.")
308
  return []
309
+ if not conversation_service_sid_val:
310
+ st.warning("Twilio Conversation Service SID not provided.")
311
+ return []
312
  if not bot_whatsapp_identity_val:
313
  st.warning("Twilio Bot WhatsApp Identity not provided.")
314
  return []
315
 
316
  new_messages_to_process = []
317
  try:
318
+ conversations = twilio_client.conversations.v1 \
319
+ .services(conversation_service_sid_val) \
320
+ .conversations \
321
+ .list(limit=50)
322
 
323
  for conv in conversations:
324
  if conv.date_updated and conv.date_updated > bot_start_time_utc:
325
  messages = twilio_client.conversations.v1 \
326
+ .services(conversation_service_sid_val) \
327
  .conversations(conv.sid) \
328
  .messages \
329
+ .list(order='desc', limit=10)
330
 
331
  for msg in messages:
332
  if msg.sid in processed_message_sids:
333
+ continue
334
 
 
335
  if msg.author and msg.author.lower() != bot_whatsapp_identity_val.lower() and \
336
+ msg.date_created and msg.date_created > bot_start_time_utc:
 
337
  new_messages_to_process.append({
338
  "conversation_sid": conv.sid, "message_sid": msg.sid,
339
  "author_identity": msg.author, "message_body": msg.body,
340
  "timestamp_utc": msg.date_created
341
  })
342
+ break
343
  except Exception as e:
344
  st.error(f"Error fetching Twilio messages: {e}")
345
+ return sorted(new_messages_to_process, key=lambda m: m['timestamp_utc'])
346
 
347
+ def send_whatsapp_message(twilio_client, conversation_service_sid_val, conversation_sid, message_body, bot_identity_val):
348
  if not twilio_client:
349
  st.error("Twilio client not initialized for sending message.")
350
  return False
351
+ if not conversation_service_sid_val:
352
+ st.error("Twilio Conversation Service SID not provided for sending message.")
353
+ return False
354
  if not bot_identity_val:
355
  st.error("Bot identity not provided for sending message.")
356
  return False
357
  try:
358
  twilio_client.conversations.v1 \
359
+ .services(conversation_service_sid_val) \
360
  .conversations(conversation_sid) \
361
  .messages \
362
  .create(author=bot_identity_val, body=message_body)
 
366
  st.error(f"Error sending Twilio message to {conversation_sid}: {e}")
367
  return False
368
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
369
  # --- Main Application Logic & UI ---
370
  st.title("🤖 RAG-Based Customer Support Chatbot")
371
  st.markdown("Powered by Streamlit, Twilio, GROQ LLaMA3, and FAISS.")
 
394
  st.sidebar.warning("Secret 'GROQ_API_KEY' not found.")
395
  groq_api_key_to_use = st.sidebar.text_input("GROQ API Key (Enter Manually)", value=DEFAULT_GROQ_API_KEY_FALLBACK, type="password")
396
 
397
+ # twilio_conversation_service_sid_to_use = st.sidebar.text_input(
398
+ # "Twilio Conversation Service SID (IS...)",
399
+ # value=APP_TWILIO_CONVERSATION_SERVICE_SID_SECRET or DEFAULT_TWILIO_CONVERSATION_SERVICE_SID,
400
+ # type="password",
401
+ # help="The SID of your Twilio Conversations Service. Can be set by 'TWILIO_CONVERSATION_SERVICE_SID' secret."
402
+ # )
403
  twilio_bot_whatsapp_identity_to_use = st.sidebar.text_input(
404
  "Twilio Bot WhatsApp Identity",
405
  value=APP_TWILIO_BOT_WHATSAPP_IDENTITY_SECRET or DEFAULT_TWILIO_BOT_WHATSAPP_IDENTITY,
406
+ help="e.g., 'whatsapp:+1234567890'. Can be set by 'TWILIO_BOT_WHATSAPP_IDENTITY' secret."
407
  )
408
  embedding_model_name_to_use = st.sidebar.text_input(
409
  "Embedding Model Name",
 
425
  if "processed_message_sids" not in st.session_state: st.session_state.processed_message_sids = set()
426
  if "manual_chat_history" not in st.session_state: st.session_state.manual_chat_history = []
427
 
428
+ # --- Helper: Simple Intent Classifier ---
429
+ def simple_intent_classifier(query):
430
+ query_lower = query.lower()
431
+ order_keywords = ["order", "status", "track", "delivery"]
432
+ order_id_match = re.search(r'\b(ord\d{3,})\b', query_lower, re.IGNORECASE)
433
+
434
+ if any(k in query_lower for k in order_keywords):
435
+ if order_id_match:
436
+ return "ORDER_STATUS", order_id_match.group(1).upper()
437
+ return "ORDER_STATUS", None
438
+
439
+ product_keywords = ["product", "item", "buy", "price", "feature", "stock"]
440
+ product_id_match = re.search(r'\b(prd\d{3,})\b', query_lower, re.IGNORECASE)
441
+ if any(k in query_lower for k in product_keywords) or product_id_match:
442
+ return "PRODUCT_INFO", None
443
+
444
+ if any(k in query_lower for k in ["return", "policy", "refund", "exchange", "faq", "question", "how to", "support"]):
445
+ return "GENERAL_POLICY_FAQ", None
446
+
447
+ return "UNKNOWN", None
448
+
449
  # --- Main Application Controls ---
450
  col1, col2, col3, col4 = st.columns(4)
451
  with col1:
 
506
  st.rerun()
507
  with col3:
508
  if st.button("💬 Start WhatsApp Bot", disabled=not st.session_state.app_started or st.session_state.bot_started, use_container_width=True):
509
+ if not all([twilio_account_sid_to_use, twilio_auth_token_to_use, twilio_conversation_service_sid_to_use, twilio_bot_whatsapp_identity_to_use]):
510
+ st.error("Twilio Account SID, Auth Token, Conversation Service SID, and Bot WhatsApp Identity are all required.")
511
  else:
512
  st.session_state.twilio_client = initialize_twilio_client(twilio_account_sid_to_use, twilio_auth_token_to_use)
513
  if st.session_state.twilio_client:
 
560
  extracted_customer_name, extracted_item_name, extracted_shipping_address, \
561
  extracted_delivery_date, extracted_order_id, extracted_order_status = [None] * 6
562
 
563
+
564
  if intent == "ORDER_STATUS":
565
  order_id_to_check = None
566
  if potential_oid_from_intent:
 
572
 
573
  if order_id_to_check:
574
  raw_context_data = get_order_details(order_id_to_check, st.session_state.customer_orders_data)
575
+ # context_for_llm will be used as the 'context' parameter in generate_response_groq
576
+ # For ORDER_STATUS, this raw_context_data (JSON string) is still useful for LLM's reference,
577
+ # even though specific fields are extracted for the specialized prompt.
578
  context_for_llm = raw_context_data
579
 
580
  if isinstance(raw_context_data, str) and not raw_context_data.startswith("No order found") and not raw_context_data.startswith("Customer order data is not loaded"):
 
585
  if items and len(items) > 0 and isinstance(items[0], dict):
586
  extracted_item_name = items[0].get("name", "your item(s)")
587
  else:
588
+ extracted_item_name = "your item(s)" # Fallback
589
  extracted_shipping_address = order_data_dict.get("shipping_address")
590
  extracted_delivery_date = order_data_dict.get("delivered_on")
591
  extracted_order_status = order_data_dict.get("status")
592
+ extracted_order_id = order_data_dict.get("order_id") # Should be same as order_id_to_check
593
  except json.JSONDecodeError:
594
  st.warning(f"Could not parse order details JSON for {order_id_to_check} for personalization.")
595
  context_for_llm = f"Error parsing order details for {order_id_to_check}. Raw data: {raw_context_data}"
596
+ elif isinstance(raw_context_data, str): # Handle "No order found" or "data not loaded"
597
+ context_for_llm = raw_context_data # LLM will state this
598
  else:
599
  context_for_llm = "To check an order status, please provide a valid Order ID (e.g., ORD123)."
600
  raw_context_data = {"message": "Order ID needed or not found in query."}
601
 
602
  elif intent == "PRODUCT_INFO":
603
  raw_context_data = get_product_info(user_query_manual, st.session_state.products_data)
604
+ context_for_llm = raw_context_data # Product info is directly used as context
605
 
606
  elif intent == "GENERAL_POLICY_FAQ" or intent == "UNKNOWN":
607
  if st.session_state.faiss_index_pdfs and st.session_state.embedding_model and st.session_state.indexed_pdf_chunks:
 
627
  item_name=extracted_item_name,
628
  shipping_address=extracted_shipping_address,
629
  delivery_date=extracted_delivery_date,
630
+ order_id=extracted_order_id, # This will be the specific order ID from user query
631
  order_status=extracted_order_status
632
  )
633
 
 
646
  except (json.JSONDecodeError, TypeError):
647
  st.text(str(raw_context_data))
648
  st.session_state.manual_chat_history.append({"role": "assistant", "content": llm_response, "context": raw_context_data})
649
+ st.rerun()
650
 
651
  # --- Twilio Bot Polling Logic ---
652
  if st.session_state.get("bot_started") and st.session_state.get("rag_pipeline_ready"):
 
657
  if (current_time - st.session_state.last_twilio_poll_time) > polling_interval_to_use:
658
  st.session_state.last_twilio_poll_time = current_time
659
 
660
+ if not st.session_state.get("twilio_client") or \
661
+ not twilio_conversation_service_sid_to_use or \
662
+ not twilio_bot_whatsapp_identity_to_use or \
663
+ not st.session_state.get("bot_start_time_utc"):
664
+ st.warning("Twilio client/config missing for polling. Ensure bot is started and SIDs are set.")
665
  else:
666
  with st.spinner(f"Checking WhatsApp messages (last poll: {datetime.fromtimestamp(st.session_state.last_twilio_poll_time).strftime('%H:%M:%S')})..."):
667
+ new_messages = get_new_whatsapp_messages(st.session_state.twilio_client,
668
+ twilio_conversation_service_sid_to_use,
669
+ st.session_state.bot_start_time_utc,
670
+ st.session_state.processed_message_sids,
671
+ twilio_bot_whatsapp_identity_to_use)
 
 
672
  if new_messages:
673
  st.info(f"Found {len(new_messages)} new WhatsApp message(s) to process.")
674
  for msg_data in new_messages:
 
688
  wa_customer_name, wa_item_name, wa_shipping_address, \
689
  wa_delivery_date, wa_order_id, wa_order_status = [None] * 6
690
 
691
+
692
  if intent_whatsapp == "ORDER_STATUS":
693
  order_id_to_check_whatsapp = None
694
  if potential_oid_whatsapp:
 
700
 
701
  if order_id_to_check_whatsapp:
702
  raw_context_data_whatsapp = get_order_details(order_id_to_check_whatsapp, st.session_state.customer_orders_data)
703
+ context_for_llm_whatsapp = raw_context_data_whatsapp # Full JSON string as context
704
 
705
  if isinstance(raw_context_data_whatsapp, str) and not raw_context_data_whatsapp.startswith("No order found") and not raw_context_data_whatsapp.startswith("Customer order data is not loaded"):
706
  try:
 
718
  except json.JSONDecodeError:
719
  st.warning(f"Could not parse order details JSON for {order_id_to_check_whatsapp} (WhatsApp) for personalization.")
720
  context_for_llm_whatsapp = f"Error parsing order details for {order_id_to_check_whatsapp}. Raw data: {raw_context_data_whatsapp}"
721
+ elif isinstance(raw_context_data_whatsapp, str):
722
+ context_for_llm_whatsapp = raw_context_data_whatsapp
723
+ else:
724
+ context_for_llm_whatsapp = "To check an order status, please provide a valid Order ID (e.g., ORD123)."
725
+ raw_context_data_whatsapp = {"message": "Order ID needed or not found in query."}
726
+
727
+
728
+ elif intent_whatsapp == "PRODUCT_INFO":
729
+ raw_context_data_whatsapp = get_product_info(user_query_whatsapp, st.session_state.products_data)
730
+ context_for_llm_whatsapp = raw_context_data_whatsapp
731
+
732
+ elif intent_whatsapp == "GENERAL_POLICY_FAQ" or intent_whatsapp == "UNKNOWN":
733
+ if st.session_state.faiss_index_pdfs and st.session_state.embedding_model and st.session_state.indexed_pdf_chunks:
734
+ k_val_whatsapp = 3 if intent_whatsapp == "GENERAL_POLICY_FAQ" else 2
735
+ chunks_whatsapp = search_faiss_index(st.session_state.faiss_index_pdfs, user_query_whatsapp,
736
+ st.session_state.embedding_model, st.session_state.indexed_pdf_chunks, k=k_val_whatsapp)
737
+ if chunks_whatsapp:
738
+ context_for_llm_whatsapp = "Relevant information from documents:\n\n" + "\n\n---\n\n".join(chunks_whatsapp)
739
+ raw_context_data_whatsapp = chunks_whatsapp
740
+ else:
741
+ context_for_llm_whatsapp = "I couldn't find specific information in our policy or FAQ documents regarding your query."
742
+ raw_context_data_whatsapp = {"message": "No relevant PDF chunks found."}
743
+ else:
744
+ context_for_llm_whatsapp = "Our policy and FAQ documents are currently unavailable for search."
745
+ raw_context_data_whatsapp = {"message": "PDF index or embedding model not ready."}
746
+
747
+ response_whatsapp = generate_response_groq(
748
+ _groq_client=st.session_state.groq_client,
749
+ query=user_query_whatsapp,
750
+ context=context_for_llm_whatsapp,
751
+ intent=intent_whatsapp,
752
+ customer_name=wa_customer_name,
753
+ item_name=wa_item_name,
754
+ shipping_address=wa_shipping_address,
755
+ delivery_date=wa_delivery_date,
756
+ order_id=wa_order_id,
757
+ order_status=wa_order_status
758
+ )
759
+
760
+ if send_whatsapp_message(st.session_state.twilio_client, twilio_conversation_service_sid_to_use,
761
+ conv_sid, response_whatsapp, twilio_bot_whatsapp_identity_to_use):
762
+ st.session_state.processed_message_sids.add(msg_sid)
763
+ st.success(f"Successfully responded to WhatsApp message SID {msg_sid} from {author_id}.")
764
+ else:
765
+ st.error(f"Failed to send WhatsApp response for message SID {msg_sid} from {author_id}.")
766
+ st.experimental_rerun()
767
+
768
+
769
+ # --- Footer & Status ---
770
+ st.sidebar.markdown("---")
771
+ st.sidebar.info("Ensure all keys and SIDs are correctly configured. Primary API keys (Twilio SID/Token, GROQ Key) are loaded from secrets if available.")
772
+ if st.session_state.get("app_started"):
773
+ status_color = "green" if st.session_state.get("rag_pipeline_ready") else "orange"
774
+ app_status_text = "App RUNNING" if st.session_state.get("rag_pipeline_ready") else "App Initializing/Error"
775
+ bot_status_text = "WhatsApp Bot RUNNING" if st.session_state.get("bot_started") else "WhatsApp Bot STOPPED"
776
+ st.sidebar.markdown(f"<span style='color:{status_color};'>{app_status_text}</span>. {bot_status_text}.", unsafe_allow_html=True)
777
+
778
+ else:
779
+ st.sidebar.warning("App is STOPPED.")
780
+