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
|