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
|