diff --git "a/app.py" "b/app.py" --- "a/app.py" +++ "b/app.py" @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ -Apex Biotical Veterinary WhatsApp Assistant - Premium Edition -The most effective and accurate veterinary Assistant in the market +Apex Biotical Veterinary WhatsApp Bot - Premium Edition +The most effective and accurate veterinary chatbot in the market """ import os @@ -60,11 +60,7 @@ logger = logging.getLogger(__name__) load_dotenv() # Initialize FastAPI app -app = FastAPI(title="Apex Biotical Veterinary Assistant", version="2.0.0") - -# Ensure static and uploads directories exist before mounting -os.makedirs('static', exist_ok=True) -os.makedirs('uploads', exist_ok=True) +app = FastAPI(title="Apex Biotical Veterinary Bot", version="2.0.0") # Mount static files and templates app.mount("/static", StaticFiles(directory="static"), name="static") @@ -260,7 +256,7 @@ async def download_voice_file(media_url: str, filename: str) -> str: return None async def transcribe_voice_with_openai(file_path: str) -> str: - """Transcribe voice file using OpenAI Whisper with comprehensive veterinary domain system prompt""" + """Transcribe voice file using OpenAI Whisper with improved error handling and language restriction""" try: # Check if file exists and has content if not os.path.exists(file_path): @@ -274,82 +270,13 @@ async def transcribe_voice_with_openai(file_path: str) -> str: logger.info(f"[Transcribe] Transcribing file: {file_path} (size: {file_size} bytes)") - # Comprehensive system prompt for veterinary WhatsApp assistant - system_prompt = """ -You are transcribing voice messages for Apex Biotical Veterinary WhatsApp Assistant. This is a professional veterinary products chatbot. - -CONTEXT: Users can speak product names, menu selections, numbers, and general queries in English or Urdu. - -PRODUCT NAMES (Veterinary Products): -- Hydropex (electrolyte supplement) -- Respira Aid Plus (respiratory support) -- Heposel (liver tonic) -- Bromacid (respiratory/mucolytic) -- Hexatox (liver & kidney support) -- APMA Fort (mycotoxin binder) -- Para C.E (heat stress support) -- Tribiotic (antibiotic) -- PHYTO-SAL (phytogenic supplement) -- Mycopex Super (mycotoxin binder) -- Eflin KT-20 (antibiotic) -- Salcozine ST-30 (anticoccidial) -- Oftilex UA-10 (antibiotic) -- Biscomin 10 (injectable antibiotic) -- Apvita Plus (vitamin supplement) -- B-G Aspro-C (aspirin + vitamin C) -- EC-Immune (immune booster) -- Liverpex (liver tonic) -- Symodex (multivitamin) -- Respira Aid (respiratory support) -- Adek Gold (multivitamin) -- Immuno DX (immune enhancer) - -MENU SELECTIONS: -- Main menu options: 1, 2, 3, 4 -- Product numbers: 1-23 -- Category numbers: 1-10 -- Navigation: main, menu, back, home, start - -NUMBERS (English & Urdu): -English: one, two, three, four, five, six, seven, eight, nine, ten, eleven, twelve, thirteen, fourteen, fifteen, sixteen, seventeen, eighteen, nineteen, twenty, twenty-one, twenty-two, twenty-three -Urdu (Roman): aik, ek, do, teen, char, panch, che, sat, ath, nau, das, gyara, bara, tera, choda, pandra, sola, satara, athara, unnees, bees, ikkees, baees, tees -Urdu (Script): ایک, دو, تین, چار, پانچ, چھ, سات, آٹھ, نو, دس, گیارہ, بارہ, تیرہ, چودہ, پندرہ, سولہ, سترہ, اٹھارہ, انیس, بیس, اکیس, بائیس, تئیس - -COMMON GREETINGS: -English: hi, hello, hey, good morning, good afternoon, good evening, how are you -Urdu: salam, assalamu alaikum, adaab, namaste, khuda hafiz - -MENU COMMANDS: -English: search, browse, download, catalog, contact, availability, main menu, option, number, choice -Urdu: تلاش, براؤز, ڈاؤن لوڈ, کیٹلاگ, رابطہ, دستیابی, مین مینو, آپشن, نمبر, اختیار - -TRANSCRIPTION RULES: -1. Transcribe product names exactly as listed above -2. Convert spoken numbers to digits (1, 2, 3, etc.) -3. Handle both English and Urdu speech -4. Preserve exact spelling for product names -5. Convert menu selections to numbers -6. Handle common transcription errors (opium->option, numara->number) -7. Maintain context for veterinary domain - -EXAMPLES: -- "hydropex" -> "hydropex" -- "respira aid plus" -> "respira aid plus" -- "option number one" -> "1" -- "aik" -> "1" -- "do" -> "2" -- "main menu" -> "main" -- "salam" -> "salam" -- "search products" -> "search products" -""" - - # First attempt with comprehensive system prompt + # First attempt with English-specific prompt and language restriction with open(file_path, 'rb') as audio_file: transcript = openai.Audio.transcribe( model="whisper-1", file=audio_file, - language="en", # Start with English - prompt=system_prompt + language="en", # Force English first + prompt="This is a voice message for a veterinary products bot. Language: English or Urdu only. Common greetings: hi, hello, hey, salam, assalamualaikum. Numbers: one, two, three, 1, 2, 3, aik, do, teen. Menu options: search, browse, download, catalog, products, categories, contact, availability. Products: hydropex, heposel, bromacid, tribiotic, symodex, adek gold. Please transcribe clearly and accurately." ) transcribed_text = transcript.text.strip() @@ -359,51 +286,12 @@ EXAMPLES: if not transcribed_text or len(transcribed_text.strip()) < 2: logger.warning(f"[Transcribe] First attempt failed, trying with Urdu-specific prompt") - urdu_system_prompt = """ -You are transcribing Urdu voice messages for Apex Biotical Veterinary WhatsApp Assistant. - -PRODUCT NAMES (Urdu/English): -- ہائیڈروپیکس (Hydropex) -- ریسپیرا ایڈ پلس (Respira Aid Plus) -- ہیپوسیل (Heposel) -- بروماسڈ (Bromacid) -- ہیکساٹوکس (Hexatox) -- اے پی ایم اے فورٹ (APMA Fort) -- پیرا سی ای (Para C.E) -- ٹرائی بیوٹک (Tribiotic) -- فائٹو سال (PHYTO-SAL) -- مائیکوپیکس سپر (Mycopex Super) - -URDU NUMBERS: -- ایک (1), دو (2), تین (3), چار (4), پانچ (5) -- چھ (6), سات (7), آٹھ (8), نو (9), دس (10) -- گیارہ (11), بارہ (12), تیرہ (13), چودہ (14), پندرہ (15) -- سولہ (16), سترہ (17), اٹھارہ (18), انیس (19), بیس (20) -- اکیس (21), بائیس (22), تئیس (23) - -URDU GREETINGS: -- سلام (salam), السلام علیکم (assalamu alaikum) -- آداب (adaab), نمستے (namaste), خدا حافظ (khuda hafiz) - -URDU MENU COMMANDS: -- مین مینو (main menu), آپشن (option), نمبر (number) -- تلاش (search), براؤز (browse), ڈاؤن لوڈ (download) -- کیٹلاگ (catalog), رابطہ (contact), دستیابی (availability) - -TRANSCRIPTION RULES: -1. Transcribe Urdu words in Urdu script -2. Convert Urdu numbers to digits -3. Handle mixed Urdu-English speech -4. Preserve product names exactly -5. Convert menu selections to numbers -""" - with open(file_path, 'rb') as audio_file: transcript = openai.Audio.transcribe( model="whisper-1", file=audio_file, language="ur", # Force Urdu - prompt=urdu_system_prompt + prompt="This is a voice message in Urdu for a veterinary products bot. Common Urdu greetings: سلام, ہیلو, ہائے, السلام علیکم, وعلیکم السلام. Numbers: ایک, دو, تین, چار, پانچ, 1, 2, 3, 4, 5. Menu options: تلاش, براؤز, ڈاؤن لوڈ, کیٹلاگ, پروڈکٹ, کیٹیگری, رابطہ, دستیابی. Products: ہائیڈروپیکس, ہیپوسیل, بروماسڈ, ٹرائیبیوٹک. Please transcribe clearly in Urdu or English." ) transcribed_text = transcript.text.strip() @@ -413,35 +301,11 @@ TRANSCRIPTION RULES: if not transcribed_text or len(transcribed_text.strip()) < 2: logger.warning(f"[Transcribe] Second attempt failed, trying with mixed language prompt") - mixed_system_prompt = """ -You are transcribing voice messages for a veterinary products WhatsApp assistant. The user may speak in English, Urdu, or a mix of both languages. - -PRODUCT NAMES (exact spelling required): -Hydropex, Respira Aid Plus, Heposel, Bromacid, Hexatox, APMA Fort, Para C.E, Tribiotic, PHYTO-SAL, Mycopex Super, Eflin KT-20, Salcozine ST-30, Oftilex UA-10, Biscomin 10, Apvita Plus, B-G Aspro-C, EC-Immune, Liverpex, Symodex, Respira Aid, Adek Gold, Immuno DX - -NUMBERS (convert to digits): -English: one->1, two->2, three->3, etc. -Urdu: aik->1, ek->1, do->2, teen->3, etc. - -MENU COMMANDS: -main, menu, back, home, start, option, number, search, browse, download, catalog, contact, availability - -GREETINGS: -hi, hello, salam, assalamu alaikum, adaab, namaste - -TRANSCRIPTION RULES: -1. Transcribe exactly what you hear -2. Convert numbers to digits -3. Preserve product names exactly -4. Handle both languages -5. Convert menu selections to numbers -""" - with open(file_path, 'rb') as audio_file: transcript = openai.Audio.transcribe( model="whisper-1", file=audio_file, - prompt=mixed_system_prompt + prompt="This is a voice message for a veterinary products bot. Language: English or Urdu only. Common words: hi, hello, salam, one, two, three, aik, do, teen, search, browse, download, catalog, products, categories, contact, availability, hydropex, heposel, bromacid, tribiotic, symodex, adek gold. Please transcribe any speech you can hear, even if unclear. Numbers and menu selections are important." ) transcribed_text = transcript.text.strip() @@ -460,7 +324,7 @@ TRANSCRIPTION RULES: return None def process_voice_input(text: str) -> str: - """Process and clean voice input text with veterinary domain-specific transcription error correction""" + """Process and clean voice input text with common transcription error correction""" if not text: return "" @@ -473,9 +337,12 @@ def process_voice_input(text: str) -> str: # Basic punctuation cleanup processed_text = processed_text.replace(' ,', ',').replace(' .', '.') - # Veterinary domain-specific transcription error corrections + # Common transcription error corrections transcription_fixes = { - # Common menu selection errors + 'bye': 'hi', + 'goodbye': 'hello', + 'good bye': 'hello', + 'good by': 'hello', 'opium': 'option', 'opium numara': 'option number', 'opium number': 'option number', @@ -494,7 +361,6 @@ def process_voice_input(text: str) -> str: 'numbra 1': 'number 1', 'numbra 2': 'number 2', 'numbra 3': 'number 3', - # Number fixes - only when they appear as standalone numbers 'aik': '1', 'ek': '1', @@ -512,7 +378,6 @@ def process_voice_input(text: str) -> str: 'ath': '8', 'nau': '9', 'das': '10', - # Navigation command fixes 'man': 'main', 'men': 'main', @@ -522,34 +387,7 @@ def process_voice_input(text: str) -> str: 'menu': 'main', 'home': 'main', 'back': 'main', - 'return': 'main', - - # Veterinary product name corrections - 'hydro pex': 'hydropex', - 'hydro pex': 'hydropex', - 'respira aid': 'respira aid plus', - 'respira aid plus': 'respira aid plus', - 'hepo sel': 'heposel', - 'brom acid': 'bromacid', - 'hexa tox': 'hexatox', - 'apma fort': 'apma fort', - 'para c': 'para c.e', - 'para ce': 'para c.e', - 'tribiotic': 'tribiotic', - 'phyto sal': 'phyto-sal', - 'mycopex': 'mycopex super', - 'mycopex super': 'mycopex super', - 'eflin': 'eflin kt-20', - 'salcozine': 'salcozine st-30', - 'oftilex': 'oftilex ua-10', - 'biscomin': 'biscomin 10', - 'apvita': 'apvita plus', - 'bg aspro': 'b-g aspro-c', - 'ec immune': 'ec-immune', - 'liverpex': 'liverpex', - 'symodex': 'symodex', - 'adek': 'adek gold', - 'immuno': 'immuno dx' + 'return': 'main' } # Apply transcription fixes - but be careful with Islamic greetings @@ -808,22 +646,18 @@ context_manager = VeterinaryContextManager() # Enhanced product response with veterinary domain expertise def generate_veterinary_product_response(product_info: Dict[str, Any], user_context: Dict[str, Any]) -> str: """Generate comprehensive veterinary product response with intelligent information handling""" - def clean_text(text): if pd.isna(text) or text is None: return "Not specified" return str(text).strip() - # Extract product details product_name = clean_text(product_info.get('Product Name', '')) product_type = clean_text(product_info.get('Type', '')) category = clean_text(product_info.get('Category', '')) indications = clean_text(product_info.get('Indications', '')) - # Check for PDF link in the CSV data pdf_link = "" try: - # Load CSV data to check for PDF link csv_data = pd.read_csv('Veterinary.csv') product_row = csv_data[csv_data['Product Name'] == product_name] if not product_row.empty: @@ -832,27 +666,15 @@ def generate_veterinary_product_response(product_info: Dict[str, Any], user_cont pdf_link = brochure_link.strip() except Exception as e: logger.warning(f"Error checking PDF link for {product_name}: {e}") - - # Build the response - response = f"""🧪 *Name:* {product_name} -📦 *Type:* {product_type} -🏥 *Category:* {category} -💊 *Used For:* {indications}""" - - # Add PDF link if available, in the requested format + response = f"""🧪 *Name:* {product_name}\n📦 *Type:* {product_type}\n🏥 *Category:* {category}\n💊 *Used For:* {indications}""" if pdf_link: response += f"\n\n📄 Product Brochure Available\n🔗 {product_name} PDF:\n{pdf_link}" - - # Add menu options response += f""" - -💬 *Available Actions:* +\n💬 *Available Actions:* 1️⃣ Talk to Veterinary Consultant 2️⃣ Inquire About Availability 3️⃣ Back to Main Menu - -💬 Select an option or ask about related products""" - +\n💬 Select an option or ask about related products""" return response def clean_text_for_pdf(text: str) -> str: @@ -986,8 +808,10 @@ def generate_veterinary_pdf(product: Dict[str, Any]) -> bytes: async def send_catalog_pdf(phone_number: str): """Send the complete product catalog as a link to the PDF""" try: - # Use the correct Google Drive link converted to direct download format - catalog_url = "https://drive.google.com/uc?export=download&id=1mxpkFf3DY-n3QHzZBe_CdksR-gHu2f_0" + # Use the static file URL for Hugging Face Spaces + server_url = os.getenv("SERVER_URL", "https://your-huggingface-space-url.hf.space") + catalog_url = f"{server_url}/static/Hydropex.pdf" + message = ( "📋 *Apex Biotical Veterinary Products Catalog*\n\n" "📄 Here's your complete product catalog with all our veterinary products:\n" @@ -1014,16 +838,16 @@ async def send_individual_product_pdf(phone_number: str, product: Dict[str, Any] filename = f"{safe_name}_{timestamp}.pdf" # Save PDF to uploads directory - uploads_dir = "../uploads" + uploads_dir = "uploads" os.makedirs(uploads_dir, exist_ok=True) pdf_path = os.path.join(uploads_dir, filename) with open(pdf_path, 'wb') as f: f.write(pdf_content) - # Generate download URL - base_url = os.getenv("PUBLIC_BASE_URL", "http://localhost:8000") - download_url = f"{base_url}/uploads/{filename}" + # Generate download URL for Hugging Face Spaces + server_url = os.getenv("SERVER_URL", "https://your-huggingface-space-url.hf.space") + download_url = f"{server_url}/uploads/{filename}" # Send PDF via WhatsApp media success = send_whatsjet_message( @@ -1038,7 +862,7 @@ async def send_individual_product_pdf(phone_number: str, product: Dict[str, Any] if success: message = ( f"📄 *{product_name} - Product Information*\n\n" - "📎 [Direct Download Link]({download_url})\n\n" + f"📎 [Direct Download Link]({download_url})\n\n" "💬 *If the PDF didn't download, use the link above*\n" "Type 'main' to return to main menu." ) @@ -1047,7 +871,7 @@ async def send_individual_product_pdf(phone_number: str, product: Dict[str, Any] # If media send failed, send only the link message = ( f"📄 *{product_name} - Product Information*\n\n" - "📎 [Download Product PDF]({download_url})\n\n" + f"📎 [Download Product PDF]({download_url})\n\n" "💬 *Click the link above to download the product information*\n" "Type 'main' to return to main menu." ) @@ -1075,25 +899,63 @@ def send_whatsjet_message(phone_number: str, message: str, media_type: str = Non if media_type and media_path: # If media_path is a public URL, use media_url and send caption if isinstance(media_path, str) and media_path.startswith("http"): - payload = { - "phone_number": phone_number, - "caption": message, # Use 'caption' instead of 'message_body' - "media_type": media_type, - "media_url": media_path, - "media_filename": filename or os.path.basename(media_path) - } - try: - response = httpx.post( - url, - json=payload, - timeout=15 - ) - response.raise_for_status() - logger.info(f"[WhatsJet] Media URL message sent successfully to {phone_number}") - return True - except Exception as e: - logger.error(f"[WhatsJet] Exception sending media URL message: {e}") - return False + # Try different payload formats for WhatsJet API + payload_formats = [ + # Format 1: Using caption field + { + "phone_number": phone_number, + "caption": message, + "media_type": media_type, + "media_url": media_path, + "media_filename": filename or os.path.basename(media_path) + }, + # Format 2: Using message_body instead of caption + { + "phone_number": phone_number, + "message_body": message, + "media_type": media_type, + "media_url": media_path, + "media_filename": filename or os.path.basename(media_path) + }, + # Format 3: Simplified format without media_filename + { + "phone_number": phone_number, + "message_body": message, + "media_type": media_type, + "media_url": media_path + }, + # Format 4: Using different field names + { + "phone_number": phone_number, + "caption": message, + "type": media_type, + "url": media_path + } + ] + + for i, payload in enumerate(payload_formats, 1): + try: + logger.info(f"[WhatsJet] Trying payload format {i}: {payload}") + response = httpx.post( + url, + json=payload, + timeout=15 + ) + + if response.status_code == 200: + logger.info(f"[WhatsJet] Media URL message sent successfully with format {i} to {phone_number}") + return True + else: + logger.warning(f"[WhatsJet] Format {i} failed with status {response.status_code}: {response.text[:200]}") + + except Exception as e: + logger.warning(f"[WhatsJet] Format {i} exception: {e}") + continue + + # If all formats failed, log the error and return False + logger.error(f"[WhatsJet] All media URL payload formats failed for {phone_number}") + return False + else: # Local file logic as before try: @@ -1144,6 +1006,7 @@ def send_whatsjet_message(phone_number: str, message: str, media_type: str = Non except Exception as e: logger.error(f"[WhatsJet] Exception preparing text chunk: {str(e)}") return False + logger.info(f"[WhatsJet] Successfully sent complete text message to {phone_number}") return True @@ -1176,6 +1039,15 @@ def send_whatsjet_media_image_only(phone_number: str, image_url: str, filename: logger.error(f"[WhatsJet] Exception sending image only: {e}") return False +# Test endpoint for WhatsJet media format debugging +@app.get("/test-whatsjet-media-formats") +async def test_whatsjet_media_formats(): + try: + resp = requests.get("https://api.whatsjet.com", timeout=5) + return {"status": resp.status_code, "text": resp.text[:200]} + except Exception as e: + return {"error": str(e)} + # --- Health Check Endpoint --- @app.get("/health") async def health_check(): @@ -1222,9 +1094,6 @@ async def get_catalog(): @app.get("/", response_class=HTMLResponse) async def root(): return """ -

Apex Biotical Veterinary WhatsApp Assistant

-

The Assistant is running! Use the API endpoints for WhatsApp integration.

-