import gradio as gr import os import requests import json # Simple RAG system for HuggingFace Spaces deployment class SimpleRAGChatbot: def __init__(self): # Restaurant policies data self.data_dict = { "refund_policy": """ Our refund policy is customer-friendly and straightforward: - Full refunds are available within 2 hours of ordering if the food hasn't been prepared - If you're unsatisfied with your meal, we offer a full refund or replacement - For delivered orders, refunds are processed within 3-5 business days - No questions asked policy - customer satisfaction is our priority - Special dietary restriction mix-ups receive immediate full refunds plus a $20 credit """, "privacy_policy": """ We take your privacy seriously at our restaurant: - We only collect necessary information: name, phone, email, and payment details - Your data is never sold or shared with third parties - We use your information only for order processing and customer service - You can request data deletion at any time by emailing privacy@restaurant.com - We use encrypted payment processing and secure servers - Marketing emails are opt-in only and you can unsubscribe anytime """, "baby_policy": """ We absolutely welcome families with babies! - High chairs are available upon request (we have 12 high chairs) - Baby changing stations are available in both restrooms - We provide complimentary baby food heating service - Quiet family seating area is available in the back section - Baby bottles and sippy cups can be cleaned by our staff - We have a kids menu with healthy options for toddlers - No age restrictions - babies are welcome at all times """, "weekend_hours": """ Our weekend hours are designed for your convenience: - Friday: 11:00 AM - 11:00 PM (extended hours) - Saturday: 10:00 AM - 12:00 AM (brunch starts at 10 AM) - Sunday: 10:00 AM - 10:00 PM (brunch until 3 PM) - Weekend reservations are highly recommended - Happy hour on weekends: 3:00 PM - 6:00 PM - Late night menu available until 1 hour before closing - Takeout and delivery available during all operating hours """, "allergen_policy": """ We take food allergies very seriously: - Please inform staff of any allergies when ordering - We have detailed allergen information for all menu items - Separate prep areas for gluten-free orders - Nut-free options are clearly marked on the menu - We can accommodate most dietary restrictions with advance notice - Our staff is trained in allergen awareness and cross-contamination prevention - Emergency protocols in place for severe allergic reactions """, "reservation_policy": """ Making reservations is easy and recommended: - Reservations can be made online, by phone, or in person - We accept reservations up to 30 days in advance - Party size limit: 12 people (larger groups need special arrangements) - 15-minute grace period for late arrivals - Cancellations must be made at least 2 hours in advance - Weekend reservations during peak hours (6-8 PM) have a 90-minute time limit - Walk-ins are welcome but may have longer wait times """, "menu_highlights": """ Our signature dishes and popular items: - Wood-fired pizza made with organic ingredients - Fresh pasta made daily in-house - Sustainable seafood sourced locally - Vegan and vegetarian options available - Gluten-free pasta and pizza bases - Seasonal specials featuring local produce - Craft cocktails and local beer selection - Homemade desserts including our famous tiramisu """, "contact_delivery": """ Contact and delivery information: - Phone: (555) 123-4567 - Email: info@restaurant.com - Address: 123 Main Street, Food City, FC 12345 - Delivery radius: 5 miles - Delivery fee: $3.99 (free over $30) - Delivery time: 30-45 minutes - We use DoorDash, UberEats, and our own delivery service - Online ordering available at our website """ } # Create keyword mappings for simple search self.keyword_mappings = { "refund": ["refund_policy"], "money": ["refund_policy"], "return": ["refund_policy"], "privacy": ["privacy_policy"], "data": ["privacy_policy"], "information": ["privacy_policy"], "baby": ["baby_policy"], "child": ["baby_policy"], "kid": ["baby_policy"], "family": ["baby_policy"], "weekend": ["weekend_hours"], "saturday": ["weekend_hours"], "sunday": ["weekend_hours"], "friday": ["weekend_hours"], "hours": ["weekend_hours"], "open": ["weekend_hours"], "time": ["weekend_hours"], "allergy": ["allergen_policy"], "allergic": ["allergen_policy"], "gluten": ["allergen_policy"], "nut": ["allergen_policy"], "reservation": ["reservation_policy"], "book": ["reservation_policy"], "table": ["reservation_policy"], "menu": ["menu_highlights"], "food": ["menu_highlights"], "dish": ["menu_highlights"], "pizza": ["menu_highlights"], "pasta": ["menu_highlights"], "contact": ["contact_delivery"], "phone": ["contact_delivery"], "address": ["contact_delivery"], "delivery": ["contact_delivery"], "order": ["contact_delivery"] } def search_documents(self, query): """Simple keyword-based search""" query_lower = query.lower() relevant_docs = set() # Check for keyword matches for keyword, doc_ids in self.keyword_mappings.items(): if keyword in query_lower: relevant_docs.update(doc_ids) # If no keyword matches, return most general documents if not relevant_docs: relevant_docs = {"menu_highlights", "contact_delivery", "weekend_hours"} # Return the matching documents results = [] for doc_id in relevant_docs: results.append({ 'id': doc_id, 'title': doc_id.replace('_', ' ').title(), 'content': self.data_dict[doc_id] }) return results def call_together_ai(self, prompt, context): """Call Together.ai API with context""" # Try multiple ways to get the API key api_key = ( os.getenv("TOGETHER_API_KEY") or os.getenv("TOGETHER_API_KEY_SECRET") or os.getenv("HF_TOKEN") or # Sometimes HF uses this None ) # If no API key, provide a smart fallback response if not api_key: return self.create_smart_fallback_response(prompt, context) url = "https://api.together.xyz/v1/chat/completions" headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } system_prompt = f"""You are a friendly and knowledgeable restaurant customer service assistant. Use the following context to answer questions about our restaurant policies, services, and offerings. Context: {context} Instructions: - Answer based on the provided context when possible - Be warm, friendly, and helpful - If the information isn't in the context, politely say so and offer to help in other ways - Keep responses conversational and informative - Use emojis appropriately to make responses more engaging """ data = { "model": "meta-llama/Llama-3.2-3B-Instruct-Turbo", "messages": [ {"role": "system", "content": system_prompt}, {"role": "user", "content": prompt} ], "max_tokens": 400, "temperature": 0.7 } try: response = requests.post(url, headers=headers, json=data) if response.status_code == 200: result = response.json() return result["choices"][0]["message"]["content"] else: print(f"API Error: {response.status_code} - {response.text}") return self.create_smart_fallback_response(prompt, context) except Exception as e: print(f"API Exception: {str(e)}") return self.create_smart_fallback_response(prompt, context) def create_smart_fallback_response(self, prompt, context): """Create a smart response without AI API""" prompt_lower = prompt.lower() # Extract key information from context context_lines = [line.strip() for line in context.split('\n') if line.strip() and not line.strip().startswith('Information about')] # Create a personalized response based on the question type if any(word in prompt_lower for word in ['menu', 'food', 'dish', 'eat', 'pizza', 'pasta']): response = "šŸ• **Our Menu Highlights:**\n\n" for line in context_lines: if line.startswith('- '): response += line + "\n" response += "\n✨ All our dishes are prepared fresh daily with high-quality ingredients!" elif any(word in prompt_lower for word in ['baby', 'child', 'kid', 'family']): response = "šŸ‘¶ **Family-Friendly Features:**\n\n" for line in context_lines: if line.startswith('- '): response += line + "\n" response += "\nšŸ  We love welcoming families and have everything you need for a comfortable dining experience!" elif any(word in prompt_lower for word in ['refund', 'money', 'return', 'policy']): response = "šŸ’° **Our Refund Policy:**\n\n" for line in context_lines: if line.startswith('- '): response += line + "\n" response += "\n😊 Customer satisfaction is our top priority!" elif any(word in prompt_lower for word in ['hour', 'open', 'weekend', 'time']): response = "šŸ•’ **Restaurant Hours:**\n\n" for line in context_lines: if line.startswith('- '): response += line + "\n" response += "\nšŸ“ž We recommend calling ahead for weekend reservations!" elif any(word in prompt_lower for word in ['allergy', 'allergic', 'gluten', 'vegan']): response = "šŸ„— **Dietary Accommodations:**\n\n" for line in context_lines: if line.startswith('- '): response += line + "\n" response += "\nāš ļø Please always inform our staff about any allergies when ordering!" elif any(word in prompt_lower for word in ['contact', 'phone', 'address', 'delivery']): response = "šŸ“ž **Contact & Delivery Info:**\n\n" for line in context_lines: if line.startswith('- '): response += line + "\n" response += "\n🚚 We're here to serve you however you prefer!" else: # General response response = "šŸ¤– **Here's what I found for you:**\n\n" for line in context_lines[:5]: # Show first 5 relevant lines if line.startswith('- '): response += line + "\n" response += "\nšŸ’¬ Feel free to ask me more specific questions about our restaurant!" return response def answer_question(self, question): """Answer a question using simple RAG""" if not question.strip(): return "šŸ‘‹ Hi there! I'm here to help you with questions about our restaurant. What would you like to know?" # Search for relevant documents search_results = self.search_documents(question) # Prepare context from search results context = "" for result in search_results: context += f"Information about {result['title']}:\n" context += f"{result['content']}\n\n" if not context: return "šŸ¤” I don't have specific information about that in my knowledge base. Could you try asking about our policies, hours, reservations, or menu? I'm here to help!" # Generate answer using LLM (or fallback to context) answer = self.call_together_ai(question, context) return answer # Initialize the RAG chatbot rag_chatbot = SimpleRAGChatbot() def chatbot_interface(message, history): """Interface function for Gradio""" return rag_chatbot.answer_question(message) # Create the Gradio interface demo = gr.ChatInterface( fn=chatbot_interface, title="šŸ• Restaurant Assistant - RAG Powered", description=""" **Welcome to our restaurant's AI assistant!** I'm powered by RAG (Retrieval-Augmented Generation) technology, which means I can provide accurate, up-to-date information about our restaurant by searching through our knowledge base. šŸŖ **Ask me about:** - šŸ½ļø Menu highlights and special dishes - šŸ“‹ Restaurant policies (refund, privacy, allergens) - šŸ‘¶ Family-friendly amenities and baby policies - šŸ•’ Weekend hours and reservations - šŸ“ž Contact information and delivery options **How it works:** I search through our restaurant's knowledge base to find the most relevant information for your questions, then provide helpful, accurate answers! *Try asking me anything about our restaurant!* """, examples=[ "What are your most popular dishes?", "Can I bring my baby to the restaurant?", "What's your refund policy?", "Are you open on weekends?", "Do you handle food allergies?", "How do I make a reservation?", "What's your contact information?", "Do you offer delivery?", "What's your privacy policy?", "Do you have vegan options?" ], theme=gr.themes.Soft(), css=""" .gradio-container { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } .contain { max-width: 1200px; margin: 0 auto; } .message.user { background: linear-gradient(135deg, #667eea, #764ba2); color: white; border-radius: 18px; padding: 12px 16px; margin: 8px; } .message.bot { background: linear-gradient(135deg, #f093fb, #f5576c); color: white; border-radius: 18px; padding: 12px 16px; margin: 8px; } .chat-interface { border-radius: 15px; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.1); } """ ) # Launch the interface if __name__ == "__main__": demo.launch( share=True, show_error=True, server_name="0.0.0.0", server_port=7860 )