HassanJalil's picture
Update app.py
e57837e verified
import streamlit as st
import google.generativeai as genai
import json
import os
from typing import List, Dict, Any
import re
# Configure page
st.set_page_config(
page_title="🍳 AI Recipe Generator",
page_icon="🍳",
layout="wide",
initial_sidebar_state="collapsed"
)
# Custom CSS for better styling
st.markdown("""
<style>
.main-header {
text-align: center;
padding: 2rem 0;
background: linear-gradient(90deg, #ff6b6b, #4ecdc4);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
font-size: 3rem;
font-weight: bold;
margin-bottom: 2rem;
}
.recipe-card {
background: #f8f9fa;
padding: 1.5rem;
border-radius: 10px;
border-left: 4px solid #4ecdc4;
margin: 1rem 0;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.recipe-title {
color: #2c3e50;
font-size: 1.5rem;
font-weight: bold;
margin-bottom: 1rem;
}
.time-badge {
background: #e74c3c;
color: white;
padding: 0.3rem 0.8rem;
border-radius: 15px;
font-size: 0.8rem;
margin-right: 0.5rem;
}
.tip-box {
background: #fff3cd;
border: 1px solid #ffeaa7;
padding: 1rem;
border-radius: 5px;
margin-top: 1rem;
}
</style>
""", unsafe_allow_html=True)
class RecipeRAG:
"""Recipe Retrieval-Augmented Generation System"""
def __init__(self):
self.api_key = None
self.model = None
self.sample_recipes = self.load_sample_recipes()
def load_sample_recipes(self) -> List[Dict]:
"""Load sample recipe database for RAG retrieval"""
return [
{
"name": "Classic Scrambled Eggs",
"ingredients": ["eggs", "butter", "salt", "pepper", "milk"],
"category": "breakfast"
},
{
"name": "Tomato Garlic Pasta",
"ingredients": ["pasta", "tomato", "garlic", "olive oil", "basil"],
"category": "dinner"
},
{
"name": "French Omelette",
"ingredients": ["eggs", "butter", "herbs", "cheese", "salt"],
"category": "breakfast"
},
{
"name": "Garlic Butter Rice",
"ingredients": ["rice", "garlic", "butter", "onion", "herbs"],
"category": "side"
},
{
"name": "Simple Fried Rice",
"ingredients": ["rice", "eggs", "soy sauce", "garlic", "vegetables"],
"category": "main"
},
{
"name": "Tomato Onion Salad",
"ingredients": ["tomato", "onion", "olive oil", "vinegar", "herbs"],
"category": "salad"
},
{
"name": "Garlic Roasted Vegetables",
"ingredients": ["vegetables", "garlic", "olive oil", "herbs", "salt"],
"category": "side"
}
]
def setup_gemini(self, api_key: str) -> bool:
"""Initialize Gemini API"""
try:
genai.configure(api_key=api_key)
self.model = genai.GenerativeModel('gemini-1.5-flash')
self.api_key = api_key
return True
except Exception as e:
st.error(f"Failed to initialize Gemini API: {str(e)}")
return False
def retrieve_relevant_recipes(self, user_ingredients: List[str]) -> List[Dict]:
"""RAG Step 1: Retrieve relevant recipes based on ingredients"""
user_ingredients = [ing.lower().strip() for ing in user_ingredients]
relevant_recipes = []
for recipe in self.sample_recipes:
# Calculate ingredient overlap
recipe_ingredients = [ing.lower() for ing in recipe["ingredients"]]
overlap = len(set(user_ingredients) & set(recipe_ingredients))
if overlap > 0:
recipe_score = overlap / len(recipe_ingredients)
relevant_recipes.append({
**recipe,
"relevance_score": recipe_score,
"matching_ingredients": overlap
})
# Sort by relevance and return top matches
relevant_recipes.sort(key=lambda x: x["relevance_score"], reverse=True)
return relevant_recipes[:3] # Return top 3 matches
def generate_recipes_with_gemini(self, user_ingredients: List[str], relevant_recipes: List[Dict]) -> List[Dict]:
"""RAG Step 2: Use Gemini to generate complete recipes based on retrieved context"""
ingredients_text = ", ".join(user_ingredients)
context_recipes = "\n".join([f"- {r['name']}: {', '.join(r['ingredients'])}"
for r in relevant_recipes])
prompt = f"""
Based on the following ingredients: {ingredients_text}
Context from similar recipes:
{context_recipes}
Generate 4 complete recipes that can be made using the given ingredients. For each recipe, provide:
1. Recipe Name
2. Complete ingredient list (including quantities)
3. Step-by-step cooking instructions (numbered)
4. Preparation time (in minutes)
5. Cooking time (in minutes)
6. A helpful cooking tip or variation
Format your response as JSON with this structure:
{{
"recipes": [
{{
"name": "Recipe Name",
"ingredients_with_quantities": ["2 eggs", "1 tbsp butter", "..."],
"instructions": ["Step 1: ...", "Step 2: ...", "..."],
"prep_time": 5,
"cook_time": 10,
"tip": "Helpful tip here"
}}
]
}}
Make sure recipes are practical and can actually be made with the provided ingredients.
"""
try:
response = self.model.generate_content(prompt)
# Extract JSON from response
response_text = response.text.strip()
# Try to find JSON in the response
json_match = re.search(r'\{.*\}', response_text, re.DOTALL)
if json_match:
json_text = json_match.group()
recipes_data = json.loads(json_text)
return recipes_data.get("recipes", [])
else:
# Fallback: parse response manually
return self.parse_text_response(response_text)
except Exception as e:
st.error(f"Error generating recipes: {str(e)}")
return []
def parse_text_response(self, text: str) -> List[Dict]:
"""Fallback parser for non-JSON responses"""
recipes = []
# This is a simple fallback - in production you'd want more robust parsing
lines = text.split('\n')
current_recipe = {}
for line in lines:
line = line.strip()
if 'Recipe:' in line or 'Name:' in line:
if current_recipe:
recipes.append(current_recipe)
current_recipe = {"name": line.split(':')[-1].strip()}
elif 'Prep time:' in line:
current_recipe["prep_time"] = 10 # Default fallback
elif 'Cook time:' in line:
current_recipe["cook_time"] = 15 # Default fallback
return recipes[:4] if recipes else []
def main():
# Header
st.markdown('<h1 class="main-header">🍳 AI Recipe Generator</h1>', unsafe_allow_html=True)
st.markdown("### Transform your ingredients into delicious recipes using AI magic! ✨")
# Initialize RAG system
if 'rag_system' not in st.session_state:
st.session_state.rag_system = RecipeRAG()
rag_system = st.session_state.rag_system
# API Key input
with st.sidebar:
st.header("πŸ”‘ Configuration")
api_key = st.text_input(
"Google Gemini API Key",
type="password",
help="Get your API key from Google AI Studio"
)
if api_key and api_key != st.session_state.get('current_api_key'):
if rag_system.setup_gemini(api_key):
st.session_state.current_api_key = api_key
st.success("βœ… API key configured successfully!")
else:
st.error("❌ Invalid API key")
# Main interface
col1, col2 = st.columns([2, 1])
with col1:
st.markdown("#### πŸ“ Enter Your Ingredients")
ingredients_input = st.text_input(
"",
placeholder="e.g., onion, tomato, garlic, eggs, rice",
help="Enter ingredients separated by commas"
)
with col2:
st.markdown("#### πŸš€ Generate Recipes")
generate_button = st.button(
"Generate Recipes",
type="primary",
use_container_width=True
)
# Validation and processing
if generate_button:
if not api_key:
st.error("⚠️ Please enter your Google Gemini API key in the sidebar first!")
st.info("πŸ’‘ Get your free API key from [Google AI Studio](https://makersuite.google.com/app/apikey)")
return
if not ingredients_input.strip():
st.error("⚠️ Please enter some ingredients first!")
return
# Process ingredients
user_ingredients = [ing.strip() for ing in ingredients_input.split(',') if ing.strip()]
if len(user_ingredients) < 2:
st.error("⚠️ Please enter at least 2 ingredients for better recipe suggestions!")
return
# Show loading
with st.spinner("πŸ€– AI is cooking up some amazing recipes for you..."):
# RAG Step 1: Retrieve relevant recipes
relevant_recipes = rag_system.retrieve_relevant_recipes(user_ingredients)
# RAG Step 2: Generate recipes with Gemini
generated_recipes = rag_system.generate_recipes_with_gemini(user_ingredients, relevant_recipes)
# Display results
if generated_recipes:
st.markdown("---")
st.markdown("## 🍽️ Your Personalized Recipes")
for i, recipe in enumerate(generated_recipes, 1):
with st.expander(f"πŸ“– Recipe {i}: {recipe.get('name', 'Delicious Recipe')}", expanded=i==1):
# Recipe header with timing
col1, col2, col3 = st.columns(3)
with col1:
st.markdown(f"**⏱️ Prep:** {recipe.get('prep_time', 10)} mins")
with col2:
st.markdown(f"**πŸ”₯ Cook:** {recipe.get('cook_time', 15)} mins")
with col3:
total_time = recipe.get('prep_time', 10) + recipe.get('cook_time', 15)
st.markdown(f"**⏰ Total:** {total_time} mins")
st.markdown("---")
# Ingredients
st.markdown("#### πŸ›’ Ingredients:")
ingredients = recipe.get('ingredients_with_quantities', recipe.get('ingredients', []))
for ingredient in ingredients:
st.markdown(f"β€’ {ingredient}")
# Instructions
st.markdown("#### πŸ‘¨β€πŸ³ Instructions:")
instructions = recipe.get('instructions', [])
for j, instruction in enumerate(instructions, 1):
st.markdown(f"**{j}.** {instruction}")
# Tip
tip = recipe.get('tip', 'Enjoy your cooking!')
if tip:
st.markdown(f"""
<div class="tip-box">
<strong>πŸ’‘ Pro Tip:</strong> {tip}
</div>
""", unsafe_allow_html=True)
else:
st.error("πŸ˜” Couldn't generate recipes. Please try again with different ingredients!")
# Footer
st.markdown("---")
st.markdown("""
<div style="text-align: center; color: #666; padding: 2rem;">
Made with ❀️ using Streamlit and Google Gemini Pro API<br>
🌟 Happy Cooking! 🌟
</div>
""", unsafe_allow_html=True)
if __name__ == "__main__":
main()