From a4c9456c3844b7264c44293c8a433c28f74aff61 Mon Sep 17 00:00:00 2001 From: Sarita Bandarupalli Date: Wed, 20 Aug 2025 16:56:30 -0400 Subject: [PATCH] Added Sarita_B contributions to community-contributions --- .../fitness-nutrition-planner-agent/README.md | 101 +++++ .../fitness-nutrition-planner-agent/agent.py | 411 ++++++++++++++++++ .../fitness-nutrition-planner-agent/app.py | 75 ++++ .../demo_output.md | 84 ++++ .../sample_profile.json | 17 + 5 files changed, 688 insertions(+) create mode 100644 community-contributions/fitness-nutrition-planner-agent/README.md create mode 100644 community-contributions/fitness-nutrition-planner-agent/agent.py create mode 100644 community-contributions/fitness-nutrition-planner-agent/app.py create mode 100644 community-contributions/fitness-nutrition-planner-agent/demo_output.md create mode 100644 community-contributions/fitness-nutrition-planner-agent/sample_profile.json diff --git a/community-contributions/fitness-nutrition-planner-agent/README.md b/community-contributions/fitness-nutrition-planner-agent/README.md new file mode 100644 index 0000000..a447792 --- /dev/null +++ b/community-contributions/fitness-nutrition-planner-agent/README.md @@ -0,0 +1,101 @@ + +# Fitness & Nutrition Planner Agent (Community Contribution) + +A tool-using agent that generates a **7‑day vegetarian-friendly meal plan** with **calorie/macro targets** and a **consolidated grocery list**. It supports **targeted swaps** (e.g., "swap Tuesday lunch") while honoring dietary patterns, allergies, and dislikes. + +> **Disclaimer**: This project is for educational purposes and is **not** medical advice. Consult a licensed professional for medical or specialized dietary needs. + +--- + +## ✨ Features +- Calculates **TDEE** and **macro targets** via Mifflin–St Jeor + activity factors. +- Builds a **7‑day plan** (breakfast/lunch/dinner) respecting dietary constraints. +- Produces an aggregated **grocery list** for the week. +- Supports **swap** of any single meal while keeping macros reasonable. +- Minimal **Streamlit UI** for demos. +- Extensible **tool-based architecture** to plug real recipe APIs/DBs. + +--- + +## 🧱 Architecture +- **Agent core**: OpenAI function-calling (tools) with a simple orchestration loop. +- **Tools**: + 1. `calc_calories_and_macros` – computes targets. + 2. `compose_meal_plan` – creates the 7‑day plan. + 3. `grocery_list_from_plan` – consolidates ingredients/quantities. + 4. `swap_meal` – replaces one meal (by kcal proximity and constraints). +- **Recipe source**: a tiny in-memory recipe DB for demo; replace with a real API or your own dataset. + +--- + +## πŸš€ Quickstart + +### 1) Install +```bash +pip install openai streamlit pydantic python-dotenv +``` + +### 2) Configure +Create a `.env` file in this folder: +``` +OPENAI_API_KEY=your_key_here +OPENAI_MODEL=gpt-4o-mini +``` + +### 3) Run CLI (example) +```bash +python agent.py +``` + +### 4) Run UI +```bash +streamlit run app.py +``` + +--- + +## πŸ§ͺ Sample Profile (from issue author) +See `sample_profile.json` for the exact values used to produce `demo_output.md`. +- **Sex**: female +- **Age**: 45 +- **Height**: 152 cm (~5 ft) +- **Weight**: 62 kg +- **Activity**: light +- **Goal**: maintain +- **Diet**: vegetarian + +--- + +## πŸ”§ Extend +- Replace the in-memory recipes with: + - A real **recipe API** (e.g., Spoonacular) or + - Your **own dataset** (CSV/DB) + filters/tags +- Add price lookups to produce a **budget-aware** grocery list. +- Add **adherence tracking** and charts. +- Integrate **wearables** or daily steps to refine TDEE dynamically. +- Add **snacks** for days slightly under target kcals. + +--- + +## πŸ›‘οΈ Safety Notes +- The agent warns for extreme deficits but does **not** diagnose conditions. +- For calorie targets below commonly recommended minimums (e.g., ~1200 kcal/day for many adults), advise consulting a professional. + +--- + +## πŸ“ Project Layout +``` +fitness-nutrition-planner-agent/ +β”œβ”€ README.md +β”œβ”€ agent.py +β”œβ”€ app.py +β”œβ”€ sample_profile.json +└─ demo_output.md +``` + +--- + +## 🀝 How to contribute +- Keep notebooks (if any) with **cleared outputs**. +- Follow the course repo’s contribution guidelines. +- Include screenshots or a short Loom/YT demo link in your PR description. diff --git a/community-contributions/fitness-nutrition-planner-agent/agent.py b/community-contributions/fitness-nutrition-planner-agent/agent.py new file mode 100644 index 0000000..75bcd10 --- /dev/null +++ b/community-contributions/fitness-nutrition-planner-agent/agent.py @@ -0,0 +1,411 @@ + +# agent.py +import os, math, json, copy +from dataclasses import dataclass +from typing import List, Dict, Any, Optional, Tuple +from pydantic import BaseModel, Field, ValidationError +from dotenv import load_dotenv +from openai import OpenAI + +load_dotenv() + +# ------------------------------ +# Data models +# ------------------------------ +class UserProfile(BaseModel): + sex: str = Field(..., description="male or female") + age: int + height_cm: float + weight_kg: float + activity_level: str = Field(..., description="sedentary, light, moderate, active, very_active") + goal: str = Field(..., description="lose, maintain, gain") + dietary_pattern: Optional[str] = Field(None, description="e.g., vegetarian, vegan, halal, kosher") + allergies: List[str] = Field(default_factory=list) + dislikes: List[str] = Field(default_factory=list) + daily_meals: int = 3 + cuisine_prefs: List[str] = Field(default_factory=list) + time_per_meal_minutes: int = 30 + budget_level: Optional[str] = Field(None, description="low, medium, high") + +class MacroTargets(BaseModel): + tdee: int + target_kcal: int + protein_g: int + carbs_g: int + fat_g: int + +class Meal(BaseModel): + name: str + ingredients: List[Dict[str, Any]] # {item, qty, unit} + kcal: int + protein_g: int + carbs_g: int + fat_g: int + tags: List[str] = Field(default_factory=list) + instructions: Optional[str] = None + +class DayPlan(BaseModel): + day: str + meals: List[Meal] + totals: MacroTargets + +class WeekPlan(BaseModel): + days: List[DayPlan] + meta: Dict[str, Any] + +# ------------------------------ +# Tiny in-memory recipe β€œDB” +# (extend/replace with a real source) +# ------------------------------ +RECIPE_DB: List[Meal] = [ + Meal( + name="Greek Yogurt Parfait", + ingredients=[{"item":"nonfat greek yogurt","qty":200,"unit":"g"}, + {"item":"berries","qty":150,"unit":"g"}, + {"item":"granola","qty":30,"unit":"g"}, + {"item":"honey","qty":10,"unit":"g"}], + kcal=380, protein_g=30, carbs_g=52, fat_g=8, + tags=["vegetarian","breakfast","5-min","no-cook"] + ), + Meal( + name="Tofu Veggie Stir-Fry with Rice", + ingredients=[{"item":"firm tofu","qty":150,"unit":"g"}, + {"item":"mixed vegetables","qty":200,"unit":"g"}, + {"item":"soy sauce (low sodium)","qty":15,"unit":"ml"}, + {"item":"olive oil","qty":10,"unit":"ml"}, + {"item":"brown rice (cooked)","qty":200,"unit":"g"}], + kcal=650, protein_g=28, carbs_g=85, fat_g=20, + tags=["vegan","gluten-free","dinner","20-min","stovetop","soy"] + ), + Meal( + name="Chicken Quinoa Bowl", + ingredients=[{"item":"chicken breast","qty":140,"unit":"g"}, + {"item":"quinoa (cooked)","qty":185,"unit":"g"}, + {"item":"spinach","qty":60,"unit":"g"}, + {"item":"olive oil","qty":10,"unit":"ml"}, + {"item":"lemon","qty":0.5,"unit":"unit"}], + kcal=620, protein_g=45, carbs_g=55, fat_g=20, + tags=["gluten-free","dinner","25-min","high-protein","poultry"] + ), + Meal( + name="Lentil Soup + Wholegrain Bread", + ingredients=[{"item":"lentils (cooked)","qty":200,"unit":"g"}, + {"item":"vegetable broth","qty":400,"unit":"ml"}, + {"item":"carrot","qty":80,"unit":"g"}, + {"item":"celery","qty":60,"unit":"g"}, + {"item":"onion","qty":60,"unit":"g"}, + {"item":"wholegrain bread","qty":60,"unit":"g"}], + kcal=520, protein_g=25, carbs_g=78, fat_g=8, + tags=["vegan","lunch","30-min","budget"] + ), + Meal( + name="Salmon, Potatoes & Greens", + ingredients=[{"item":"salmon fillet","qty":150,"unit":"g"}, + {"item":"potatoes","qty":200,"unit":"g"}, + {"item":"broccoli","qty":150,"unit":"g"}, + {"item":"olive oil","qty":10,"unit":"ml"}], + kcal=680, protein_g=42, carbs_g=52, fat_g=30, + tags=["gluten-free","dinner","omega-3","fish"] + ), + Meal( + name="Cottage Cheese Bowl", + ingredients=[{"item":"low-fat cottage cheese","qty":200,"unit":"g"}, + {"item":"pineapple","qty":150,"unit":"g"}, + {"item":"chia seeds","qty":15,"unit":"g"}], + kcal=380, protein_g=32, carbs_g=35, fat_g=10, + tags=["vegetarian","snack","5-min","high-protein","dairy"] + ), +] + +# ------------------------------ +# Tool implementations +# ------------------------------ +ACTIVITY_FACTORS = { + "sedentary": 1.2, + "light": 1.375, + "moderate": 1.55, + "active": 1.725, + "very_active": 1.9 +} + +def mifflin_st_jeor(weight_kg: float, height_cm: float, age: int, sex: str) -> float: + # BMR (kcal/day) + if sex.lower().startswith("m"): + return 10*weight_kg + 6.25*height_cm - 5*age + 5 + else: + return 10*weight_kg + 6.25*height_cm - 5*age - 161 + +def compute_targets(profile: UserProfile) -> MacroTargets: + bmr = mifflin_st_jeor(profile.weight_kg, profile.height_cm, profile.age, profile.sex) + tdee = int(round(bmr * ACTIVITY_FACTORS.get(profile.activity_level, 1.2))) + # goal adjustment + if profile.goal == "lose": + target_kcal = max(1200, int(tdee - 400)) # conservative deficit + elif profile.goal == "gain": + target_kcal = int(tdee + 300) + else: + target_kcal = tdee + + # Macro split (modifiable): P 30%, C 40%, F 30% + protein_kcal = target_kcal * 0.30 + carbs_kcal = target_kcal * 0.40 + fat_kcal = target_kcal * 0.30 + protein_g = int(round(protein_kcal / 4)) + carbs_g = int(round(carbs_kcal / 4)) + fat_g = int(round(fat_kcal / 9)) + + return MacroTargets(tdee=tdee, target_kcal=target_kcal, + protein_g=protein_g, carbs_g=carbs_g, fat_g=fat_g) + +def _allowed(meal: Meal, profile: UserProfile) -> bool: + # dietary patterns/allergies/dislikes filters (simple; extend as needed) + diet = (profile.dietary_pattern or "").lower() + if diet == "vegetarian" and ("fish" in meal.tags or "poultry" in meal.tags): + return False + if diet == "vegan" and ("dairy" in meal.tags or "fish" in meal.tags or "poultry" in meal.tags): + return False + # allergies & dislikes + for a in profile.allergies: + if a and a.lower() in meal.name.lower(): return False + if any(a.lower() in (ing["item"]).lower() for ing in meal.ingredients): return False + if a.lower() in " ".join(meal.tags).lower(): return False + for d in profile.dislikes: + if d and d.lower() in meal.name.lower(): return False + if any(d.lower() in (ing["item"]).lower() for ing in meal.ingredients): return False + return True + +def meal_db_search(profile: UserProfile, tags: Optional[List[str]] = None) -> List[Meal]: + tags = tags or [] + out = [] + for m in RECIPE_DB: + if not _allowed(m, profile): + continue + if tags and not any(t in m.tags for t in tags): + continue + out.append(m) + return out or [] # may be empty; agent should handle + +def compose_meal_plan(profile: UserProfile, targets: MacroTargets) -> WeekPlan: + # naive heuristic: pick meals that roughly match per-meal macro budget + per_meal_kcal = targets.target_kcal / profile.daily_meals + days = [] + weekdays = ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"] + + # simple pools + breakfasts = meal_db_search(profile, tags=["breakfast","no-cook","5-min"]) + lunches = meal_db_search(profile, tags=["lunch","budget"]) + dinners = meal_db_search(profile, tags=["dinner","high-protein"]) + + # fallback to any allowed meals if pools too small + allowed_all = meal_db_search(profile) + if len(breakfasts) < 2: breakfasts = allowed_all + if len(lunches) < 2: lunches = allowed_all + if len(dinners) < 2: dinners = allowed_all + + for i, day in enumerate(weekdays): + day_meals = [] + for slot in range(profile.daily_meals): + pool = breakfasts if slot == 0 else (lunches if slot == 1 else dinners) + # pick the meal closest in kcal to per_meal_kcal + pick = min(pool, key=lambda m: abs(m.kcal - per_meal_kcal)) + day_meals.append(copy.deepcopy(pick)) + # compute totals + kcal = sum(m.kcal for m in day_meals) + p = sum(m.protein_g for m in day_meals) + c = sum(m.carbs_g for m in day_meals) + f = sum(m.fat_g for m in day_meals) + day_targets = MacroTargets(tdee=targets.tdee, target_kcal=int(round(kcal)), + protein_g=p, carbs_g=c, fat_g=f) + days.append(DayPlan(day=day, meals=day_meals, totals=day_targets)) + return WeekPlan(days=days, meta={"per_meal_target_kcal": int(round(per_meal_kcal))}) + +def grocery_list_from_plan(plan: WeekPlan) -> List[Dict[str, Any]]: + # aggregate identical ingredients + agg: Dict[Tuple[str,str], float] = {} + units: Dict[Tuple[str,str], str] = {} + for d in plan.days: + for m in d.meals: + for ing in m.ingredients: + key = (ing["item"].lower(), ing.get("unit","")) + agg[key] = agg.get(key, 0) + float(ing.get("qty", 0)) + units[key] = ing.get("unit","") + items = [] + for (item, unit), qty in sorted(agg.items()): + items.append({"item": item, "qty": round(qty, 2), "unit": unit}) + return items + +def swap_meal(plan: WeekPlan, day: str, meal_index: int, profile: UserProfile) -> WeekPlan: + # replace one meal by closest-kcal allowed alternative that isn't the same + day_idx = next((i for i,d in enumerate(plan.days) if d.day.lower().startswith(day[:3].lower())), None) + if day_idx is None: return plan + current_meal = plan.days[day_idx].meals[meal_index] + candidates = [m for m in meal_db_search(profile) if m.name != current_meal.name] + if not candidates: return plan + pick = min(candidates, key=lambda m: abs(m.kcal - current_meal.kcal)) + plan.days[day_idx].meals[meal_index] = copy.deepcopy(pick) + # recalc day totals + d = plan.days[day_idx] + kcal = sum(m.kcal for m in d.meals) + p = sum(m.protein_g for m in d.meals) + c = sum(m.carbs_g for m in d.meals) + f = sum(m.fat_g for m in d.meals) + d.totals = MacroTargets(tdee=d.totals.tdee, target_kcal=kcal, protein_g=p, carbs_g=c, fat_g=f) + return plan + +# ------------------------------ +# Agent (LLM + tools) +# ------------------------------ +SYS_PROMPT = """You are FitnessPlanner, an agentic planner that: +- Respects dietary patterns, allergies, dislikes, budget, time limits. +- Uses tools to compute targets, assemble a 7-day plan, produce a grocery list, and swap meals on request. +- If a request is unsafe (extreme deficits, medical conditions), warn and suggest professional guidance. +- Keep responses concise and structured (headings + bullet lists).""" + +# Tool registry for function-calling +def get_tools_schema(): + return [ + { + "type": "function", + "function": { + "name": "calc_calories_and_macros", + "description": "Compute TDEE and macro targets from the user's profile.", + "parameters": { + "type":"object", + "properties": {"profile":{"type":"object"}}, + "required":["profile"] + } + } + }, + { + "type": "function", + "function": { + "name": "compose_meal_plan", + "description": "Create a 7-day meal plan matching targets and constraints.", + "parameters": { + "type":"object", + "properties": { + "profile":{"type":"object"}, + "targets":{"type":"object"} + }, + "required":["profile","targets"] + } + } + }, + { + "type": "function", + "function": { + "name": "grocery_list_from_plan", + "description": "Make a consolidated grocery list from a week plan.", + "parameters": { + "type":"object", + "properties": {"plan":{"type":"object"}}, + "required":["plan"] + } + } + }, + { + "type": "function", + "function": { + "name": "swap_meal", + "description": "Swap a single meal in the plan while keeping macros reasonable.", + "parameters": { + "type":"object", + "properties": { + "plan":{"type":"object"}, + "day":{"type":"string"}, + "meal_index":{"type":"integer","description":"0=breakfast,1=lunch,2=dinner"}, + "profile":{"type":"object"} + }, + "required":["plan","day","meal_index","profile"] + } + } + } + ] + +class FitnessPlannerAgent: + def __init__(self, model: Optional[str] = None): + self.client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) + self.model = model or os.getenv("OPENAI_MODEL", "gpt-4o-mini") + self.plan_cache: Optional[WeekPlan] = None + self.targets_cache: Optional[MacroTargets] = None + + # Tool dispatch + def _call_tool(self, name: str, args: Dict[str, Any]) -> str: + if name == "calc_calories_and_macros": + profile = UserProfile(**args["profile"]) + targets = compute_targets(profile) + self.targets_cache = targets + return targets.model_dump_json() + elif name == "compose_meal_plan": + profile = UserProfile(**args["profile"]) + targets = MacroTargets(**args["targets"]) + plan = compose_meal_plan(profile, targets) + self.plan_cache = plan + return plan.model_dump_json() + elif name == "grocery_list_from_plan": + plan = WeekPlan(**args["plan"]) + items = grocery_list_from_plan(plan) + return json.dumps(items) + elif name == "swap_meal": + plan = WeekPlan(**args["plan"]) + profile = UserProfile(**args["profile"]) + day = args["day"] + idx = args["meal_index"] + new_plan = swap_meal(plan, day, idx, profile) + self.plan_cache = new_plan + return new_plan.model_dump_json() + else: + return json.dumps({"error":"unknown tool"}) + + def chat(self, user_message: str, profile: Optional[UserProfile] = None) -> str: + messages = [{"role":"system","content":SYS_PROMPT}] + if profile: + messages.append({"role":"user","content":f"User profile: {profile.model_dump_json()}"} ) + messages.append({"role":"user","content":user_message}) + + # First call + resp = self.client.chat.completions.create( + model=self.model, + messages=messages, + tools=get_tools_schema(), + tool_choice="auto", + temperature=0.3 + ) + + # Handle tool calls (simple, single-step or brief multi-step) + messages_llm = messages + [{"role":"assistant","content":resp.choices[0].message.content or "", + "tool_calls":resp.choices[0].message.tool_calls}] + if resp.choices[0].message.tool_calls: + for tc in resp.choices[0].message.tool_calls: + name = tc.function.name + args = json.loads(tc.function.arguments or "{}") + out = self._call_tool(name, args) + messages_llm.append({ + "role":"tool", + "tool_call_id":tc.id, + "name":name, + "content":out + }) + + # Finalization + resp2 = self.client.chat.completions.create( + model=self.model, + messages=messages_llm, + temperature=0.2 + ) + return resp2.choices[0].message.content + + return resp.choices[0].message.content + +# ------------------------------ +# Quick CLI demo +# ------------------------------ +if __name__ == "__main__": + profile = UserProfile( + sex="female", age=45, height_cm=152, weight_kg=62, + activity_level="light", goal="maintain", + dietary_pattern="vegetarian", allergies=[], dislikes=[], + daily_meals=3, cuisine_prefs=["mediterranean"], time_per_meal_minutes=25, budget_level="medium" + ) + agent = FitnessPlannerAgent() + print(agent.chat("Create my 7-day plan and grocery list.", profile)) diff --git a/community-contributions/fitness-nutrition-planner-agent/app.py b/community-contributions/fitness-nutrition-planner-agent/app.py new file mode 100644 index 0000000..a1f1102 --- /dev/null +++ b/community-contributions/fitness-nutrition-planner-agent/app.py @@ -0,0 +1,75 @@ + +# app.py +import json +import streamlit as st +from agent import FitnessPlannerAgent, UserProfile, WeekPlan + +st.set_page_config(page_title="Fitness & Nutrition Planner Agent", layout="wide") + +st.title("πŸ‹οΈ Fitness & Nutrition Planner Agent") + +with st.sidebar: + st.header("Your Profile") + sex = st.selectbox("Sex", ["female","male"]) + age = st.number_input("Age", 18, 90, 45) + height_cm = st.number_input("Height (cm)", 120, 220, 152) + weight_kg = st.number_input("Weight (kg)", 35.0, 200.0, 62.0) + activity_level = st.selectbox("Activity Level", ["sedentary","light","moderate","active","very_active"], index=1) + goal = st.selectbox("Goal", ["lose","maintain","gain"], index=1) + dietary_pattern = st.selectbox("Dietary Pattern", ["none","vegetarian","vegan","halal","kosher"], index=1) + if dietary_pattern == "none": dietary_pattern = None + allergies = st.text_input("Allergies (comma-separated)", "") + dislikes = st.text_input("Dislikes (comma-separated)", "") + daily_meals = st.slider("Meals per day", 2, 5, 3) + time_per_meal_minutes = st.slider("Time per meal (min)", 5, 90, 25) + budget_level = st.selectbox("Budget", ["medium","low","high"], index=0) + cuisine_prefs = st.text_input("Cuisine prefs (comma-separated)", "mediterranean") + + build_btn = st.button("Generate 7-Day Plan") + +agent = FitnessPlannerAgent() + +if build_btn: + profile = UserProfile( + sex=sex, age=int(age), height_cm=float(height_cm), weight_kg=float(weight_kg), + activity_level=activity_level, goal=goal, dietary_pattern=dietary_pattern, + allergies=[a.strip() for a in allergies.split(",") if a.strip()], + dislikes=[d.strip() for d in dislikes.split(",") if d.strip()], + daily_meals=int(daily_meals), cuisine_prefs=[c.strip() for c in cuisine_prefs.split(",") if c.strip()], + time_per_meal_minutes=int(time_per_meal_minutes), budget_level=budget_level + ) + st.session_state["profile_json"] = profile.model_dump_json() + with st.spinner("Planning your week..."): + result = agent.chat("Create my 7-day plan and grocery list.", profile) + st.session_state["last_response"] = result + +if "last_response" in st.session_state: + st.subheader("Plan & Groceries") + st.markdown(st.session_state["last_response"]) + +st.divider() +st.subheader("Tweaks") +col1, col2, col3 = st.columns(3) +with col1: + day = st.selectbox("Day to change", ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]) +with col2: + meal_index = st.selectbox("Meal slot", ["Breakfast (0)","Lunch (1)","Dinner (2)"]) + meal_index = int(meal_index[-2]) # 0/1/2 +with col3: + swap_btn = st.button("Swap Meal") + +if swap_btn and agent.plan_cache: + profile_json = st.session_state.get("profile_json") + if not profile_json: + st.warning("Please generate a plan first.") + else: + new_plan_json = agent._call_tool("swap_meal", { + "plan": agent.plan_cache.model_dump(), + "day": day, + "meal_index": meal_index, + "profile": json.loads(profile_json) + }) + agent.plan_cache = WeekPlan(**json.loads(new_plan_json)) + summary = agent.chat(f"Update summary for {day}: show the swapped meal and new day totals.") + st.session_state["last_response"] = summary + st.markdown(summary) diff --git a/community-contributions/fitness-nutrition-planner-agent/demo_output.md b/community-contributions/fitness-nutrition-planner-agent/demo_output.md new file mode 100644 index 0000000..841fb1e --- /dev/null +++ b/community-contributions/fitness-nutrition-planner-agent/demo_output.md @@ -0,0 +1,84 @@ + +# Demo Output (Sample Profile) + +**Profile**: female, 45, 152 cm, 62 kg, activity: light, goal: maintain, diet: vegetarian + +## Targets +- TDEE β‰ˆ **1680 kcal/day** +- Macros (30/40/30): **Protein 126 g**, **Carbs 168 g**, **Fat 56 g** + +> These are estimates using Mifflin–St Jeor and a light activity factor. Not medical advice. + +--- + +## Example 7-Day Plan (Breakfast / Lunch / Dinner) + +**Mon** +- Greek Yogurt Parfait (380 kcal, 30P/52C/8F) +- Lentil Soup + Wholegrain Bread (520 kcal, 25P/78C/8F) +- Tofu Veggie Stir-Fry with Rice (650 kcal, 28P/85C/20F) +- **Totals** β‰ˆ 1550 kcal, 83P, 215C, 36F + +**Tue** +- Cottage Cheese Bowl (380 kcal, 32P/35C/10F) +- Lentil Soup + Wholegrain Bread (520 kcal, 25P/78C/8F) +- Tofu Veggie Stir-Fry with Rice (650 kcal, 28P/85C/20F) +- **Totals** β‰ˆ 1550 kcal, 85P, 198C, 38F + +**Wed** +- Greek Yogurt Parfait +- Lentil Soup + Wholegrain Bread +- Tofu Veggie Stir-Fry with Rice +- **Totals** β‰ˆ 1550 kcal + +**Thu** +- Cottage Cheese Bowl +- Lentil Soup + Wholegrain Bread +- Tofu Veggie Stir-Fry with Rice +- **Totals** β‰ˆ 1550 kcal + +**Fri** +- Greek Yogurt Parfait +- Lentil Soup + Wholegrain Bread +- Tofu Veggie Stir-Fry with Rice +- **Totals** β‰ˆ 1550 kcal + +**Sat** +- Cottage Cheese Bowl +- Lentil Soup + Wholegrain Bread +- Tofu Veggie Stir-Fry with Rice +- **Totals** β‰ˆ 1550 kcal + +**Sun** +- Greek Yogurt Parfait +- Lentil Soup + Wholegrain Bread +- Tofu Veggie Stir-Fry with Rice +- **Totals** β‰ˆ 1550 kcal + +> Notes: The demo DB is intentionally small. In practice, plug in a larger vegetarian recipe set for more variety. Add snacks if you'd like to reach ~1680 kcal/day. + +--- + +## Grocery List (aggregated, approx for 7 days) + +- nonfat greek yogurt β€” **1400 g** +- berries β€” **1050 g** +- granola β€” **210 g** +- honey β€” **70 g** +- lentils (cooked) β€” **1400 g** +- vegetable broth β€” **2800 ml** +- carrot β€” **560 g** +- celery β€” **420 g** +- onion β€” **420 g** +- wholegrain bread β€” **420 g** +- firm tofu β€” **1050 g** +- mixed vegetables β€” **1400 g** +- soy sauce (low sodium) β€” **105 ml** +- olive oil β€” **140 ml** +- brown rice (cooked) β€” **1400 g** +- low-fat cottage cheese β€” **600 g** +- pineapple β€” **450 g** +- chia seeds β€” **45 g** + +**Tip:** Use the app’s *Swap Meal* to replace any item (e.g., swap Wed dinner). + diff --git a/community-contributions/fitness-nutrition-planner-agent/sample_profile.json b/community-contributions/fitness-nutrition-planner-agent/sample_profile.json new file mode 100644 index 0000000..0f54a9d --- /dev/null +++ b/community-contributions/fitness-nutrition-planner-agent/sample_profile.json @@ -0,0 +1,17 @@ +{ + "sex": "female", + "age": 45, + "height_cm": 152, + "weight_kg": 62, + "activity_level": "light", + "goal": "maintain", + "dietary_pattern": "vegetarian", + "allergies": [], + "dislikes": [], + "daily_meals": 3, + "cuisine_prefs": [ + "mediterranean" + ], + "time_per_meal_minutes": 25, + "budget_level": "medium" +} \ No newline at end of file