0x1ay's picture
Update app.py
ff48030 verified
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 [email protected]
- 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: [email protected]
- 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
)