Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -2687,6 +2687,9 @@ async def handle_ai_chat_mode(from_number: str, query: str, reply_language: str
|
|
| 2687 |
try:
|
| 2688 |
logger.info(f"[AI Chat] Processing query: '{query}' for {from_number} in {reply_language}")
|
| 2689 |
|
|
|
|
|
|
|
|
|
|
| 2690 |
# Check for navigation commands first
|
| 2691 |
if query.lower().strip() in ['main', 'menu', 'start', 'home', 'back']:
|
| 2692 |
logger.info(f"[AI Chat] Navigation command detected: '{query}' -> returning to main menu")
|
|
@@ -2723,13 +2726,15 @@ async def handle_ai_chat_mode(from_number: str, query: str, reply_language: str
|
|
| 2723 |
|
| 2724 |
# Get all products data for context
|
| 2725 |
all_products = []
|
|
|
|
| 2726 |
if products_df is not None and not products_df.empty:
|
| 2727 |
all_products = products_df.to_dict('records')
|
|
|
|
| 2728 |
|
| 2729 |
# Create comprehensive context for AI
|
| 2730 |
products_context = ""
|
| 2731 |
if all_products:
|
| 2732 |
-
products_context = "Available Veterinary Products:\n"
|
| 2733 |
for i, product in enumerate(all_products[:50], 1): # Limit to first 50 products for context
|
| 2734 |
product_name = product.get('Product Name', 'N/A')
|
| 2735 |
category = product.get('Category', 'N/A')
|
|
@@ -2739,13 +2744,15 @@ async def handle_ai_chat_mode(from_number: str, query: str, reply_language: str
|
|
| 2739 |
products_context += f" Composition: {composition}\n"
|
| 2740 |
products_context += f" Target Species: {target_species}\n\n"
|
| 2741 |
|
| 2742 |
-
# Create AI prompt
|
| 2743 |
if reply_language == 'ur':
|
| 2744 |
prompt = f"""
|
| 2745 |
آپ Apex Biotical کے Veterinary AI Assistant ہیں۔ آپ کو veterinary products اور treatments کے بارے میں معلومات فراہم کرنی ہیں۔
|
| 2746 |
|
| 2747 |
یوزر کا سوال: {query}
|
| 2748 |
|
|
|
|
|
|
|
| 2749 |
دستیاب veterinary products:
|
| 2750 |
{products_context}
|
| 2751 |
|
|
@@ -2755,6 +2762,7 @@ async def handle_ai_chat_mode(from_number: str, query: str, reply_language: str
|
|
| 2755 |
3. اگر یہ general veterinary advice ہے تو professional guidance دیں
|
| 2756 |
4. اردو میں جواب دیں
|
| 2757 |
5. جواب professional اور helpful ہو
|
|
|
|
| 2758 |
|
| 2759 |
جواب:
|
| 2760 |
"""
|
|
@@ -2764,6 +2772,8 @@ You are Apex Biotical's Veterinary AI Assistant. You provide information about v
|
|
| 2764 |
|
| 2765 |
User Query: {query}
|
| 2766 |
|
|
|
|
|
|
|
| 2767 |
Available Veterinary Products:
|
| 2768 |
{products_context}
|
| 2769 |
|
|
@@ -2773,6 +2783,7 @@ Please:
|
|
| 2773 |
3. If it's general veterinary advice, provide professional guidance
|
| 2774 |
4. Answer in English
|
| 2775 |
5. Keep the response professional and helpful
|
|
|
|
| 2776 |
|
| 2777 |
Response:
|
| 2778 |
"""
|
|
@@ -2819,16 +2830,20 @@ Response:
|
|
| 2819 |
error_msg = "❌ AI Assistant encountered an error. Please try again or type 'main' to return to main menu."
|
| 2820 |
send_whatsjet_message(from_number, error_msg)
|
| 2821 |
|
| 2822 |
-
def generate_veterinary_welcome_message(phone_number, user_context):
|
| 2823 |
-
|
| 2824 |
-
|
| 2825 |
-
"
|
| 2826 |
-
"
|
|
|
|
|
|
|
| 2827 |
"2️⃣ Browse Categories\n"
|
| 2828 |
"3️⃣ Download Catalog\n"
|
| 2829 |
-
"4️⃣ Chat with Veterinary AI Assistant\n"
|
| 2830 |
-
"
|
|
|
|
| 2831 |
)
|
|
|
|
| 2832 |
|
| 2833 |
@app.get("/test-whatsjet")
|
| 2834 |
async def test_whatsjet():
|
|
@@ -2839,6 +2854,331 @@ async def test_whatsjet():
|
|
| 2839 |
except Exception as e:
|
| 2840 |
return {"error": str(e)}
|
| 2841 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2842 |
if __name__ == "__main__":
|
| 2843 |
# Launch FastAPI app
|
| 2844 |
import uvicorn
|
|
|
|
| 2687 |
try:
|
| 2688 |
logger.info(f"[AI Chat] Processing query: '{query}' for {from_number} in {reply_language}")
|
| 2689 |
|
| 2690 |
+
# Get user context
|
| 2691 |
+
user_context = context_manager.get_context(from_number)
|
| 2692 |
+
|
| 2693 |
# Check for navigation commands first
|
| 2694 |
if query.lower().strip() in ['main', 'menu', 'start', 'home', 'back']:
|
| 2695 |
logger.info(f"[AI Chat] Navigation command detected: '{query}' -> returning to main menu")
|
|
|
|
| 2726 |
|
| 2727 |
# Get all products data for context
|
| 2728 |
all_products = []
|
| 2729 |
+
total_products = 0
|
| 2730 |
if products_df is not None and not products_df.empty:
|
| 2731 |
all_products = products_df.to_dict('records')
|
| 2732 |
+
total_products = len(products_df)
|
| 2733 |
|
| 2734 |
# Create comprehensive context for AI
|
| 2735 |
products_context = ""
|
| 2736 |
if all_products:
|
| 2737 |
+
products_context = f"Available Veterinary Products (Total: {total_products} products):\n"
|
| 2738 |
for i, product in enumerate(all_products[:50], 1): # Limit to first 50 products for context
|
| 2739 |
product_name = product.get('Product Name', 'N/A')
|
| 2740 |
category = product.get('Category', 'N/A')
|
|
|
|
| 2744 |
products_context += f" Composition: {composition}\n"
|
| 2745 |
products_context += f" Target Species: {target_species}\n\n"
|
| 2746 |
|
| 2747 |
+
# Create AI prompt with accurate product count
|
| 2748 |
if reply_language == 'ur':
|
| 2749 |
prompt = f"""
|
| 2750 |
آپ Apex Biotical کے Veterinary AI Assistant ہیں۔ آپ کو veterinary products اور treatments کے بارے میں معلومات فراہم کرنی ہیں۔
|
| 2751 |
|
| 2752 |
یوزر کا سوال: {query}
|
| 2753 |
|
| 2754 |
+
کل veterinary products کی تعداد: {total_products}
|
| 2755 |
+
|
| 2756 |
دستیاب veterinary products:
|
| 2757 |
{products_context}
|
| 2758 |
|
|
|
|
| 2762 |
3. اگر یہ general veterinary advice ہے تو professional guidance دیں
|
| 2763 |
4. اردو میں جواب دیں
|
| 2764 |
5. جواب professional اور helpful ہو
|
| 2765 |
+
6. اگر یوزر نے products کی تعداد کے بارے میں پوچھا ہے تو صحیح تعداد ({total_products}) بتائیں
|
| 2766 |
|
| 2767 |
جواب:
|
| 2768 |
"""
|
|
|
|
| 2772 |
|
| 2773 |
User Query: {query}
|
| 2774 |
|
| 2775 |
+
Total number of veterinary products: {total_products}
|
| 2776 |
+
|
| 2777 |
Available Veterinary Products:
|
| 2778 |
{products_context}
|
| 2779 |
|
|
|
|
| 2783 |
3. If it's general veterinary advice, provide professional guidance
|
| 2784 |
4. Answer in English
|
| 2785 |
5. Keep the response professional and helpful
|
| 2786 |
+
6. If the user asks about the number of products, provide the accurate count ({total_products})
|
| 2787 |
|
| 2788 |
Response:
|
| 2789 |
"""
|
|
|
|
| 2830 |
error_msg = "❌ AI Assistant encountered an error. Please try again or type 'main' to return to main menu."
|
| 2831 |
send_whatsjet_message(from_number, error_msg)
|
| 2832 |
|
| 2833 |
+
def generate_veterinary_welcome_message(phone_number=None, user_context=None):
|
| 2834 |
+
"""Generate enhanced welcome message with veterinary domain expertise"""
|
| 2835 |
+
welcome_msg = (
|
| 2836 |
+
"🏥 *Welcome to Apex Biotical Veterinary Bot*\n\n"
|
| 2837 |
+
"We provide comprehensive veterinary products and support.\n\n"
|
| 2838 |
+
"📋 *Available Options:*\n"
|
| 2839 |
+
"1️⃣ Search Veterinary Products\n"
|
| 2840 |
"2️⃣ Browse Categories\n"
|
| 2841 |
"3️⃣ Download Catalog\n"
|
| 2842 |
+
"4️⃣ Chat with Veterinary AI Assistant\n\n"
|
| 2843 |
+
"💬 *Select an option or ask about specific products*\n"
|
| 2844 |
+
"🎤 *You can also send voice messages*"
|
| 2845 |
)
|
| 2846 |
+
return welcome_msg
|
| 2847 |
|
| 2848 |
@app.get("/test-whatsjet")
|
| 2849 |
async def test_whatsjet():
|
|
|
|
| 2854 |
except Exception as e:
|
| 2855 |
return {"error": str(e)}
|
| 2856 |
|
| 2857 |
+
# Load products data on startup
|
| 2858 |
+
def load_products_data():
|
| 2859 |
+
"""Load products data from CSV file"""
|
| 2860 |
+
global products_df
|
| 2861 |
+
try:
|
| 2862 |
+
if os.path.exists(CSV_FILE):
|
| 2863 |
+
products_df = pd.read_csv(CSV_FILE)
|
| 2864 |
+
logger.info(f"✅ Loaded {len(products_df)} products from {CSV_FILE}")
|
| 2865 |
+
else:
|
| 2866 |
+
logger.warning(f"⚠️ CSV file {CSV_FILE} not found")
|
| 2867 |
+
products_df = pd.DataFrame()
|
| 2868 |
+
except Exception as e:
|
| 2869 |
+
logger.error(f"❌ Error loading products data: {e}")
|
| 2870 |
+
products_df = pd.DataFrame()
|
| 2871 |
+
|
| 2872 |
+
def convert_drive_link(link: str) -> str:
|
| 2873 |
+
"""Convert Google Drive link to direct download link"""
|
| 2874 |
+
if 'drive.google.com' in link:
|
| 2875 |
+
file_id = link.split('/')[-2] if '/d/' in link else link.split('/')[-1]
|
| 2876 |
+
return f"https://drive.google.com/uc?export=download&id={file_id}"
|
| 2877 |
+
return link
|
| 2878 |
+
|
| 2879 |
+
def format_number_with_emoji(number: int) -> str:
|
| 2880 |
+
"""Format number with emoji"""
|
| 2881 |
+
emoji_map = {
|
| 2882 |
+
1: "1️⃣", 2: "2️⃣", 3: "3️⃣", 4: "4️⃣", 5: "5️⃣",
|
| 2883 |
+
6: "6️⃣", 7: "7️⃣", 8: "8️⃣", 9: "9️⃣", 10: "🔟",
|
| 2884 |
+
11: "1️⃣1️⃣", 12: "1️⃣2️⃣", 13: "1️⃣3️⃣", 14: "1️⃣4️⃣", 15: "1️⃣5️⃣",
|
| 2885 |
+
16: "1️⃣6️⃣", 17: "1️⃣7️⃣", 18: "1️⃣8️⃣", 19: "1️⃣9️⃣", 20: "2️⃣0️⃣",
|
| 2886 |
+
21: "2️⃣1️⃣", 22: "2️⃣2️⃣", 23: "2️⃣3️⃣"
|
| 2887 |
+
}
|
| 2888 |
+
return emoji_map.get(number, f"{number}.")
|
| 2889 |
+
|
| 2890 |
+
async def display_all_products(from_number: str):
|
| 2891 |
+
"""Display all products in multiple messages and update menu context"""
|
| 2892 |
+
try:
|
| 2893 |
+
user_context = context_manager.get_context(from_number)
|
| 2894 |
+
current_state = user_context.get('current_state', 'main_menu')
|
| 2895 |
+
logger.info(f"[Display] display_all_products called for {from_number} in state: {current_state}")
|
| 2896 |
+
if current_state == 'all_products_menu':
|
| 2897 |
+
logger.warning(f"[Display] Already in all_products_menu state for {from_number}, skipping display")
|
| 2898 |
+
return
|
| 2899 |
+
if products_df is None or products_df.empty:
|
| 2900 |
+
send_whatsjet_message(from_number, "❌ No products available at the moment.")
|
| 2901 |
+
return
|
| 2902 |
+
# Set state to all_products_menu and store menu context
|
| 2903 |
+
products = products_df.to_dict('records')
|
| 2904 |
+
context_manager.update_context(
|
| 2905 |
+
from_number,
|
| 2906 |
+
current_state='all_products_menu',
|
| 2907 |
+
current_menu='all_products_menu',
|
| 2908 |
+
current_menu_options=[p.get('Product Name', 'Unknown') for p in products],
|
| 2909 |
+
available_products=products
|
| 2910 |
+
)
|
| 2911 |
+
logger.info(f"[Display] Set state to all_products_menu for {from_number}")
|
| 2912 |
+
# Send products in chunks
|
| 2913 |
+
chunk_size = 5
|
| 2914 |
+
for i in range(0, len(products), chunk_size):
|
| 2915 |
+
chunk = products[i:i + chunk_size]
|
| 2916 |
+
message = f"📋 *Products ({i+1}-{min(i+chunk_size, len(products))} of {len(products)})*\n\n"
|
| 2917 |
+
for j, product in enumerate(chunk, i+1):
|
| 2918 |
+
message += f"{format_number_with_emoji(j)} {product.get('Product Name', 'Unknown')}\n"
|
| 2919 |
+
if product.get('Category'):
|
| 2920 |
+
message += f" Category: {product.get('Category')}\n"
|
| 2921 |
+
message += "\n"
|
| 2922 |
+
send_whatsjet_message(from_number, message)
|
| 2923 |
+
send_whatsjet_message(from_number,
|
| 2924 |
+
"💬 Type a product name to get detailed information, or type 'main' to return to main menu.")
|
| 2925 |
+
except Exception as e:
|
| 2926 |
+
logger.error(f"[Display] Error displaying products: {e}")
|
| 2927 |
+
send_whatsjet_message(from_number, "❌ Error displaying products. Please try again.")
|
| 2928 |
+
|
| 2929 |
+
def get_all_categories():
|
| 2930 |
+
"""Return a list of all unique categories from the products DataFrame"""
|
| 2931 |
+
if products_df is not None and not products_df.empty:
|
| 2932 |
+
return list(products_df['Category'].unique())
|
| 2933 |
+
return []
|
| 2934 |
+
|
| 2935 |
+
def get_products_by_category(category: str):
|
| 2936 |
+
"""Get products by category"""
|
| 2937 |
+
if products_df is None or products_df.empty:
|
| 2938 |
+
return []
|
| 2939 |
+
category_products = products_df[products_df['Category'] == category]
|
| 2940 |
+
return category_products.to_dict('records')
|
| 2941 |
+
|
| 2942 |
+
# Enhanced product follow-up handling
|
| 2943 |
+
async def handle_veterinary_product_followup(selection: str, from_number: str) -> None:
|
| 2944 |
+
"""
|
| 2945 |
+
Handle product follow-up selections with enhanced veterinary domain support
|
| 2946 |
+
"""
|
| 2947 |
+
try:
|
| 2948 |
+
user_context = context_manager.get_context(from_number)
|
| 2949 |
+
current_product = user_context.get('current_product')
|
| 2950 |
+
|
| 2951 |
+
if selection == '1':
|
| 2952 |
+
# Talk to Veterinary Consultant
|
| 2953 |
+
current_product = user_context.get('current_product')
|
| 2954 |
+
product_name = current_product.get('Product Name', 'the selected product') if current_product else 'the selected product'
|
| 2955 |
+
consultant_msg = (
|
| 2956 |
+
f"📞 Contact Veterinary Consultant\n\n"
|
| 2957 |
+
f"Product: {product_name}\n\n"
|
| 2958 |
+
"Please provide your details:\n"
|
| 2959 |
+
"* Name and location\n"
|
| 2960 |
+
"* Specific inquiry\n\n"
|
| 2961 |
+
"💬 Example: Dr. Ali - Multan - Need consultation for respiratory problems\n\n"
|
| 2962 |
+
"Type main at any time to go to main menu."
|
| 2963 |
+
)
|
| 2964 |
+
send_whatsjet_message(from_number, consultant_msg)
|
| 2965 |
+
elif selection == '2':
|
| 2966 |
+
# Inquire about Product Availability
|
| 2967 |
+
await handle_availability_inquiry(from_number, user_context)
|
| 2968 |
+
elif selection == '3':
|
| 2969 |
+
# Back to Main Menu
|
| 2970 |
+
welcome_msg = generate_veterinary_welcome_message(from_number, user_context)
|
| 2971 |
+
send_whatsjet_message(from_number, welcome_msg)
|
| 2972 |
+
context_manager.update_context(
|
| 2973 |
+
from_number,
|
| 2974 |
+
current_state='main_menu',
|
| 2975 |
+
current_menu='main_menu',
|
| 2976 |
+
current_menu_options=list(MENU_CONFIG['main_menu']['option_descriptions'].values())
|
| 2977 |
+
)
|
| 2978 |
+
return
|
| 2979 |
+
else:
|
| 2980 |
+
send_whatsjet_message(from_number, "❌ Invalid selection. Please choose 1, 2, or 3.")
|
| 2981 |
+
return
|
| 2982 |
+
except Exception as e:
|
| 2983 |
+
logger.error(f"Error in product follow-up: {e}")
|
| 2984 |
+
welcome_msg = generate_veterinary_welcome_message(from_number, user_context)
|
| 2985 |
+
send_whatsjet_message(from_number, welcome_msg)
|
| 2986 |
+
context_manager.update_context(
|
| 2987 |
+
from_number,
|
| 2988 |
+
current_state='main_menu',
|
| 2989 |
+
current_menu='main_menu',
|
| 2990 |
+
current_menu_options=list(MENU_CONFIG['main_menu']['option_descriptions'].values())
|
| 2991 |
+
)
|
| 2992 |
+
|
| 2993 |
+
# Enhanced product inquiry handling
|
| 2994 |
+
async def handle_veterinary_product_inquiry(product_name: str, from_number: str) -> None:
|
| 2995 |
+
"""
|
| 2996 |
+
Handle product inquiry with enhanced veterinary domain support
|
| 2997 |
+
"""
|
| 2998 |
+
try:
|
| 2999 |
+
# Search for the product
|
| 3000 |
+
products = get_veterinary_product_matches(product_name)
|
| 3001 |
+
|
| 3002 |
+
if products:
|
| 3003 |
+
selected_product = products[0]
|
| 3004 |
+
context_manager.update_context(
|
| 3005 |
+
from_number,
|
| 3006 |
+
current_product=selected_product,
|
| 3007 |
+
current_state='product_inquiry',
|
| 3008 |
+
current_menu='product_inquiry',
|
| 3009 |
+
current_menu_options=list(MENU_CONFIG['product_inquiry']['option_descriptions'].values())
|
| 3010 |
+
)
|
| 3011 |
+
# Get updated context with last message
|
| 3012 |
+
context = context_manager.get_context(from_number)
|
| 3013 |
+
response = generate_veterinary_product_response(selected_product, context)
|
| 3014 |
+
send_whatsjet_message(from_number, response)
|
| 3015 |
+
|
| 3016 |
+
# Add to conversation history
|
| 3017 |
+
context_manager.add_to_history(from_number, product_name, response)
|
| 3018 |
+
|
| 3019 |
+
else:
|
| 3020 |
+
# Enhanced "not found" response with veterinary suggestions
|
| 3021 |
+
message = (
|
| 3022 |
+
"❌ *Product Not Found*\n\n"
|
| 3023 |
+
f"🔍 *We couldn't find '{product_name}' in our veterinary database.*\n\n"
|
| 3024 |
+
"💡 *Try these alternatives:*\n"
|
| 3025 |
+
"• Check spelling (e.g., 'Hydropex' not 'Hydro pex')\n"
|
| 3026 |
+
"• Search by symptoms (e.g., 'respiratory', 'liver support')\n"
|
| 3027 |
+
"• Search by category (e.g., 'antibiotic', 'vitamin')\n"
|
| 3028 |
+
"• Search by species (e.g., 'poultry', 'livestock')\n\n"
|
| 3029 |
+
"🏥 *Popular Veterinary Products:*\n"
|
| 3030 |
+
"• Hydropex (Electrolyte supplement)\n"
|
| 3031 |
+
"• Heposel (Liver tonic)\n"
|
| 3032 |
+
"• Bromacid (Respiratory support)\n"
|
| 3033 |
+
"• Tribiotic (Antibiotic)\n"
|
| 3034 |
+
"• Symodex (Multivitamin)\n\n"
|
| 3035 |
+
"💬 *Type 'main' to return to main menu or try another search.*"
|
| 3036 |
+
)
|
| 3037 |
+
|
| 3038 |
+
send_whatsjet_message(from_number, message)
|
| 3039 |
+
|
| 3040 |
+
except Exception as e:
|
| 3041 |
+
logger.error(f"Error in product inquiry: {e}")
|
| 3042 |
+
# Instead of sending a generic error, return to main menu
|
| 3043 |
+
welcome_msg = generate_veterinary_welcome_message(from_number, user_context)
|
| 3044 |
+
send_whatsjet_message(from_number, welcome_msg)
|
| 3045 |
+
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()))
|
| 3046 |
+
|
| 3047 |
+
async def handle_category_selection(selection: str, from_number: str):
|
| 3048 |
+
"""Handle category selection from category_selection_menu state"""
|
| 3049 |
+
try:
|
| 3050 |
+
user_context = context_manager.get_context(from_number)
|
| 3051 |
+
available_categories = user_context.get('available_categories', [])
|
| 3052 |
+
|
| 3053 |
+
logger.info(f"[Category] Handling selection '{selection}' for {from_number}")
|
| 3054 |
+
logger.info(f"[Category] Available categories: {len(available_categories)}")
|
| 3055 |
+
|
| 3056 |
+
if not available_categories:
|
| 3057 |
+
logger.warning("[Category] No available categories")
|
| 3058 |
+
send_whatsjet_message(from_number, "❌ No categories available. Please type 'main' to return to main menu.")
|
| 3059 |
+
return
|
| 3060 |
+
|
| 3061 |
+
if selection.isdigit() and 1 <= int(selection) <= len(available_categories):
|
| 3062 |
+
selected_category = available_categories[int(selection) - 1]
|
| 3063 |
+
logger.info(f"[Category] Selected category: '{selected_category}'")
|
| 3064 |
+
|
| 3065 |
+
products = get_products_by_category(selected_category)
|
| 3066 |
+
logger.info(f"[Category] Found {len(products)} products in category '{selected_category}'")
|
| 3067 |
+
|
| 3068 |
+
if products:
|
| 3069 |
+
product_message = f"📦 *Products in {selected_category}*\n\n"
|
| 3070 |
+
for i, product in enumerate(products[:10], 1): # Show first 10 products
|
| 3071 |
+
product_message += f"{format_number_with_emoji(i)} {product.get('Product Name', 'Unknown')}\n"
|
| 3072 |
+
|
| 3073 |
+
if len(products) > 10:
|
| 3074 |
+
product_message += f"\n... and {len(products) - 10} more products"
|
| 3075 |
+
|
| 3076 |
+
product_message += "\n\nSelect a product number or type 'main' to return to main menu."
|
| 3077 |
+
|
| 3078 |
+
logger.info(f"[Category] Sending product message for category '{selected_category}'")
|
| 3079 |
+
send_whatsjet_message(from_number, product_message)
|
| 3080 |
+
context_manager.update_context(
|
| 3081 |
+
from_number,
|
| 3082 |
+
current_state='category_products_menu',
|
| 3083 |
+
current_menu='category_products_menu',
|
| 3084 |
+
current_menu_options=[f"Product {i+1}" for i in range(len(products))],
|
| 3085 |
+
available_products=products,
|
| 3086 |
+
current_category=selected_category
|
| 3087 |
+
)
|
| 3088 |
+
logger.info(f"[Category] Updated context to category_products_menu with {len(products)} products")
|
| 3089 |
+
else:
|
| 3090 |
+
logger.warning(f"[Category] No products found in category '{selected_category}'")
|
| 3091 |
+
send_whatsjet_message(from_number, f"❌ No products found in {selected_category}")
|
| 3092 |
+
else:
|
| 3093 |
+
logger.warning(f"[Category] Invalid category selection: '{selection}' (valid range: 1-{len(available_categories)})")
|
| 3094 |
+
send_whatsjet_message(from_number, "❌ Invalid selection. Please choose a valid category number.")
|
| 3095 |
+
|
| 3096 |
+
except Exception as e:
|
| 3097 |
+
logger.error(f"[Category] Error handling category selection: {e}")
|
| 3098 |
+
# Instead of sending a generic error, return to main menu
|
| 3099 |
+
welcome_msg = generate_veterinary_welcome_message(from_number, user_context)
|
| 3100 |
+
send_whatsjet_message(from_number, welcome_msg)
|
| 3101 |
+
context_manager.update_context(
|
| 3102 |
+
from_number,
|
| 3103 |
+
current_state='main_menu',
|
| 3104 |
+
current_menu='main_menu',
|
| 3105 |
+
current_menu_options=list(MENU_CONFIG['main_menu']['option_descriptions'].values())
|
| 3106 |
+
)
|
| 3107 |
+
|
| 3108 |
+
def get_menu_validation_message(current_state: str, user_context: dict) -> str:
|
| 3109 |
+
"""Get specific validation message for the current menu state"""
|
| 3110 |
+
if current_state == 'main_menu':
|
| 3111 |
+
return "❌ Invalid selection for Main Menu. Please choose:\n1️⃣ Search Veterinary Products\n2️⃣ Browse Categories\n3️⃣ Download Catalog\n4️⃣ Chat with Veterinary AI Assistant\n\n💬 Type 'main' to return to main menu."
|
| 3112 |
+
|
| 3113 |
+
elif current_state == 'all_products_menu':
|
| 3114 |
+
available_products = user_context.get('available_products', [])
|
| 3115 |
+
total_products = len(available_products) if available_products else 23
|
| 3116 |
+
return f"❌ Invalid selection for Product List. Please choose a number between 1 and {total_products}.\n\n💬 Type 'main' to return to main menu."
|
| 3117 |
+
|
| 3118 |
+
elif current_state == 'category_selection_menu':
|
| 3119 |
+
available_categories = user_context.get('available_categories', [])
|
| 3120 |
+
total_categories = len(available_categories) if available_categories else 0
|
| 3121 |
+
if total_categories > 0:
|
| 3122 |
+
return f"❌ Invalid selection for Category List. Please choose a number between 1 and {total_categories}.\n\n💬 Type 'main' to return to main menu."
|
| 3123 |
+
else:
|
| 3124 |
+
return "❌ No categories available. Please type 'main' to return to main menu."
|
| 3125 |
+
|
| 3126 |
+
elif current_state == 'category_products_menu':
|
| 3127 |
+
available_products = user_context.get('available_products', [])
|
| 3128 |
+
total_products = len(available_products) if available_products else 0
|
| 3129 |
+
if total_products > 0:
|
| 3130 |
+
return f"❌ Invalid selection for Category Products. Please choose a number between 1 and {total_products}.\n\n💬 Type 'main' to return to main menu."
|
| 3131 |
+
else:
|
| 3132 |
+
return "❌ No products available in this category. Please type 'main' to return to main menu."
|
| 3133 |
+
|
| 3134 |
+
elif current_state == 'product_inquiry':
|
| 3135 |
+
return "❌ Invalid selection for Product Options. Please choose:\n1️⃣ Talk to Veterinary Consultant\n2️⃣ Inquire about Product Availability\n3️⃣ Back to Main Menu"
|
| 3136 |
+
|
| 3137 |
+
elif current_state == 'ai_chat_mode':
|
| 3138 |
+
return "💬 *You're in AI Chat mode. Ask me anything about veterinary care!*\n\nType 'main' to return to main menu."
|
| 3139 |
+
|
| 3140 |
+
else:
|
| 3141 |
+
return "❌ Invalid selection. Please type 'main' to return to main menu."
|
| 3142 |
+
|
| 3143 |
+
def is_valid_menu_selection(selection: str, current_state: str, user_context: dict) -> bool:
|
| 3144 |
+
"""Check if a selection is valid for the current menu state"""
|
| 3145 |
+
# Use the intelligent voice command processor for consistent handling
|
| 3146 |
+
mapped_selection = process_intelligent_voice_command(selection, current_state, user_context)
|
| 3147 |
+
|
| 3148 |
+
if not mapped_selection.isdigit():
|
| 3149 |
+
return False
|
| 3150 |
+
|
| 3151 |
+
selection_num = int(mapped_selection)
|
| 3152 |
+
|
| 3153 |
+
if current_state == 'main_menu':
|
| 3154 |
+
return 1 <= selection_num <= 4
|
| 3155 |
+
|
| 3156 |
+
elif current_state == 'all_products_menu':
|
| 3157 |
+
available_products = user_context.get('available_products', [])
|
| 3158 |
+
total_products = len(available_products) if available_products else 23
|
| 3159 |
+
return 1 <= selection_num <= total_products
|
| 3160 |
+
|
| 3161 |
+
elif current_state == 'category_selection_menu':
|
| 3162 |
+
available_categories = user_context.get('available_categories', [])
|
| 3163 |
+
total_categories = len(available_categories) if available_categories else 0
|
| 3164 |
+
return 1 <= selection_num <= total_categories
|
| 3165 |
+
|
| 3166 |
+
elif current_state == 'category_products_menu':
|
| 3167 |
+
available_products = user_context.get('available_products', [])
|
| 3168 |
+
total_products = len(available_products) if available_products else 0
|
| 3169 |
+
return 1 <= selection_num <= total_products
|
| 3170 |
+
|
| 3171 |
+
elif current_state == 'product_inquiry':
|
| 3172 |
+
return 1 <= selection_num <= 3
|
| 3173 |
+
|
| 3174 |
+
elif current_state == 'ai_chat_mode':
|
| 3175 |
+
return mapped_selection == 'main'
|
| 3176 |
+
|
| 3177 |
+
return False
|
| 3178 |
+
|
| 3179 |
+
# Load products on startup
|
| 3180 |
+
load_products_data()
|
| 3181 |
+
|
| 3182 |
if __name__ == "__main__":
|
| 3183 |
# Launch FastAPI app
|
| 3184 |
import uvicorn
|