Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -600,6 +600,19 @@ def get_veterinary_product_matches(query: str) -> List[Dict[str, Any]]:
|
|
| 600 |
logger.info(f"[Veterinary Search] Skipping menu selection: '{normalized_query}'")
|
| 601 |
return []
|
| 602 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 603 |
scored_matches = []
|
| 604 |
|
| 605 |
# Veterinary-specific query expansion
|
|
@@ -620,27 +633,29 @@ def get_veterinary_product_matches(query: str) -> List[Dict[str, Any]]:
|
|
| 620 |
if category_key in normalized_query:
|
| 621 |
expanded_queries.extend(categories)
|
| 622 |
|
| 623 |
-
#
|
| 624 |
veterinary_variations = {
|
| 625 |
-
'hydropex': ['hydropex', 'hydro pex', 'electrolyte', 'dehydration', 'heat stress'],
|
| 626 |
-
'heposel': ['heposel', 'hepo sel', 'liver tonic', 'hepatoprotective'],
|
| 627 |
-
'bromacid': ['bromacid', 'brom acid', 'respiratory', 'mucolytic'],
|
| 628 |
-
'respira aid': ['respira aid', 'respira aid plus', 'respiratory support'],
|
| 629 |
-
'hexatox': ['hexatox', 'hexa tox', 'liver support', 'kidney support'],
|
| 630 |
-
'apma fort': ['apma fort', 'mycotoxin', 'liver support'],
|
| 631 |
-
'para c': ['para c', 'para c.e', 'heat stress', 'paracetamol'],
|
| 632 |
'tribiotic': ['tribiotic', 'antibiotic', 'respiratory infection'],
|
| 633 |
-
'phyto-sal': ['phyto-sal', 'phytogenic', 'vitamin supplement'],
|
| 634 |
-
'mycopex': ['mycopex', 'mycotoxin binder', 'mold'],
|
| 635 |
-
'oftilex': ['oftilex', 'ofloxacin', 'antibiotic'],
|
| 636 |
-
'biscomin': ['biscomin', 'oxytetracycline', 'injectable'],
|
| 637 |
-
'apvita': ['apvita', 'vitamin b', 'amino acid'],
|
| 638 |
-
'bg aspro': ['bg aspro', 'aspirin', 'vitamin c'],
|
| 639 |
-
'ec-immune': ['ec-immune', 'immune', 'immunity'],
|
| 640 |
'liverpex': ['liverpex', 'liver', 'metabolic'],
|
| 641 |
'symodex': ['symodex', 'multivitamin', 'vitamin'],
|
| 642 |
-
'adek gold': ['adek gold', 'vitamin', 'multivitamin'],
|
| 643 |
-
'immuno dx': ['immuno dx', 'immune', 'antioxidant']
|
|
|
|
|
|
|
| 644 |
}
|
| 645 |
|
| 646 |
# Add veterinary variations
|
|
@@ -678,11 +693,11 @@ def get_veterinary_product_matches(query: str) -> List[Dict[str, Any]]:
|
|
| 678 |
best_match_type = "exact"
|
| 679 |
match_details = {"field": field_name, "query": expanded_query}
|
| 680 |
|
| 681 |
-
# Fuzzy matching for close matches
|
| 682 |
for expanded_query in expanded_queries:
|
| 683 |
if len(expanded_query) > 3: # Only fuzzy match longer queries
|
| 684 |
score = fuzz.partial_ratio(normalized_query, field_str) * weight
|
| 685 |
-
if score > best_score and score >
|
| 686 |
best_score = score
|
| 687 |
best_match_type = "fuzzy"
|
| 688 |
match_details = {"field": field_name, "query": expanded_query}
|
|
@@ -1955,37 +1970,9 @@ async def process_incoming_message(from_number: str, msg: dict):
|
|
| 1955 |
return
|
| 1956 |
|
| 1957 |
else:
|
| 1958 |
-
#
|
| 1959 |
-
|
| 1960 |
-
|
| 1961 |
-
f"🔍 *We couldn't find '{message_body}' in our veterinary database.*\n\n"
|
| 1962 |
-
"💡 *Try these alternatives:*\n"
|
| 1963 |
-
"• Check spelling (e.g., 'Hydropex' not 'Hydro pex')\n"
|
| 1964 |
-
"• Search by symptoms (e.g., 'respiratory', 'liver support')\n"
|
| 1965 |
-
"• Search by category (e.g., 'antibiotic', 'vitamin')\n"
|
| 1966 |
-
"• Search by species (e.g., 'poultry', 'livestock')\n\n"
|
| 1967 |
-
"🏥 *Popular Veterinary Products:*\n"
|
| 1968 |
-
"• Hydropex (Electrolyte supplement)\n"
|
| 1969 |
-
"• Heposel (Liver tonic)\n"
|
| 1970 |
-
"• Bromacid (Respiratory support)\n"
|
| 1971 |
-
"• Tribiotic (Antibiotic)\n"
|
| 1972 |
-
"• Symodex (Multivitamin)\n\n"
|
| 1973 |
-
"💬 *Type 'main' to return to main menu or try another search.*"
|
| 1974 |
-
)
|
| 1975 |
-
|
| 1976 |
-
# Translate response if needed
|
| 1977 |
-
if reply_language == 'ur':
|
| 1978 |
-
try:
|
| 1979 |
-
translated_message = GoogleTranslator(source='auto', target='ur').translate(message)
|
| 1980 |
-
send_whatsjet_message(from_number, translated_message)
|
| 1981 |
-
except Exception as e:
|
| 1982 |
-
logger.error(f"[AI] Translation error: {e}")
|
| 1983 |
-
send_whatsjet_message(from_number, message)
|
| 1984 |
-
else:
|
| 1985 |
-
send_whatsjet_message(from_number, message)
|
| 1986 |
-
|
| 1987 |
-
# 🎯 PRIORITY 5: Default: treat as general query with intelligent product inquiry
|
| 1988 |
-
await handle_intelligent_product_inquiry(from_number, message_body, user_context, reply_language)
|
| 1989 |
|
| 1990 |
except Exception as e:
|
| 1991 |
logger.error(f"Error in process_incoming_message: {e}")
|
|
@@ -2001,20 +1988,50 @@ async def handle_general_query_with_ai(from_number: str, query: str, user_contex
|
|
| 2001 |
logger.info(f"[AI General] Forcing reply_language to Urdu for Option 4.")
|
| 2002 |
|
| 2003 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2004 |
if not OPENAI_API_KEY:
|
| 2005 |
-
|
| 2006 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
#
|
| 2014 |
-
|
| 2015 |
-
|
| 2016 |
-
|
| 2017 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.
|
|
@@ -2023,21 +2040,40 @@ User Query: "{query}"
|
|
| 2023 |
Current State: {current_state}
|
| 2024 |
Current Product: {current_product.get('Product Name', 'None') if current_product else 'None'}
|
| 2025 |
|
| 2026 |
-
|
| 2027 |
-
|
| 2028 |
-
|
| 2029 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2030 |
"""
|
| 2031 |
|
| 2032 |
response = openai.ChatCompletion.create(
|
| 2033 |
model="gpt-4o",
|
| 2034 |
messages=[{"role": "user", "content": prompt}],
|
| 2035 |
temperature=0.7,
|
| 2036 |
-
max_tokens=
|
| 2037 |
)
|
| 2038 |
|
| 2039 |
ai_response = response.choices[0].message.content.strip()
|
| 2040 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2041 |
# Translate response if needed
|
| 2042 |
if reply_language == 'ur':
|
| 2043 |
try:
|
|
@@ -2054,9 +2090,13 @@ Always keep responses professional, concise, and user-friendly.
|
|
| 2054 |
|
| 2055 |
except Exception as e:
|
| 2056 |
logger.error(f"[AI] Error handling general query: {e}")
|
| 2057 |
-
#
|
| 2058 |
-
|
| 2059 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2060 |
context_manager.update_context(
|
| 2061 |
from_number,
|
| 2062 |
current_state='main_menu',
|
|
@@ -3714,9 +3754,96 @@ async def test_send_product_image(phone: str, product_name: str = "Bromacid"):
|
|
| 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 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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:
|
|
@@ -3845,47 +3972,48 @@ Format your response professionally with emojis and clear structure. Keep it con
|
|
| 3845 |
current_menu_options=list(MENU_CONFIG['product_inquiry']['option_descriptions'].values())
|
| 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 |
-
|
| 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 |
-
|
|
|
|
|
|
|
| 3883 |
|
| 3884 |
except Exception as e:
|
| 3885 |
logger.error(f"Error in product inquiry: {e}")
|
| 3886 |
-
#
|
| 3887 |
-
|
| 3888 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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):
|
|
|
|
| 600 |
logger.info(f"[Veterinary Search] Skipping menu selection: '{normalized_query}'")
|
| 601 |
return []
|
| 602 |
|
| 603 |
+
# Check if this is a mode of action query
|
| 604 |
+
mode_of_action_keywords = ['mode of action', 'mechanism', 'how does it work', 'what does it do', 'how it works', 'mood of action']
|
| 605 |
+
is_mode_of_action_query = any(keyword in normalized_query for keyword in mode_of_action_keywords)
|
| 606 |
+
|
| 607 |
+
# Extract product name from mode of action query
|
| 608 |
+
if is_mode_of_action_query:
|
| 609 |
+
# Try to extract product name from the query
|
| 610 |
+
for _, row in products_df.iterrows():
|
| 611 |
+
product_name = str(row.get('Product Name', '')).lower()
|
| 612 |
+
if product_name in normalized_query:
|
| 613 |
+
logger.info(f"[Veterinary Search] Mode of action query for product: {product_name}")
|
| 614 |
+
return [row.to_dict()]
|
| 615 |
+
|
| 616 |
scored_matches = []
|
| 617 |
|
| 618 |
# Veterinary-specific query expansion
|
|
|
|
| 633 |
if category_key in normalized_query:
|
| 634 |
expanded_queries.extend(categories)
|
| 635 |
|
| 636 |
+
# Enhanced veterinary product variations with more variations
|
| 637 |
veterinary_variations = {
|
| 638 |
+
'hydropex': ['hydropex', 'hydro pex', 'hydropex', 'electrolyte', 'dehydration', 'heat stress'],
|
| 639 |
+
'heposel': ['heposel', 'hepo sel', 'heposel', 'liver tonic', 'hepatoprotective'],
|
| 640 |
+
'bromacid': ['bromacid', 'brom acid', 'bromacid', 'respiratory', 'mucolytic'],
|
| 641 |
+
'respira aid': ['respira aid', 'respira aid plus', 'respiraaid', 'respiratory support'],
|
| 642 |
+
'hexatox': ['hexatox', 'hexa tox', 'hexatox', 'liver support', 'kidney support'],
|
| 643 |
+
'apma fort': ['apma fort', 'apmafort', 'mycotoxin', 'liver support'],
|
| 644 |
+
'para c': ['para c', 'para c.e', 'parace', 'heat stress', 'paracetamol'],
|
| 645 |
'tribiotic': ['tribiotic', 'antibiotic', 'respiratory infection'],
|
| 646 |
+
'phyto-sal': ['phyto-sal', 'phytosal', 'phytogenic', 'vitamin supplement'],
|
| 647 |
+
'mycopex': ['mycopex', 'mycopex super', 'mycotoxin binder', 'mold'],
|
| 648 |
+
'oftilex': ['oftilex', 'oftilex ua-10', 'ofloxacin', 'antibiotic'],
|
| 649 |
+
'biscomin': ['biscomin', 'biscomin 10', 'oxytetracycline', 'injectable'],
|
| 650 |
+
'apvita': ['apvita', 'apvita plus', 'vitamin b', 'amino acid'],
|
| 651 |
+
'bg aspro': ['bg aspro', 'b-g aspro-c', 'aspirin', 'vitamin c'],
|
| 652 |
+
'ec-immune': ['ec-immune', 'ec immune', 'ecimmune', 'immune', 'immunity'],
|
| 653 |
'liverpex': ['liverpex', 'liver', 'metabolic'],
|
| 654 |
'symodex': ['symodex', 'multivitamin', 'vitamin'],
|
| 655 |
+
'adek gold': ['adek gold', 'adekgold', 'vitamin', 'multivitamin'],
|
| 656 |
+
'immuno dx': ['immuno dx', 'immunodx', 'immune', 'antioxidant'],
|
| 657 |
+
'apex': ['apex', 'aapex', 'apex biotical'],
|
| 658 |
+
'apex biotical': ['apex biotical', 'apex', 'aapex']
|
| 659 |
}
|
| 660 |
|
| 661 |
# Add veterinary variations
|
|
|
|
| 693 |
best_match_type = "exact"
|
| 694 |
match_details = {"field": field_name, "query": expanded_query}
|
| 695 |
|
| 696 |
+
# Fuzzy matching for close matches (improved threshold)
|
| 697 |
for expanded_query in expanded_queries:
|
| 698 |
if len(expanded_query) > 3: # Only fuzzy match longer queries
|
| 699 |
score = fuzz.partial_ratio(normalized_query, field_str) * weight
|
| 700 |
+
if score > best_score and score > 75: # Increased threshold for better accuracy
|
| 701 |
best_score = score
|
| 702 |
best_match_type = "fuzzy"
|
| 703 |
match_details = {"field": field_name, "query": expanded_query}
|
|
|
|
| 1970 |
return
|
| 1971 |
|
| 1972 |
else:
|
| 1973 |
+
# Use intelligent product inquiry for all non-found queries
|
| 1974 |
+
await handle_intelligent_product_inquiry(from_number, message_body, user_context, reply_language)
|
| 1975 |
+
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1976 |
|
| 1977 |
except Exception as e:
|
| 1978 |
logger.error(f"Error in process_incoming_message: {e}")
|
|
|
|
| 1988 |
logger.info(f"[AI General] Forcing reply_language to Urdu for Option 4.")
|
| 1989 |
|
| 1990 |
try:
|
| 1991 |
+
# Check if this is a non-product query that should get a simple response
|
| 1992 |
+
non_product_keywords = ['what is', 'who are', 'where is', 'when', 'why', 'how to', 'can you', 'do you', 'is this', 'are you']
|
| 1993 |
+
is_simple_query = any(keyword in query.lower() for keyword in non_product_keywords)
|
| 1994 |
+
|
| 1995 |
+
# Check for company-related queries
|
| 1996 |
+
company_queries = ['apex', 'aapex', 'apex biotical', 'company', 'about', 'who are you', 'what are you']
|
| 1997 |
+
is_company_query = any(keyword in query.lower() for keyword in company_queries)
|
| 1998 |
+
|
| 1999 |
+
if is_company_query:
|
| 2000 |
+
if reply_language == 'ur':
|
| 2001 |
+
response = "🏥 *Apex Biotical Solutions*\n\nہم ایک پیشہ ور ویٹرنری پروڈکٹس کمپنی ہیں۔\n\n💬 *براہ کرم 'main' لکھ کر مین مینو پر جائیں*"
|
| 2002 |
+
else:
|
| 2003 |
+
response = "🏥 *Apex Biotical Solutions*\n\nWe are a professional veterinary products company.\n\n💬 *Please type 'main' to go to main menu*"
|
| 2004 |
+
send_whatsjet_message(from_number, response)
|
| 2005 |
+
return
|
| 2006 |
+
|
| 2007 |
if not OPENAI_API_KEY:
|
| 2008 |
+
if reply_language == 'ur':
|
| 2009 |
+
send_whatsjet_message(from_number,
|
| 2010 |
+
"❌ *AI assistance دستیاب نہیں ہے*\n\n💬 *براہ کرم 'main' لکھ کر مین مینو پر جائیں*")
|
| 2011 |
+
else:
|
| 2012 |
+
send_whatsjet_message(from_number,
|
| 2013 |
+
"❌ AI assistance is not available. Please type 'main' for the menu.")
|
| 2014 |
return
|
| 2015 |
|
| 2016 |
# Create context-aware prompt
|
| 2017 |
current_state = user_context.get('current_state', 'main_menu')
|
| 2018 |
current_product = user_context.get('current_product')
|
| 2019 |
|
| 2020 |
+
# Get all products data for context
|
| 2021 |
+
all_products = []
|
| 2022 |
+
if products_df is not None and not products_df.empty:
|
| 2023 |
+
all_products = products_df.to_dict('records')
|
| 2024 |
+
|
| 2025 |
+
# Create comprehensive context for AI
|
| 2026 |
+
products_context = ""
|
| 2027 |
+
if all_products:
|
| 2028 |
+
products_context = "Available Veterinary Products:\n"
|
| 2029 |
+
for i, product in enumerate(all_products[:30], 1): # Limit to first 30 products for context
|
| 2030 |
+
product_name = product.get('Product Name', 'N/A')
|
| 2031 |
+
category = product.get('Category', 'N/A')
|
| 2032 |
+
products_context += f"{i}. {product_name} - {category}\n"
|
| 2033 |
+
|
| 2034 |
+
# Enhanced prompt for better responses
|
| 2035 |
prompt = f"""
|
| 2036 |
You are a professional veterinary product assistant for Apex Biotical, helping users on WhatsApp.
|
| 2037 |
Always answer in a clear, accurate, and helpful manner.
|
|
|
|
| 2040 |
Current State: {current_state}
|
| 2041 |
Current Product: {current_product.get('Product Name', 'None') if current_product else 'None'}
|
| 2042 |
|
| 2043 |
+
Available Products:
|
| 2044 |
+
{products_context}
|
| 2045 |
+
|
| 2046 |
+
IMPORTANT RULES:
|
| 2047 |
+
1. If the user asks about products, list relevant products with brief descriptions
|
| 2048 |
+
2. If the user asks general veterinary questions, provide concise, expert answers
|
| 2049 |
+
3. If the query is unclear or not related to veterinary products, politely redirect to main menu
|
| 2050 |
+
4. Keep responses professional, concise, and user-friendly
|
| 2051 |
+
5. Always end with instructions to type 'main' to return to main menu
|
| 2052 |
+
6. If the query seems like a test or unrelated question, provide a simple redirect response
|
| 2053 |
+
|
| 2054 |
+
Response Guidelines:
|
| 2055 |
+
- Be professional and helpful
|
| 2056 |
+
- Keep responses concise (max 3-4 sentences)
|
| 2057 |
+
- Always include menu navigation instructions
|
| 2058 |
+
- If unsure, redirect to main menu
|
| 2059 |
"""
|
| 2060 |
|
| 2061 |
response = openai.ChatCompletion.create(
|
| 2062 |
model="gpt-4o",
|
| 2063 |
messages=[{"role": "user", "content": prompt}],
|
| 2064 |
temperature=0.7,
|
| 2065 |
+
max_tokens=200 # Reduced for more concise responses
|
| 2066 |
)
|
| 2067 |
|
| 2068 |
ai_response = response.choices[0].message.content.strip()
|
| 2069 |
|
| 2070 |
+
# Ensure the response includes navigation instructions
|
| 2071 |
+
if 'main' not in ai_response.lower():
|
| 2072 |
+
if reply_language == 'ur':
|
| 2073 |
+
ai_response += "\n\n💬 *براہ کرم 'main' لکھ کر مین مینو پر جائیں*"
|
| 2074 |
+
else:
|
| 2075 |
+
ai_response += "\n\n💬 *Please type 'main' to go to main menu*"
|
| 2076 |
+
|
| 2077 |
# Translate response if needed
|
| 2078 |
if reply_language == 'ur':
|
| 2079 |
try:
|
|
|
|
| 2090 |
|
| 2091 |
except Exception as e:
|
| 2092 |
logger.error(f"[AI] Error handling general query: {e}")
|
| 2093 |
+
# Professional error response
|
| 2094 |
+
if reply_language == 'ur':
|
| 2095 |
+
error_msg = "❌ *خطا آ گئی ہے*\n\n💬 *براہ کرم 'main' لکھ کر مین مینو پر جائیں*"
|
| 2096 |
+
else:
|
| 2097 |
+
error_msg = "❌ *An error occurred*\n\n💬 *Please type 'main' to go to main menu*"
|
| 2098 |
+
|
| 2099 |
+
send_whatsjet_message(from_number, error_msg)
|
| 2100 |
context_manager.update_context(
|
| 2101 |
from_number,
|
| 2102 |
current_state='main_menu',
|
|
|
|
| 3754 |
async def handle_intelligent_product_inquiry(from_number: str, query: str, user_context: dict, reply_language: str = 'en'):
|
| 3755 |
"""Handle product inquiry with OpenAI intelligence and media support"""
|
| 3756 |
try:
|
| 3757 |
+
# Clean and normalize the query
|
| 3758 |
+
clean_query = query.strip().lower()
|
| 3759 |
+
|
| 3760 |
+
# Check for common misspellings and variations
|
| 3761 |
+
misspelling_corrections = {
|
| 3762 |
+
'aapex': 'apex',
|
| 3763 |
+
'apex': 'apex biotical',
|
| 3764 |
+
'ec immune': 'ec-immune',
|
| 3765 |
+
'ecimmune': 'ec-immune',
|
| 3766 |
+
'hydro pex': 'hydropex',
|
| 3767 |
+
'hydropex': 'hydropex',
|
| 3768 |
+
'respira aid': 'respira aid plus',
|
| 3769 |
+
'respiraaid': 'respira aid plus',
|
| 3770 |
+
'hepo sel': 'heposel',
|
| 3771 |
+
'heposel': 'heposel',
|
| 3772 |
+
'brom acid': 'bromacid',
|
| 3773 |
+
'bromacid': 'bromacid',
|
| 3774 |
+
'hexa tox': 'hexatox',
|
| 3775 |
+
'hexatox': 'hexatox',
|
| 3776 |
+
'apma fort': 'apma fort',
|
| 3777 |
+
'apmafort': 'apma fort',
|
| 3778 |
+
'para c': 'para c.e',
|
| 3779 |
+
'para ce': 'para c.e',
|
| 3780 |
+
'parace': 'para c.e',
|
| 3781 |
+
'tribiotic': 'tribiotic',
|
| 3782 |
+
'phyto sal': 'phyto-sal',
|
| 3783 |
+
'phytosal': 'phyto-sal',
|
| 3784 |
+
'mycopex': 'mycopex super',
|
| 3785 |
+
'mycopexsuper': 'mycopex super',
|
| 3786 |
+
'eflin': 'eflin kt-20',
|
| 3787 |
+
'eflinkt20': 'eflin kt-20',
|
| 3788 |
+
'salcozine': 'salcozine st-30',
|
| 3789 |
+
'salcozinest30': 'salcozine st-30',
|
| 3790 |
+
'oftilex': 'oftilex ua-10',
|
| 3791 |
+
'oftilexua10': 'oftilex ua-10',
|
| 3792 |
+
'biscomin': 'biscomin 10',
|
| 3793 |
+
'biscomin10': 'biscomin 10',
|
| 3794 |
+
'apvita': 'apvita plus',
|
| 3795 |
+
'apvitaplus': 'apvita plus',
|
| 3796 |
+
'bg aspro': 'b-g aspro-c',
|
| 3797 |
+
'bgaspro': 'b-g aspro-c',
|
| 3798 |
+
'liverpex': 'liverpex',
|
| 3799 |
+
'symodex': 'symodex',
|
| 3800 |
+
'adek': 'adek gold',
|
| 3801 |
+
'adekgold': 'adek gold',
|
| 3802 |
+
'immuno': 'immuno dx',
|
| 3803 |
+
'immunodx': 'immuno dx',
|
| 3804 |
+
'mood of action': 'mode of action',
|
| 3805 |
+
'mode of action': 'mode of action',
|
| 3806 |
+
'mechanism of action': 'mode of action',
|
| 3807 |
+
'how does it work': 'mode of action',
|
| 3808 |
+
'what does it do': 'mode of action',
|
| 3809 |
+
'how it works': 'mode of action'
|
| 3810 |
+
}
|
| 3811 |
+
|
| 3812 |
+
# Apply misspelling corrections
|
| 3813 |
+
corrected_query = clean_query
|
| 3814 |
+
for misspelling, correction in misspelling_corrections.items():
|
| 3815 |
+
if misspelling in clean_query:
|
| 3816 |
+
corrected_query = clean_query.replace(misspelling, correction)
|
| 3817 |
+
logger.info(f"[Query] Applied correction: '{misspelling}' -> '{correction}'")
|
| 3818 |
+
break
|
| 3819 |
+
|
| 3820 |
+
# Check if query is asking about mode of action or mechanism
|
| 3821 |
+
mode_of_action_keywords = ['mode of action', 'mechanism', 'how does it work', 'what does it do', 'how it works']
|
| 3822 |
+
is_mode_of_action_query = any(keyword in clean_query for keyword in mode_of_action_keywords)
|
| 3823 |
+
|
| 3824 |
+
# First try direct product search with original query
|
| 3825 |
products = get_veterinary_product_matches(query)
|
| 3826 |
|
| 3827 |
+
# If no products found, try with corrected query
|
| 3828 |
+
if not products and corrected_query != clean_query:
|
| 3829 |
+
products = get_veterinary_product_matches(corrected_query)
|
| 3830 |
+
if products:
|
| 3831 |
+
logger.info(f"[Query] Found products using corrected query: '{corrected_query}'")
|
| 3832 |
+
|
| 3833 |
+
# If still no products, try fuzzy matching with product names
|
| 3834 |
+
if not products:
|
| 3835 |
+
if products_df is not None and not products_df.empty:
|
| 3836 |
+
all_product_names = [str(name).lower() for name in products_df['Product Name'].dropna()]
|
| 3837 |
+
|
| 3838 |
+
# Use fuzzy matching to find similar product names
|
| 3839 |
+
for product_name in all_product_names:
|
| 3840 |
+
similarity = fuzz.ratio(clean_query, product_name)
|
| 3841 |
+
if similarity >= 80: # 80% similarity threshold
|
| 3842 |
+
logger.info(f"[Query] Fuzzy match found: '{clean_query}' -> '{product_name}' (similarity: {similarity}%)")
|
| 3843 |
+
products = get_veterinary_product_matches(product_name)
|
| 3844 |
+
if products:
|
| 3845 |
+
break
|
| 3846 |
+
|
| 3847 |
if products:
|
| 3848 |
# Check if this is a broad/category query (multiple products found)
|
| 3849 |
if len(products) > 1:
|
|
|
|
| 3972 |
current_menu_options=list(MENU_CONFIG['product_inquiry']['option_descriptions'].values())
|
| 3973 |
)
|
| 3974 |
|
| 3975 |
+
# If it's a mode of action query, provide detailed mechanism information
|
| 3976 |
+
if is_mode_of_action_query:
|
| 3977 |
+
product_name = selected_product.get('Product Name', 'Unknown')
|
| 3978 |
+
composition = selected_product.get('Composition', 'Not specified')
|
| 3979 |
+
indications = selected_product.get('Indications', 'Not specified')
|
| 3980 |
+
|
| 3981 |
+
mode_of_action_response = (
|
| 3982 |
+
f"🧪 *{product_name} - Mode of Action*\n\n"
|
| 3983 |
+
f"📋 *Composition:* {composition}\n\n"
|
| 3984 |
+
f"💊 *Mechanism:* {indications}\n\n"
|
| 3985 |
+
f"💬 *For complete product details, select an option below:*\n"
|
| 3986 |
+
f"1️⃣ Talk to Veterinary Consultant\n"
|
| 3987 |
+
f"2️⃣ Inquire About Availability\n"
|
| 3988 |
+
f"3️⃣ Back to Main Menu"
|
| 3989 |
+
)
|
| 3990 |
+
|
| 3991 |
+
send_whatsjet_message(from_number, mode_of_action_response)
|
| 3992 |
+
else:
|
| 3993 |
+
# Send product image with caption using the new function
|
| 3994 |
+
await send_product_image_with_caption(from_number, selected_product, user_context)
|
| 3995 |
|
| 3996 |
# Add to conversation history
|
| 3997 |
context_manager.add_to_history(from_number, query, f"Product inquiry for {selected_product.get('Product Name', 'Unknown')}")
|
| 3998 |
|
| 3999 |
else:
|
| 4000 |
+
# Generic, professional "not found" response
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4001 |
if reply_language == 'ur':
|
| 4002 |
+
message = "❌ *سوال درست نہیں ہے*\n\n💬 *براہ کرم اپنا سوال درست کریں یا 'main' لکھ کر مین مینو پر جائیں*"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4003 |
else:
|
| 4004 |
+
message = "❌ *Please correct your question*\n\n💬 *Type 'main' to go to main menu*"
|
| 4005 |
+
|
| 4006 |
+
send_whatsjet_message(from_number, message)
|
| 4007 |
|
| 4008 |
except Exception as e:
|
| 4009 |
logger.error(f"Error in product inquiry: {e}")
|
| 4010 |
+
# Professional error response
|
| 4011 |
+
if reply_language == 'ur':
|
| 4012 |
+
error_msg = "❌ *خطا آ گئی ہے*\n\n💬 *براہ کرم 'main' لکھ کر مین مینو پر جائیں*"
|
| 4013 |
+
else:
|
| 4014 |
+
error_msg = "❌ *An error occurred*\n\n💬 *Please type 'main' to go to main menu*"
|
| 4015 |
+
|
| 4016 |
+
send_whatsjet_message(from_number, error_msg)
|
| 4017 |
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()))
|
| 4018 |
|
| 4019 |
async def handle_contact_request(from_number: str):
|