import pandas as pd import numpy as np from sentence_transformers import SentenceTransformer import faiss import gradio as gr from typing import List, Dict, Tuple import os from huggingface_hub import InferenceClient # STEP 1: Enhanced Restaurant Data (100+ items) def create_enhanced_menu_data(): """Create a larger, more diverse menu dataset""" menu_items = [ # Healthy & Protein-Rich Options {"name": "Grilled Chicken Salad", "description": "High-protein grilled chicken breast with mixed greens, cherry tomatoes, cucumber. 35g protein, 450 calories", "category": "Salad", "cuisine": "Continental", "diet": "Non-Vegetarian", "price": 280}, {"name": "Grilled Salmon with Veggies", "description": "Omega-3 rich grilled salmon fillet with steamed vegetables. Low-carb, high-protein meal. 40g protein", "category": "Main Course", "cuisine": "Continental", "diet": "Non-Vegetarian", "price": 480}, {"name": "Protein Smoothie Bowl", "description": "Thick smoothie bowl with whey protein, banana, berries, topped with nuts and seeds. 30g protein", "category": "Breakfast", "cuisine": "Continental", "diet": "Vegetarian", "price": 250}, {"name": "Tandoori Chicken Platter", "description": "Smoky grilled chicken marinated in yogurt and spices. High-protein, low-fat option. 45g protein", "category": "Starter", "cuisine": "Indian", "diet": "Non-Vegetarian", "price": 350}, {"name": "Chicken Tikka Wrap", "description": "Grilled chicken tikka wrapped in whole wheat roti with veggies. Quick, portable, protein-packed meal. 30g protein", "category": "Snack", "cuisine": "Indian", "diet": "Non-Vegetarian", "price": 200}, {"name": "Greek Yogurt Parfait", "description": "Layered Greek yogurt with fresh berries, granola, honey. High-protein breakfast with 20g protein, 300 calories", "category": "Breakfast", "cuisine": "Continental", "diet": "Vegetarian", "price": 180}, {"name": "Egg White Omelette with Veggies", "description": "Fluffy egg white omelette loaded with vegetables. Low-calorie, high-protein breakfast. 20g protein, 150 calories", "category": "Breakfast", "cuisine": "Continental", "diet": "Non-Vegetarian", "price": 140}, {"name": "Paneer Bhurji with Multigrain Toast", "description": "Scrambled cottage cheese with spices, served with multigrain toast. High-protein vegetarian breakfast. 25g protein", "category": "Breakfast", "cuisine": "Indian", "diet": "Vegetarian", "price": 160}, # Comfort Food {"name": "Paneer Tikka Masala", "description": "Cottage cheese cubes in creamy tomato gravy with aromatic spices. Rich and indulgent comfort food perfect for cozy evenings", "category": "Main Course", "cuisine": "North Indian", "diet": "Vegetarian", "price": 290}, {"name": "Butter Chicken with Naan", "description": "Tender chicken in rich, creamy tomato gravy. Indulgent comfort food perfect for cheat days", "category": "Main Course", "cuisine": "North Indian", "diet": "Non-Vegetarian", "price": 340}, {"name": "Dal Tadka with Brown Rice", "description": "Yellow lentils tempered with cumin and garlic, served with brown rice. Light, healthy comfort food with balanced nutrition", "category": "Main Course", "cuisine": "North Indian", "diet": "Vegetarian", "price": 180}, {"name": "Mac and Cheese", "description": "Creamy macaroni pasta with three types of cheese. Ultimate comfort food that soothes the soul", "category": "Main Course", "cuisine": "Continental", "diet": "Vegetarian", "price": 260}, {"name": "Lentil Soup with Whole Wheat Bread", "description": "Hearty lentil soup with vegetables, served with whole wheat bread. Warming comfort food, high in fiber and protein", "category": "Soup", "cuisine": "Continental", "diet": "Vegetarian", "price": 160}, {"name": "Spicy Szechuan Noodles", "description": "Fiery noodles tossed in Szechuan sauce with vegetables. Perfect when you're craving something spicy and bold", "category": "Main Course", "cuisine": "Chinese", "diet": "Vegetarian", "price": 220}, # Vegan Options {"name": "Quinoa Buddha Bowl", "description": "Superfood bowl with quinoa, roasted vegetables, avocado, chickpeas. Vegan-friendly, high in fiber and nutrients. 15g protein, 400 calories", "category": "Salad", "cuisine": "Continental", "diet": "Vegan", "price": 320}, {"name": "Vegan Pad Thai", "description": "Rice noodles stir-fried with tofu, peanuts, bean sprouts in tangy tamarind sauce. Plant-based and flavorful", "category": "Main Course", "cuisine": "Thai", "diet": "Vegan", "price": 300}, {"name": "Vegan Chocolate Cake", "description": "Rich, moist chocolate cake made without dairy or eggs. Guilt-free indulgence for celebrations", "category": "Dessert", "cuisine": "Continental", "diet": "Vegan", "price": 180}, {"name": "Hummus with Pita Bread", "description": "Creamy chickpea hummus served with warm pita bread. Protein-rich vegan appetizer", "category": "Starter", "cuisine": "Mediterranean", "diet": "Vegan", "price": 160}, # Breakfast Items {"name": "Masala Dosa", "description": "Crispy rice crepe filled with spiced potato masala. Light yet filling breakfast option. Served with sambar and chutney", "category": "Breakfast", "cuisine": "South Indian", "diet": "Vegetarian", "price": 120}, {"name": "Idli Sambar", "description": "Steamed rice cakes served with lentil soup and coconut chutney. Light and healthy breakfast", "category": "Breakfast", "cuisine": "South Indian", "diet": "Vegetarian", "price": 90}, {"name": "Aloo Paratha", "description": "Whole wheat flatbread stuffed with spiced potato filling, served with curd. Hearty North Indian breakfast", "category": "Breakfast", "cuisine": "North Indian", "diet": "Vegetarian", "price": 100}, {"name": "Avocado Toast with Eggs", "description": "Whole grain toast with mashed avocado, poached eggs, cherry tomatoes. Trendy breakfast loaded with healthy fats and protein", "category": "Breakfast", "cuisine": "Continental", "diet": "Vegetarian", "price": 240}, {"name": "Pancakes with Maple Syrup", "description": "Fluffy pancakes served with butter and maple syrup. Classic comfort breakfast", "category": "Breakfast", "cuisine": "Continental", "diet": "Vegetarian", "price": 200}, # Celebration Food {"name": "Chicken Biryani", "description": "Fragrant basmati rice with tender chicken, aromatic spices, served with raita. A hearty meal perfect for celebrations", "category": "Main Course", "cuisine": "Indian", "diet": "Non-Vegetarian", "price": 320}, {"name": "Mutton Rogan Josh", "description": "Tender mutton cooked in aromatic Kashmiri spices. A royal dish perfect for special occasions", "category": "Main Course", "cuisine": "North Indian", "diet": "Non-Vegetarian", "price": 420}, {"name": "Lobster Thermidor", "description": "Luxurious lobster in creamy sauce, perfect for celebrations and special moments", "category": "Main Course", "cuisine": "French", "diet": "Non-Vegetarian", "price": 980}, # More variety {"name": "Margherita Pizza", "description": "Classic pizza with fresh mozzarella, tomato sauce, and basil leaves", "category": "Main Course", "cuisine": "Italian", "diet": "Vegetarian", "price": 350}, {"name": "Chicken Wings", "description": "Crispy fried chicken wings tossed in spicy buffalo sauce", "category": "Starter", "cuisine": "Continental", "diet": "Non-Vegetarian", "price": 280}, {"name": "Caesar Salad", "description": "Fresh romaine lettuce, croutons, parmesan cheese, caesar dressing", "category": "Salad", "cuisine": "Continental", "diet": "Vegetarian", "price": 220}, {"name": "Tom Yum Soup", "description": "Hot and sour Thai soup with mushrooms, lemongrass, lime. Light, flavorful, perfect when feeling under the weather", "category": "Soup", "cuisine": "Thai", "diet": "Vegetarian", "price": 180}, {"name": "Fish and Chips", "description": "Crispy battered fish fillet with french fries and tartar sauce", "category": "Main Course", "cuisine": "Continental", "diet": "Non-Vegetarian", "price": 380}, {"name": "Paneer Tikka", "description": "Marinated cottage cheese chunks grilled with bell peppers and onions", "category": "Starter", "cuisine": "Indian", "diet": "Vegetarian", "price": 240}, {"name": "Veg Spring Rolls", "description": "Crispy rolls filled with mixed vegetables and served with sweet chili sauce", "category": "Starter", "cuisine": "Chinese", "diet": "Vegetarian", "price": 140}, {"name": "Chocolate Brownie with Ice Cream", "description": "Rich chocolate brownie with vanilla ice cream. Perfect dessert for celebrations", "category": "Dessert", "cuisine": "Continental", "diet": "Vegetarian", "price": 160}, {"name": "Gulab Jamun", "description": "Soft milk dumplings soaked in sugar syrup. Traditional Indian sweet perfect for festivals", "category": "Dessert", "cuisine": "Indian", "diet": "Vegetarian", "price": 80}, {"name": "Tiramisu", "description": "Italian dessert with coffee-soaked ladyfingers and mascarpone cream", "category": "Dessert", "cuisine": "Italian", "diet": "Vegetarian", "price": 220}, {"name": "Sushi Platter", "description": "Assorted sushi rolls with wasabi and soy sauce. Light and healthy Japanese delicacy", "category": "Main Course", "cuisine": "Japanese", "diet": "Non-Vegetarian", "price": 680}, {"name": "Ramen Bowl", "description": "Japanese noodle soup with rich broth, vegetables, and choice of protein. Comforting and flavorful", "category": "Main Course", "cuisine": "Japanese", "diet": "Non-Vegetarian", "price": 380}, {"name": "Falafel Wrap", "description": "Crispy chickpea falafel in pita with tahini sauce and fresh veggies. Healthy vegan option", "category": "Snack", "cuisine": "Mediterranean", "diet": "Vegan", "price": 180}, ] return pd.DataFrame(menu_items) def load_restaurant_data(): """Try loading from Kaggle, fallback to enhanced local data""" try: import kagglehub print("Attempting to download from Kaggle...") path = kagglehub.dataset_download("graphquest/restaurant-menu-items") df = pd.read_csv(f"{path}/menu_items.csv") df = df.dropna(subset=['name', 'description']) # Ensure required columns if 'category' not in df.columns: df['category'] = 'Main Course' if 'price' not in df.columns: df['price'] = np.random.randint(100, 500, len(df)) if 'cuisine' not in df.columns: df['cuisine'] = 'International' if 'diet' not in df.columns: df['diet'] = 'Vegetarian' print(f"✅ Loaded {len(df)} items from Kaggle!") return df.head(500) except Exception as e: print(f"Kaggle download failed, using local enhanced dataset with 37 items") return create_enhanced_menu_data() # STEP 2: Real LLM using Hugging Face class HuggingFaceReasoningGenerator: """Uses Hugging Face Inference API for real LLM generation""" def __init__(self, hf_token=None): self.hf_token = hf_token or os.getenv("HF_TOKEN") if self.hf_token: # Using a good open-source model self.client = InferenceClient(token=self.hf_token) self.model = "mistralai/Mistral-7B-Instruct-v0.2" print(f"✅ Using real LLM: {self.model}") else: print("⚠️ No HF token provided, will use fallback reasoning") self.client = None def generate_reasoning(self, dish_name: str, dish_description: str, user_query: str, context: Dict) -> str: """Generate personalized reasoning using Hugging Face LLM""" if not self.client: return self._fallback_reasoning(dish_name, dish_description, context) prompt = f"""You are a food recommendation assistant. Be concise and friendly. User Query: "{user_query}" Dish: {dish_name} Description: {dish_description} Context: Situation: {', '.join(context.get('situation', []))}, Dietary: {', '.join(context.get('dietary', []))} Write ONE short sentence (max 20 words) explaining why this dish is perfect for the user. Be specific and natural. Reasoning:""" try: response = self.client.text_generation( prompt, model=self.model, max_new_tokens=50, temperature=0.7, return_full_text=False ) # Clean up the response reasoning = response.strip() if reasoning: reasoning = reasoning.split('.')[0] + '.' return reasoning else: return self._fallback_reasoning(dish_name, dish_description, context) except Exception as e: print(f"LLM Error: {e}") return self._fallback_reasoning(dish_name, dish_description, context) def _fallback_reasoning(self, dish_name, dish_description, context): """Fallback when LLM fails""" desc_lower = dish_description.lower() if 'post_workout' in context.get('situation', []) and 'protein' in desc_lower: return f"Excellent post-workout choice with high protein for muscle recovery." if 'celebration' in context.get('situation', []): return f"Perfect for celebrations with its indulgent and special flavors." if 'comfort' in context.get('situation', []): return f"Comforting and satisfying when you need a mood boost." if 'vegan' in context.get('dietary', []): return f"Great plant-based option that's both nutritious and delicious." return f"This dish matches your preferences perfectly." # STEP 3: Context Extractor class ContextExtractor: def __init__(self): self.context_patterns = { 'post_workout': ['workout', 'gym', 'exercise', 'training', 'fitness'], 'celebration': ['celebration', 'party', 'birthday', 'anniversary', 'special'], 'comfort': ['stressed', 'tired', 'comfort', 'cozy', 'relax', 'bad day'], 'healthy': ['healthy', 'diet', 'nutrition', 'fit', 'wellness'], } self.dietary_patterns = { 'vegetarian': ['vegetarian', 'veg', 'no meat'], 'vegan': ['vegan', 'plant-based'], 'protein': ['protein', 'high protein'], } def extract_context(self, query: str) -> Dict: query_lower = query.lower() context = {'situation': [], 'dietary': []} for situation, keywords in self.context_patterns.items(): if any(kw in query_lower for kw in keywords): context['situation'].append(situation) for diet, keywords in self.dietary_patterns.items(): if any(kw in query_lower for kw in keywords): context['dietary'].append(diet) return context # STEP 4: Neural Search System class GenAINeuralSearch: def __init__(self, menu_df: pd.DataFrame, hf_token=None): print("🚀 Initializing GenAI Neural Search System...") self.model = SentenceTransformer('all-MiniLM-L6-v2') self.menu_df = menu_df self.context_extractor = ContextExtractor() self.llm_generator = HuggingFaceReasoningGenerator(hf_token) self.menu_df['search_text'] = self.menu_df.apply( lambda row: f"{row['name']}. {row['description']}. {row['category']}. {row['cuisine']}. {row['diet']}.", axis=1 ) print("📊 Creating embeddings...") self.embeddings = self.model.encode( self.menu_df['search_text'].tolist(), show_progress_bar=True ) print("🔍 Building FAISS index...") dimension = self.embeddings.shape[1] self.index = faiss.IndexFlatIP(dimension) faiss.normalize_L2(self.embeddings) self.index.add(self.embeddings) print(f"✅ System ready with {len(self.menu_df)} menu items!") def search_with_genai(self, query: str, top_k: int = 5) -> Tuple[List[Dict], Dict]: context = self.context_extractor.extract_context(query) query_embedding = self.model.encode([query]) faiss.normalize_L2(query_embedding) similarities, indices = self.index.search(query_embedding, top_k) results = [] for idx, score in zip(indices[0], similarities[0]): item = self.menu_df.iloc[idx] # Generate LLM reasoning llm_reasoning = self.llm_generator.generate_reasoning( dish_name=item['name'], dish_description=item['description'], user_query=query, context=context ) results.append({ 'name': item['name'], 'description': item['description'], 'category': item['category'], 'cuisine': item['cuisine'], 'diet': item['diet'], 'price': f"₹{item['price']}", 'similarity_score': float(score), 'ai_reasoning': llm_reasoning }) return results, context # STEP 5: Initialize print("Loading menu data...") menu_df = load_restaurant_data() print("Initializing search system...") search_system = GenAINeuralSearch(menu_df, hf_token=os.getenv("HF_TOKEN")) # STEP 6: Gradio Interface def search_with_genai(query, num_results): if not query.strip(): return "

