diff --git a/advanced_ai_agents/single_agent_apps/ai_recipe_meal_planning_agent/README.md b/advanced_ai_agents/single_agent_apps/ai_recipe_meal_planning_agent/README.md new file mode 100644 index 0000000..d92b820 --- /dev/null +++ b/advanced_ai_agents/single_agent_apps/ai_recipe_meal_planning_agent/README.md @@ -0,0 +1,164 @@ +# 🍽️ AI Recipe & Meal Planning Agent + +An intelligent meal planning agent built with Agno that helps you discover recipes, analyze nutrition, estimate costs, and create weekly meal plans based on your ingredients and dietary preferences. + +## Features + +🔍 **Recipe Discovery** +- Find recipes based on available ingredients +- Support for dietary restrictions (vegetarian, vegan, keto, paleo, etc.) +- Ingredient substitution suggestions +- Detailed cooking instructions and timing + +📊 **Nutrition Analysis** +- Comprehensive nutritional breakdown per serving +- User-friendly health assessments +- Calorie, protein, carb, and fat tracking +- Sodium and fiber content analysis + +💰 **Cost Estimation** +- Grocery cost estimation for ingredients +- Budget-friendly meal suggestions +- Cost per serving calculations + +📅 **Weekly Meal Planning** +- Balanced meal plans for any household size +- Dietary preference accommodation +- Shopping list optimization +- Budget-conscious planning + +🧠 **Session-Based Conversations** +- Remembers context during your current browser session +- Preferences are not persisted after restart (no long-term storage) + +### How to get Started? + +1. Clone the GitHub repository + +```bash +git clone https://github.com/Shubhamsaboo/awesome-llm-apps.git +cd advanced_ai_agents/single_agent_apps/ai_recipe_meal_planning_agent +``` + +2. Install the required dependencies: + +```bash +pip install -r requirements.txt +``` + +3. Get your OpenAI API Key + +- Sign up for an [OpenAI account](https://platform.openai.com/) and obtain your API key. + +4. Get your Spoonacular API Key + +- Sign up for a [Spoonacular account](https://spoonacular.com/food-api) and obtain your API key (free tier ~50 requests/day). + +5. Create a `.env` file in this folder + +```bash +# Required +OPENAI_API_KEY=your_openai_api_key_here + +# Optional but recommended for full recipe & nutrition functionality +SPOONACULAR_API_KEY=your_spoonacular_api_key_here +``` + +6. Run the Streamlit App + +```bash +streamlit run ai_recipe_meal_planning_agent.py +``` + +7. Open your browser at `http://localhost:8501` + +## Example Interactions + +**Recipe Discovery:** +- "I have chicken, broccoli, and rice. What can I make?" +- "Find me vegan recipes using lentils" +- "Show me quick 30-minute dinner ideas" + +**Nutrition Analysis:** +- "What's the nutritional content of this recipe?" +- "Is this meal high in protein?" +- "How many calories per serving?" + +**Meal Planning:** +- "Create a week's worth of vegetarian meals for 2 people" +- "I need a low-sodium meal plan" +- "Plan budget-friendly meals for a family of 4" + +**Cost Estimation:** +- "How much will these ingredients cost?" +- "What's the most budget-friendly option?" +- "Estimate weekly grocery costs for this meal plan" + +## Application Architecture + +### Built with Agno Framework +- **Agent**: OpenAI GPT-5 mini powered meal planning agent +- **Memory**: Conversation memory for personalized recommendations +- **Tools**: Custom tools for recipe search and analysis + DuckDuckGo web search +- **Interface**: Streamlit web application + +### Custom Tools +1. `search_recipes(ingredients, diet_type=None)` - Recipe discovery via Spoonacular API with detailed instructions +2. `analyze_nutrition(recipe_name)` - Detailed nutritional analysis via Spoonacular +3. `estimate_costs(ingredients, servings=4)` - Budget planning and cost estimation +4. `create_meal_plan(dietary_preference="balanced", people=2, days=7, budget="moderate")` - Comprehensive weekly meal planning with shopping list +5. `DuckDuckGoTools` - Web search for additional context + +### Key Technologies +- **Agno**: AI agent framework +- **Streamlit**: Web interface and user interaction +- **Spoonacular API**: Recipe and nutrition data +- **OpenAI GPT-5 mini**: Natural language understanding and generation + +## Customization + +### Adding New Dietary Preferences +Modify the `search_recipes` tool to include additional diet types supported by Spoonacular API. + +### Extending Cost Database +Update the `ingredient_costs` dictionary in `estimate_grocery_costs()` with local pricing. + +### Custom Meal Categories +Edit the `meal_categories` in `create_weekly_meal_plan()` to match your preferences. + +## Troubleshooting + +**API Key Issues:** +- Ensure your `.env` file is in the correct directory +- Verify API keys are valid and have sufficient credits +- Check API key format (no extra spaces or quotes) + - Note: Without `SPOONACULAR_API_KEY`, recipe search and nutrition tools will return an error; other features will still load. + +**Recipe Search Not Working:** +- Verify Spoonacular API key is set correctly +- Check your API usage limits (150 requests/day for free tier) +- Try simpler ingredient searches + +**Memory Issues:** +- The agent uses conversation memory to remember preferences +- Clear browser cache if experiencing persistent issues +- Restart the application to reset conversation history + +## Contributing + +Feel free to contribute by: +- Adding new recipe sources or APIs +- Improving nutrition analysis algorithms +- Enhancing cost estimation accuracy +- Adding new meal planning features + +## License + +This project is open source. Please check the main repository for license details. + +## Support + +For issues and questions: +- Check the troubleshooting section above +- Review the Agno documentation +- Open an issue in the main repository diff --git a/advanced_ai_agents/single_agent_apps/ai_recipe_meal_planning_agent/ai_recipe_meal_planning_agent.py b/advanced_ai_agents/single_agent_apps/ai_recipe_meal_planning_agent/ai_recipe_meal_planning_agent.py new file mode 100644 index 0000000..3c419d3 --- /dev/null +++ b/advanced_ai_agents/single_agent_apps/ai_recipe_meal_planning_agent/ai_recipe_meal_planning_agent.py @@ -0,0 +1,375 @@ +import asyncio +import os +import streamlit as st +import random +from textwrap import dedent +from typing import Dict, List, Optional + +from agno.agent import Agent +from agno.models.openai import OpenAIChat +from agno.tools import tool +import requests +from dotenv import load_dotenv +from agno.tools.duckduckgo import DuckDuckGoTools + +load_dotenv() + +SPOONACULAR_API_KEY = os.getenv("SPOONACULAR_API_KEY") +OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") + +@tool +def search_recipes(ingredients: str, diet_type: Optional[str] = None) -> Dict: + """Search for detailed recipes with cooking instructions.""" + if not SPOONACULAR_API_KEY: + return {"error": "Spoonacular API key not found"} + + url = "https://api.spoonacular.com/recipes/findByIngredients" + params = { + "apiKey": SPOONACULAR_API_KEY, + "ingredients": ingredients, + "number": 5, + "ranking": 2, + "ignorePantry": True + } + if diet_type: + params["diet"] = diet_type + + try: + response = requests.get(url, params=params, timeout=15) + response.raise_for_status() + recipes = response.json() + + detailed_recipes = [] + for recipe in recipes[:3]: + detail_url = f"https://api.spoonacular.com/recipes/{recipe['id']}/information" + detail_response = requests.get(detail_url, params={"apiKey": SPOONACULAR_API_KEY}, timeout=10) + + if detail_response.status_code == 200: + detail_data = detail_response.json() + detailed_recipes.append({ + "id": recipe['id'], + "title": recipe['title'], + "ready_in_minutes": detail_data.get('readyInMinutes', 'N/A'), + "servings": detail_data.get('servings', 'N/A'), + "health_score": detail_data.get('healthScore', 0), + "used_ingredients": [i['name'] for i in recipe['usedIngredients']], + "missing_ingredients": [i['name'] for i in recipe['missedIngredients']], + "instructions": detail_data.get('instructions', 'Instructions not available') + }) + + return { + "recipes": detailed_recipes, + "total_found": len(recipes) + } + except: + return {"error": "Recipe search failed"} + +@tool +def analyze_nutrition(recipe_name: str) -> Dict: + """Get nutrition analysis for a recipe by searching for it.""" + if not SPOONACULAR_API_KEY: + return {"error": "API key not found"} + + # First search for the recipe + search_url = "https://api.spoonacular.com/recipes/complexSearch" + search_params = { + "apiKey": SPOONACULAR_API_KEY, + "query": recipe_name, + "number": 1, + "addRecipeInformation": True, + "addRecipeNutrition": True + } + + try: + search_response = requests.get(search_url, params=search_params, timeout=15) + search_response.raise_for_status() + search_data = search_response.json() + + if not search_data.get('results'): + return {"error": f"No recipe found for '{recipe_name}'"} + + recipe = search_data['results'][0] + + if 'nutrition' not in recipe: + return {"error": "No nutrition data available for this recipe"} + + nutrients = {n['name']: n['amount'] for n in recipe['nutrition']['nutrients']} + calories = round(nutrients.get('Calories', 0)) + protein = round(nutrients.get('Protein', 0), 1) + carbs = round(nutrients.get('Carbohydrates', 0), 1) + fat = round(nutrients.get('Fat', 0), 1) + fiber = round(nutrients.get('Fiber', 0), 1) + sodium = round(nutrients.get('Sodium', 0), 1) + + # Health insights + health_insights = [] + if protein > 25: + health_insights.append("✅ High protein - great for muscle building") + if fiber > 5: + health_insights.append("✅ High fiber - supports digestive health") + if sodium < 600: + health_insights.append("✅ Low sodium - heart-friendly") + if calories < 400: + health_insights.append("✅ Low calorie - good for weight management") + + return { + "recipe_title": recipe.get('title', 'Recipe'), + "servings": recipe.get('servings', 1), + "ready_in_minutes": recipe.get('readyInMinutes', 'N/A'), + "health_score": recipe.get('healthScore', 0), + "calories": calories, + "protein": protein, + "carbs": carbs, + "fat": fat, + "fiber": fiber, + "sodium": sodium, + "health_insights": health_insights + } + except: + return {"error": "Nutrition analysis failed"} + +@tool +def estimate_costs(ingredients: List[str], servings: int = 4) -> Dict: + """Detailed cost estimation with budget tips.""" + prices = { + "chicken breast": 6.99, "ground beef": 5.99, "salmon": 12.99, + "rice": 2.99, "pasta": 1.99, "broccoli": 2.99, "tomatoes": 3.99, + "cheese": 5.99, "onion": 1.49, "garlic": 2.99, "olive oil": 7.99 + } + + cost_breakdown = [] + total_cost = 0 + + for ingredient in ingredients: + ingredient_lower = ingredient.lower().strip() + cost = 3.99 # default + + for key, price in prices.items(): + if key in ingredient_lower or any(word in ingredient_lower for word in key.split()): + cost = price + break + + adjusted_cost = (cost * servings) / 4 + total_cost += adjusted_cost + cost_breakdown.append({ + "name": ingredient.title(), + "cost": round(adjusted_cost, 2) + }) + + # Budget tips + budget_tips = [] + if total_cost > 30: + budget_tips.append("💡 Consider buying in bulk for better prices") + if total_cost > 40: + budget_tips.append("💡 Look for seasonal alternatives to reduce costs") + budget_tips.append("💡 Shop at local markets for fresher, cheaper produce") + + return { + "total_cost": round(total_cost, 2), + "cost_per_serving": round(total_cost / servings, 2), + "servings": servings, + "breakdown": cost_breakdown, + "budget_tips": budget_tips + } + +@tool +def create_meal_plan(dietary_preference: str = "balanced", people: int = 2, days: int = 7, budget: str = "moderate") -> Dict: + """Create comprehensive weekly meal plan with nutrition and shopping list.""" + + meals = { + "breakfast": [ + {"name": "Overnight Oats with Berries", "calories": 320, "protein": 12, "cost": 2.50}, + {"name": "Veggie Scramble with Toast", "calories": 280, "protein": 18, "cost": 3.20}, + {"name": "Greek Yogurt Parfait", "calories": 250, "protein": 15, "cost": 2.80} + ], + "lunch": [ + {"name": "Quinoa Buddha Bowl", "calories": 420, "protein": 16, "cost": 4.50}, + {"name": "Chicken Caesar Wrap", "calories": 380, "protein": 25, "cost": 5.20}, + {"name": "Lentil Vegetable Soup", "calories": 340, "protein": 18, "cost": 3.80} + ], + "dinner": [ + {"name": "Grilled Salmon with Vegetables", "calories": 520, "protein": 35, "cost": 8.90}, + {"name": "Chicken Stir Fry with Brown Rice", "calories": 480, "protein": 32, "cost": 6.50}, + {"name": "Vegetable Curry with Quinoa", "calories": 450, "protein": 15, "cost": 5.20} + ] + } + + budget_multipliers = {"low": 0.7, "moderate": 1.0, "high": 1.3} + multiplier = budget_multipliers.get(budget.lower(), 1.0) + + weekly_plan = {} + shopping_list = set() + total_weekly_cost = 0 + total_weekly_calories = 0 + total_weekly_protein = 0 + + day_names = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] + + for day in day_names[:days]: + daily_meals = {} + daily_calories = 0 + daily_protein = 0 + daily_cost = 0 + + for meal_type in ["breakfast", "lunch", "dinner"]: + selected_meal = random.choice(meals[meal_type]) + daily_meals[meal_type] = { + "name": selected_meal["name"], + "calories": selected_meal["calories"], + "protein": selected_meal["protein"] + } + + meal_cost = selected_meal["cost"] * people * multiplier + daily_calories += selected_meal["calories"] + daily_protein += selected_meal["protein"] + daily_cost += meal_cost + + # Add to shopping list + if "chicken" in selected_meal["name"].lower(): + shopping_list.add("Chicken breast") + if "salmon" in selected_meal["name"].lower(): + shopping_list.add("Salmon fillets") + if "vegetable" in selected_meal["name"].lower(): + shopping_list.update(["Mixed vegetables", "Onions", "Garlic"]) + if "quinoa" in selected_meal["name"].lower(): + shopping_list.add("Quinoa") + if "oats" in selected_meal["name"].lower(): + shopping_list.add("Rolled oats") + + weekly_plan[day] = daily_meals + total_weekly_cost += daily_cost + total_weekly_calories += daily_calories + total_weekly_protein += daily_protein + + # Generate insights + avg_daily_calories = round(total_weekly_calories / days) + avg_daily_protein = round(total_weekly_protein / days, 1) + + insights = [] + if avg_daily_calories < 1800: + insights.append("⚠️ Consider adding healthy snacks to meet calorie needs") + elif avg_daily_calories > 2200: + insights.append("💡 Calorie-dense meals - great for active lifestyles") + + if avg_daily_protein > 80: + insights.append("✅ Excellent protein intake for muscle maintenance") + elif avg_daily_protein < 60: + insights.append("💡 Consider adding more protein sources") + + return { + "meal_plan": weekly_plan, + "total_weekly_cost": round(total_weekly_cost, 2), + "cost_per_person_per_day": round(total_weekly_cost / (people * days), 2), + "avg_daily_calories": avg_daily_calories, + "avg_daily_protein": avg_daily_protein, + "dietary_preference": dietary_preference, + "serves": people, + "days": days, + "shopping_list": sorted(list(shopping_list)), + "insights": insights + } + +async def create_agent(): + agent = Agent( + name="MealPlanningExpert", + model=OpenAIChat(id="gpt-5-mini"), + tools=[search_recipes, analyze_nutrition, estimate_costs, create_meal_plan, DuckDuckGoTools()], + instructions=dedent("""\ + You are an expert meal planning assistant. Provide detailed, helpful responses: + + 🔍 **Recipe Searches**: Include cooking time, health scores, ingredient lists, and instructions + 📊 **Nutrition Analysis**: Provide health insights, nutritional breakdowns, and dietary advice + 💰 **Cost Estimation**: Include budget tips and cost per serving breakdowns + 📅 **Meal Planning**: Create detailed weekly plans with nutritional balance and shopping lists + + **Always**: + - Use clear headings and bullet points + - Include practical cooking tips + - Consider dietary restrictions and budgets + - Provide actionable next steps + - Be encouraging and supportive + """), + markdown=True, + show_tool_calls=True + ) + return agent + +def main(): + st.set_page_config(page_title="AI Meal Planning Agent", page_icon="🍽️", layout="wide") + + st.title("🍽️ AI Meal Planning Agent") + st.markdown("*Your intelligent companion for recipes, nutrition, and meal planning*") + + if not OPENAI_API_KEY: + st.error("Please add OPENAI_API_KEY to your .env file") + st.stop() + + # Initialize agent + if "agent" not in st.session_state: + with st.spinner("Initializing agent..."): + try: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + st.session_state.agent = loop.run_until_complete(create_agent()) + except Exception as e: + st.error(f"Failed to initialize agent: {e}") + st.stop() + + # Initialize messages + if "messages" not in st.session_state: + st.session_state.messages = [{ + "role": "assistant", + "content": """👋 **Welcome! I'm your AI Meal Planning Expert.** + +I can help you with: +- 🔍 **Recipe Discovery** - Find recipes based on your ingredients +- 📊 **Nutrition Analysis** - Get detailed nutritional insights +- 💰 **Cost Estimation** - Smart budget planning with money-saving tips +- 📅 **Meal Planning** - Complete weekly meal plans with shopping lists + +**Try asking:** +- "Find healthy chicken recipes for dinner" +- "What's the nutrition info for chicken teriyaki?" +- "Create a vegetarian meal plan for 2 people for one week" +- "Estimate costs for pasta, tomatoes, cheese, and basil for 4 servings" + +What would you like to explore? 🍽️""" + }] + + # Chat interface + for message in st.session_state.messages: + with st.chat_message(message["role"]): + st.markdown(message["content"]) + + # Chat input + if user_input := st.chat_input("Ask about recipes, nutrition, meal planning, or costs..."): + st.session_state.messages.append({"role": "user", "content": user_input}) + + with st.chat_message("user"): + st.markdown(user_input) + + with st.chat_message("assistant"): + with st.spinner("Thinking..."): + try: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + response = loop.run_until_complete( + st.session_state.agent.arun(user_input) + ) + + st.markdown(response.content) + st.session_state.messages.append({ + "role": "assistant", + "content": response.content + }) + + except Exception as e: + error_msg = f"Error: {str(e)}" + st.error(error_msg) + st.session_state.messages.append({ + "role": "assistant", + "content": error_msg + }) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/advanced_ai_agents/single_agent_apps/ai_recipe_meal_planning_agent/requirements.txt b/advanced_ai_agents/single_agent_apps/ai_recipe_meal_planning_agent/requirements.txt new file mode 100644 index 0000000..2cc370b --- /dev/null +++ b/advanced_ai_agents/single_agent_apps/ai_recipe_meal_planning_agent/requirements.txt @@ -0,0 +1,5 @@ +streamlit +agno +python-dotenv +requests +duckduckgo-search \ No newline at end of file