DreamStream-1 commited on
Commit
23c7b7f
·
verified ·
1 Parent(s): fd355af

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +369 -972
app.py CHANGED
@@ -129,7 +129,7 @@ MENU_CONFIG = {
129
  'name': 'Main Menu',
130
  'valid_options': ['1', '2', '3', '4'],
131
  'option_descriptions': {
132
- '1': 'Complete Products List',
133
  '2': 'Browse Categories',
134
  '3': 'Download Catalog',
135
  '4': 'Chat with Veterinary AI Assistant'
@@ -275,7 +275,7 @@ async def download_voice_file(media_url: str, filename: str) -> str:
275
  return None
276
 
277
  async def transcribe_voice_with_openai(file_path: str) -> str:
278
- """Transcribe voice file using OpenAI Whisper with intelligent English/Urdu focus"""
279
  try:
280
  # Check if file exists and has content
281
  if not os.path.exists(file_path):
@@ -289,11 +289,19 @@ async def transcribe_voice_with_openai(file_path: str) -> str:
289
 
290
  logger.info(f"[Transcribe] Transcribing file: {file_path} (size: {file_size} bytes)")
291
 
292
- # Intelligent English/Urdu focused system prompt
293
  system_prompt = """
294
- You are transcribing voice messages for Apex Biotical Veterinary WhatsApp Assistant.
295
 
296
- FOCUS: The user will speak in English, Urdu, or a mix of both languages. Be intelligent and natural in understanding their speech.
 
 
 
 
 
 
 
 
297
 
298
  PRODUCT NAMES (exact spelling required):
299
  - Hydropex, Respira Aid Plus, Heposel, Bromacid, Hexatox
@@ -302,25 +310,22 @@ PRODUCT NAMES (exact spelling required):
302
  - Apvita Plus, B-G Aspro-C, EC-Immune, Liverpex, Symodex
303
  - Respira Aid, Adek Gold, Immuno DX
304
 
305
- ENGLISH NUMBERS: one->1, two->2, three->3, four->4, five->5, six->6, seven->7, eight->8, nine->9, ten->10
306
-
307
- URDU NUMBERS: aik->1, ek->1, do->2, teen->3, char->4, panch->5, cheh->6, saat->7, aath->8, nau->9, das->10
308
-
309
  MENU COMMANDS:
310
- - English: main, menu, back, home, start, option, number, search, browse, download, catalog, contact, availability
311
- - Urdu: main menu, option, number, search, browse, download, catalog, contact, availability
 
312
 
313
  GREETINGS:
314
  - English: hi, hello, hey, good morning, good afternoon, good evening
315
  - Urdu: salam, assalamu alaikum, adaab, namaste, khuda hafiz
316
 
317
  TRANSCRIPTION RULES:
318
- 1. Intelligently transcribe English and Urdu speech
319
- 2. Handle mixed English-Urdu speech naturally
320
- 3. Convert numbers to digits
321
- 4. Preserve product names exactly
322
- 5. Only return "unclear audio" if the voice is genuinely unclear or inaudible
323
- 6. Be natural and conversational in understanding
324
 
325
  EXAMPLES:
326
  - "hydropex" -> "hydropex"
@@ -329,12 +334,10 @@ EXAMPLES:
329
  - "main menu" -> "main"
330
  - "salam" -> "salam"
331
  - "search products" -> "search products"
332
- - "how many products" -> "how many products"
333
- - "kitne products hain" -> "kitne products hain"
334
- - Genuinely unclear audio -> "unclear audio"
335
  """
336
 
337
- # First attempt with intelligent English/Urdu focus
338
  with open(file_path, 'rb') as audio_file:
339
  transcript = openai.Audio.transcribe(
340
  model="whisper-1",
@@ -353,8 +356,6 @@ EXAMPLES:
353
  urdu_system_prompt = """
354
  You are transcribing Urdu voice messages for Apex Biotical Veterinary WhatsApp Assistant.
355
 
356
- FOCUS: The user will speak in Urdu, English, or a mix of both. Be intelligent and natural.
357
-
358
  PRODUCT NAMES (Urdu/English):
359
  - ہائیڈروپیکس (Hydropex)
360
  - ریسپیرا ایڈ پلس (Respira Aid Plus)
@@ -367,11 +368,12 @@ PRODUCT NAMES (Urdu/English):
367
  - فائٹو سال (PHYTO-SAL)
368
  - مائیکوپیکس سپر (Mycopex Super)
369
 
370
- URDU NUMBERS (convert to digits):
371
  - ایک (1), دو (2), تین (3), چار (4), پانچ (5)
372
  - چھ (6), سات (7), آٹھ (8), نو (9), دس (10)
373
  - گیارہ (11), بارہ (12), تیرہ (13), چودہ (14), پندرہ (15)
374
  - سولہ (16), سترہ (17), اٹھارہ (18), انیس (19), بیس (20)
 
375
 
376
  URDU GREETINGS:
377
  - سلام (salam), السلام علیکم (assalamu alaikum)
@@ -383,12 +385,11 @@ URDU MENU COMMANDS:
383
  - کیٹلاگ (catalog), رابطہ (contact), دستیابی (availability)
384
 
385
  TRANSCRIPTION RULES:
386
- 1. Intelligently transcribe Urdu and English speech
387
- 2. Handle mixed language naturally
388
- 3. Convert Urdu numbers to digits
389
  4. Preserve product names exactly
390
- 5. Only return "unclear audio" if voice is genuinely unclear
391
- 6. Be natural and conversational
392
  """
393
 
394
  with open(file_path, 'rb') as audio_file:
@@ -401,16 +402,59 @@ TRANSCRIPTION RULES:
401
 
402
  transcribed_text = transcript.text.strip()
403
  logger.info(f"[Transcribe] Second attempt transcribed (Urdu): '{transcribed_text}'")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
404
 
405
- # Final check for genuinely unclear audio
406
  if not transcribed_text or len(transcribed_text.strip()) < 2:
407
  logger.warning(f"[Transcribe] Very short or empty transcription: '{transcribed_text}'")
408
  return "unclear audio"
409
 
410
- # Check for too many special characters (indicates unclear audio)
 
 
 
 
 
411
  special_char_ratio = len(re.findall(r'[^\w\s]', transcribed_text)) / len(transcribed_text)
412
- if special_char_ratio > 0.5: # More than 50% special characters
413
- logger.warning(f"[Transcribe] Too many special characters, unclear audio: '{transcribed_text}'")
414
  return "unclear audio"
415
 
416
  return transcribed_text
@@ -431,80 +475,6 @@ def process_voice_input(text: str) -> str:
431
  # Remove extra whitespace
432
  processed_text = re.sub(r'\s+', ' ', processed_text)
433
 
434
- # Remove all leading/trailing quotes and dots
435
- processed_text = re.sub(r'^["\']*[. ]*', '', processed_text)
436
- processed_text = re.sub(r'["\']*[. ]*$', '', processed_text)
437
-
438
- # Remove repeated phrases (if two sentences are very similar, keep only one)
439
- # Split by sentence-ending punctuation
440
- from rapidfuzz import fuzz
441
- sentences = re.split(r'[.!?]+', processed_text)
442
- unique_sentences = []
443
- for s in sentences:
444
- s = s.strip()
445
- if not s:
446
- continue
447
- # Only add if not very similar to any already added
448
- if not any(fuzz.ratio(s, us) > 85 for us in unique_sentences):
449
- unique_sentences.append(s)
450
- processed_text = '. '.join(unique_sentences)
451
-
452
- # Remove any remaining duplicate words/phrases
453
- words = processed_text.split()
454
- if len(words) > 5:
455
- mid = len(words) // 2
456
- first_half = ' '.join(words[:mid])
457
- second_half = ' '.join(words[mid:])
458
- if fuzz.ratio(first_half, second_half) > 85:
459
- processed_text = first_half
460
-
461
- # Fix special characters and encoding issues
462
- processed_text = clean_special_characters(processed_text)
463
-
464
- return processed_text
465
-
466
- def clean_special_characters(text: str) -> str:
467
- """Clean special characters and fix encoding issues"""
468
- if not text:
469
- return text
470
-
471
- # Fix common encoding issues
472
- replacements = {
473
- 'â€"': '–', # Fix en dash
474
- 'â€"': '—', # Fix em dash
475
- '’': "'", # Fix apostrophe
476
- '“': '"', # Fix opening quote
477
- 'â€': '"', # Fix closing quote
478
- '…': '...', # Fix ellipsis
479
- '•': '•', # Fix bullet
480
- '‰': '°', # Fix degree symbol
481
- '′': '′', # Fix prime
482
- '″': '″', # Fix double prime
483
- '‼': '™', # Fix trademark
484
- '‮': '®', # Fix registered
485
- '
': '©', # Fix copyright
486
- 'â€': '–', # Generic fix for en dash
487
- 'â€"': '—', # Generic fix for em dash
488
- '’': "'", # Generic fix for apostrophe
489
- '“': '"', # Generic fix for opening quote
490
- 'â€': '"', # Generic fix for closing quote
491
- '…': '...', # Generic fix for ellipsis
492
- '•': '•', # Generic fix for bullet
493
- '‰': '°', # Generic fix for degree symbol
494
- }
495
-
496
- # Apply replacements
497
- for old_char, new_char in replacements.items():
498
- text = text.replace(old_char, new_char)
499
-
500
- # Remove any remaining problematic characters
501
- text = re.sub(r'â€[^"]*', '', text) # Remove any remaining †patterns
502
-
503
- # Clean up multiple spaces
504
- text = re.sub(r'\s+', ' ', text)
505
-
506
- return text.strip()
507
-
508
  # Basic punctuation cleanup
509
  processed_text = processed_text.replace(' ,', ',').replace(' .', '.')
510
 
@@ -630,19 +600,6 @@ def get_veterinary_product_matches(query: str) -> List[Dict[str, Any]]:
630
  logger.info(f"[Veterinary Search] Skipping menu selection: '{normalized_query}'")
631
  return []
632
 
633
- # Check if this is a mode of action query
634
- mode_of_action_keywords = ['mode of action', 'mechanism', 'how does it work', 'what does it do', 'how it works', 'mood of action']
635
- is_mode_of_action_query = any(keyword in normalized_query for keyword in mode_of_action_keywords)
636
-
637
- # Extract product name from mode of action query
638
- if is_mode_of_action_query:
639
- # Try to extract product name from the query
640
- for _, row in products_df.iterrows():
641
- product_name = str(row.get('Product Name', '')).lower()
642
- if product_name in normalized_query:
643
- logger.info(f"[Veterinary Search] Mode of action query for product: {product_name}")
644
- return [row.to_dict()]
645
-
646
  scored_matches = []
647
 
648
  # Veterinary-specific query expansion
@@ -663,29 +620,27 @@ def get_veterinary_product_matches(query: str) -> List[Dict[str, Any]]:
663
  if category_key in normalized_query:
664
  expanded_queries.extend(categories)
665
 
666
- # Enhanced veterinary product variations with more variations
667
  veterinary_variations = {
668
- 'hydropex': ['hydropex', 'hydro pex', 'hydropex', 'electrolyte', 'dehydration', 'heat stress'],
669
- 'heposel': ['heposel', 'hepo sel', 'heposel', 'liver tonic', 'hepatoprotective'],
670
- 'bromacid': ['bromacid', 'brom acid', 'bromacid', 'respiratory', 'mucolytic'],
671
- 'respira aid': ['respira aid', 'respira aid plus', 'respiraaid', 'respiratory support'],
672
- 'hexatox': ['hexatox', 'hexa tox', 'hexatox', 'liver support', 'kidney support'],
673
- 'apma fort': ['apma fort', 'apmafort', 'mycotoxin', 'liver support'],
674
- 'para c': ['para c', 'para c.e', 'parace', 'heat stress', 'paracetamol'],
675
  'tribiotic': ['tribiotic', 'antibiotic', 'respiratory infection'],
676
- 'phyto-sal': ['phyto-sal', 'phytosal', 'phytogenic', 'vitamin supplement'],
677
- 'mycopex': ['mycopex', 'mycopex super', 'mycotoxin binder', 'mold'],
678
- 'oftilex': ['oftilex', 'oftilex ua-10', 'ofloxacin', 'antibiotic'],
679
- 'biscomin': ['biscomin', 'biscomin 10', 'oxytetracycline', 'injectable'],
680
- 'apvita': ['apvita', 'apvita plus', 'vitamin b', 'amino acid'],
681
- 'bg aspro': ['bg aspro', 'b-g aspro-c', 'aspirin', 'vitamin c'],
682
- 'ec-immune': ['ec-immune', 'ec immune', 'ecimmune', 'immune', 'immunity'],
683
  'liverpex': ['liverpex', 'liver', 'metabolic'],
684
  'symodex': ['symodex', 'multivitamin', 'vitamin'],
685
- 'adek gold': ['adek gold', 'adekgold', 'vitamin', 'multivitamin'],
686
- 'immuno dx': ['immuno dx', 'immunodx', 'immune', 'antioxidant'],
687
- 'apex': ['apex', 'aapex', 'apex biotical'],
688
- 'apex biotical': ['apex biotical', 'apex', 'aapex']
689
  }
690
 
691
  # Add veterinary variations
@@ -723,11 +678,11 @@ def get_veterinary_product_matches(query: str) -> List[Dict[str, Any]]:
723
  best_match_type = "exact"
724
  match_details = {"field": field_name, "query": expanded_query}
725
 
726
- # Fuzzy matching for close matches (improved threshold)
727
  for expanded_query in expanded_queries:
728
  if len(expanded_query) > 3: # Only fuzzy match longer queries
729
  score = fuzz.partial_ratio(normalized_query, field_str) * weight
730
- if score > best_score and score > 75: # Increased threshold for better accuracy
731
  best_score = score
732
  best_match_type = "fuzzy"
733
  match_details = {"field": field_name, "query": expanded_query}
@@ -752,59 +707,20 @@ def get_veterinary_product_matches(query: str) -> List[Dict[str, Any]]:
752
  return unique_matches
753
 
754
  def normalize(text: str) -> str:
755
- """Normalize text for better matching"""
756
- return re.sub(r'[^\w\s\u0600-\u06FF]', '', text).lower().strip()
757
-
758
- def apply_intelligent_corrections(query: str) -> str:
759
- """Apply intelligent corrections to common misspellings and variations"""
760
- corrections = {
761
- # Common misspellings
762
- 'hydropex': 'hydropex',
763
- 'respira': 'respira aid plus',
764
- 'bromacid': 'bromacid',
765
- 'heposel': 'heposel',
766
- 'tribiotic': 'tribiotic',
767
- 'para ce': 'para c.e',
768
- 'phytosal': 'phyto-sal',
769
- 'mycopex': 'mycopex super',
770
- 'eflin': 'eflin kt-20',
771
- 'salcozine': 'salcozine st-30',
772
- 'oftilex': 'oftilex ua-10',
773
- 'biscomin': 'biscomin 10',
774
- 'apvita': 'apvita plus',
775
- 'b gaspro': 'b-g aspro-c',
776
- 'ec immune': 'ec-immune',
777
- 'liverpex': 'liverpex',
778
- 'symodex': 'symodex',
779
- 'adek': 'adek gold',
780
- 'immuno dx': 'immuno dx',
781
-
782
- # Category corrections
783
- 'respiratory': 'respiratory',
784
- 'liver': 'liver health',
785
- 'antibiotic': 'antibiotics',
786
- 'vitamin': 'vitamins',
787
- 'supplement': 'supplements',
788
- 'immune': 'immune system',
789
-
790
- # Common variations
791
- 'apex': 'apex biotical',
792
- 'aapex': 'apex biotical',
793
- }
794
 
795
- normalized_query = query.lower().strip()
 
796
 
797
- # Check for exact matches first
798
- if normalized_query in corrections:
799
- return corrections[normalized_query]
800
 
801
- # Check for partial matches
802
- for wrong, correct in corrections.items():
803
- if wrong in normalized_query:
804
- return normalized_query.replace(wrong, correct)
805
 
806
- # If no corrections found, return original
807
- return query
808
 
809
  # Enhanced context management with veterinary domain awareness
810
  class VeterinaryContextManager:
@@ -895,16 +811,13 @@ class VeterinaryContextManager:
895
  context_manager = VeterinaryContextManager()
896
 
897
  # Enhanced product response with veterinary domain expertise
898
- def generate_veterinary_product_response(product_info: Dict[str, Any], user_context: Dict[str, Any], reply_language: str = 'en') -> str:
899
  """Generate comprehensive veterinary product response with intelligent information handling"""
900
 
901
  def clean_text(text):
902
  if pd.isna(text) or text is None:
903
  return "Not specified"
904
- cleaned = str(text).strip()
905
- # Apply special character cleaning
906
- cleaned = clean_special_characters(cleaned)
907
- return cleaned
908
 
909
  # Extract product details
910
  product_name = clean_text(product_info.get('Product Name', ''))
@@ -925,38 +838,18 @@ def generate_veterinary_product_response(product_info: Dict[str, Any], user_cont
925
  except Exception as e:
926
  logger.warning(f"Error checking PDF link for {product_name}: {e}")
927
 
928
- # Build the response based on language
929
- if reply_language == 'ur':
930
- response = f"""🧪 *نام:* {product_name}
931
- 📦 *قسم:* {product_type}
932
- 🏥 *زمرہ:* {category}
933
- 💊 *استعمال:* {indications}"""
934
-
935
- # Add PDF link if available, in the requested format
936
- if pdf_link:
937
- response += f"\n\n📄 پروڈکٹ بروشر دستیاب ہے\n🔗 {product_name} پی ڈی ایف:\n{pdf_link}"
938
-
939
- # Add menu options in Urdu
940
- response += f"""
941
-
942
- 💬 *دستیاب اختیارات:*
943
- 1️⃣ ویٹرنری کنسلٹنٹ سے بات کریں
944
- 2️⃣ دستیابی کے بارے میں پوچھیں
945
- 3️⃣ مین مینو پر واپس جائیں
946
-
947
- 💬 ایک اختیار منتخب کریں یا متعلقہ مصنوعات کے بارے میں پوچھیں"""
948
- else:
949
- response = f"""🧪 *Name:* {product_name}
950
  📦 *Type:* {product_type}
951
  🏥 *Category:* {category}
952
  💊 *Used For:* {indications}"""
953
-
954
- # Add PDF link if available, in the requested format
955
- if pdf_link:
956
- response += f"\n\n📄 Product Brochure Available\n🔗 {product_name} PDF:\n{pdf_link}"
957
-
958
- # Add menu options
959
- response += f"""
960
 
961
  💬 *Available Actions:*
962
  1️⃣ Talk to Veterinary Consultant
@@ -973,8 +866,6 @@ def clean_text_for_pdf(text: str) -> str:
973
  return "N/A"
974
 
975
  cleaned = str(text)
976
- # Apply special character cleaning first
977
- cleaned = clean_special_characters(cleaned)
978
  # Remove or replace problematic characters for PDF
979
  cleaned = cleaned.replace('â€"', '-').replace('â€"', '"').replace('’', "'")
980
  cleaned = cleaned.replace('“', '"').replace('â€', '"').replace('…', '...')
@@ -1803,36 +1694,7 @@ async def process_incoming_message(from_number: str, msg: dict):
1803
  # Handle voice messages FIRST - before checking message_body
1804
  if message_type in ['audio', 'voice'] or media_type == 'audio':
1805
  logger.info(f"[Process] Processing voice message from {from_number}")
1806
- # Extract transcription (simulate as if already transcribed for this logic)
1807
- transcribed_text = msg.get('transcribed_text') or ''
1808
- # Only use menu navigation logic for strict commands
1809
- menu_commands = ['main', 'menu', 'start', 'home', 'back', '1', '2', '3', '4', '5']
1810
- if transcribed_text.lower().strip() in menu_commands:
1811
- # Existing menu navigation logic (unchanged)
1812
- if reply_language == 'ur':
1813
- welcome_msg = (
1814
- "🤖 ویٹرنری ورچوئل اسسٹنٹ\n\n"
1815
- "آپ مجھ سے پوچھ سکتے ہیں:\n"
1816
- "* ویٹرنری سوالات\n"
1817
- "* پروڈکٹ کی سفارشات\n"
1818
- "* علاج کے مشورے\n"
1819
- "* عمومی معلومات\n\n"
1820
- "💬 'main' لکھ کر مین مینو پر واپس جائیں۔"
1821
- )
1822
- else:
1823
- welcome_msg = generate_veterinary_welcome_message()
1824
- send_whatsjet_message(from_number, welcome_msg)
1825
- context_manager.update_context(
1826
- from_number,
1827
- current_state='main_menu',
1828
- current_menu='main_menu',
1829
- current_menu_options=list(MENU_CONFIG['main_menu']['option_descriptions'].values()),
1830
- reply_language=reply_language
1831
- )
1832
- return
1833
- # All other voice queries go to OpenAI/gpt-4o
1834
- user_context = context_manager.get_context(from_number)
1835
- await handle_general_query_with_ai(from_number, transcribed_text, user_context, reply_language)
1836
  return
1837
 
1838
  # For text messages, check if body exists
@@ -1841,41 +1703,23 @@ async def process_incoming_message(from_number: str, msg: dict):
1841
  return
1842
 
1843
  message_body = message_body.strip()
 
1844
  logger.info(f"[Process] Processing {message_type} message from {from_number}: {message_body}")
1845
 
1846
  # Get user context
1847
  user_context = context_manager.get_context(from_number)
1848
  current_state = user_context.get('current_state', 'main_menu')
 
 
1849
  context_manager.update_context(from_number, last_message=message_body)
 
 
1850
  logger.info(f"[Process] Current state: {current_state}, Message: '{message_body}' from {from_number}")
1851
-
 
1852
  if not message_body:
1853
  return
1854
-
1855
- # LANGUAGE DETECTION FOR TEXT MESSAGES - STRICTLY ENGLISH OR URDU ONLY
1856
- reply_language = 'en' # Default to English
1857
- try:
1858
- detected_lang = detect(message_body)
1859
- logger.info(f"[Process] Raw detected language: {detected_lang}")
1860
- if detected_lang in ['en', 'ur']:
1861
- reply_language = detected_lang
1862
- logger.info(f"[Process] Valid language detected: {detected_lang}")
1863
- else:
1864
- reply_language = 'en'
1865
- logger.warning(f"[Process] Invalid language '{detected_lang}' detected - forcing to English")
1866
- urdu_arabic_pattern = re.compile(r'[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]')
1867
- islamic_greetings = ['assalamu', 'assalam', 'salam', 'salaam', 'adaab', 'namaste', 'khuda', 'allah']
1868
- has_urdu_chars = bool(urdu_arabic_pattern.search(message_body))
1869
- has_islamic_greeting = any(greeting in message_body.lower() for greeting in islamic_greetings)
1870
- if has_urdu_chars or has_islamic_greeting:
1871
- detected_lang = 'ur'
1872
- reply_language = 'ur'
1873
- logger.info(f"[Process] Overriding language detection to Urdu due to Arabic/Urdu characters or Islamic greeting")
1874
- logger.info(f"[Process] Final language set to: {reply_language}")
1875
- except Exception as e:
1876
- logger.warning(f"[Process] Language detection failed: {e}, defaulting to English")
1877
- reply_language = 'en'
1878
-
1879
  # Check for greetings with multilingual support
1880
  if is_greeting(message_body):
1881
  # Check if user is currently in AI chat mode - if so, don't trigger menu mode
@@ -1886,337 +1730,45 @@ async def process_incoming_message(from_number: str, msg: dict):
1886
  return
1887
  else:
1888
  # Only trigger menu mode if not in AI chat mode
1889
- # Generate welcome message in detected language
1890
- if reply_language == 'ur':
1891
- welcome_msg = (
1892
- "🤖 ویٹرنری ورچوئل اسسٹنٹ\n\n"
1893
- "آپ مجھ سے پوچھ سکتے ہیں:\n"
1894
- "* ویٹرنری سوالات\n"
1895
- "* پروڈکٹ کی سفارشات\n"
1896
- "* علاج کے مشورے\n"
1897
- "* عمومی معلومات\n\n"
1898
- "💬 'main' لکھ کر مین مینو پر واپس جائیں۔"
1899
- )
1900
- else:
1901
- welcome_msg = generate_veterinary_welcome_message()
1902
-
1903
- send_whatsjet_message(from_number, welcome_msg)
1904
- context_manager.update_context(
1905
- from_number,
1906
- current_state='main_menu',
1907
- current_menu='main_menu',
1908
- current_menu_options=list(MENU_CONFIG['main_menu']['option_descriptions'].values()),
1909
- reply_language=reply_language
1910
- )
1911
- return
1912
-
1913
- # 🎯 PRIORITY 1: Navigation commands - work from ANY state
1914
- # Check for "main" command - now works for both text and voice
1915
- if current_state != 'main_menu' and current_state != 'ai_chat_mode': # Only check for main if not already in main menu and not in AI chat mode
1916
- mapped_navigation = process_intelligent_voice_command(message_body, current_state, user_context)
1917
- if mapped_navigation == 'main':
1918
- logger.info(f"[Process] Navigation command detected: '{message_body}' -> 'main'")
1919
- # Generate welcome message in detected language
1920
- if reply_language == 'ur':
1921
- welcome_msg = (
1922
- "🤖 ویٹرنری ورچوئل اسسٹنٹ\n\n"
1923
- "آپ مجھ سے پوچھ سکتے ہیں:\n"
1924
- "* ویٹرنری سوالات\n"
1925
- "* پروڈکٹ کی سفارشات\n"
1926
- "* علاج کے مشورے\n"
1927
- "* عمومی معلومات\n\n"
1928
- "💬 'main' لکھ کر مین مینو پر واپس جائیں۔"
1929
- )
1930
- else:
1931
- welcome_msg = generate_veterinary_welcome_message()
1932
-
1933
  send_whatsjet_message(from_number, welcome_msg)
1934
  context_manager.update_context(
1935
  from_number,
1936
  current_state='main_menu',
1937
  current_menu='main_menu',
1938
- current_menu_options=list(MENU_CONFIG['main_menu']['option_descriptions'].values()),
1939
- reply_language=reply_language
1940
- )
1941
- return
1942
-
1943
- # Also check for text-based navigation commands
1944
- if message_body.lower() in ['main', 'menu', 'start', 'home', 'back']:
1945
- # Generate welcome message in detected language
1946
- if reply_language == 'ur':
1947
- welcome_msg = (
1948
- "🤖 ویٹرنری ورچوئل اسسٹنٹ\n\n"
1949
- "آپ مجھ سے پوچھ سکتے ہیں:\n"
1950
- "* ویٹرنری سوالات\n"
1951
- "* پروڈکٹ کی سفارشات\n"
1952
- "* علاج کے مشورے\n"
1953
- "* عمومی معلومات\n\n"
1954
- "💬 'main' لکھ کر مین مینو پر واپس جائیں۔"
1955
- )
1956
- else:
1957
- welcome_msg = generate_veterinary_welcome_message()
1958
-
1959
- send_whatsjet_message(from_number, welcome_msg)
1960
- context_manager.update_context(
1961
- from_number,
1962
- current_state='main_menu',
1963
- current_menu='main_menu',
1964
- current_menu_options=list(MENU_CONFIG['main_menu']['option_descriptions'].values()),
1965
- reply_language=reply_language
1966
- )
1967
- return
1968
-
1969
- # 🎯 PRIORITY 2: State-specific handling (contact_request, availability_request, ai_chat_mode, clarification)
1970
- if current_state == 'contact_request':
1971
- await handle_contact_request_response(from_number, message_body)
1972
- return
1973
- elif current_state == 'availability_request':
1974
- await handle_availability_request_response(from_number, message_body)
1975
- return
1976
- elif current_state == 'ai_chat_mode':
1977
- await handle_ai_chat_mode(from_number, message_body, reply_language)
1978
- return
1979
- elif user_context.get('awaiting_clarification', False):
1980
- # Handle clarification responses
1981
- await handle_clarification_response(from_number, message_body, user_context)
1982
- return
1983
-
1984
- # 🎯 PRIORITY 3: Menu selection validation and handling
1985
- if current_state == 'main_menu':
1986
- # Validate menu selection
1987
- is_valid, validation_message = validate_menu_selection(message_body, current_state, user_context)
1988
-
1989
- if not is_valid:
1990
- send_whatsjet_message(from_number, validation_message)
1991
- return
1992
-
1993
- # Handle valid menu selection
1994
- if message_body == '1':
1995
- # Complete Products List - Display all 23 products
1996
- await display_all_products(from_number)
1997
- return
1998
-
1999
- elif message_body == '2':
2000
- # Category Browse - Show all categories
2001
- categories = get_all_categories()
2002
- if reply_language == 'ur':
2003
- message = "📂 *زمرے براؤز کریں:*\n\n"
2004
- for i, category in enumerate(categories, 1):
2005
- message += f"{format_number_with_emoji(i)} {category}\n"
2006
- message += "\n💬 اپنا انتخاب لکھیں یا 'main' لکھ کر مین مینو پر واپس جائیں۔"
2007
- else:
2008
- message = "📂 *Browse Categories:*\n\n"
2009
- for i, category in enumerate(categories, 1):
2010
- message += f"{format_number_with_emoji(i)} {category}\n"
2011
- message += "\n💬 Type your choice or 'main' to return to main menu"
2012
-
2013
- send_whatsjet_message(from_number, message)
2014
- context_manager.update_context(
2015
- from_number,
2016
- current_state='category_selection',
2017
- current_menu='category_selection',
2018
- current_menu_options=categories,
2019
- available_categories=categories,
2020
- reply_language=reply_language
2021
- )
2022
- return
2023
-
2024
- elif message_body == '3':
2025
- # Catalog Download
2026
- await send_catalog_pdf(from_number)
2027
- return
2028
-
2029
- elif message_body == '4':
2030
- # AI Chat Mode
2031
- context_manager.update_context(
2032
- from_number,
2033
- current_state='ai_chat_mode',
2034
- current_menu='ai_chat_mode',
2035
- current_menu_options=['main'],
2036
- reply_language=reply_language
2037
  )
2038
- if reply_language == 'ur':
2039
- message = (
2040
- "🤖 ویٹرنری ورچوئل اسسٹنٹ\n\n"
2041
- "آپ مجھ سے پوچھ سکتے ہیں:\n"
2042
- "* ویٹرنری سوالات\n"
2043
- "* پروڈکٹ کی سفارشات\n"
2044
- "* علاج کے مشورے\n"
2045
- "* عمومی معلومات\n\n"
2046
- "💬 'main' لکھ کر مین مینو پر واپس جائیں۔"
2047
- )
2048
- else:
2049
- message = (
2050
- "🤖 Veterinary Virtual Assistant\n\n"
2051
- "You can ask me about:\n"
2052
- "* Veterinary questions\n"
2053
- "* Product recommendations\n"
2054
- "* Treatment advice\n"
2055
- "* General information\n\n"
2056
- "💬 Type 'main' to return to main menu"
2057
- )
2058
- send_whatsjet_message(from_number, message)
2059
  return
2060
 
2061
- # 🎯 PRIORITY 4: Category selection handling
2062
- elif current_state == 'category_selection':
2063
- await handle_category_selection(message_body, from_number)
2064
- return
2065
-
2066
-
2067
-
2068
- # 🎯 PRIORITY 5: Product inquiry handling (for direct product searches)
2069
- elif current_state == 'product_inquiry':
2070
- # Check for voice commands first
2071
- mapped_command = process_intelligent_voice_command(message_body, current_state, user_context)
2072
- if mapped_command != message_body:
2073
- logger.info(f"[Voice Command] Processing: '{message_body}' in state: {current_state}")
2074
- message_body = mapped_command
2075
- logger.info(f"[Voice Command] Mapped to: '{message_body}'")
2076
-
2077
- # Search for products
2078
- matches = get_veterinary_product_matches(message_body)
2079
-
2080
- if matches:
2081
- logger.info(f"[Process] Product query detected from menu state '{current_state}': '{message_body}' -> Found {len(matches)} products")
2082
-
2083
- # Apply intelligent corrections
2084
- corrected_query = apply_intelligent_corrections(message_body)
2085
- if corrected_query != message_body:
2086
- logger.info(f"[Query] Applied correction: '{message_body}' -> '{corrected_query}'")
2087
- matches = get_veterinary_product_matches(corrected_query)
2088
-
2089
- # Handle product matches
2090
- if len(matches) == 1:
2091
- # Single product match - show detailed info
2092
- product = matches[0]
2093
- context_manager.update_context(from_number, current_product=product)
2094
- await send_product_with_image(from_number, product, user_context)
2095
-
2096
- # Send follow-up options
2097
- await handle_veterinary_product_followup(message_body, from_number)
2098
-
2099
- elif len(matches) <= 5:
2100
- # Multiple products - show list
2101
- if reply_language == 'ur':
2102
- response = f"🔍 *{len(matches)} پروڈکٹ ملے:*\n\n"
2103
- else:
2104
- response = f"🔍 *Found {len(matches)} products:*\n\n"
2105
-
2106
- for i, product in enumerate(matches, 1):
2107
- product_name = product.get('Product Name', 'N/A')
2108
- category = product.get('Category', 'N/A')
2109
- if reply_language == 'ur':
2110
- response += f"{format_number_with_emoji(i)} {product_name} ({category})\n"
2111
- else:
2112
- response += f"{format_number_with_emoji(i)} {product_name} ({category})\n"
2113
-
2114
- if reply_language == 'ur':
2115
- response += "\n💬 اپنا انتخاب لکھیں یا 'main' لکھ کر مین مینو پر واپس جائیں۔"
2116
- else:
2117
- response += "\n💬 Type your choice or 'main' to return to main menu"
2118
-
2119
- send_whatsjet_message(from_number, response)
2120
- context_manager.update_context(
2121
- from_number,
2122
- current_state='product_selection',
2123
- current_menu='product_selection',
2124
- current_menu_options=[p.get('Product Name', 'N/A') for p in matches],
2125
- reply_language=reply_language
2126
- )
2127
-
2128
- else:
2129
- # Too many matches - ask for clarification
2130
- if reply_language == 'ur':
2131
- response = f"🔍 *{len(matches)} سے زیادہ پروڈکٹ ملے۔ براہ کرم مزید تفصیل دیں:*\n\n"
2132
- else:
2133
- response = f"🔍 *Found {len(matches)}+ products. Please be more specific:*\n\n"
2134
-
2135
- # Show first 10 matches as examples
2136
- for i, product in enumerate(matches[:10], 1):
2137
- product_name = product.get('Product Name', 'N/A')
2138
- category = product.get('Category', 'N/A')
2139
- if reply_language == 'ur':
2140
- response += f"{format_number_with_emoji(i)} {product_name} ({category})\n"
2141
- else:
2142
- response += f"{format_number_with_emoji(i)} {product_name} ({category})\n"
2143
-
2144
- if reply_language == 'ur':
2145
- response += "\n💬 براہ کرم مزید تفصیل دیں یا 'main' لکھ کر مین مینو پر واپس جائیں۔"
2146
- else:
2147
- response += "\n💬 Please provide more details or type 'main' to return to main menu"
2148
-
2149
- send_whatsjet_message(from_number, response)
2150
- context_manager.update_context(from_number, awaiting_clarification=True)
2151
-
2152
- else:
2153
- # No matches found - use OpenAI for intelligent response
2154
- await handle_general_query_with_ai(from_number, message_body, user_context, reply_language)
2155
- return
2156
-
2157
- # 🎯 PRIORITY 6: All other queries - use OpenAI for intelligent response
2158
- await handle_general_query_with_ai(from_number, message_body, user_context, reply_language)
2159
- return
2160
-
2161
  # 🎯 PRIORITY 1: Navigation commands - work from ANY state
2162
  # Check for "main" command - now works for both text and voice
2163
  if current_state != 'main_menu' and current_state != 'ai_chat_mode': # Only check for main if not already in main menu and not in AI chat mode
2164
  mapped_navigation = process_intelligent_voice_command(message_body, current_state, user_context)
2165
  if mapped_navigation == 'main':
2166
  logger.info(f"[Process] Navigation command detected: '{message_body}' -> 'main'")
2167
- # Generate welcome message in detected language
2168
- if reply_language == 'ur':
2169
- welcome_msg = (
2170
- "🩺 *اپیکس بائیوٹیکل ویٹرنری اسسٹنٹ*\n\n"
2171
- "آپ کا خیر مقدم ہے! میں آپ کی ویٹرنری مصنوعات کے بارے میں مدد کر سکتا ہوں۔\n\n"
2172
- "📋 *مین مینو:*\n"
2173
- "1️⃣ مصنوعات تلاش کریں\n"
2174
- "2️⃣ زمرے براؤز کریں\n"
2175
- "3️⃣ کیٹلاگ ڈاؤن لوڈ کریں\n"
2176
- "4️⃣ اے آئی چیٹ موڈ\n\n"
2177
- "💬 اپنا انتخاب لکھیں یا 'main' لکھ کر مین مینو پر واپس جائیں۔"
2178
- )
2179
- else:
2180
- welcome_msg = generate_veterinary_welcome_message()
2181
-
2182
  send_whatsjet_message(from_number, welcome_msg)
2183
  context_manager.update_context(
2184
  from_number,
2185
  current_state='main_menu',
2186
  current_menu='main_menu',
2187
- current_menu_options=list(MENU_CONFIG['main_menu']['option_descriptions'].values()),
2188
- reply_language=reply_language
2189
  )
2190
  return
2191
 
2192
  # Also check for text-based navigation commands
2193
  if message_body.lower() in ['main', 'menu', 'start', 'home', 'back']:
2194
- # Generate welcome message in detected language
2195
- if reply_language == 'ur':
2196
- welcome_msg = (
2197
- "🩺 *اپیکس بائیوٹیکل ویٹرنری اسسٹنٹ*\n\n"
2198
- "آپ کا خیر مقدم ہے! میں آپ کی ویٹرنری مصنوعات کے بارے میں مدد کر سکتا ہوں۔\n\n"
2199
- "📋 *مین مینو:*\n"
2200
- "1️⃣ مصنوعات تلاش کریں\n"
2201
- "2️⃣ زمرے براؤز کریں\n"
2202
- "3️⃣ کیٹلاگ ڈاؤن لوڈ کریں\n"
2203
- "4️⃣ اے آئی چیٹ موڈ\n\n"
2204
- "💬 اپنا انتخاب لکھیں یا 'main' لکھ کر مین مینو پر واپس جائیں۔"
2205
- )
2206
- else:
2207
- welcome_msg = generate_veterinary_welcome_message()
2208
-
2209
  send_whatsjet_message(from_number, welcome_msg)
2210
  context_manager.update_context(
2211
  from_number,
2212
  current_state='main_menu',
2213
  current_menu='main_menu',
2214
- current_menu_options=list(MENU_CONFIG['main_menu']['option_descriptions'].values()),
2215
- reply_language=reply_language
2216
  )
2217
  return
2218
 
2219
- # 🎯 PRIORITY 2: State-specific handling (contact_request, availability_request, ai_chat_mode, clarification)
2220
  if current_state == 'contact_request':
2221
  await handle_contact_request_response(from_number, message_body)
2222
  return
@@ -2226,29 +1778,8 @@ async def process_incoming_message(from_number: str, msg: dict):
2226
  elif current_state == 'ai_chat_mode':
2227
  await handle_ai_chat_mode(from_number, message_body, reply_language)
2228
  return
2229
- elif user_context.get('awaiting_clarification', False):
2230
- # Handle clarification responses
2231
- await handle_clarification_response(from_number, message_body, user_context)
2232
- return
2233
-
2234
- # 🎯 PRIORITY 3: Intelligent product queries from any menu state
2235
- # Check if the message is about a product from CSV, regardless of current menu
2236
- products = get_veterinary_product_matches(message_body)
2237
-
2238
- if products:
2239
- logger.info(f"[Process] Product query detected from menu state '{current_state}': '{message_body}' -> Found {len(products)} products")
2240
-
2241
- # If user is in a specific menu but asks about a product, handle it intelligently
2242
- if current_state in ['main_menu', 'category_selection_menu', 'category_products_menu', 'all_products_menu', 'product_inquiry', 'intelligent_products_menu']:
2243
- # Use intelligent product inquiry to handle the product query
2244
- await handle_intelligent_product_inquiry(from_number, message_body, user_context, reply_language)
2245
- return
2246
- else:
2247
- # For other menu states, still handle product queries but remind about menu context
2248
- await handle_intelligent_product_inquiry(from_number, message_body, user_context, reply_language)
2249
- return
2250
 
2251
- # 🎯 PRIORITY 4: Menu selections - check if this is a valid menu selection for current state
2252
  if current_state in ['main_menu', 'category_selection_menu', 'category_products_menu', 'all_products_menu', 'product_inquiry', 'intelligent_products_menu']:
2253
  # Validate menu selection
2254
  is_valid, error_msg = validate_menu_selection(message_body, current_state, user_context)
@@ -2290,12 +1821,12 @@ async def process_incoming_message(from_number: str, msg: dict):
2290
  reply_language='ur'
2291
  )
2292
  message = (
2293
- "🤖 ویٹرنری ورچوئل اسسٹنٹ\n\n"
2294
  "آپ مجھ سے پوچھ سکتے ہیں:\n"
2295
- "* ویٹرنری سوالات\n"
2296
- "* پروڈکٹ کی سفارشات\n"
2297
- "* علاج کے مشورے\n"
2298
- "* عمومی معلومات\n\n"
2299
  "💬 'main' لکھ کر مین مینو پر واپس جائیں۔"
2300
  )
2301
  send_whatsjet_message(from_number, message)
@@ -2318,24 +1849,26 @@ async def process_incoming_message(from_number: str, msg: dict):
2318
  send_whatsjet_message(from_number, get_menu_validation_message(current_state, user_context))
2319
  elif current_state == 'all_products_menu':
2320
  # Handle product selection from all products
2321
- available_products = user_context.get('available_products', [])
2322
- if message_body.isdigit() and 1 <= int(message_body) <= len(available_products):
2323
- selected_product = available_products[int(message_body) - 1]
2324
- context_manager.update_context(
2325
- from_number,
2326
- current_product=selected_product,
2327
- current_state='product_inquiry',
2328
- current_menu='product_inquiry',
2329
- current_menu_options=list(MENU_CONFIG['product_inquiry']['option_descriptions'].values())
2330
- )
2331
- await send_product_image_with_caption(from_number, selected_product, user_context)
2332
- return
 
 
2333
  else:
2334
- send_whatsjet_message(from_number, get_menu_validation_message(current_state, user_context))
2335
- return
2336
  elif current_state == 'product_inquiry':
2337
  await handle_veterinary_product_followup(message_body, from_number)
2338
  elif current_state == 'intelligent_products_menu':
 
2339
  available_products = user_context.get('available_products', [])
2340
  if message_body.isdigit() and 1 <= int(message_body) <= len(available_products):
2341
  selected_product = available_products[int(message_body) - 1]
@@ -2353,7 +1886,7 @@ async def process_incoming_message(from_number: str, msg: dict):
2353
  return
2354
  return # Exit after handling menu selection
2355
 
2356
- # 🎯 PRIORITY 6: Product names - works from ANY menu state
2357
  # This ensures users can say product names like "hydropex", "respira aid plus", etc. from any menu
2358
  logger.info(f"[Process] Checking for product name in message: '{message_body}' from state: {current_state}")
2359
  products = get_veterinary_product_matches(message_body)
@@ -2463,254 +1996,52 @@ async def process_incoming_message(from_number: str, msg: dict):
2463
 
2464
  async def handle_general_query_with_ai(from_number: str, query: str, user_context: dict, reply_language: str = 'en'):
2465
  """Handle general queries with OpenAI intelligence"""
2466
- logger.info(f"[AI General] Processing query: '{query}' with language: {reply_language}")
 
 
2467
 
2468
  try:
2469
- # Let OpenAI handle all queries intelligently first
2470
- if OPENAI_API_KEY and products_df is not None and not products_df.empty:
2471
- try:
2472
- # Get all products data for context
2473
- all_products = products_df.to_dict('records')
2474
-
2475
- # Create comprehensive context for AI
2476
- products_context = ""
2477
- if all_products:
2478
- products_context = "Available Veterinary Products:\n"
2479
- for i, product in enumerate(all_products[:50], 1): # Limit to first 50 products for context
2480
- product_name = product.get('Product Name', 'N/A')
2481
- category = product.get('Category', 'N/A')
2482
- target_species = product.get('Target Species', 'N/A')
2483
- products_context += f"{i}. {product_name} - {category} ({target_species})\n"
2484
-
2485
- # Expert-level AI prompt for comprehensive veterinary assistance
2486
- prompt = f"""
2487
- You are an EXPERT veterinary pharmaceutical assistant for Apex Biotical, with deep knowledge of veterinary medicine, animal health, and pharmaceutical products. You help users on WhatsApp with professional, accurate, and context-aware responses.
2488
-
2489
- USER QUERY: "{query}"
2490
-
2491
- AVAILABLE PRODUCTS DATABASE:
2492
- {products_context}
2493
-
2494
- EXPERT ANALYSIS FRAMEWORK:
2495
-
2496
- 1. **QUERY TYPE DETECTION:**
2497
- - COUNT/QUANTITY queries: "how many", "count", "total", "number of"
2498
- - PRODUCT INQUIRIES: specific product names, categories, or therapeutic areas
2499
- - COMPANY INQUIRIES: "apex", "company", "about", "who are you"
2500
- - VETERINARY ADVICE: medical questions, treatment recommendations, dosage
2501
- - GENERAL QUESTIONS: greetings, navigation, help requests
2502
-
2503
- 2. **CONTEXT-AWARE RESPONSE STRATEGY:**
2504
- - For COUNT queries: Provide exact numbers with category breakdowns
2505
- - For PRODUCT queries: List relevant products with therapeutic benefits, target species, and key features
2506
- - For COMPANY queries: Professional company overview with expertise areas
2507
- - For VETERINARY ADVICE: Evidence-based recommendations with product suggestions
2508
- - For GENERAL queries: Helpful guidance with menu navigation
2509
-
2510
- 3. **EXPERT KNOWLEDGE INTEGRATION:**
2511
- - Veterinary pharmacology and therapeutics
2512
- - Animal health and disease management
2513
- - Pharmaceutical product applications
2514
- - Professional veterinary communication
2515
- - Regulatory and safety considerations
2516
-
2517
- RESPONSE GUIDELINES:
2518
- - Be PROFESSIONAL, EXPERT, and HELPFUL
2519
- - Use appropriate veterinary terminology
2520
- - Include relevant emojis for clarity
2521
- - Provide accurate, evidence-based information
2522
- - Always include navigation instructions
2523
- - Show understanding of user's intent and context
2524
- - Be concise but comprehensive
2525
- - Maintain professional veterinary standards
2526
-
2527
- IMPORTANT: Demonstrate expert-level veterinary knowledge while being accessible and helpful. Always provide accurate information from the database and professional veterinary guidance.
2528
- """
2529
-
2530
- response = openai.ChatCompletion.create(
2531
- model="gpt-4o",
2532
- messages=[{"role": "user", "content": prompt}],
2533
- temperature=0.3, # Lower temperature for more consistent, expert responses
2534
- max_tokens=600 # Increased for more comprehensive expert responses
2535
- )
2536
-
2537
- ai_response = response.choices[0].message.content.strip()
2538
-
2539
- # Ensure the response includes navigation instructions
2540
- if 'main' not in ai_response.lower():
2541
- if reply_language == 'ur':
2542
- ai_response += "\n\n💬 *براہ کرم 'main' لکھ کر مین مینو پر جائیں*"
2543
- else:
2544
- ai_response += "\n\n💬 *Please type 'main' to go to main menu*"
2545
-
2546
- # Translate response if needed (ENGLISH/URDU ONLY)
2547
- if reply_language == 'ur':
2548
- try:
2549
- # Only translate from English to Urdu - no other languages
2550
- translated_response = GoogleTranslator(source='en', target='ur').translate(ai_response)
2551
- send_whatsjet_message(from_number, translated_response)
2552
- except Exception as e:
2553
- logger.error(f"[AI] Translation error: {e}")
2554
- send_whatsjet_message(from_number, ai_response)
2555
- else:
2556
- send_whatsjet_message(from_number, ai_response)
2557
-
2558
- # Add to conversation history
2559
- context_manager.add_to_history(from_number, query, ai_response)
2560
- return
2561
-
2562
- except Exception as e:
2563
- logger.error(f"[AI] Error handling query with OpenAI: {e}")
2564
- # Fall back to existing logic if OpenAI fails
2565
- pass
2566
-
2567
- # Check if this is a non-product query that should get a simple response
2568
- non_product_keywords = ['what is', 'who are', 'where is', 'when', 'why', 'how to', 'can you', 'do you', 'is this', 'are you']
2569
- is_simple_query = any(keyword in query.lower() for keyword in non_product_keywords)
2570
-
2571
- # Check for company-related queries
2572
- company_queries = ['apex', 'aapex', 'apex biotical', 'company', 'about', 'who are you', 'what are you']
2573
- is_company_query = any(keyword in query.lower() for keyword in company_queries)
2574
-
2575
- # Check for irrelevant or unclear queries
2576
- irrelevant_keywords = ['weather', 'football', 'cricket', 'movie', 'music', 'food', 'restaurant', 'hotel', 'travel', 'shopping', 'fashion', 'beauty', 'sports', 'game', 'entertainment']
2577
- is_irrelevant_query = any(keyword in query.lower() for keyword in irrelevant_keywords)
2578
-
2579
- if is_irrelevant_query:
2580
- response = get_irrelevant_query_response(query, user_context.get('current_state', 'main_menu'), reply_language)
2581
- send_whatsjet_message(from_number, response)
2582
- return
2583
-
2584
- if is_company_query:
2585
- if reply_language == 'ur':
2586
- response = (
2587
- "🏥 *Apex Biotical Solutions*\n\n"
2588
- "ہم ایک پیشہ ور ویٹرنری فارماسیوٹیکل کمپنی ہیں جو مندرجہ ذیل میں مہارت رکھتے ہیں:\n\n"
2589
- "📦 *ہماری مصنوعات:*\n"
2590
- "• سانس کی مدد (Respira Aid Plus, Bromacid)\n"
2591
- "• جگر کی صحت (Heposel, Liverpex)\n"
2592
- "• مدافعتی نظام (EC-Immune)\n"
2593
- "• اینٹی بائیوٹکس (Tribiotic, Para C.E)\n"
2594
- "• وٹامنز اور سپلیمنٹس (Symodex, Adek Gold)\n\n"
2595
- "💬 *مصنوعات دیکھنے کے لیے:*\n"
2596
- "• 'main' لکھ کر مین مینو پر جائیں"
2597
- )
2598
- else:
2599
- response = (
2600
- "🏥 *Apex Biotical Solutions*\n\n"
2601
- "We are a leading veterinary pharmaceutical company specializing in:\n\n"
2602
- "📦 *Our Products:*\n"
2603
- "• Respiratory support (Respira Aid Plus, Bromacid)\n"
2604
- "• Liver health (Heposel, Liverpex)\n"
2605
- "• Immune system (EC-Immune)\n"
2606
- "• Antibiotics (Tribiotic, Para C.E)\n"
2607
- "• Vitamins & supplements (Symodex, Adek Gold)\n\n"
2608
- "🌍 *Our Focus:*\n"
2609
- "• Professional veterinary solutions\n"
2610
- "• Quality pharmaceutical products\n"
2611
- "• Comprehensive animal healthcare\n\n"
2612
- "💬 *To explore our products:*\n"
2613
- "• Type 'main' to see the main menu\n"
2614
- "• Type a product name (e.g., 'hydropex', 'respira aid plus')"
2615
- )
2616
- send_whatsjet_message(from_number, response)
2617
- return
2618
-
2619
  if not OPENAI_API_KEY:
2620
- if reply_language == 'ur':
2621
- send_whatsjet_message(from_number,
2622
- "❌ *AI assistance دستیاب نہیں ہے*\n\n💬 *براہ کرم 'main' لکھ کر مین مینو پر جائیں*")
2623
- else:
2624
- send_whatsjet_message(from_number,
2625
- "❌ AI assistance is not available. Please type 'main' for the menu.")
2626
  return
2627
 
2628
  # Create context-aware prompt
2629
  current_state = user_context.get('current_state', 'main_menu')
2630
  current_product = user_context.get('current_product')
2631
 
2632
- # Get all products data for context
2633
- all_products = []
2634
- if products_df is not None and not products_df.empty:
2635
- all_products = products_df.to_dict('records')
2636
-
2637
- # Create comprehensive context for AI
2638
- products_context = ""
2639
- if all_products:
2640
- products_context = "Available Veterinary Products:\n"
2641
- for i, product in enumerate(all_products[:30], 1): # Limit to first 30 products for context
2642
- product_name = product.get('Product Name', 'N/A')
2643
- category = product.get('Category', 'N/A')
2644
- products_context += f"{i}. {product_name} - {category}\n"
2645
-
2646
- # Expert-level prompt for comprehensive veterinary assistance
2647
  prompt = f"""
2648
- You are an EXPERT veterinary pharmaceutical assistant for Apex Biotical, with deep knowledge of veterinary medicine, animal health, and pharmaceutical products. You help users on WhatsApp with professional, accurate, and context-aware responses.
2649
-
2650
- USER QUERY: "{query}"
2651
- CURRENT STATE: {current_state}
2652
- CURRENT PRODUCT: {current_product.get('Product Name', 'None') if current_product else 'None'}
2653
 
2654
- AVAILABLE PRODUCTS:
2655
- {products_context}
 
2656
 
2657
- EXPERT ANALYSIS FRAMEWORK:
2658
-
2659
- 1. **QUERY TYPE DETECTION:**
2660
- - COUNT/QUANTITY queries: "how many", "count", "total", "number of"
2661
- - PRODUCT INQUIRIES: specific product names, categories, or therapeutic areas
2662
- - COMPANY INQUIRIES: "apex", "company", "about", "who are you"
2663
- - VETERINARY ADVICE: medical questions, treatment recommendations, dosage
2664
- - GENERAL QUESTIONS: greetings, navigation, help requests
2665
-
2666
- 2. **CONTEXT-AWARE RESPONSE STRATEGY:**
2667
- - For COUNT queries: Provide exact numbers with category breakdowns
2668
- - For PRODUCT queries: List relevant products with therapeutic benefits, target species, and key features
2669
- - For COMPANY queries: Professional company overview with expertise areas
2670
- - For VETERINARY ADVICE: Evidence-based recommendations with product suggestions
2671
- - For GENERAL queries: Helpful guidance with menu navigation
2672
-
2673
- 3. **EXPERT KNOWLEDGE INTEGRATION:**
2674
- - Veterinary pharmacology and therapeutics
2675
- - Animal health and disease management
2676
- - Pharmaceutical product applications
2677
- - Professional veterinary communication
2678
- - Regulatory and safety considerations
2679
-
2680
- RESPONSE GUIDELINES:
2681
- - Be PROFESSIONAL, EXPERT, and HELPFUL
2682
- - Use appropriate veterinary terminology
2683
- - Include relevant emojis for clarity
2684
- - Provide accurate, evidence-based information
2685
- - Always include navigation instructions
2686
- - Show understanding of user's intent and context
2687
- - Be concise but comprehensive
2688
- - Maintain professional veterinary standards
2689
-
2690
- IMPORTANT: Demonstrate expert-level veterinary knowledge while being accessible and helpful. Always provide accurate information from the database and professional veterinary guidance.
2691
  """
2692
 
2693
  response = openai.ChatCompletion.create(
2694
  model="gpt-4o",
2695
  messages=[{"role": "user", "content": prompt}],
2696
- temperature=0.3, # Lower temperature for more consistent, expert responses
2697
- max_tokens=400 # Increased for more comprehensive expert responses
2698
  )
2699
 
2700
  ai_response = response.choices[0].message.content.strip()
2701
 
2702
- # Ensure the response includes navigation instructions
2703
- if 'main' not in ai_response.lower():
2704
- if reply_language == 'ur':
2705
- ai_response += "\n\n💬 *براہ کرم 'main' لکھ کر مین مینو پر جائیں*"
2706
- else:
2707
- ai_response += "\n\n💬 *Please type 'main' to go to main menu*"
2708
-
2709
- # Translate response if needed (ENGLISH/URDU ONLY)
2710
  if reply_language == 'ur':
2711
  try:
2712
- # Only translate from English to Urdu - no other languages
2713
- translated_response = GoogleTranslator(source='en', target='ur').translate(ai_response)
2714
  send_whatsjet_message(from_number, translated_response)
2715
  except Exception as e:
2716
  logger.error(f"[AI] Translation error: {e}")
@@ -2723,13 +2054,9 @@ IMPORTANT: Demonstrate expert-level veterinary knowledge while being accessible
2723
 
2724
  except Exception as e:
2725
  logger.error(f"[AI] Error handling general query: {e}")
2726
- # Professional error response
2727
- if reply_language == 'ur':
2728
- error_msg = "❌ *خطا آ گئی ہے*\n\n💬 *براہ کرم 'main' لکھ کر مین مینو پر جائیں*"
2729
- else:
2730
- error_msg = "❌ *An error occurred*\n\n💬 *Please type 'main' to go to main menu*"
2731
-
2732
- send_whatsjet_message(from_number, error_msg)
2733
  context_manager.update_context(
2734
  from_number,
2735
  current_state='main_menu',
@@ -2737,58 +2064,6 @@ IMPORTANT: Demonstrate expert-level veterinary knowledge while being accessible
2737
  current_menu_options=list(MENU_CONFIG['main_menu']['option_descriptions'].values())
2738
  )
2739
 
2740
- async def handle_clarification_response(from_number: str, response: str, user_context: dict):
2741
- """Handle user response to clarification questions"""
2742
- try:
2743
- clean_response = response.strip().lower()
2744
-
2745
- if clean_response in ['yes', 'y', 'apex', 'apex biotical', 'apex biotical solution']:
2746
- # User confirmed they want Apex Biotical - provide company information
2747
- company_info = (
2748
- "🏥 *Apex Biotical Solutions*\n\n"
2749
- "We are a leading veterinary pharmaceutical company specializing in:\n\n"
2750
- "📦 *Our Products:*\n"
2751
- "• Respiratory support (Respira Aid Plus, Bromacid)\n"
2752
- "• Liver health (Heposel, Liverpex)\n"
2753
- "• Immune system (EC-Immune)\n"
2754
- "• Antibiotics (Tribiotic, Para C.E)\n"
2755
- "• Vitamins & supplements (Symodex, Adek Gold)\n\n"
2756
- "🌍 *Our Focus:*\n"
2757
- "• Professional veterinary solutions\n"
2758
- "• Quality pharmaceutical products\n"
2759
- "• Comprehensive animal healthcare\n\n"
2760
- "💬 *To explore our products:*\n"
2761
- "• Type a product name (e.g., 'hydropex', 'respira aid plus')\n"
2762
- "• Type 'main' to see the main menu\n"
2763
- "• Ask about specific symptoms or conditions"
2764
- )
2765
-
2766
- send_whatsjet_message(from_number, company_info)
2767
-
2768
- # Update context
2769
- user_context['awaiting_clarification'] = False
2770
- user_context.pop('clarification_product', None)
2771
- context_manager.update_context(from_number, **user_context)
2772
-
2773
- else:
2774
- # User didn't confirm - use OpenAI for intelligent response
2775
- if OPENAI_API_KEY:
2776
- await handle_ai_chat_mode(from_number, response, 'en')
2777
- else:
2778
- send_whatsjet_message(from_number, "💬 *Type 'main' to go to main menu or ask another question.*")
2779
-
2780
- # Clear clarification context
2781
- user_context['awaiting_clarification'] = False
2782
- user_context.pop('clarification_product', None)
2783
- context_manager.update_context(from_number, **user_context)
2784
-
2785
- except Exception as e:
2786
- logger.error(f"[Clarification] Error: {str(e)}")
2787
- send_whatsjet_message(from_number, "❌ *An error occurred. Please type 'main' to go to main menu.*")
2788
- user_context['awaiting_clarification'] = False
2789
- user_context.pop('clarification_product', None)
2790
- context_manager.update_context(from_number, **user_context)
2791
-
2792
  async def handle_contact_request(from_number: str):
2793
  """Handle contact request"""
2794
  try:
@@ -3298,14 +2573,13 @@ Response:
3298
  else:
3299
  ai_response += "\n\n💬 *Type 'main' to return to main menu*"
3300
 
3301
- # Translate response if needed (ENGLISH/URDU ONLY)
3302
  if reply_language == 'ur':
3303
  try:
3304
  # Get all product and category names
3305
  product_names = [str(p.get('Product Name', '')) for p in all_products if p.get('Product Name')]
3306
  category_names = list(set([str(p.get('Category', '')) for p in all_products if p.get('Category')]))
3307
- # Only translate from English to Urdu - no other languages
3308
- translated_response = GoogleTranslator(source='en', target='ur').translate(ai_response)
3309
  # Restore English terms
3310
  translated_response = restore_english_terms(translated_response, ai_response, product_names, category_names)
3311
  send_whatsjet_message(from_number, translated_response)
@@ -3522,10 +2796,7 @@ def generate_veterinary_product_response_with_media(product_info: Dict[str, Any]
3522
  def clean_text(text):
3523
  if pd.isna(text) or text is None:
3524
  return "Not specified"
3525
- cleaned = str(text).strip()
3526
- # Apply special character cleaning
3527
- cleaned = clean_special_characters(cleaned)
3528
- return cleaned
3529
 
3530
  product_name = clean_text(product_info.get('Product Name', ''))
3531
  product_type = clean_text(product_info.get('Type', ''))
@@ -3572,7 +2843,7 @@ def ensure_images_dir():
3572
  logger.info(f"[Image] Ensured images directory exists: {images_dir}")
3573
 
3574
  # New feature: Send product image with caption (product details)
3575
- async def send_product_image_with_caption(from_number: str, product: Dict[str, Any], user_context: Dict[str, Any], reply_language: str = 'en'):
3576
  """
3577
  Send product image (if available) with product details as caption in a single WhatsApp message.
3578
  Only uses cPanel images from https://amgocus.com/uploads/images/
@@ -3580,7 +2851,7 @@ async def send_product_image_with_caption(from_number: str, product: Dict[str, A
3580
  """
3581
  ensure_images_dir()
3582
  product_name = product.get('Product Name', 'Unknown Product')
3583
- details = generate_veterinary_product_response(product, user_context, reply_language)
3584
 
3585
  logger.info(f"[Product] Processing cPanel image for product: {product_name}")
3586
 
@@ -3682,7 +2953,7 @@ async def test_product_image_with_caption(phone: str):
3682
  product = products_df.iloc[0].to_dict()
3683
  user_context = {}
3684
 
3685
- await send_product_image_with_caption(phone, product, user_context, 'en')
3686
 
3687
  return {
3688
  "success": True,
@@ -4047,9 +3318,11 @@ async def handle_veterinary_product_followup(selection: str, from_number: str) -
4047
  current_menu='contact_request',
4048
  current_menu_options=['Provide contact details']
4049
  )
 
4050
  elif selection == '2':
4051
  # Inquire about Product Availability
4052
  await handle_availability_inquiry(from_number, user_context)
 
4053
  elif selection == '3':
4054
  welcome_msg = generate_veterinary_welcome_message(from_number, user_context)
4055
  send_whatsjet_message(from_number, welcome_msg)
@@ -4174,17 +3447,14 @@ async def handle_voice_message_complete(from_number: str, msg: dict):
4174
  detected_lang = detect(transcribed_text)
4175
  logger.info(f"[Voice] Raw detected language: {detected_lang}")
4176
 
4177
- # STRICTLY ENGLISH OR URDU ONLY - REJECT ALL OTHER LANGUAGES
 
4178
  if detected_lang in ['en', 'ur']:
4179
  reply_language = detected_lang
4180
- logger.info(f"[Voice] Valid language detected: {detected_lang}")
4181
  else:
4182
- # Reject any other language and force to English
4183
  reply_language = 'en'
4184
- logger.warning(f"[Voice] Invalid language '{detected_lang}' detected - forcing to English")
4185
- # If it's clearly not English/Urdu, mark as unclear
4186
- if detected_lang not in ['en', 'ur', 'unknown']:
4187
- logger.warning(f"[Voice] Non-English/Urdu language detected: {detected_lang}")
4188
 
4189
  # Check if text contains Urdu/Arabic characters or Islamic greetings
4190
  urdu_arabic_pattern = re.compile(r'[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]')
@@ -4204,12 +3474,11 @@ async def handle_voice_message_complete(from_number: str, msg: dict):
4204
  logger.warning(f"[Voice] Language detection failed: {e}, defaulting to English")
4205
  reply_language = 'en'
4206
 
4207
- # For Urdu voice notes, translate to English for processing (ENGLISH/URDU ONLY)
4208
  processing_text = transcribed_text
4209
  if reply_language == 'ur' and detected_lang == 'ur':
4210
  try:
4211
  logger.info(f"[Voice] Translating Urdu voice note to English for processing")
4212
- # Only translate from Urdu to English - no other languages
4213
  translated_text = GoogleTranslator(source='ur', target='en').translate(transcribed_text)
4214
  processing_text = translated_text
4215
  logger.info(f"[Voice] Translated to English: {translated_text}")
@@ -4217,17 +3486,6 @@ async def handle_voice_message_complete(from_number: str, msg: dict):
4217
  logger.error(f"[Voice] Translation failed: {e}")
4218
  # If translation fails, use original text
4219
  processing_text = transcribed_text
4220
- elif detected_lang not in ['en', 'ur']:
4221
- # If language is not English or Urdu, reject it
4222
- logger.warning(f"[Voice] Non-English/Urdu language detected: {detected_lang}")
4223
- send_whatsjet_message(from_number,
4224
- "🎤 *Voice Message Issue*\n\n"
4225
- "I can only process voice messages in English or Urdu.\n\n"
4226
- "💡 *Please:*\n"
4227
- "• Speak in English or Urdu only\n"
4228
- "• Send a text message instead\n"
4229
- "• Type 'main' to see menu options")
4230
- return
4231
 
4232
  # Determine reply language - always respond in English or Urdu
4233
  if detected_lang == 'ur':
@@ -4239,22 +3497,33 @@ async def handle_voice_message_complete(from_number: str, msg: dict):
4239
  logger.info(f"[Voice] Reply language set to: {reply_language}")
4240
 
4241
  # Check if this is a greeting in voice note (check both original and translated)
4242
- user_context = context_manager.get_context(from_number)
4243
- current_state = user_context.get('current_state', 'main_menu')
4244
  if is_greeting(transcribed_text) or is_greeting(processing_text):
4245
  logger.info(f"[Voice] Greeting detected in voice note: {transcribed_text}")
 
 
 
 
 
4246
  if current_state == 'ai_chat_mode':
4247
  logger.info(f"[Voice] User is in AI chat mode, treating greeting as AI query instead of menu trigger")
 
4248
  await handle_general_query_with_ai(from_number, processing_text, user_context, reply_language)
4249
  return
4250
  else:
 
4251
  welcome_msg = generate_veterinary_welcome_message(from_number, user_context)
4252
  send_whatsjet_message(from_number, welcome_msg)
4253
  context_manager.update_context(from_number, current_state='main_menu', current_menu='main_menu', current_menu_options=list(MENU_CONFIG['main_menu']['option_descriptions'].values()))
4254
  return
4255
- # For all other cases, always pass to OpenAI for intelligent response
4256
- await handle_general_query_with_ai(from_number, processing_text, user_context, reply_language)
4257
- return
 
 
 
 
 
 
4258
 
4259
  except Exception as e:
4260
  logger.error(f"[Voice] Error processing voice message: {e}")
@@ -4430,7 +3699,7 @@ async def test_send_product_image(phone: str, product_name: str = "Bromacid"):
4430
  user_context = context_manager.get_context(phone)
4431
 
4432
  logger.info(f"[Test] Testing send_product_image_with_caption for product: {product_name}")
4433
- await send_product_image_with_caption(phone, product, user_context, 'en')
4434
 
4435
  return {
4436
  "status": "sent",
@@ -4443,53 +3712,181 @@ async def test_send_product_image(phone: str, product_name: str = "Bromacid"):
4443
  return {"error": str(e)}
4444
 
4445
  async def handle_intelligent_product_inquiry(from_number: str, query: str, user_context: dict, reply_language: str = 'en'):
4446
- """Handle product inquiry with OpenAI intelligence and media support, matching WhatsApp screenshot logic"""
4447
  try:
 
4448
  products = get_veterinary_product_matches(query)
 
4449
  if products:
 
4450
  if len(products) > 1:
4451
- # Numbered list with short descriptions
4452
- message = f"Certainly, here are the relevant products for your query:\n\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4453
  for i, product in enumerate(products, 1):
4454
- product_name = product.get('Product Name', 'Unknown')
4455
- category = product.get('Category', '')
4456
- short_desc = product.get('Type', '') or product.get('Indications', '')
4457
- message += f"{i}. {product_name}"
4458
- if category:
4459
- message += f" - {category}"
4460
- if short_desc:
4461
- message += f" / {short_desc}"
4462
- message += "\n"
4463
- message += (f"\nTo view detailed information about any product, reply with its number (1-{len(products)})\n"
4464
- "Type 'main' to return to the main menu")
4465
- send_whatsjet_message(from_number, message)
4466
- # Store product list in context for selection
 
 
 
 
 
 
 
 
 
 
4467
  context_manager.update_context(
4468
- from_number,
4469
  current_state='intelligent_products_menu',
4470
  current_menu='intelligent_products_menu',
4471
- current_menu_options=[str(i) for i in range(1, len(products)+1)],
4472
- available_products=products
 
4473
  )
4474
- return
 
 
 
4475
  else:
4476
- # Single product found - show details
4477
  selected_product = products[0]
4478
  context_manager.update_context(
4479
- from_number,
4480
- current_product=selected_product,
4481
  current_state='product_inquiry',
4482
  current_menu='product_inquiry',
4483
  current_menu_options=list(MENU_CONFIG['product_inquiry']['option_descriptions'].values())
4484
  )
 
 
4485
  await send_product_image_with_caption(from_number, selected_product, user_context)
4486
- return
 
 
 
4487
  else:
4488
- # No products found
4489
- send_whatsjet_message(from_number, "❌ No products found matching your query.\nType 'main' to return to the main menu.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4490
  except Exception as e:
4491
- logger.error(f"Error in handle_intelligent_product_inquiry: {e}")
4492
- send_whatsjet_message(from_number, "❌ Error processing your request. Type 'main' to return to the main menu.")
 
 
 
4493
 
4494
  async def handle_contact_request(from_number: str):
4495
  """Handle contact request"""
@@ -4543,4 +3940,4 @@ if __name__ == "__main__":
4543
 
4544
  # Launch FastAPI app
4545
  import uvicorn
4546
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
129
  'name': 'Main Menu',
130
  'valid_options': ['1', '2', '3', '4'],
131
  'option_descriptions': {
132
+ '1': 'Search Products',
133
  '2': 'Browse Categories',
134
  '3': 'Download Catalog',
135
  '4': 'Chat with Veterinary AI Assistant'
 
275
  return None
276
 
277
  async def transcribe_voice_with_openai(file_path: str) -> str:
278
+ """Transcribe voice file using OpenAI Whisper with comprehensive veterinary domain system prompt"""
279
  try:
280
  # Check if file exists and has content
281
  if not os.path.exists(file_path):
 
289
 
290
  logger.info(f"[Transcribe] Transcribing file: {file_path} (size: {file_size} bytes)")
291
 
292
+ # Comprehensive system prompt for veterinary WhatsApp assistant
293
  system_prompt = """
294
+ You are transcribing voice messages for Apex Biotical Veterinary WhatsApp Assistant. This is a professional veterinary products chatbot.
295
 
296
+ CRITICAL: TRANSCRIBE ONLY ENGLISH OR URDU SPEECH - NOTHING ELSE
297
+
298
+ IMPORTANT RULES:
299
+ 1. ONLY transcribe English or Urdu speech
300
+ 2. If you hear unclear audio, transcribe as English
301
+ 3. If you hear mixed languages, transcribe as English
302
+ 4. Never transcribe gibberish or random characters
303
+ 5. If audio is unclear, transcribe as "unclear audio"
304
+ 6. Keep transcriptions simple and clean
305
 
306
  PRODUCT NAMES (exact spelling required):
307
  - Hydropex, Respira Aid Plus, Heposel, Bromacid, Hexatox
 
310
  - Apvita Plus, B-G Aspro-C, EC-Immune, Liverpex, Symodex
311
  - Respira Aid, Adek Gold, Immuno DX
312
 
 
 
 
 
313
  MENU COMMANDS:
314
+ - Numbers: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
315
+ - Navigation: main, menu, back, home, start
316
+ - Options: option, number, choice, select
317
 
318
  GREETINGS:
319
  - English: hi, hello, hey, good morning, good afternoon, good evening
320
  - Urdu: salam, assalamu alaikum, adaab, namaste, khuda hafiz
321
 
322
  TRANSCRIPTION RULES:
323
+ 1. Transcribe exactly what you hear in English or Urdu
324
+ 2. Convert numbers to digits (one->1, two->2, etc.)
325
+ 3. Preserve product names exactly
326
+ 4. If unclear, transcribe as "unclear audio"
327
+ 5. Keep it simple and clean
328
+ 6. No random characters or mixed languages
329
 
330
  EXAMPLES:
331
  - "hydropex" -> "hydropex"
 
334
  - "main menu" -> "main"
335
  - "salam" -> "salam"
336
  - "search products" -> "search products"
337
+ - Unclear audio -> "unclear audio"
 
 
338
  """
339
 
340
+ # First attempt with comprehensive system prompt
341
  with open(file_path, 'rb') as audio_file:
342
  transcript = openai.Audio.transcribe(
343
  model="whisper-1",
 
356
  urdu_system_prompt = """
357
  You are transcribing Urdu voice messages for Apex Biotical Veterinary WhatsApp Assistant.
358
 
 
 
359
  PRODUCT NAMES (Urdu/English):
360
  - ہائیڈروپیکس (Hydropex)
361
  - ریسپیرا ایڈ پلس (Respira Aid Plus)
 
368
  - فائٹو سال (PHYTO-SAL)
369
  - مائیکوپیکس سپر (Mycopex Super)
370
 
371
+ URDU NUMBERS:
372
  - ایک (1), دو (2), تین (3), چار (4), پانچ (5)
373
  - چھ (6), سات (7), آٹھ (8), نو (9), دس (10)
374
  - گیارہ (11), بارہ (12), تیرہ (13), چودہ (14), پندرہ (15)
375
  - سولہ (16), سترہ (17), اٹھارہ (18), انیس (19), بیس (20)
376
+ - اکیس (21), بائیس (22), تئیس (23)
377
 
378
  URDU GREETINGS:
379
  - سلام (salam), السلام علیکم (assalamu alaikum)
 
385
  - کیٹلاگ (catalog), رابطہ (contact), دستیابی (availability)
386
 
387
  TRANSCRIPTION RULES:
388
+ 1. Transcribe Urdu words in Urdu script
389
+ 2. Convert Urdu numbers to digits
390
+ 3. Handle mixed Urdu-English speech
391
  4. Preserve product names exactly
392
+ 5. Convert menu selections to numbers
 
393
  """
394
 
395
  with open(file_path, 'rb') as audio_file:
 
402
 
403
  transcribed_text = transcript.text.strip()
404
  logger.info(f"[Transcribe] Second attempt transcribed (Urdu): '{transcribed_text}'")
405
+
406
+ # Third attempt with mixed language prompt if still failing
407
+ if not transcribed_text or len(transcribed_text.strip()) < 2:
408
+ logger.warning(f"[Transcribe] Second attempt failed, trying with mixed language prompt")
409
+
410
+ mixed_system_prompt = """
411
+ You are transcribing voice messages for a veterinary products WhatsApp assistant. The user may speak in English, Urdu, or a mix of both languages.
412
+
413
+ PRODUCT NAMES (exact spelling required):
414
+ Hydropex, Respira Aid Plus, Heposel, Bromacid, Hexatox, APMA Fort, Para C.E, Tribiotic, PHYTO-SAL, Mycopex Super, Eflin KT-20, Salcozine ST-30, Oftilex UA-10, Biscomin 10, Apvita Plus, B-G Aspro-C, EC-Immune, Liverpex, Symodex, Respira Aid, Adek Gold, Immuno DX
415
+
416
+ NUMBERS (convert to digits):
417
+ English: one->1, two->2, three->3, etc.
418
+ Urdu: aik->1, ek->1, do->2, teen->3, etc.
419
+
420
+ MENU COMMANDS:
421
+ main, menu, back, home, start, option, number, search, browse, download, catalog, contact, availability
422
+
423
+ GREETINGS:
424
+ hi, hello, salam, assalamu alaikum, adaab, namaste
425
+
426
+ TRANSCRIPTION RULES:
427
+ 1. Transcribe exactly what you hear
428
+ 2. Convert numbers to digits
429
+ 3. Preserve product names exactly
430
+ 4. Handle both languages
431
+ 5. Convert menu selections to numbers
432
+ """
433
+
434
+ with open(file_path, 'rb') as audio_file:
435
+ transcript = openai.Audio.transcribe(
436
+ model="whisper-1",
437
+ file=audio_file,
438
+ prompt=mixed_system_prompt
439
+ )
440
+
441
+ transcribed_text = transcript.text.strip()
442
+ logger.info(f"[Transcribe] Third attempt (mixed) transcribed: '{transcribed_text}'")
443
 
444
+ # Final check for empty transcription or unclear audio
445
  if not transcribed_text or len(transcribed_text.strip()) < 2:
446
  logger.warning(f"[Transcribe] Very short or empty transcription: '{transcribed_text}'")
447
  return "unclear audio"
448
 
449
+ # Check for gibberish or mixed characters
450
+ if len(transcribed_text) > 10 and not re.search(r'[a-zA-Z\u0600-\u06FF]', transcribed_text):
451
+ logger.warning(f"[Transcribe] Gibberish detected: '{transcribed_text}'")
452
+ return "unclear audio"
453
+
454
+ # Check for too many special characters
455
  special_char_ratio = len(re.findall(r'[^\w\s]', transcribed_text)) / len(transcribed_text)
456
+ if special_char_ratio > 0.3:
457
+ logger.warning(f"[Transcribe] Too many special characters: '{transcribed_text}'")
458
  return "unclear audio"
459
 
460
  return transcribed_text
 
475
  # Remove extra whitespace
476
  processed_text = re.sub(r'\s+', ' ', processed_text)
477
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
478
  # Basic punctuation cleanup
479
  processed_text = processed_text.replace(' ,', ',').replace(' .', '.')
480
 
 
600
  logger.info(f"[Veterinary Search] Skipping menu selection: '{normalized_query}'")
601
  return []
602
 
 
 
 
 
 
 
 
 
 
 
 
 
 
603
  scored_matches = []
604
 
605
  # Veterinary-specific query expansion
 
620
  if category_key in normalized_query:
621
  expanded_queries.extend(categories)
622
 
623
+ # Common veterinary product variations
624
  veterinary_variations = {
625
+ 'hydropex': ['hydropex', 'hydro pex', 'electrolyte', 'dehydration', 'heat stress'],
626
+ 'heposel': ['heposel', 'hepo sel', 'liver tonic', 'hepatoprotective'],
627
+ 'bromacid': ['bromacid', 'brom acid', 'respiratory', 'mucolytic'],
628
+ 'respira aid': ['respira aid', 'respira aid plus', 'respiratory support'],
629
+ 'hexatox': ['hexatox', 'hexa tox', 'liver support', 'kidney support'],
630
+ 'apma fort': ['apma fort', 'mycotoxin', 'liver support'],
631
+ 'para c': ['para c', 'para c.e', 'heat stress', 'paracetamol'],
632
  'tribiotic': ['tribiotic', 'antibiotic', 'respiratory infection'],
633
+ 'phyto-sal': ['phyto-sal', 'phytogenic', 'vitamin supplement'],
634
+ 'mycopex': ['mycopex', 'mycotoxin binder', 'mold'],
635
+ 'oftilex': ['oftilex', 'ofloxacin', 'antibiotic'],
636
+ 'biscomin': ['biscomin', 'oxytetracycline', 'injectable'],
637
+ 'apvita': ['apvita', 'vitamin b', 'amino acid'],
638
+ 'bg aspro': ['bg aspro', 'aspirin', 'vitamin c'],
639
+ 'ec-immune': ['ec-immune', 'immune', 'immunity'],
640
  'liverpex': ['liverpex', 'liver', 'metabolic'],
641
  'symodex': ['symodex', 'multivitamin', 'vitamin'],
642
+ 'adek gold': ['adek gold', 'vitamin', 'multivitamin'],
643
+ 'immuno dx': ['immuno dx', 'immune', 'antioxidant']
 
 
644
  }
645
 
646
  # Add veterinary variations
 
678
  best_match_type = "exact"
679
  match_details = {"field": field_name, "query": expanded_query}
680
 
681
+ # Fuzzy matching for close matches
682
  for expanded_query in expanded_queries:
683
  if len(expanded_query) > 3: # Only fuzzy match longer queries
684
  score = fuzz.partial_ratio(normalized_query, field_str) * weight
685
+ if score > best_score and score > 70:
686
  best_score = score
687
  best_match_type = "fuzzy"
688
  match_details = {"field": field_name, "query": expanded_query}
 
707
  return unique_matches
708
 
709
  def normalize(text: str) -> str:
710
+ """Normalize text for search"""
711
+ if not text:
712
+ return ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
713
 
714
+ # Convert to lowercase and remove extra whitespace
715
+ normalized = text.lower().strip()
716
 
717
+ # Remove special characters but keep spaces
718
+ normalized = re.sub(r'[^\w\s]', '', normalized)
 
719
 
720
+ # Replace multiple spaces with single space
721
+ normalized = re.sub(r'\s+', ' ', normalized)
 
 
722
 
723
+ return normalized
 
724
 
725
  # Enhanced context management with veterinary domain awareness
726
  class VeterinaryContextManager:
 
811
  context_manager = VeterinaryContextManager()
812
 
813
  # Enhanced product response with veterinary domain expertise
814
+ def generate_veterinary_product_response(product_info: Dict[str, Any], user_context: Dict[str, Any]) -> str:
815
  """Generate comprehensive veterinary product response with intelligent information handling"""
816
 
817
  def clean_text(text):
818
  if pd.isna(text) or text is None:
819
  return "Not specified"
820
+ return str(text).strip()
 
 
 
821
 
822
  # Extract product details
823
  product_name = clean_text(product_info.get('Product Name', ''))
 
838
  except Exception as e:
839
  logger.warning(f"Error checking PDF link for {product_name}: {e}")
840
 
841
+ # Build the response
842
+ response = f"""🧪 *Name:* {product_name}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
843
  📦 *Type:* {product_type}
844
  🏥 *Category:* {category}
845
  💊 *Used For:* {indications}"""
846
+
847
+ # Add PDF link if available, in the requested format
848
+ if pdf_link:
849
+ response += f"\n\n📄 Product Brochure Available\n🔗 {product_name} PDF:\n{pdf_link}"
850
+
851
+ # Add menu options
852
+ response += f"""
853
 
854
  💬 *Available Actions:*
855
  1️⃣ Talk to Veterinary Consultant
 
866
  return "N/A"
867
 
868
  cleaned = str(text)
 
 
869
  # Remove or replace problematic characters for PDF
870
  cleaned = cleaned.replace('â€"', '-').replace('â€"', '"').replace('’', "'")
871
  cleaned = cleaned.replace('“', '"').replace('â€', '"').replace('…', '...')
 
1694
  # Handle voice messages FIRST - before checking message_body
1695
  if message_type in ['audio', 'voice'] or media_type == 'audio':
1696
  logger.info(f"[Process] Processing voice message from {from_number}")
1697
+ await handle_voice_message_complete(from_number, msg)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1698
  return
1699
 
1700
  # For text messages, check if body exists
 
1703
  return
1704
 
1705
  message_body = message_body.strip()
1706
+
1707
  logger.info(f"[Process] Processing {message_type} message from {from_number}: {message_body}")
1708
 
1709
  # Get user context
1710
  user_context = context_manager.get_context(from_number)
1711
  current_state = user_context.get('current_state', 'main_menu')
1712
+
1713
+ # Update context with last message for intelligent responses
1714
  context_manager.update_context(from_number, last_message=message_body)
1715
+
1716
+ # Debug logging
1717
  logger.info(f"[Process] Current state: {current_state}, Message: '{message_body}' from {from_number}")
1718
+
1719
+ # Handle text messages
1720
  if not message_body:
1721
  return
1722
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1723
  # Check for greetings with multilingual support
1724
  if is_greeting(message_body):
1725
  # Check if user is currently in AI chat mode - if so, don't trigger menu mode
 
1730
  return
1731
  else:
1732
  # Only trigger menu mode if not in AI chat mode
1733
+ welcome_msg = generate_veterinary_welcome_message()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1734
  send_whatsjet_message(from_number, welcome_msg)
1735
  context_manager.update_context(
1736
  from_number,
1737
  current_state='main_menu',
1738
  current_menu='main_menu',
1739
+ current_menu_options=list(MENU_CONFIG['main_menu']['option_descriptions'].values())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1740
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1741
  return
1742
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1743
  # 🎯 PRIORITY 1: Navigation commands - work from ANY state
1744
  # Check for "main" command - now works for both text and voice
1745
  if current_state != 'main_menu' and current_state != 'ai_chat_mode': # Only check for main if not already in main menu and not in AI chat mode
1746
  mapped_navigation = process_intelligent_voice_command(message_body, current_state, user_context)
1747
  if mapped_navigation == 'main':
1748
  logger.info(f"[Process] Navigation command detected: '{message_body}' -> 'main'")
1749
+ welcome_msg = generate_veterinary_welcome_message()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1750
  send_whatsjet_message(from_number, welcome_msg)
1751
  context_manager.update_context(
1752
  from_number,
1753
  current_state='main_menu',
1754
  current_menu='main_menu',
1755
+ current_menu_options=list(MENU_CONFIG['main_menu']['option_descriptions'].values())
 
1756
  )
1757
  return
1758
 
1759
  # Also check for text-based navigation commands
1760
  if message_body.lower() in ['main', 'menu', 'start', 'home', 'back']:
1761
+ welcome_msg = generate_veterinary_welcome_message()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1762
  send_whatsjet_message(from_number, welcome_msg)
1763
  context_manager.update_context(
1764
  from_number,
1765
  current_state='main_menu',
1766
  current_menu='main_menu',
1767
+ current_menu_options=list(MENU_CONFIG['main_menu']['option_descriptions'].values())
 
1768
  )
1769
  return
1770
 
1771
+ # 🎯 PRIORITY 2: State-specific handling (contact_request, availability_request, ai_chat_mode)
1772
  if current_state == 'contact_request':
1773
  await handle_contact_request_response(from_number, message_body)
1774
  return
 
1778
  elif current_state == 'ai_chat_mode':
1779
  await handle_ai_chat_mode(from_number, message_body, reply_language)
1780
  return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1781
 
1782
+ # 🎯 PRIORITY 3: Menu selections - check if this is a valid menu selection for current state
1783
  if current_state in ['main_menu', 'category_selection_menu', 'category_products_menu', 'all_products_menu', 'product_inquiry', 'intelligent_products_menu']:
1784
  # Validate menu selection
1785
  is_valid, error_msg = validate_menu_selection(message_body, current_state, user_context)
 
1821
  reply_language='ur'
1822
  )
1823
  message = (
1824
+ "🤖 ویٹرنری اے آئی اسسٹنٹ\n\n"
1825
  "آپ مجھ سے پوچھ سکتے ہیں:\n"
1826
+ " ویٹرنری سوالات\n"
1827
+ " پروڈکٹ کی سفارشات\n"
1828
+ " علاج کے مشورے\n"
1829
+ " عمومی معلومات\n\n"
1830
  "💬 'main' لکھ کر مین مینو پر واپس جائیں۔"
1831
  )
1832
  send_whatsjet_message(from_number, message)
 
1849
  send_whatsjet_message(from_number, get_menu_validation_message(current_state, user_context))
1850
  elif current_state == 'all_products_menu':
1851
  # Handle product selection from all products
1852
+ if products_df is not None and not products_df.empty:
1853
+ all_products = products_df.to_dict('records')
1854
+ if message_body.isdigit() and 1 <= int(message_body) <= len(all_products):
1855
+ selected_product = all_products[int(message_body) - 1]
1856
+ context_manager.update_context(
1857
+ from_number,
1858
+ current_product=selected_product,
1859
+ current_state='product_inquiry',
1860
+ current_menu='product_inquiry',
1861
+ current_menu_options=list(MENU_CONFIG['product_inquiry']['option_descriptions'].values())
1862
+ )
1863
+ await send_product_image_with_caption(from_number, selected_product, user_context)
1864
+ else:
1865
+ send_whatsjet_message(from_number, get_menu_validation_message(current_state, user_context))
1866
  else:
1867
+ send_whatsjet_message(from_number, "❌ No products available. Type 'main' to return to main menu.")
 
1868
  elif current_state == 'product_inquiry':
1869
  await handle_veterinary_product_followup(message_body, from_number)
1870
  elif current_state == 'intelligent_products_menu':
1871
+ # Handle product selection from intelligent products menu
1872
  available_products = user_context.get('available_products', [])
1873
  if message_body.isdigit() and 1 <= int(message_body) <= len(available_products):
1874
  selected_product = available_products[int(message_body) - 1]
 
1886
  return
1887
  return # Exit after handling menu selection
1888
 
1889
+ # 🎯 PRIORITY 4: Product names - works from ANY menu state
1890
  # This ensures users can say product names like "hydropex", "respira aid plus", etc. from any menu
1891
  logger.info(f"[Process] Checking for product name in message: '{message_body}' from state: {current_state}")
1892
  products = get_veterinary_product_matches(message_body)
 
1996
 
1997
  async def handle_general_query_with_ai(from_number: str, query: str, user_context: dict, reply_language: str = 'en'):
1998
  """Handle general queries with OpenAI intelligence"""
1999
+ # Force Urdu replies for Option 4
2000
+ reply_language = 'ur'
2001
+ logger.info(f"[AI General] Forcing reply_language to Urdu for Option 4.")
2002
 
2003
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2004
  if not OPENAI_API_KEY:
2005
+ send_whatsjet_message(from_number,
2006
+ "❌ AI assistance is not available. Please try searching for a specific product or type 'main' for the menu.")
 
 
 
 
2007
  return
2008
 
2009
  # Create context-aware prompt
2010
  current_state = user_context.get('current_state', 'main_menu')
2011
  current_product = user_context.get('current_product')
2012
 
2013
+ # --- SYSTEM PROMPT FOR GENERAL/OUT-OF-MENU QUERIES ---
2014
+ # This prompt ensures the assistant answers professionally, accurately, and helpfully.
2015
+ # If the query is about products, list all relevant products (not just one) with brief details.
2016
+ # If the query is general, provide a concise, expert veterinary answer.
2017
+ # Clarify when a query is outside the menu system and offer to return to the main menu if needed.
 
 
 
 
 
 
 
 
 
 
2018
  prompt = f"""
2019
+ You are a professional veterinary product assistant for Apex Biotical, helping users on WhatsApp.
2020
+ Always answer in a clear, accurate, and helpful manner.
 
 
 
2021
 
2022
+ User Query: "{query}"
2023
+ Current State: {current_state}
2024
+ Current Product: {current_product.get('Product Name', 'None') if current_product else 'None'}
2025
 
2026
+ If the user asks about products (e.g., 'poultry products', 'respiratory medicine'), list ALL relevant products from the database with a short description for each. If there are many, summarize or group them.
2027
+ If the user asks a general veterinary question, provide a concise, expert answer.
2028
+ If the query is outside the menu system, politely clarify and offer to return to the main menu (type 'main').
2029
+ Always keep responses professional, concise, and user-friendly.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2030
  """
2031
 
2032
  response = openai.ChatCompletion.create(
2033
  model="gpt-4o",
2034
  messages=[{"role": "user", "content": prompt}],
2035
+ temperature=0.7,
2036
+ max_tokens=300
2037
  )
2038
 
2039
  ai_response = response.choices[0].message.content.strip()
2040
 
2041
+ # Translate response if needed
 
 
 
 
 
 
 
2042
  if reply_language == 'ur':
2043
  try:
2044
+ translated_response = GoogleTranslator(source='auto', target='ur').translate(ai_response)
 
2045
  send_whatsjet_message(from_number, translated_response)
2046
  except Exception as e:
2047
  logger.error(f"[AI] Translation error: {e}")
 
2054
 
2055
  except Exception as e:
2056
  logger.error(f"[AI] Error handling general query: {e}")
2057
+ # Instead of sending a generic error, return to main menu
2058
+ welcome_msg = generate_veterinary_welcome_message()
2059
+ send_whatsjet_message(from_number, welcome_msg)
 
 
 
 
2060
  context_manager.update_context(
2061
  from_number,
2062
  current_state='main_menu',
 
2064
  current_menu_options=list(MENU_CONFIG['main_menu']['option_descriptions'].values())
2065
  )
2066
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2067
  async def handle_contact_request(from_number: str):
2068
  """Handle contact request"""
2069
  try:
 
2573
  else:
2574
  ai_response += "\n\n💬 *Type 'main' to return to main menu*"
2575
 
2576
+ # Translate response if needed
2577
  if reply_language == 'ur':
2578
  try:
2579
  # Get all product and category names
2580
  product_names = [str(p.get('Product Name', '')) for p in all_products if p.get('Product Name')]
2581
  category_names = list(set([str(p.get('Category', '')) for p in all_products if p.get('Category')]))
2582
+ translated_response = GoogleTranslator(source='auto', target='ur').translate(ai_response)
 
2583
  # Restore English terms
2584
  translated_response = restore_english_terms(translated_response, ai_response, product_names, category_names)
2585
  send_whatsjet_message(from_number, translated_response)
 
2796
  def clean_text(text):
2797
  if pd.isna(text) or text is None:
2798
  return "Not specified"
2799
+ return str(text).strip()
 
 
 
2800
 
2801
  product_name = clean_text(product_info.get('Product Name', ''))
2802
  product_type = clean_text(product_info.get('Type', ''))
 
2843
  logger.info(f"[Image] Ensured images directory exists: {images_dir}")
2844
 
2845
  # New feature: Send product image with caption (product details)
2846
+ async def send_product_image_with_caption(from_number: str, product: Dict[str, Any], user_context: Dict[str, Any]):
2847
  """
2848
  Send product image (if available) with product details as caption in a single WhatsApp message.
2849
  Only uses cPanel images from https://amgocus.com/uploads/images/
 
2851
  """
2852
  ensure_images_dir()
2853
  product_name = product.get('Product Name', 'Unknown Product')
2854
+ details = generate_veterinary_product_response(product, user_context)
2855
 
2856
  logger.info(f"[Product] Processing cPanel image for product: {product_name}")
2857
 
 
2953
  product = products_df.iloc[0].to_dict()
2954
  user_context = {}
2955
 
2956
+ await send_product_image_with_caption(phone, product, user_context)
2957
 
2958
  return {
2959
  "success": True,
 
3318
  current_menu='contact_request',
3319
  current_menu_options=['Provide contact details']
3320
  )
3321
+
3322
  elif selection == '2':
3323
  # Inquire about Product Availability
3324
  await handle_availability_inquiry(from_number, user_context)
3325
+
3326
  elif selection == '3':
3327
  welcome_msg = generate_veterinary_welcome_message(from_number, user_context)
3328
  send_whatsjet_message(from_number, welcome_msg)
 
3447
  detected_lang = detect(transcribed_text)
3448
  logger.info(f"[Voice] Raw detected language: {detected_lang}")
3449
 
3450
+ # STRICTLY ENGLISH OR URDU ONLY - NO OTHER LANGUAGES
3451
+ # Only allow English and Urdu, reject everything else
3452
  if detected_lang in ['en', 'ur']:
3453
  reply_language = detected_lang
 
3454
  else:
3455
+ # Force any other language to English
3456
  reply_language = 'en'
3457
+ logger.warning(f"[Voice] Detected language '{detected_lang}' is not English or Urdu, forcing to English")
 
 
 
3458
 
3459
  # Check if text contains Urdu/Arabic characters or Islamic greetings
3460
  urdu_arabic_pattern = re.compile(r'[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]')
 
3474
  logger.warning(f"[Voice] Language detection failed: {e}, defaulting to English")
3475
  reply_language = 'en'
3476
 
3477
+ # For Urdu voice notes, translate to English for processing
3478
  processing_text = transcribed_text
3479
  if reply_language == 'ur' and detected_lang == 'ur':
3480
  try:
3481
  logger.info(f"[Voice] Translating Urdu voice note to English for processing")
 
3482
  translated_text = GoogleTranslator(source='ur', target='en').translate(transcribed_text)
3483
  processing_text = translated_text
3484
  logger.info(f"[Voice] Translated to English: {translated_text}")
 
3486
  logger.error(f"[Voice] Translation failed: {e}")
3487
  # If translation fails, use original text
3488
  processing_text = transcribed_text
 
 
 
 
 
 
 
 
 
 
 
3489
 
3490
  # Determine reply language - always respond in English or Urdu
3491
  if detected_lang == 'ur':
 
3497
  logger.info(f"[Voice] Reply language set to: {reply_language}")
3498
 
3499
  # Check if this is a greeting in voice note (check both original and translated)
 
 
3500
  if is_greeting(transcribed_text) or is_greeting(processing_text):
3501
  logger.info(f"[Voice] Greeting detected in voice note: {transcribed_text}")
3502
+
3503
+ # Check if user is currently in AI chat mode - if so, don't trigger menu mode
3504
+ user_context = context_manager.get_context(from_number)
3505
+ current_state = user_context.get('current_state', 'main_menu')
3506
+
3507
  if current_state == 'ai_chat_mode':
3508
  logger.info(f"[Voice] User is in AI chat mode, treating greeting as AI query instead of menu trigger")
3509
+ # Treat greeting as a general query in AI chat mode
3510
  await handle_general_query_with_ai(from_number, processing_text, user_context, reply_language)
3511
  return
3512
  else:
3513
+ # Only trigger menu mode if not in AI chat mode
3514
  welcome_msg = generate_veterinary_welcome_message(from_number, user_context)
3515
  send_whatsjet_message(from_number, welcome_msg)
3516
  context_manager.update_context(from_number, current_state='main_menu', current_menu='main_menu', current_menu_options=list(MENU_CONFIG['main_menu']['option_descriptions'].values()))
3517
  return
3518
+
3519
+ # Process the translated text using the same strict state-based logic as text messages
3520
+ # This ensures voice messages follow the same menu and state rules as text messages
3521
+ await process_incoming_message(from_number, {
3522
+ 'body': processing_text, # Use translated text for processing
3523
+ 'type': 'text',
3524
+ 'reply_language': reply_language,
3525
+ 'original_transcription': transcribed_text # Keep original for context
3526
+ })
3527
 
3528
  except Exception as e:
3529
  logger.error(f"[Voice] Error processing voice message: {e}")
 
3699
  user_context = context_manager.get_context(phone)
3700
 
3701
  logger.info(f"[Test] Testing send_product_image_with_caption for product: {product_name}")
3702
+ await send_product_image_with_caption(phone, product, user_context)
3703
 
3704
  return {
3705
  "status": "sent",
 
3712
  return {"error": str(e)}
3713
 
3714
  async def handle_intelligent_product_inquiry(from_number: str, query: str, user_context: dict, reply_language: str = 'en'):
3715
+ """Handle product inquiry with OpenAI intelligence and media support"""
3716
  try:
3717
+ # First try direct product search
3718
  products = get_veterinary_product_matches(query)
3719
+
3720
  if products:
3721
+ # Check if this is a broad/category query (multiple products found)
3722
  if len(products) > 1:
3723
+ # Use OpenAI to generate a professional summary and list all products
3724
+ if OPENAI_API_KEY:
3725
+ try:
3726
+ # Create a comprehensive prompt for multiple products
3727
+ products_info = []
3728
+ for i, product in enumerate(products, 1):
3729
+ product_name = product.get('Product Name', 'N/A')
3730
+ category = product.get('Category', 'N/A')
3731
+ target_species = product.get('Target Species', 'N/A')
3732
+ products_info.append(f"{i}. {product_name} - {category} ({target_species})")
3733
+
3734
+ products_text = "\n".join(products_info)
3735
+
3736
+ prompt = f"""
3737
+ You are a professional veterinary product assistant for Apex Biotical. The user asked about "{query}" and we found {len(products)} relevant products.
3738
+
3739
+ Available Products:
3740
+ {products_text}
3741
+
3742
+ Please provide:
3743
+ 1. A professional, welcoming response acknowledging their query
3744
+ 2. A brief summary of what these products are for (if it's a category like "poultry products", explain the category)
3745
+ 3. List all products with their numbers and brief descriptions
3746
+ 4. Clear instructions on how to proceed
3747
+
3748
+ Format your response professionally with emojis and clear structure. Keep it concise but informative.
3749
+ """
3750
+
3751
+ response = openai.ChatCompletion.create(
3752
+ model="gpt-4o",
3753
+ messages=[{"role": "user", "content": prompt}],
3754
+ temperature=0.7,
3755
+ max_tokens=400
3756
+ )
3757
+
3758
+ ai_response = response.choices[0].message['content'].strip()
3759
+
3760
+ # Add instructions for selection
3761
+ selection_instructions = (
3762
+ f"\n\n💬 *To view detailed information about any product, reply with its number (1-{len(products)})*\n"
3763
+ "💬 *Type 'main' to return to the main menu*"
3764
+ )
3765
+
3766
+ full_response = ai_response + selection_instructions
3767
+
3768
+ # Translate response if needed
3769
+ if reply_language == 'ur':
3770
+ try:
3771
+ translated_response = GoogleTranslator(source='auto', target='ur').translate(full_response)
3772
+ send_whatsjet_message(from_number, translated_response)
3773
+ except Exception as e:
3774
+ logger.error(f"[AI] Translation error: {e}")
3775
+ send_whatsjet_message(from_number, full_response)
3776
+ else:
3777
+ send_whatsjet_message(from_number, full_response)
3778
+
3779
+ # Store the product list in context for selection handling
3780
+ context_manager.update_context(
3781
+ from_number,
3782
+ current_state='intelligent_products_menu',
3783
+ current_menu='intelligent_products_menu',
3784
+ current_menu_options=[f"Product {i+1}" for i in range(len(products))],
3785
+ available_products=products,
3786
+ last_query=query
3787
+ )
3788
+
3789
+ # Add to conversation history
3790
+ context_manager.add_to_history(from_number, query, full_response)
3791
+ return
3792
+
3793
+ except Exception as e:
3794
+ logger.error(f"[AI] Error generating product summary: {e}")
3795
+ # Fall back to simple listing if AI fails
3796
+ pass
3797
+
3798
+ # Fallback: Simple listing without AI
3799
+ message = f"🔍 *Found {len(products)} products matching '{query}':*\n\n"
3800
+
3801
  for i, product in enumerate(products, 1):
3802
+ product_name = product.get('Product Name', 'N/A')
3803
+ category = product.get('Category', 'N/A')
3804
+ target_species = product.get('Target Species', 'N/A')
3805
+ message += f"{format_number_with_emoji(i)} {product_name}\n"
3806
+ message += f" 📦 {category} ({target_species})\n\n"
3807
+
3808
+ message += (
3809
+ f"💬 *To view detailed information about any product, reply with its number (1-{len(products)})*\n"
3810
+ "💬 *Type 'main' to return to the main menu*"
3811
+ )
3812
+
3813
+ # Translate response if needed
3814
+ if reply_language == 'ur':
3815
+ try:
3816
+ translated_message = GoogleTranslator(source='auto', target='ur').translate(message)
3817
+ send_whatsjet_message(from_number, translated_message)
3818
+ except Exception as e:
3819
+ logger.error(f"[AI] Translation error: {e}")
3820
+ send_whatsjet_message(from_number, message)
3821
+ else:
3822
+ send_whatsjet_message(from_number, message)
3823
+
3824
+ # Store the product list in context for selection handling
3825
  context_manager.update_context(
3826
+ from_number,
3827
  current_state='intelligent_products_menu',
3828
  current_menu='intelligent_products_menu',
3829
+ current_menu_options=[f"Product {i+1}" for i in range(len(products))],
3830
+ available_products=products,
3831
+ last_query=query
3832
  )
3833
+
3834
+ # Add to conversation history
3835
+ context_manager.add_to_history(from_number, query, message)
3836
+
3837
  else:
3838
+ # Single product found - show detailed information with media support
3839
  selected_product = products[0]
3840
  context_manager.update_context(
3841
+ from_number,
3842
+ current_product=selected_product,
3843
  current_state='product_inquiry',
3844
  current_menu='product_inquiry',
3845
  current_menu_options=list(MENU_CONFIG['product_inquiry']['option_descriptions'].values())
3846
  )
3847
+
3848
+ # Send product image with caption using the new function
3849
  await send_product_image_with_caption(from_number, selected_product, user_context)
3850
+
3851
+ # Add to conversation history
3852
+ context_manager.add_to_history(from_number, query, f"Product inquiry for {selected_product.get('Product Name', 'Unknown')}")
3853
+
3854
  else:
3855
+ # Enhanced "not found" response with veterinary suggestions
3856
+ message = (
3857
+ "❌ *Product Not Found*\n\n"
3858
+ f"🔍 *We couldn't find '{query}' in our veterinary database.*\n\n"
3859
+ "💡 *Try these alternatives:*\n"
3860
+ "• Check spelling (e.g., 'Hydropex' not 'Hydro pex')\n"
3861
+ "• Search by symptoms (e.g., 'respiratory', 'liver support')\n"
3862
+ "• Search by category (e.g., 'antibiotic', 'vitamin')\n"
3863
+ "• Search by species (e.g., 'poultry', 'livestock')\n\n"
3864
+ "🏥 *Popular Veterinary Products:*\n"
3865
+ "• Hydropex (Electrolyte supplement)\n"
3866
+ "• Heposel (Liver tonic)\n"
3867
+ "• Bromacid (Respiratory support)\n"
3868
+ "• Tribiotic (Antibiotic)\n"
3869
+ "• Symodex (Multivitamin)\n\n"
3870
+ "💬 *Type 'main' to return to main menu or try another search.*"
3871
+ )
3872
+
3873
+ # Translate response if needed
3874
+ if reply_language == 'ur':
3875
+ try:
3876
+ translated_message = GoogleTranslator(source='auto', target='ur').translate(message)
3877
+ send_whatsjet_message(from_number, translated_message)
3878
+ except Exception as e:
3879
+ logger.error(f"[AI] Translation error: {e}")
3880
+ send_whatsjet_message(from_number, message)
3881
+ else:
3882
+ send_whatsjet_message(from_number, message)
3883
+
3884
  except Exception as e:
3885
+ logger.error(f"Error in product inquiry: {e}")
3886
+ # Instead of sending a generic error, return to main menu
3887
+ welcome_msg = generate_veterinary_welcome_message(from_number, user_context)
3888
+ send_whatsjet_message(from_number, welcome_msg)
3889
+ context_manager.update_context(from_number, current_state='main_menu', current_menu='main_menu', current_menu_options=list(MENU_CONFIG['main_menu']['option_descriptions'].values()))
3890
 
3891
  async def handle_contact_request(from_number: str):
3892
  """Handle contact request"""
 
3940
 
3941
  # Launch FastAPI app
3942
  import uvicorn
3943
+ uvicorn.run(app, host="0.0.0.0", port=7860)