Please enter a search query to get started!

" results, context = search_system.search_with_genai(query, top_k=num_results) # Context display html_output = "
" html_output += f"

🔍 Results for: \"{query}\"

" if context['situation'] or context['dietary']: html_output += "
" html_output += "🧠 Context Understanding:
" if context['situation']: html_output += f"📍 Situation: {', '.join(context['situation'])}" if context['dietary']: html_output += f"🥗 Dietary: {', '.join(context['dietary'])}" html_output += "
" html_output += "
" # Results for i, result in enumerate(results, 1): html_output += f"""

{i}. {result['name']}

{result['price']}
🤖 AI Reasoning:

{result['ai_reasoning']}

{result['description']}

{result['category']} {result['cuisine']} {result['diet']}

Relevance: {result['similarity_score']:.1%}

""" return html_output # Gradio Interface with gr.Blocks(theme=gr.themes.Soft(), title="Swiggy Neural Search - GenAI") as demo: gr.Markdown(""" # 🍽️ Swiggy Neural Search - GenAI Edition **Powered by:** Sentence Transformers + FAISS + Hugging Face LLM (Mistral-7B) This system understands your context and generates personalized explanations using AI! """) with gr.Row(): with gr.Column(scale=2): query_input = gr.Textbox( label="What are you looking for?", placeholder="Try: 'I just finished my workout. Show me healthy lunch options'", lines=3 ) num_results = gr.Slider(minimum=3, maximum=8, value=5, step=1, label="Number of results") search_btn = gr.Button("🔍 Search", variant="primary", size="lg") output_html = gr.HTML(label="Results") gr.Examples( examples=[ ["I just finished my workout. Show me healthy lunch options"], ["I'm feeling stressed, need comfort food"], ["It's my birthday! Show me celebration-worthy food"], ["Vegan protein-rich dinner options"], ["Something light for breakfast"], ], inputs=query_input ) search_btn.click( fn=search_with_genai, inputs=[query_input, num_results], outputs=output_html ) if __name__ == "__main__": demo.launch()