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 |
|