Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -147,11 +147,13 @@ MENU_CONFIG = {
|
|
| 147 |
},
|
| 148 |
'product_inquiry': {
|
| 149 |
'name': 'Product Inquiry Menu',
|
| 150 |
-
'valid_options': ['1', '2', '3'],
|
| 151 |
'option_descriptions': {
|
| 152 |
'1': 'Talk to Veterinary Consultant',
|
| 153 |
'2': 'Inquire about Product Availability',
|
| 154 |
-
'3': '
|
|
|
|
|
|
|
| 155 |
}
|
| 156 |
},
|
| 157 |
'ai_chat': {
|
|
@@ -2367,7 +2369,8 @@ async def handle_contact_request_response(from_number: str, response: str):
|
|
| 2367 |
)
|
| 2368 |
except Exception as e:
|
| 2369 |
logger.error(f"[Contact] Error handling contact response: {e}")
|
| 2370 |
-
#
|
|
|
|
| 2371 |
welcome_msg = generate_veterinary_welcome_message(from_number, user_context)
|
| 2372 |
send_whatsjet_message(from_number, welcome_msg)
|
| 2373 |
context_manager.update_context(
|
|
@@ -2471,7 +2474,8 @@ async def handle_availability_request_response(from_number: str, response: str):
|
|
| 2471 |
)
|
| 2472 |
except Exception as e:
|
| 2473 |
logger.error(f"[Availability] Error handling availability response: {e}")
|
| 2474 |
-
#
|
|
|
|
| 2475 |
welcome_msg = generate_veterinary_welcome_message(from_number, user_context)
|
| 2476 |
send_whatsjet_message(from_number, welcome_msg)
|
| 2477 |
context_manager.update_context(
|
|
@@ -2535,6 +2539,8 @@ def send_helpful_guidance(from_number: str, current_state: str):
|
|
| 2535 |
)
|
| 2536 |
except Exception as e:
|
| 2537 |
logger.error(f"Error sending helpful guidance: {e}")
|
|
|
|
|
|
|
| 2538 |
welcome_msg = generate_veterinary_welcome_message(from_number, user_context)
|
| 2539 |
send_whatsjet_message(from_number, welcome_msg)
|
| 2540 |
context_manager.update_context(
|
|
@@ -2847,7 +2853,6 @@ def generate_veterinary_welcome_message(phone_number=None, user_context=None):
|
|
| 2847 |
|
| 2848 |
@app.get("/test-whatsjet")
|
| 2849 |
async def test_whatsjet():
|
| 2850 |
-
import requests
|
| 2851 |
try:
|
| 2852 |
resp = requests.get("https://api.whatsjet.com", timeout=5)
|
| 2853 |
return {"status": resp.status_code, "text": resp.text[:200]}
|
|
@@ -2948,10 +2953,13 @@ async def handle_veterinary_product_followup(selection: str, from_number: str) -
|
|
| 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 |
-
|
| 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"
|
|
@@ -2962,10 +2970,44 @@ async def handle_veterinary_product_followup(selection: str, from_number: str) -
|
|
| 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)
|
|
@@ -2977,10 +3019,11 @@ async def handle_veterinary_product_followup(selection: str, from_number: str) -
|
|
| 2977 |
)
|
| 2978 |
return
|
| 2979 |
else:
|
| 2980 |
-
send_whatsjet_message(from_number, "β Invalid selection. Please choose 1, 2, or
|
| 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(
|
|
@@ -2993,7 +3036,7 @@ async def handle_veterinary_product_followup(selection: str, from_number: str) -
|
|
| 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
|
|
@@ -3008,13 +3051,18 @@ async def handle_veterinary_product_inquiry(product_name: str, from_number: str)
|
|
| 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 |
-
|
| 3014 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3015 |
|
| 3016 |
# Add to conversation history
|
| 3017 |
-
context_manager.add_to_history(from_number, product_name,
|
| 3018 |
|
| 3019 |
else:
|
| 3020 |
# Enhanced "not found" response with veterinary suggestions
|
|
@@ -3037,6 +3085,190 @@ async def handle_veterinary_product_inquiry(product_name: str, from_number: str)
|
|
| 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
|
|
@@ -3095,7 +3327,8 @@ async def handle_category_selection(selection: str, from_number: str):
|
|
| 3095 |
|
| 3096 |
except Exception as e:
|
| 3097 |
logger.error(f"[Category] Error handling category selection: {e}")
|
| 3098 |
-
#
|
|
|
|
| 3099 |
welcome_msg = generate_veterinary_welcome_message(from_number, user_context)
|
| 3100 |
send_whatsjet_message(from_number, welcome_msg)
|
| 3101 |
context_manager.update_context(
|
|
@@ -3132,7 +3365,7 @@ def get_menu_validation_message(current_state: str, user_context: dict) -> str:
|
|
| 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."
|
|
@@ -3169,7 +3402,7 @@ def is_valid_menu_selection(selection: str, current_state: str, user_context: di
|
|
| 3169 |
return 1 <= selection_num <= total_products
|
| 3170 |
|
| 3171 |
elif current_state == 'product_inquiry':
|
| 3172 |
-
return 1 <= selection_num <=
|
| 3173 |
|
| 3174 |
elif current_state == 'ai_chat_mode':
|
| 3175 |
return mapped_selection == 'main'
|
|
@@ -3179,6 +3412,225 @@ def is_valid_menu_selection(selection: str, current_state: str, user_context: di
|
|
| 3179 |
# Load products on startup
|
| 3180 |
load_products_data()
|
| 3181 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3182 |
if __name__ == "__main__":
|
| 3183 |
# Launch FastAPI app
|
| 3184 |
import uvicorn
|
|
|
|
| 147 |
},
|
| 148 |
'product_inquiry': {
|
| 149 |
'name': 'Product Inquiry Menu',
|
| 150 |
+
'valid_options': ['1', '2', '3', '4', '5'],
|
| 151 |
'option_descriptions': {
|
| 152 |
'1': 'Talk to Veterinary Consultant',
|
| 153 |
'2': 'Inquire about Product Availability',
|
| 154 |
+
'3': 'View Product Image',
|
| 155 |
+
'4': 'Download Detailed PDF',
|
| 156 |
+
'5': 'Back to Main Menu'
|
| 157 |
}
|
| 158 |
},
|
| 159 |
'ai_chat': {
|
|
|
|
| 2369 |
)
|
| 2370 |
except Exception as e:
|
| 2371 |
logger.error(f"[Contact] Error handling contact response: {e}")
|
| 2372 |
+
# Get user context before using it
|
| 2373 |
+
user_context = context_manager.get_context(from_number)
|
| 2374 |
welcome_msg = generate_veterinary_welcome_message(from_number, user_context)
|
| 2375 |
send_whatsjet_message(from_number, welcome_msg)
|
| 2376 |
context_manager.update_context(
|
|
|
|
| 2474 |
)
|
| 2475 |
except Exception as e:
|
| 2476 |
logger.error(f"[Availability] Error handling availability response: {e}")
|
| 2477 |
+
# Get user context before using it
|
| 2478 |
+
user_context = context_manager.get_context(from_number)
|
| 2479 |
welcome_msg = generate_veterinary_welcome_message(from_number, user_context)
|
| 2480 |
send_whatsjet_message(from_number, welcome_msg)
|
| 2481 |
context_manager.update_context(
|
|
|
|
| 2539 |
)
|
| 2540 |
except Exception as e:
|
| 2541 |
logger.error(f"Error sending helpful guidance: {e}")
|
| 2542 |
+
# Get user context before using it
|
| 2543 |
+
user_context = context_manager.get_context(from_number)
|
| 2544 |
welcome_msg = generate_veterinary_welcome_message(from_number, user_context)
|
| 2545 |
send_whatsjet_message(from_number, welcome_msg)
|
| 2546 |
context_manager.update_context(
|
|
|
|
| 2853 |
|
| 2854 |
@app.get("/test-whatsjet")
|
| 2855 |
async def test_whatsjet():
|
|
|
|
| 2856 |
try:
|
| 2857 |
resp = requests.get("https://api.whatsjet.com", timeout=5)
|
| 2858 |
return {"status": resp.status_code, "text": resp.text[:200]}
|
|
|
|
| 2953 |
user_context = context_manager.get_context(from_number)
|
| 2954 |
current_product = user_context.get('current_product')
|
| 2955 |
|
| 2956 |
+
if not current_product:
|
| 2957 |
+
send_whatsjet_message(from_number, "β No product selected. Please search for a product first.")
|
| 2958 |
+
return
|
| 2959 |
+
|
| 2960 |
if selection == '1':
|
| 2961 |
# Talk to Veterinary Consultant
|
| 2962 |
+
product_name = current_product.get('Product Name', 'the selected product')
|
|
|
|
| 2963 |
consultant_msg = (
|
| 2964 |
f"π Contact Veterinary Consultant\n\n"
|
| 2965 |
f"Product: {product_name}\n\n"
|
|
|
|
| 2970 |
"Type main at any time to go to main menu."
|
| 2971 |
)
|
| 2972 |
send_whatsjet_message(from_number, consultant_msg)
|
| 2973 |
+
context_manager.update_context(
|
| 2974 |
+
from_number,
|
| 2975 |
+
current_state='contact_request',
|
| 2976 |
+
current_menu='contact_request',
|
| 2977 |
+
current_menu_options=['Provide contact details']
|
| 2978 |
+
)
|
| 2979 |
+
|
| 2980 |
elif selection == '2':
|
| 2981 |
# Inquire about Product Availability
|
| 2982 |
await handle_availability_inquiry(from_number, user_context)
|
| 2983 |
+
|
| 2984 |
elif selection == '3':
|
| 2985 |
+
# View Product Image
|
| 2986 |
+
await send_product_with_image(from_number, current_product, user_context)
|
| 2987 |
+
# Return to product inquiry menu
|
| 2988 |
+
response_with_media = generate_veterinary_product_response_with_media(current_product, user_context)
|
| 2989 |
+
send_whatsjet_message(from_number, response_with_media['text'])
|
| 2990 |
+
context_manager.update_context(
|
| 2991 |
+
from_number,
|
| 2992 |
+
current_state='product_inquiry',
|
| 2993 |
+
current_menu='product_inquiry',
|
| 2994 |
+
current_menu_options=list(MENU_CONFIG['product_inquiry']['option_descriptions'].values())
|
| 2995 |
+
)
|
| 2996 |
+
|
| 2997 |
+
elif selection == '4':
|
| 2998 |
+
# Download Detailed PDF
|
| 2999 |
+
await send_enhanced_pdf(from_number, current_product)
|
| 3000 |
+
# Return to product inquiry menu
|
| 3001 |
+
response_with_media = generate_veterinary_product_response_with_media(current_product, user_context)
|
| 3002 |
+
send_whatsjet_message(from_number, response_with_media['text'])
|
| 3003 |
+
context_manager.update_context(
|
| 3004 |
+
from_number,
|
| 3005 |
+
current_state='product_inquiry',
|
| 3006 |
+
current_menu='product_inquiry',
|
| 3007 |
+
current_menu_options=list(MENU_CONFIG['product_inquiry']['option_descriptions'].values())
|
| 3008 |
+
)
|
| 3009 |
+
|
| 3010 |
+
elif selection == '5':
|
| 3011 |
# Back to Main Menu
|
| 3012 |
welcome_msg = generate_veterinary_welcome_message(from_number, user_context)
|
| 3013 |
send_whatsjet_message(from_number, welcome_msg)
|
|
|
|
| 3019 |
)
|
| 3020 |
return
|
| 3021 |
else:
|
| 3022 |
+
send_whatsjet_message(from_number, "β Invalid selection. Please choose 1, 2, 3, 4, or 5.")
|
| 3023 |
return
|
| 3024 |
except Exception as e:
|
| 3025 |
logger.error(f"Error in product follow-up: {e}")
|
| 3026 |
+
user_context = context_manager.get_context(from_number)
|
| 3027 |
welcome_msg = generate_veterinary_welcome_message(from_number, user_context)
|
| 3028 |
send_whatsjet_message(from_number, welcome_msg)
|
| 3029 |
context_manager.update_context(
|
|
|
|
| 3036 |
# Enhanced product inquiry handling
|
| 3037 |
async def handle_veterinary_product_inquiry(product_name: str, from_number: str) -> None:
|
| 3038 |
"""
|
| 3039 |
+
Handle product inquiry with enhanced veterinary domain support and media
|
| 3040 |
"""
|
| 3041 |
try:
|
| 3042 |
# Search for the product
|
|
|
|
| 3051 |
current_menu='product_inquiry',
|
| 3052 |
current_menu_options=list(MENU_CONFIG['product_inquiry']['option_descriptions'].values())
|
| 3053 |
)
|
| 3054 |
+
|
| 3055 |
# Get updated context with last message
|
| 3056 |
context = context_manager.get_context(from_number)
|
| 3057 |
+
|
| 3058 |
+
# Use enhanced response with media support
|
| 3059 |
+
response_with_media = generate_veterinary_product_response_with_media(selected_product, context)
|
| 3060 |
+
|
| 3061 |
+
# Send the response
|
| 3062 |
+
send_whatsjet_message(from_number, response_with_media['text'])
|
| 3063 |
|
| 3064 |
# Add to conversation history
|
| 3065 |
+
context_manager.add_to_history(from_number, product_name, response_with_media['text'])
|
| 3066 |
|
| 3067 |
else:
|
| 3068 |
# Enhanced "not found" response with veterinary suggestions
|
|
|
|
| 3085 |
|
| 3086 |
send_whatsjet_message(from_number, message)
|
| 3087 |
|
| 3088 |
+
except Exception as e:
|
| 3089 |
+
logger.error(f"Error in product inquiry: {e}")
|
| 3090 |
+
# Get user context before using it
|
| 3091 |
+
user_context = context_manager.get_context(from_number)
|
| 3092 |
+
welcome_msg = generate_veterinary_welcome_message(from_number, user_context)
|
| 3093 |
+
send_whatsjet_message(from_number, welcome_msg)
|
| 3094 |
+
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()))
|
| 3095 |
+
|
| 3096 |
+
async def handle_intelligent_product_inquiry(from_number: str, query: str, user_context: dict, reply_language: str = 'en'):
|
| 3097 |
+
"""Handle product inquiry with OpenAI intelligence and media support"""
|
| 3098 |
+
try:
|
| 3099 |
+
# First try direct product search
|
| 3100 |
+
products = get_veterinary_product_matches(query)
|
| 3101 |
+
|
| 3102 |
+
if products:
|
| 3103 |
+
# Check if this is a broad/category query (multiple products found)
|
| 3104 |
+
if len(products) > 1:
|
| 3105 |
+
# Use OpenAI to generate a professional summary and list all products
|
| 3106 |
+
if OPENAI_API_KEY:
|
| 3107 |
+
try:
|
| 3108 |
+
# Create a comprehensive prompt for multiple products
|
| 3109 |
+
products_info = []
|
| 3110 |
+
for i, product in enumerate(products, 1):
|
| 3111 |
+
product_name = product.get('Product Name', 'N/A')
|
| 3112 |
+
category = product.get('Category', 'N/A')
|
| 3113 |
+
target_species = product.get('Target Species', 'N/A')
|
| 3114 |
+
products_info.append(f"{i}. {product_name} - {category} ({target_species})")
|
| 3115 |
+
|
| 3116 |
+
products_text = "\n".join(products_info)
|
| 3117 |
+
|
| 3118 |
+
prompt = f"""
|
| 3119 |
+
You are a professional veterinary product assistant for Apex Biotical. The user asked about "{query}" and we found {len(products)} relevant products.
|
| 3120 |
+
|
| 3121 |
+
Available Products:
|
| 3122 |
+
{products_text}
|
| 3123 |
+
|
| 3124 |
+
Please provide:
|
| 3125 |
+
1. A professional, welcoming response acknowledging their query
|
| 3126 |
+
2. A brief summary of what these products are for (if it's a category like "poultry products", explain the category)
|
| 3127 |
+
3. List all products with their numbers and brief descriptions
|
| 3128 |
+
4. Clear instructions on how to proceed
|
| 3129 |
+
|
| 3130 |
+
Format your response professionally with emojis and clear structure. Keep it concise but informative.
|
| 3131 |
+
"""
|
| 3132 |
+
|
| 3133 |
+
response = openai.ChatCompletion.create(
|
| 3134 |
+
model="gpt-4o",
|
| 3135 |
+
messages=[{"role": "user", "content": prompt}],
|
| 3136 |
+
temperature=0.7,
|
| 3137 |
+
max_tokens=400
|
| 3138 |
+
)
|
| 3139 |
+
|
| 3140 |
+
ai_response = response.choices[0].message['content'].strip()
|
| 3141 |
+
|
| 3142 |
+
# Add instructions for selection
|
| 3143 |
+
selection_instructions = (
|
| 3144 |
+
f"\n\n㪠*To view detailed information about any product, reply with its number (1-{len(products)})*\n"
|
| 3145 |
+
"π¬ *Type 'main' to return to the main menu*"
|
| 3146 |
+
)
|
| 3147 |
+
|
| 3148 |
+
full_response = ai_response + selection_instructions
|
| 3149 |
+
|
| 3150 |
+
# Translate response if needed
|
| 3151 |
+
if reply_language == 'ur':
|
| 3152 |
+
try:
|
| 3153 |
+
translated_response = GoogleTranslator(source='auto', target='ur').translate(full_response)
|
| 3154 |
+
send_whatsjet_message(from_number, translated_response)
|
| 3155 |
+
except Exception as e:
|
| 3156 |
+
logger.error(f"[AI] Translation error: {e}")
|
| 3157 |
+
send_whatsjet_message(from_number, full_response)
|
| 3158 |
+
else:
|
| 3159 |
+
send_whatsjet_message(from_number, full_response)
|
| 3160 |
+
|
| 3161 |
+
# Store the product list in context for selection handling
|
| 3162 |
+
context_manager.update_context(
|
| 3163 |
+
from_number,
|
| 3164 |
+
current_state='intelligent_products_menu',
|
| 3165 |
+
current_menu='intelligent_products_menu',
|
| 3166 |
+
current_menu_options=[f"Product {i+1}" for i in range(len(products))],
|
| 3167 |
+
available_products=products,
|
| 3168 |
+
last_query=query
|
| 3169 |
+
)
|
| 3170 |
+
|
| 3171 |
+
# Add to conversation history
|
| 3172 |
+
context_manager.add_to_history(from_number, query, full_response)
|
| 3173 |
+
return
|
| 3174 |
+
|
| 3175 |
+
except Exception as e:
|
| 3176 |
+
logger.error(f"[AI] Error generating product summary: {e}")
|
| 3177 |
+
# Fall back to simple listing if AI fails
|
| 3178 |
+
pass
|
| 3179 |
+
|
| 3180 |
+
# Fallback: Simple listing without AI
|
| 3181 |
+
message = f"π *Found {len(products)} products matching '{query}':*\n\n"
|
| 3182 |
+
|
| 3183 |
+
for i, product in enumerate(products, 1):
|
| 3184 |
+
product_name = product.get('Product Name', 'N/A')
|
| 3185 |
+
category = product.get('Category', 'N/A')
|
| 3186 |
+
target_species = product.get('Target Species', 'N/A')
|
| 3187 |
+
message += f"{format_number_with_emoji(i)} {product_name}\n"
|
| 3188 |
+
message += f" π¦ {category} ({target_species})\n\n"
|
| 3189 |
+
|
| 3190 |
+
message += (
|
| 3191 |
+
f"π¬ *To view detailed information about any product, reply with its number (1-{len(products)})*\n"
|
| 3192 |
+
"π¬ *Type 'main' to return to the main menu*"
|
| 3193 |
+
)
|
| 3194 |
+
|
| 3195 |
+
# Translate response if needed
|
| 3196 |
+
if reply_language == 'ur':
|
| 3197 |
+
try:
|
| 3198 |
+
translated_message = GoogleTranslator(source='auto', target='ur').translate(message)
|
| 3199 |
+
send_whatsjet_message(from_number, translated_message)
|
| 3200 |
+
except Exception as e:
|
| 3201 |
+
logger.error(f"[AI] Translation error: {e}")
|
| 3202 |
+
send_whatsjet_message(from_number, message)
|
| 3203 |
+
else:
|
| 3204 |
+
send_whatsjet_message(from_number, message)
|
| 3205 |
+
|
| 3206 |
+
# Store the product list in context for selection handling
|
| 3207 |
+
context_manager.update_context(
|
| 3208 |
+
from_number,
|
| 3209 |
+
current_state='intelligent_products_menu',
|
| 3210 |
+
current_menu='intelligent_products_menu',
|
| 3211 |
+
current_menu_options=[f"Product {i+1}" for i in range(len(products))],
|
| 3212 |
+
available_products=products,
|
| 3213 |
+
last_query=query
|
| 3214 |
+
)
|
| 3215 |
+
|
| 3216 |
+
# Add to conversation history
|
| 3217 |
+
context_manager.add_to_history(from_number, query, message)
|
| 3218 |
+
|
| 3219 |
+
else:
|
| 3220 |
+
# Single product found - show detailed information with media support
|
| 3221 |
+
selected_product = products[0]
|
| 3222 |
+
context_manager.update_context(
|
| 3223 |
+
from_number,
|
| 3224 |
+
current_product=selected_product,
|
| 3225 |
+
current_state='product_inquiry',
|
| 3226 |
+
current_menu='product_inquiry',
|
| 3227 |
+
current_menu_options=list(MENU_CONFIG['product_inquiry']['option_descriptions'].values())
|
| 3228 |
+
)
|
| 3229 |
+
|
| 3230 |
+
# Get updated context with last message
|
| 3231 |
+
context = context_manager.get_context(from_number)
|
| 3232 |
+
|
| 3233 |
+
# Use enhanced response with media support
|
| 3234 |
+
response_with_media = generate_veterinary_product_response_with_media(selected_product, context)
|
| 3235 |
+
|
| 3236 |
+
# Send the response
|
| 3237 |
+
send_whatsjet_message(from_number, response_with_media['text'])
|
| 3238 |
+
|
| 3239 |
+
# Add to conversation history
|
| 3240 |
+
context_manager.add_to_history(from_number, query, response_with_media['text'])
|
| 3241 |
+
|
| 3242 |
+
else:
|
| 3243 |
+
# Enhanced "not found" response with veterinary suggestions
|
| 3244 |
+
message = (
|
| 3245 |
+
"β *Product Not Found*\n\n"
|
| 3246 |
+
f"π *We couldn't find '{query}' in our veterinary database.*\n\n"
|
| 3247 |
+
"π‘ *Try these alternatives:*\n"
|
| 3248 |
+
"β’ Check spelling (e.g., 'Hydropex' not 'Hydro pex')\n"
|
| 3249 |
+
"β’ Search by symptoms (e.g., 'respiratory', 'liver support')\n"
|
| 3250 |
+
"β’ Search by category (e.g., 'antibiotic', 'vitamin')\n"
|
| 3251 |
+
"β’ Search by species (e.g., 'poultry', 'livestock')\n\n"
|
| 3252 |
+
"π₯ *Popular Veterinary Products:*\n"
|
| 3253 |
+
"β’ Hydropex (Electrolyte supplement)\n"
|
| 3254 |
+
"β’ Heposel (Liver tonic)\n"
|
| 3255 |
+
"β’ Bromacid (Respiratory support)\n"
|
| 3256 |
+
"β’ Tribiotic (Antibiotic)\n"
|
| 3257 |
+
"β’ Symodex (Multivitamin)\n\n"
|
| 3258 |
+
"π¬ *Type 'main' to return to main menu or try another search.*"
|
| 3259 |
+
)
|
| 3260 |
+
|
| 3261 |
+
# Translate response if needed
|
| 3262 |
+
if reply_language == 'ur':
|
| 3263 |
+
try:
|
| 3264 |
+
translated_message = GoogleTranslator(source='auto', target='ur').translate(message)
|
| 3265 |
+
send_whatsjet_message(from_number, translated_message)
|
| 3266 |
+
except Exception as e:
|
| 3267 |
+
logger.error(f"[AI] Translation error: {e}")
|
| 3268 |
+
send_whatsjet_message(from_number, message)
|
| 3269 |
+
else:
|
| 3270 |
+
send_whatsjet_message(from_number, message)
|
| 3271 |
+
|
| 3272 |
except Exception as e:
|
| 3273 |
logger.error(f"Error in product inquiry: {e}")
|
| 3274 |
# Instead of sending a generic error, return to main menu
|
|
|
|
| 3327 |
|
| 3328 |
except Exception as e:
|
| 3329 |
logger.error(f"[Category] Error handling category selection: {e}")
|
| 3330 |
+
# Get user context before using it
|
| 3331 |
+
user_context = context_manager.get_context(from_number)
|
| 3332 |
welcome_msg = generate_veterinary_welcome_message(from_number, user_context)
|
| 3333 |
send_whatsjet_message(from_number, welcome_msg)
|
| 3334 |
context_manager.update_context(
|
|
|
|
| 3365 |
return "β No products available in this category. Please type 'main' to return to main menu."
|
| 3366 |
|
| 3367 |
elif current_state == 'product_inquiry':
|
| 3368 |
+
return "β Invalid selection for Product Options. Please choose:\n1οΈβ£ Talk to Veterinary Consultant\n2οΈβ£ Inquire about Product Availability\n3οΈβ£ View Product Image\n4οΈβ£ Download Detailed PDF\n5οΈβ£ Back to Main Menu"
|
| 3369 |
|
| 3370 |
elif current_state == 'ai_chat_mode':
|
| 3371 |
return "π¬ *You're in AI Chat mode. Ask me anything about veterinary care!*\n\nType 'main' to return to main menu."
|
|
|
|
| 3402 |
return 1 <= selection_num <= total_products
|
| 3403 |
|
| 3404 |
elif current_state == 'product_inquiry':
|
| 3405 |
+
return 1 <= selection_num <= 5
|
| 3406 |
|
| 3407 |
elif current_state == 'ai_chat_mode':
|
| 3408 |
return mapped_selection == 'main'
|
|
|
|
| 3412 |
# Load products on startup
|
| 3413 |
load_products_data()
|
| 3414 |
|
| 3415 |
+
# Add these functions after the existing imports and before the main functions
|
| 3416 |
+
|
| 3417 |
+
def get_product_image_path(product_name: str) -> str:
|
| 3418 |
+
"""
|
| 3419 |
+
Get the image path for a product
|
| 3420 |
+
Returns the path to the product image if it exists, otherwise None
|
| 3421 |
+
"""
|
| 3422 |
+
try:
|
| 3423 |
+
# Create images directory if it doesn't exist
|
| 3424 |
+
images_dir = "static/images"
|
| 3425 |
+
os.makedirs(images_dir, exist_ok=True)
|
| 3426 |
+
|
| 3427 |
+
# Clean product name for filename
|
| 3428 |
+
safe_name = re.sub(r'[^\w\s-]', '', product_name).replace(' ', '_').lower()
|
| 3429 |
+
|
| 3430 |
+
# Check for common image extensions
|
| 3431 |
+
image_extensions = ['.jpg', '.jpeg', '.png', '.webp', '.gif']
|
| 3432 |
+
|
| 3433 |
+
for ext in image_extensions:
|
| 3434 |
+
image_path = os.path.join(images_dir, f"{safe_name}{ext}")
|
| 3435 |
+
if os.path.exists(image_path):
|
| 3436 |
+
logger.info(f"[Image] Found product image: {image_path}")
|
| 3437 |
+
return image_path
|
| 3438 |
+
|
| 3439 |
+
# If no specific product image found, check for a default image
|
| 3440 |
+
default_image_path = os.path.join(images_dir, "default_product.jpg")
|
| 3441 |
+
if os.path.exists(default_image_path):
|
| 3442 |
+
logger.info(f"[Image] Using default product image: {default_image_path}")
|
| 3443 |
+
return default_image_path
|
| 3444 |
+
|
| 3445 |
+
logger.warning(f"[Image] No image found for product: {product_name}")
|
| 3446 |
+
return None
|
| 3447 |
+
|
| 3448 |
+
except Exception as e:
|
| 3449 |
+
logger.error(f"[Image] Error getting product image path: {e}")
|
| 3450 |
+
return None
|
| 3451 |
+
|
| 3452 |
+
def get_product_image_media_type(image_path: str) -> str:
|
| 3453 |
+
"""
|
| 3454 |
+
Determine the media type based on file extension
|
| 3455 |
+
"""
|
| 3456 |
+
if not image_path:
|
| 3457 |
+
return None
|
| 3458 |
+
|
| 3459 |
+
ext = os.path.splitext(image_path)[1].lower()
|
| 3460 |
+
|
| 3461 |
+
media_type_map = {
|
| 3462 |
+
'.jpg': 'image/jpeg',
|
| 3463 |
+
'.jpeg': 'image/jpeg',
|
| 3464 |
+
'.png': 'image/png',
|
| 3465 |
+
'.webp': 'image/webp',
|
| 3466 |
+
'.gif': 'image/gif'
|
| 3467 |
+
}
|
| 3468 |
+
|
| 3469 |
+
return media_type_map.get(ext, 'image/jpeg')
|
| 3470 |
+
|
| 3471 |
+
async def send_product_with_image(from_number: str, product: Dict[str, Any], user_context: Dict[str, Any]):
|
| 3472 |
+
"""
|
| 3473 |
+
Send product information with image if available
|
| 3474 |
+
"""
|
| 3475 |
+
try:
|
| 3476 |
+
product_name = product.get('Product Name', 'Unknown Product')
|
| 3477 |
+
|
| 3478 |
+
# Generate product response
|
| 3479 |
+
response = generate_veterinary_product_response(product, user_context)
|
| 3480 |
+
|
| 3481 |
+
# Try to get product image
|
| 3482 |
+
image_path = get_product_image_path(product_name)
|
| 3483 |
+
|
| 3484 |
+
if image_path and os.path.exists(image_path):
|
| 3485 |
+
# Send product info with image
|
| 3486 |
+
media_type = get_product_image_media_type(image_path)
|
| 3487 |
+
filename = f"{product_name.replace(' ', '_')}.jpg"
|
| 3488 |
+
|
| 3489 |
+
success = send_whatsjet_message(
|
| 3490 |
+
from_number,
|
| 3491 |
+
response,
|
| 3492 |
+
media_type=media_type,
|
| 3493 |
+
media_path=image_path,
|
| 3494 |
+
filename=filename
|
| 3495 |
+
)
|
| 3496 |
+
|
| 3497 |
+
if success:
|
| 3498 |
+
logger.info(f"[Product] Successfully sent product with image: {product_name}")
|
| 3499 |
+
else:
|
| 3500 |
+
# Fallback to text-only if image send fails
|
| 3501 |
+
logger.warning(f"[Product] Failed to send image, sending text only: {product_name}")
|
| 3502 |
+
send_whatsjet_message(from_number, response)
|
| 3503 |
+
else:
|
| 3504 |
+
# Send text-only response
|
| 3505 |
+
send_whatsjet_message(from_number, response)
|
| 3506 |
+
logger.info(f"[Product] Sent product info without image: {product_name}")
|
| 3507 |
+
|
| 3508 |
+
except Exception as e:
|
| 3509 |
+
logger.error(f"[Product] Error sending product with image: {e}")
|
| 3510 |
+
# Fallback to text-only
|
| 3511 |
+
response = generate_veterinary_product_response(product, user_context)
|
| 3512 |
+
send_whatsjet_message(from_number, response)
|
| 3513 |
+
|
| 3514 |
+
async def send_enhanced_pdf(from_number: str, product: Dict[str, Any], pdf_content: bytes = None):
|
| 3515 |
+
"""
|
| 3516 |
+
Send PDF with enhanced formatting and proper WhatsApp document sharing
|
| 3517 |
+
"""
|
| 3518 |
+
try:
|
| 3519 |
+
product_name = product.get('Product Name', 'Unknown_Product')
|
| 3520 |
+
safe_name = re.sub(r'[^\w\s-]', '', product_name).replace(' ', '_')
|
| 3521 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 3522 |
+
filename = f"{safe_name}_Product_Info_{timestamp}.pdf"
|
| 3523 |
+
|
| 3524 |
+
# Generate PDF if not provided
|
| 3525 |
+
if pdf_content is None:
|
| 3526 |
+
pdf_content = generate_veterinary_pdf(product)
|
| 3527 |
+
|
| 3528 |
+
# Save PDF to uploads directory
|
| 3529 |
+
uploads_dir = "uploads"
|
| 3530 |
+
os.makedirs(uploads_dir, exist_ok=True)
|
| 3531 |
+
pdf_path = os.path.join(uploads_dir, filename)
|
| 3532 |
+
|
| 3533 |
+
with open(pdf_path, 'wb') as f:
|
| 3534 |
+
f.write(pdf_content)
|
| 3535 |
+
|
| 3536 |
+
# Send PDF as document via WhatsApp
|
| 3537 |
+
success = send_whatsjet_message(
|
| 3538 |
+
from_number,
|
| 3539 |
+
f"π *{product_name} - Detailed Product Information*\n\n"
|
| 3540 |
+
f"π Here's the complete product information in PDF format.\n"
|
| 3541 |
+
f"π Includes: Composition, Dosage, Precautions, Storage\n\n"
|
| 3542 |
+
f"π¬ Type 'main' to return to main menu.",
|
| 3543 |
+
media_type="application/pdf",
|
| 3544 |
+
media_path=pdf_path,
|
| 3545 |
+
filename=filename
|
| 3546 |
+
)
|
| 3547 |
+
|
| 3548 |
+
if success:
|
| 3549 |
+
logger.info(f"[PDF] Successfully sent PDF for product: {product_name}")
|
| 3550 |
+
else:
|
| 3551 |
+
# Fallback: Send download link
|
| 3552 |
+
server_url = os.getenv("SERVER_URL", "https://your-huggingface-space-url.hf.space")
|
| 3553 |
+
download_url = f"{server_url}/uploads/{filename}"
|
| 3554 |
+
|
| 3555 |
+
message = (
|
| 3556 |
+
f"π *{product_name} - Product Information*\n\n"
|
| 3557 |
+
f"π [Download Product PDF]({download_url})\n\n"
|
| 3558 |
+
f"π¬ *Click the link above to download the detailed product information*\n"
|
| 3559 |
+
f"Type 'main' to return to main menu."
|
| 3560 |
+
)
|
| 3561 |
+
send_whatsjet_message(from_number, message)
|
| 3562 |
+
logger.info(f"[PDF] Sent PDF download link for product: {product_name}")
|
| 3563 |
+
|
| 3564 |
+
except Exception as e:
|
| 3565 |
+
logger.error(f"[PDF] Error sending enhanced PDF: {e}")
|
| 3566 |
+
# Fallback to basic text response
|
| 3567 |
+
response = generate_veterinary_product_response(product, {})
|
| 3568 |
+
send_whatsjet_message(from_number, response)
|
| 3569 |
+
|
| 3570 |
+
# Enhanced product response function with image support
|
| 3571 |
+
def generate_veterinary_product_response_with_media(product_info: Dict[str, Any], user_context: Dict[str, Any]) -> Dict[str, Any]:
|
| 3572 |
+
"""
|
| 3573 |
+
Generate comprehensive veterinary product response with media information
|
| 3574 |
+
Returns a dictionary with text response and media info
|
| 3575 |
+
"""
|
| 3576 |
+
|
| 3577 |
+
def clean_text(text):
|
| 3578 |
+
if pd.isna(text) or text is None:
|
| 3579 |
+
return "Not specified"
|
| 3580 |
+
return str(text).strip()
|
| 3581 |
+
|
| 3582 |
+
# Extract product details
|
| 3583 |
+
product_name = clean_text(product_info.get('Product Name', ''))
|
| 3584 |
+
product_type = clean_text(product_info.get('Type', ''))
|
| 3585 |
+
category = clean_text(product_info.get('Category', ''))
|
| 3586 |
+
indications = clean_text(product_info.get('Indications', ''))
|
| 3587 |
+
|
| 3588 |
+
# Check for PDF link in the CSV data
|
| 3589 |
+
pdf_link = ""
|
| 3590 |
+
try:
|
| 3591 |
+
# Load CSV data to check for PDF link
|
| 3592 |
+
csv_data = pd.read_csv('Veterinary.csv')
|
| 3593 |
+
product_row = csv_data[csv_data['Product Name'] == product_name]
|
| 3594 |
+
if not product_row.empty:
|
| 3595 |
+
brochure_link = product_row.iloc[0].get('Brochure (PDF)', '')
|
| 3596 |
+
if pd.notna(brochure_link) and brochure_link.strip():
|
| 3597 |
+
pdf_link = brochure_link.strip()
|
| 3598 |
+
except Exception as e:
|
| 3599 |
+
logger.warning(f"Error checking PDF link for {product_name}: {e}")
|
| 3600 |
+
|
| 3601 |
+
# Build the response
|
| 3602 |
+
response_text = f"""π§ͺ *Name:* {product_name}
|
| 3603 |
+
π¦ *Type:* {product_type}
|
| 3604 |
+
π₯ *Category:* {category}
|
| 3605 |
+
π *Used For:* {indications}"""
|
| 3606 |
+
|
| 3607 |
+
# Add PDF link if available
|
| 3608 |
+
if pdf_link:
|
| 3609 |
+
response_text += f"\n\nπ Product Brochure Available\nπ {product_name} PDF:\n{pdf_link}"
|
| 3610 |
+
|
| 3611 |
+
# Add menu options
|
| 3612 |
+
response_text += f"""
|
| 3613 |
+
|
| 3614 |
+
π¬ *Available Actions:*
|
| 3615 |
+
1οΈβ£ Talk to Veterinary Consultant
|
| 3616 |
+
2οΈβ£ Inquire About Availability
|
| 3617 |
+
3οΈβ£ View Product Image
|
| 3618 |
+
4οΈβ£ Download Detailed PDF
|
| 3619 |
+
5οΈβ£ Back to Main Menu
|
| 3620 |
+
|
| 3621 |
+
π¬ Select an option or ask about related products"""
|
| 3622 |
+
|
| 3623 |
+
# Check for product image
|
| 3624 |
+
image_path = get_product_image_path(product_name)
|
| 3625 |
+
has_image = image_path is not None and os.path.exists(image_path)
|
| 3626 |
+
|
| 3627 |
+
return {
|
| 3628 |
+
'text': response_text,
|
| 3629 |
+
'has_image': has_image,
|
| 3630 |
+
'image_path': image_path,
|
| 3631 |
+
'product_name': product_name
|
| 3632 |
+
}
|
| 3633 |
+
|
| 3634 |
if __name__ == "__main__":
|
| 3635 |
# Launch FastAPI app
|
| 3636 |
import uvicorn
|