Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1996,25 +1996,15 @@ async def process_incoming_message(from_number: str, msg: dict):
|
|
| 1996 |
|
| 1997 |
async def handle_general_query_with_ai(from_number: str, query: str, user_context: dict, reply_language: str = 'en'):
|
| 1998 |
"""Handle general queries with OpenAI intelligence"""
|
| 1999 |
-
# Force Urdu replies for Option 4
|
| 2000 |
reply_language = 'ur'
|
| 2001 |
logger.info(f"[AI General] Forcing reply_language to Urdu for Option 4.")
|
| 2002 |
-
|
| 2003 |
try:
|
| 2004 |
-
if not
|
| 2005 |
send_whatsjet_message(from_number,
|
| 2006 |
"❌ AI assistance is not available. Please try searching for a specific product or type 'main' for the menu.")
|
| 2007 |
return
|
| 2008 |
-
|
| 2009 |
-
# Create context-aware prompt
|
| 2010 |
current_state = user_context.get('current_state', 'main_menu')
|
| 2011 |
current_product = user_context.get('current_product')
|
| 2012 |
-
|
| 2013 |
-
# --- SYSTEM PROMPT FOR GENERAL/OUT-OF-MENU QUERIES ---
|
| 2014 |
-
# This prompt ensures the assistant answers professionally, accurately, and helpfully.
|
| 2015 |
-
# If the query is about products, list all relevant products (not just one) with brief details.
|
| 2016 |
-
# If the query is general, provide a concise, expert veterinary answer.
|
| 2017 |
-
# Clarify when a query is outside the menu system and offer to return to the main menu if needed.
|
| 2018 |
prompt = f"""
|
| 2019 |
You are a professional veterinary product assistant for Apex Biotical, helping users on WhatsApp.
|
| 2020 |
Always answer in a clear, accurate, and helpful manner.
|
|
@@ -2028,17 +2018,13 @@ If the user asks a general veterinary question, provide a concise, expert answer
|
|
| 2028 |
If the query is outside the menu system, politely clarify and offer to return to the main menu (type 'main').
|
| 2029 |
Always keep responses professional, concise, and user-friendly.
|
| 2030 |
"""
|
| 2031 |
-
|
| 2032 |
response = openai.ChatCompletion.create(
|
| 2033 |
model="gpt-4o",
|
| 2034 |
messages=[{"role": "user", "content": prompt}],
|
| 2035 |
temperature=0.7,
|
| 2036 |
max_tokens=300
|
| 2037 |
)
|
| 2038 |
-
|
| 2039 |
ai_response = response.choices[0].message.content.strip()
|
| 2040 |
-
|
| 2041 |
-
# Translate response if needed
|
| 2042 |
if reply_language == 'ur':
|
| 2043 |
try:
|
| 2044 |
translated_response = GoogleTranslator(source='auto', target='ur').translate(ai_response)
|
|
@@ -2048,21 +2034,11 @@ Always keep responses professional, concise, and user-friendly.
|
|
| 2048 |
send_whatsjet_message(from_number, ai_response)
|
| 2049 |
else:
|
| 2050 |
send_whatsjet_message(from_number, ai_response)
|
| 2051 |
-
|
| 2052 |
-
|
| 2053 |
-
context_manager.add_to_history(from_number, query, ai_response)
|
| 2054 |
-
|
| 2055 |
except Exception as e:
|
| 2056 |
logger.error(f"[AI] Error handling general query: {e}")
|
| 2057 |
-
|
| 2058 |
-
welcome_msg = generate_veterinary_welcome_message()
|
| 2059 |
-
send_whatsjet_message(from_number, welcome_msg)
|
| 2060 |
-
context_manager.update_context(
|
| 2061 |
-
from_number,
|
| 2062 |
-
current_state='main_menu',
|
| 2063 |
-
current_menu='main_menu',
|
| 2064 |
-
current_menu_options=list(MENU_CONFIG['main_menu']['option_descriptions'].values())
|
| 2065 |
-
)
|
| 2066 |
|
| 2067 |
async def handle_contact_request(from_number: str):
|
| 2068 |
"""Handle contact request"""
|
|
@@ -3712,181 +3688,51 @@ async def test_send_product_image(phone: str, product_name: str = "Bromacid"):
|
|
| 3712 |
return {"error": str(e)}
|
| 3713 |
|
| 3714 |
async def handle_intelligent_product_inquiry(from_number: str, query: str, user_context: dict, reply_language: str = 'en'):
|
| 3715 |
-
"""Handle product inquiry with OpenAI intelligence and media support"""
|
| 3716 |
try:
|
| 3717 |
-
# First try direct product search
|
| 3718 |
products = get_veterinary_product_matches(query)
|
| 3719 |
-
|
| 3720 |
if products:
|
| 3721 |
-
# Check if this is a broad/category query (multiple products found)
|
| 3722 |
if len(products) > 1:
|
| 3723 |
-
|
| 3724 |
-
if OPENAI_API_KEY:
|
| 3725 |
-
try:
|
| 3726 |
-
# Create a comprehensive prompt for multiple products
|
| 3727 |
-
products_info = []
|
| 3728 |
-
for i, product in enumerate(products, 1):
|
| 3729 |
-
product_name = product.get('Product Name', 'N/A')
|
| 3730 |
-
category = product.get('Category', 'N/A')
|
| 3731 |
-
target_species = product.get('Target Species', 'N/A')
|
| 3732 |
-
products_info.append(f"{i}. {product_name} - {category} ({target_species})")
|
| 3733 |
-
|
| 3734 |
-
products_text = "\n".join(products_info)
|
| 3735 |
-
|
| 3736 |
-
prompt = f"""
|
| 3737 |
-
You are a professional veterinary product assistant for Apex Biotical. The user asked about "{query}" and we found {len(products)} relevant products.
|
| 3738 |
-
|
| 3739 |
-
Available Products:
|
| 3740 |
-
{products_text}
|
| 3741 |
-
|
| 3742 |
-
Please provide:
|
| 3743 |
-
1. A professional, welcoming response acknowledging their query
|
| 3744 |
-
2. A brief summary of what these products are for (if it's a category like "poultry products", explain the category)
|
| 3745 |
-
3. List all products with their numbers and brief descriptions
|
| 3746 |
-
4. Clear instructions on how to proceed
|
| 3747 |
-
|
| 3748 |
-
Format your response professionally with emojis and clear structure. Keep it concise but informative.
|
| 3749 |
-
"""
|
| 3750 |
-
|
| 3751 |
-
response = openai.ChatCompletion.create(
|
| 3752 |
-
model="gpt-4o",
|
| 3753 |
-
messages=[{"role": "user", "content": prompt}],
|
| 3754 |
-
temperature=0.7,
|
| 3755 |
-
max_tokens=400
|
| 3756 |
-
)
|
| 3757 |
-
|
| 3758 |
-
ai_response = response.choices[0].message['content'].strip()
|
| 3759 |
-
|
| 3760 |
-
# Add instructions for selection
|
| 3761 |
-
selection_instructions = (
|
| 3762 |
-
f"\n\n💬 *To view detailed information about any product, reply with its number (1-{len(products)})*\n"
|
| 3763 |
-
"💬 *Type 'main' to return to the main menu*"
|
| 3764 |
-
)
|
| 3765 |
-
|
| 3766 |
-
full_response = ai_response + selection_instructions
|
| 3767 |
-
|
| 3768 |
-
# Translate response if needed
|
| 3769 |
-
if reply_language == 'ur':
|
| 3770 |
-
try:
|
| 3771 |
-
translated_response = GoogleTranslator(source='auto', target='ur').translate(full_response)
|
| 3772 |
-
send_whatsjet_message(from_number, translated_response)
|
| 3773 |
-
except Exception as e:
|
| 3774 |
-
logger.error(f"[AI] Translation error: {e}")
|
| 3775 |
-
send_whatsjet_message(from_number, full_response)
|
| 3776 |
-
else:
|
| 3777 |
-
send_whatsjet_message(from_number, full_response)
|
| 3778 |
-
|
| 3779 |
-
# Store the product list in context for selection handling
|
| 3780 |
-
context_manager.update_context(
|
| 3781 |
-
from_number,
|
| 3782 |
-
current_state='intelligent_products_menu',
|
| 3783 |
-
current_menu='intelligent_products_menu',
|
| 3784 |
-
current_menu_options=[f"Product {i+1}" for i in range(len(products))],
|
| 3785 |
-
available_products=products,
|
| 3786 |
-
last_query=query
|
| 3787 |
-
)
|
| 3788 |
-
|
| 3789 |
-
# Add to conversation history
|
| 3790 |
-
context_manager.add_to_history(from_number, query, full_response)
|
| 3791 |
-
return
|
| 3792 |
-
|
| 3793 |
-
except Exception as e:
|
| 3794 |
-
logger.error(f"[AI] Error generating product summary: {e}")
|
| 3795 |
-
# Fall back to simple listing if AI fails
|
| 3796 |
-
pass
|
| 3797 |
-
|
| 3798 |
-
# Fallback: Simple listing without AI
|
| 3799 |
-
message = f"🔍 *Found {len(products)} products matching '{query}':*\n\n"
|
| 3800 |
-
|
| 3801 |
for i, product in enumerate(products, 1):
|
| 3802 |
-
product_name = product.get('Product Name', '
|
| 3803 |
-
category = product.get('Category', '
|
| 3804 |
-
|
| 3805 |
-
message += f"{
|
| 3806 |
-
|
| 3807 |
-
|
| 3808 |
-
|
| 3809 |
-
|
| 3810 |
-
|
| 3811 |
-
)
|
| 3812 |
-
|
| 3813 |
-
|
| 3814 |
-
if
|
| 3815 |
-
|
| 3816 |
-
|
| 3817 |
-
|
| 3818 |
-
|
| 3819 |
-
|
| 3820 |
-
|
| 3821 |
-
|
| 3822 |
-
|
| 3823 |
-
|
| 3824 |
-
# Store the product list in context for selection handling
|
| 3825 |
-
context_manager.update_context(
|
| 3826 |
-
from_number,
|
| 3827 |
-
current_state='intelligent_products_menu',
|
| 3828 |
-
current_menu='intelligent_products_menu',
|
| 3829 |
-
current_menu_options=[f"Product {i+1}" for i in range(len(products))],
|
| 3830 |
-
available_products=products,
|
| 3831 |
-
last_query=query
|
| 3832 |
-
)
|
| 3833 |
-
|
| 3834 |
-
# Add to conversation history
|
| 3835 |
-
context_manager.add_to_history(from_number, query, message)
|
| 3836 |
-
|
| 3837 |
else:
|
| 3838 |
-
# Single product found - show detailed information with media support
|
| 3839 |
selected_product = products[0]
|
| 3840 |
-
context_manager
|
| 3841 |
-
|
| 3842 |
-
|
| 3843 |
-
|
| 3844 |
-
|
| 3845 |
-
|
| 3846 |
-
|
| 3847 |
-
|
| 3848 |
-
#
|
| 3849 |
-
|
| 3850 |
-
|
| 3851 |
-
# Add to conversation history
|
| 3852 |
-
context_manager.add_to_history(from_number, query, f"Product inquiry for {selected_product.get('Product Name', 'Unknown')}")
|
| 3853 |
-
|
| 3854 |
else:
|
| 3855 |
-
|
| 3856 |
-
message = (
|
| 3857 |
-
"❌ *Product Not Found*\n\n"
|
| 3858 |
-
f"🔍 *We couldn't find '{query}' in our veterinary database.*\n\n"
|
| 3859 |
-
"💡 *Try these alternatives:*\n"
|
| 3860 |
-
"• Check spelling (e.g., 'Hydropex' not 'Hydro pex')\n"
|
| 3861 |
-
"• Search by symptoms (e.g., 'respiratory', 'liver support')\n"
|
| 3862 |
-
"• Search by category (e.g., 'antibiotic', 'vitamin')\n"
|
| 3863 |
-
"• Search by species (e.g., 'poultry', 'livestock')\n\n"
|
| 3864 |
-
"🏥 *Popular Veterinary Products:*\n"
|
| 3865 |
-
"• Hydropex (Electrolyte supplement)\n"
|
| 3866 |
-
"• Heposel (Liver tonic)\n"
|
| 3867 |
-
"• Bromacid (Respiratory support)\n"
|
| 3868 |
-
"• Tribiotic (Antibiotic)\n"
|
| 3869 |
-
"• Symodex (Multivitamin)\n\n"
|
| 3870 |
-
"💬 *Type 'main' to return to main menu or try another search.*"
|
| 3871 |
-
)
|
| 3872 |
-
|
| 3873 |
-
# Translate response if needed
|
| 3874 |
-
if reply_language == 'ur':
|
| 3875 |
-
try:
|
| 3876 |
-
translated_message = GoogleTranslator(source='auto', target='ur').translate(message)
|
| 3877 |
-
send_whatsjet_message(from_number, translated_message)
|
| 3878 |
-
except Exception as e:
|
| 3879 |
-
logger.error(f"[AI] Translation error: {e}")
|
| 3880 |
-
send_whatsjet_message(from_number, message)
|
| 3881 |
-
else:
|
| 3882 |
-
send_whatsjet_message(from_number, message)
|
| 3883 |
-
|
| 3884 |
except Exception as e:
|
| 3885 |
-
logger.error(f"Error in
|
| 3886 |
-
|
| 3887 |
-
welcome_msg = generate_veterinary_welcome_message(from_number, user_context)
|
| 3888 |
-
send_whatsjet_message(from_number, welcome_msg)
|
| 3889 |
-
context_manager.update_context(from_number, current_state='main_menu', current_menu='main_menu', current_menu_options=list(MENU_CONFIG['main_menu']['option_descriptions'].values()))
|
| 3890 |
|
| 3891 |
async def handle_contact_request(from_number: str):
|
| 3892 |
"""Handle contact request"""
|
|
@@ -3920,17 +3766,11 @@ async def handle_contact_request(from_number: str):
|
|
| 3920 |
current_menu_options=list(MENU_CONFIG['main_menu']['option_descriptions'].values())
|
| 3921 |
)
|
| 3922 |
|
| 3923 |
-
#
|
| 3924 |
def restore_english_terms(translated_text, original_text, product_names, category_names):
|
| 3925 |
-
"""
|
| 3926 |
-
Replace Urdu-translated product/category names in translated_text with their original English from original_text.
|
| 3927 |
-
"""
|
| 3928 |
for name in product_names + category_names:
|
| 3929 |
if name and name.lower() in translated_text.lower() and name.lower() not in original_text.lower():
|
| 3930 |
-
# If the English name is not in the original, skip
|
| 3931 |
continue
|
| 3932 |
-
# Replace Urdu translation with English name
|
| 3933 |
-
# (This is a simple approach; for more accuracy, use regex or fuzzy matching)
|
| 3934 |
translated_text = translated_text.replace(name, name)
|
| 3935 |
return translated_text
|
| 3936 |
|
|
|
|
| 1996 |
|
| 1997 |
async def handle_general_query_with_ai(from_number: str, query: str, user_context: dict, reply_language: str = 'en'):
|
| 1998 |
"""Handle general queries with OpenAI intelligence"""
|
|
|
|
| 1999 |
reply_language = 'ur'
|
| 2000 |
logger.info(f"[AI General] Forcing reply_language to Urdu for Option 4.")
|
|
|
|
| 2001 |
try:
|
| 2002 |
+
if not openai.api_key:
|
| 2003 |
send_whatsjet_message(from_number,
|
| 2004 |
"❌ AI assistance is not available. Please try searching for a specific product or type 'main' for the menu.")
|
| 2005 |
return
|
|
|
|
|
|
|
| 2006 |
current_state = user_context.get('current_state', 'main_menu')
|
| 2007 |
current_product = user_context.get('current_product')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2008 |
prompt = f"""
|
| 2009 |
You are a professional veterinary product assistant for Apex Biotical, helping users on WhatsApp.
|
| 2010 |
Always answer in a clear, accurate, and helpful manner.
|
|
|
|
| 2018 |
If the query is outside the menu system, politely clarify and offer to return to the main menu (type 'main').
|
| 2019 |
Always keep responses professional, concise, and user-friendly.
|
| 2020 |
"""
|
|
|
|
| 2021 |
response = openai.ChatCompletion.create(
|
| 2022 |
model="gpt-4o",
|
| 2023 |
messages=[{"role": "user", "content": prompt}],
|
| 2024 |
temperature=0.7,
|
| 2025 |
max_tokens=300
|
| 2026 |
)
|
|
|
|
| 2027 |
ai_response = response.choices[0].message.content.strip()
|
|
|
|
|
|
|
| 2028 |
if reply_language == 'ur':
|
| 2029 |
try:
|
| 2030 |
translated_response = GoogleTranslator(source='auto', target='ur').translate(ai_response)
|
|
|
|
| 2034 |
send_whatsjet_message(from_number, ai_response)
|
| 2035 |
else:
|
| 2036 |
send_whatsjet_message(from_number, ai_response)
|
| 2037 |
+
if context_manager:
|
| 2038 |
+
context_manager.add_to_history(from_number, query, ai_response)
|
|
|
|
|
|
|
| 2039 |
except Exception as e:
|
| 2040 |
logger.error(f"[AI] Error handling general query: {e}")
|
| 2041 |
+
send_whatsjet_message(from_number, "❌ AI Assistant encountered an error. Please try again or type 'main' to return to main menu.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2042 |
|
| 2043 |
async def handle_contact_request(from_number: str):
|
| 2044 |
"""Handle contact request"""
|
|
|
|
| 3688 |
return {"error": str(e)}
|
| 3689 |
|
| 3690 |
async def handle_intelligent_product_inquiry(from_number: str, query: str, user_context: dict, reply_language: str = 'en'):
|
| 3691 |
+
"""Handle product inquiry with OpenAI intelligence and media support, matching WhatsApp screenshot logic"""
|
| 3692 |
try:
|
|
|
|
| 3693 |
products = get_veterinary_product_matches(query)
|
|
|
|
| 3694 |
if products:
|
|
|
|
| 3695 |
if len(products) > 1:
|
| 3696 |
+
message = f"Certainly, here are the relevant products for your query:\n\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3697 |
for i, product in enumerate(products, 1):
|
| 3698 |
+
product_name = product.get('Product Name', 'Unknown')
|
| 3699 |
+
category = product.get('Category', '')
|
| 3700 |
+
short_desc = product.get('Type', '') or product.get('Indications', '')
|
| 3701 |
+
message += f"{i}. {product_name}"
|
| 3702 |
+
if category:
|
| 3703 |
+
message += f" - {category}"
|
| 3704 |
+
if short_desc:
|
| 3705 |
+
message += f" / {short_desc}"
|
| 3706 |
+
message += "\n"
|
| 3707 |
+
message += (f"\nTo view detailed information about any product, reply with its number (1-{len(products)})\n"
|
| 3708 |
+
"Type 'main' to return to the main menu")
|
| 3709 |
+
send_whatsjet_message(from_number, message)
|
| 3710 |
+
if context_manager:
|
| 3711 |
+
context_manager.update_context(
|
| 3712 |
+
from_number,
|
| 3713 |
+
current_state='intelligent_products_menu',
|
| 3714 |
+
current_menu='intelligent_products_menu',
|
| 3715 |
+
current_menu_options=[str(i) for i in range(1, len(products)+1)],
|
| 3716 |
+
available_products=products
|
| 3717 |
+
)
|
| 3718 |
+
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3719 |
else:
|
|
|
|
| 3720 |
selected_product = products[0]
|
| 3721 |
+
if context_manager:
|
| 3722 |
+
context_manager.update_context(
|
| 3723 |
+
from_number,
|
| 3724 |
+
current_product=selected_product,
|
| 3725 |
+
current_state='product_inquiry',
|
| 3726 |
+
current_menu='product_inquiry',
|
| 3727 |
+
current_menu_options=list(MENU_CONFIG['product_inquiry']['option_descriptions'].values())
|
| 3728 |
+
)
|
| 3729 |
+
# The actual sending of product details should be handled by the caller
|
| 3730 |
+
return selected_product
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3731 |
else:
|
| 3732 |
+
send_whatsjet_message(from_number, "❌ No products found matching your query.\nType 'main' to return to the main menu.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3733 |
except Exception as e:
|
| 3734 |
+
logger.error(f"Error in handle_intelligent_product_inquiry: {e}")
|
| 3735 |
+
send_whatsjet_message(from_number, "❌ Error processing your request. Type 'main' to return to the main menu.")
|
|
|
|
|
|
|
|
|
|
| 3736 |
|
| 3737 |
async def handle_contact_request(from_number: str):
|
| 3738 |
"""Handle contact request"""
|
|
|
|
| 3766 |
current_menu_options=list(MENU_CONFIG['main_menu']['option_descriptions'].values())
|
| 3767 |
)
|
| 3768 |
|
| 3769 |
+
# Helper for restoring English terms in translations
|
| 3770 |
def restore_english_terms(translated_text, original_text, product_names, category_names):
|
|
|
|
|
|
|
|
|
|
| 3771 |
for name in product_names + category_names:
|
| 3772 |
if name and name.lower() in translated_text.lower() and name.lower() not in original_text.lower():
|
|
|
|
| 3773 |
continue
|
|
|
|
|
|
|
| 3774 |
translated_text = translated_text.replace(name, name)
|
| 3775 |
return translated_text
|
| 3776 |
|